Jelajahi Sumber

Remove unused exchange program and bench client (#18463)

Justin Starry 4 tahun lalu
induk
melakukan
cfece66403

+ 0 - 48
Cargo.lock

@@ -4295,35 +4295,6 @@ dependencies = [
  "tokio-stream",
  "tokio-stream",
 ]
 ]
 
 
-[[package]]
-name = "solana-bench-exchange"
-version = "1.8.0"
-dependencies = [
- "clap 2.33.3",
- "itertools 0.10.1",
- "log 0.4.14",
- "num-derive",
- "num-traits",
- "rand 0.7.3",
- "rayon",
- "serde_json",
- "serde_yaml",
- "solana-clap-utils",
- "solana-client",
- "solana-core",
- "solana-exchange-program",
- "solana-faucet",
- "solana-genesis",
- "solana-gossip",
- "solana-local-cluster",
- "solana-logger 1.8.0",
- "solana-metrics",
- "solana-net-utils",
- "solana-runtime",
- "solana-sdk",
- "solana-version",
-]
-
 [[package]]
 [[package]]
 name = "solana-bench-streamer"
 name = "solana-bench-streamer"
 version = "1.8.0"
 version = "1.8.0"
@@ -4685,23 +4656,6 @@ dependencies = [
  "tar",
  "tar",
 ]
 ]
 
 
-[[package]]
-name = "solana-exchange-program"
-version = "1.8.0"
-dependencies = [
- "bincode",
- "log 0.4.14",
- "num-derive",
- "num-traits",
- "serde",
- "serde_derive",
- "solana-logger 1.8.0",
- "solana-metrics",
- "solana-runtime",
- "solana-sdk",
- "thiserror",
-]
-
 [[package]]
 [[package]]
 name = "solana-failure-program"
 name = "solana-failure-program"
 version = "1.8.0"
 version = "1.8.0"
@@ -4803,7 +4757,6 @@ dependencies = [
  "serde_yaml",
  "serde_yaml",
  "solana-clap-utils",
  "solana-clap-utils",
  "solana-cli-config",
  "solana-cli-config",
- "solana-exchange-program",
  "solana-ledger",
  "solana-ledger",
  "solana-logger 1.8.0",
  "solana-logger 1.8.0",
  "solana-runtime",
  "solana-runtime",
@@ -5022,7 +4975,6 @@ dependencies = [
  "solana-config-program",
  "solana-config-program",
  "solana-core",
  "solana-core",
  "solana-download-utils",
  "solana-download-utils",
- "solana-exchange-program",
  "solana-faucet",
  "solana-faucet",
  "solana-gossip",
  "solana-gossip",
  "solana-ledger",
  "solana-ledger",

+ 0 - 2
Cargo.toml

@@ -1,7 +1,6 @@
 [workspace]
 [workspace]
 members = [
 members = [
     "accounts-cluster-bench",
     "accounts-cluster-bench",
-    "bench-exchange",
     "bench-streamer",
     "bench-streamer",
     "bench-tps",
     "bench-tps",
     "accounts-bench",
     "accounts-bench",
@@ -44,7 +43,6 @@ members = [
     "program-test",
     "program-test",
     "programs/bpf_loader",
     "programs/bpf_loader",
     "programs/config",
     "programs/config",
-    "programs/exchange",
     "programs/failure",
     "programs/failure",
     "programs/noop",
     "programs/noop",
     "programs/ownable",
     "programs/ownable",

+ 0 - 4
bench-exchange/.gitignore

@@ -1,4 +0,0 @@
-/target/
-/config/
-/config-local/
-/farf/

+ 0 - 39
bench-exchange/Cargo.toml

@@ -1,39 +0,0 @@
-[package]
-authors = ["Solana Maintainers <maintainers@solana.foundation>"]
-edition = "2018"
-name = "solana-bench-exchange"
-version = "1.8.0"
-repository = "https://github.com/solana-labs/solana"
-license = "Apache-2.0"
-homepage = "https://solana.com/"
-publish = false
-
-[dependencies]
-clap = "2.33.1"
-itertools = "0.10.1"
-log = "0.4.14"
-num-derive = "0.3"
-num-traits = "0.2"
-rand = "0.7.0"
-rayon = "1.5.1"
-serde_json = "1.0.64"
-serde_yaml = "0.8.17"
-solana-clap-utils = { path = "../clap-utils", version = "=1.8.0" }
-solana-core = { path = "../core", version = "=1.8.0" }
-solana-genesis = { path = "../genesis", version = "=1.8.0" }
-solana-client = { path = "../client", version = "=1.8.0" }
-solana-exchange-program = { path = "../programs/exchange", version = "=1.8.0" }
-solana-faucet = { path = "../faucet", version = "=1.8.0" }
-solana-gossip = { path = "../gossip", version = "=1.8.0" }
-solana-logger = { path = "../logger", version = "=1.8.0" }
-solana-metrics = { path = "../metrics", version = "=1.8.0" }
-solana-net-utils = { path = "../net-utils", version = "=1.8.0" }
-solana-runtime = { path = "../runtime", version = "=1.8.0" }
-solana-sdk = { path = "../sdk", version = "=1.8.0" }
-solana-version = { path = "../version", version = "=1.8.0" }
-
-[dev-dependencies]
-solana-local-cluster = { path = "../local-cluster", version = "=1.8.0" }
-
-[package.metadata.docs.rs]
-targets = ["x86_64-unknown-linux-gnu"]

+ 0 - 479
bench-exchange/README.md

@@ -1,479 +0,0 @@
-# token-exchange
-Solana Token Exchange Bench
-
-If you can't wait; jump to [Running the exchange](#Running-the-exchange) to
-learn how to start and interact with the exchange.
-
-### Table of Contents
-[Overview](#Overview)<br>
-[Premise](#Premise)<br>
-[Exchange startup](#Exchange-startup)<br>
-[Order Requests](#Trade-requests)<br>
-[Order Cancellations](#Trade-cancellations)<br>
-[Trade swap](#Trade-swap)<br>
-[Exchange program operations](#Exchange-program-operations)<br>
-[Quotes and OHLCV](#Quotes-and-OHLCV)<br>
-[Investor strategies](#Investor-strategies)<br>
-[Running the exchange](#Running-the-exchange)<br>
-
-## Overview
-
-An exchange is a marketplace where one asset can be traded for another.  This
-demo demonstrates one way to host an exchange on the Solana blockchain by
-emulating a currency exchange.
-
-The assets are virtual tokens held by investors who may post order requests to
-the exchange.  A Matcher monitors the exchange and posts swap requests for
-matching orders.  All the transactions can execute concurrently.
-
-## Premise
-
-- Exchange
-  -  An exchange is a marketplace where one asset can be traded for another.
-     The exchange in this demo is the on-chain program that implements the
-     tokens and the policies for trading those tokens.
-- Token
-  - A virtual asset that can be owned, traded, and holds virtual intrinsic value
-    compared to other assets.  There are four types of tokens in this demo, A,
-    B, C, D.  Each one may be traded for another.
-- Token account
-  - An account owned by the exchange that holds a quantity of one type of token.
-- Account request
-  - A request to create a token account
-- Token request
-  - A request to deposit tokens of a particular type into a token account.
-- Asset pair
-  - A struct with fields Base and Quote, representing the two assets which make up a 
-    trading pair,  which themselves are Tokens. The Base or 'primary' asset is the
-    numerator and the Quote is the denominator for pricing purposes.
-- Order side
-  - Describes which side of the market an investor wants to place a trade on. Options
-    are "Bid" or "Ask", where a bid represents an offer to purchase the Base asset of
-    the AssetPair for a sum of the Quote Asset and an Ask is an offer to sell Base asset
-    for the Quote asset.
-- Price ratio
-  -  An expression of the relative prices of two tokens. Calculated with the Base
-     Asset as the numerator and the Quote Asset as the denominator. Ratios are 
-     represented as fixed point numbers.  The fixed point scaler is defined in
-     [exchange_state.rs](https://github.com/solana-labs/solana/blob/c2fdd1362a029dcf89c8907c562d2079d977df11/programs/exchange_api/src/exchange_state.rs#L7)
-- Order request
-  - A Solana transaction sent by a trader to the exchange to submit an order. 
-    Order requests are made up of the token pair, the order side (bid or ask),
-    quantity of the primary token, the price ratio, and the two token accounts
-    to be credited/deducted.  An example trade request looks like "T AB 5 2" 
-    which reads "Exchange 5 A tokens to B tokens at a price ratio of 1:2"  A fulfilled trade would result in 5 A tokens
-    deducted and 10 B tokens credited to the trade initiator's token accounts.
-    Successful order requests result in an order.
-- Order
-  - The result of a successful order request.  orders are stored in
-    accounts owned by the submitter of the order request.  They can only be
-    canceled by their owner but can be used by anyone in a trade swap.  They
-    contain the same information as the order request.
-- Price spread
-  - The difference between the two matching orders. The spread is the
-    profit of the Matcher initiating the swap request.
-- Match requirements
-  - Policies that result in a successful trade swap.
-- Match request
-  - A request to fill two complementary orders (bid/ask), resulting if successful,
-    in a trade being created.
-- Trade
-  - A successful trade is created from two matching orders that meet
-    swap requirements which are submitted in a Match Request by a Matcher and
-    executed by the exchange. A trade may not wholly satisfy one or both of the
-    orders in which case the orders are adjusted appropriately. Upon execution,
-    tokens are distributed to the traders' accounts and any overlap or 
-    "negative spread" between orders is deposited into the Matcher's profit
-    account.  All successful trades are recorded in the data of a new solana 
-    account for posterity.
-- Investor
-  - Individual investors who hold a number of tokens and wish to trade them on
-    the exchange.  Investors operate as Solana thin clients who own a set of
-    accounts containing tokens and/or order requests.  Investors post
-    transactions to the exchange in order to request tokens and post or cancel
-    order requests.
-- Matcher
-  - An agent who facilitates trading between investors.  Matchers operate as
-    Solana thin clients who monitor all the orders looking for a trade
-    match.  Once found, the Matcher issues a swap request to the exchange.
-    Matchers are the engine of the exchange and are rewarded for their efforts by
-    accumulating the price spreads of the swaps they initiate.  Matchers also
-    provide current bid/ask price and OHLCV (Open, High, Low, Close, Volume)
-    information on demand via a public network port.
-- Transaction fees
-  - Solana transaction fees are paid for by the transaction submitters who are
-    the Investors and Matchers.
-
-## Exchange startup
-
-The exchange is up and running when it reaches a state where it can take
-investors' trades and Matchers' match requests.  To achieve this state the
-following must occur in order:
-
-- Start the Solana blockchain
-- Start the  thin-client
-- The Matcher subscribes to change notifications for all the accounts owned by
-  the exchange program id.  The subscription is managed via Solana's JSON RPC
-  interface.
-- The Matcher starts responding to queries for bid/ask price and OHLCV
-
-The Matcher responding successfully to price and OHLCV requests is the signal to
-the investors that trades submitted after that point will be analyzed.  <!--This
-is not ideal, and instead investors should be able to submit trades at any time,
-and the Matcher could come and go without missing a trade.  One way to achieve
-this is for the Matcher to read the current state of all accounts looking for all
-open orders.-->
-
-Investors will initially query the exchange to discover their current balance
-for each type of token.  If the investor does not already have an account for
-each type of token, they will submit account requests.  Matcher as well will
-request accounts to hold the tokens they earn by initiating trade swaps.
-
-```rust
-/// Supported token types
-pub enum Token {
-    A,
-    B,
-    C,
-    D,
-}
-
-/// Supported token pairs
-pub enum TokenPair {
-    AB,
-    AC,
-    AD,
-    BC,
-    BD,
-    CD,
-}
-
-pub enum ExchangeInstruction {
-    /// New token account
-    /// key 0 - Signer
-    /// key 1 - New token account
-    AccountRequest,
-}
-
-/// Token accounts are populated with this structure
-pub struct TokenAccountInfo {
-    /// Investor who owns this account
-    pub owner: Pubkey,
-    /// Current number of tokens this account holds
-    pub tokens: Tokens,
-}
-```
-
-For this demo investors or Matcher can request more tokens from the exchange at
-any time by submitting token requests. In non-demos, an exchange of this type
-would provide another way to exchange a 3rd party asset into tokens.
-
-To request tokens, investors submit transfer requests:
-
-```rust
-pub enum ExchangeInstruction {
-    /// Transfer tokens between two accounts
-    /// key 0 - Account to transfer tokens to
-    /// key 1 - Account to transfer tokens from.  This can be the exchange program itself,
-    ///         the exchange has a limitless number of tokens it can transfer.
-    TransferRequest(Token, u64),
-}
-```
-
-## Order Requests
-
-When an investor decides to exchange a token of one type for another, they
-submit a transaction to the Solana Blockchain containing an order request, which,
-if successful, is turned into an order.  orders do not expire but are
-cancellable. <!-- orders should have a timestamp to enable trade
-expiration -->  When an order is created, tokens are deducted from a token
-account and the order acts as an escrow.  The tokens are held until the
-order is fulfilled or canceled. If the direction is `To`, then the number
-of `tokens` are deducted from the primary account, if `From` then `tokens`
-multiplied by `price` are deducted from the secondary account.  orders are
-no longer valid when the number of `tokens` goes to zero, at which point they
-can no longer be used. <!-- Could support refilling orders, so order
-accounts are refilled rather than accumulating -->
-
-```rust
-/// Direction of the exchange between two tokens in a pair
-pub enum Direction {
-    /// Trade first token type (primary) in the pair 'To' the second
-    To,
-    /// Trade first token type in the pair 'From' the second (secondary)
-    From,
-}
-
-pub struct OrderRequestInfo {
-    /// Direction of trade
-    pub direction: Direction,
-
-    /// Token pair to trade
-    pub pair: TokenPair,
-
-    /// Number of tokens to exchange; refers to the primary or the secondary depending on the direction
-    pub tokens: u64,
-
-    /// The price ratio the primary price over the secondary price.  The primary price is fixed
-    /// and equal to the variable `SCALER`.
-    pub price: u64,
-
-    /// Token account to deposit tokens on successful swap
-    pub dst_account: Pubkey,
-}
-
-pub enum ExchangeInstruction {
-    /// order request
-    /// key 0 - Signer
-    /// key 1 - Account in which to record the swap
-    /// key 2 - Token account associated with this trade
-    TradeRequest(TradeRequestInfo),
-}
-
-/// Trade accounts are populated with this structure
-pub struct TradeOrderInfo {
-    /// Owner of the order
-    pub owner: Pubkey,
-    /// Direction of the exchange
-    pub direction: Direction,
-    /// Token pair indicating two tokens to exchange, first is primary
-    pub pair: TokenPair,
-    /// Number of tokens to exchange; primary or secondary depending on direction
-    pub tokens: u64,
-    /// Scaled price of the secondary token given the primary is equal to the scale value
-    /// If scale is 1 and price is 2 then ratio is 1:2 or 1 primary token for 2 secondary tokens
-    pub price: u64,
-    /// account which the tokens were source from.  The trade account holds the tokens in escrow
-    /// until either one or more part of a swap or the trade is canceled.
-    pub src_account: Pubkey,
-    /// account which the tokens the tokens will be deposited into on a successful trade
-    pub dst_account: Pubkey,
-}
-```
-
-## Order cancellations
-
-An investor may cancel a trade at anytime, but only trades they own.  If the
-cancellation is successful, any tokens held in escrow are returned to the
-account from which they came.
-
-```rust
-pub enum ExchangeInstruction {
-    /// order cancellation
-    /// key 0 - Signer
-    /// key 1 -order to cancel
-    TradeCancellation,
-}
-```
-
-## Trade swaps
-
-The Matcher is monitoring the accounts assigned to the exchange program and
-building a trade-order table.  The order table is used to identify
-matching orders which could be fulfilled.  When a match is found the
-Matcher should issue a swap request.  Swap requests may not satisfy the entirety
-of either order, but the exchange will greedily fulfill it.  Any leftover tokens
-in either account will keep the order valid for further swap requests in
-the future.
-
-Matching orders are defined by the following swap requirements:
-
-- Opposite polarity (one `To` and one `From`)
-- Operate on the same token pair
-- The price ratio of the `From` order is greater than or equal to the `To` order
-- There are sufficient tokens to perform the trade
-
-Orders can be written in the following format:
-
-`investor direction pair quantity price-ratio`
-
-For example:
-
-- `1 T AB 2 1`
-  - Investor 1 wishes to exchange 2 A tokens to B tokens at a ratio of 1 A to 1
-    B
-- `2 F AC 6 1.2`
-  - Investor 2 wishes to exchange A tokens from 6 B tokens at a ratio of 1 A
-    from 1.2 B
-
-An order table could look something like the following. Notice how the columns
-are sorted low to high and high to low, respectively.  Prices are dramatic and
-whole for clarity.
-
-|Row| To          | From       |
-|---|-------------|------------|
-| 1 | 1 T AB 2 4  | 2 F AB 2 8 |
-| 2 | 1 T AB 1 4  | 2 F AB 2 8 |
-| 3 | 1 T AB 6 6  | 2 F AB 2 7 |
-| 4 | 1 T AB 2 8  | 2 F AB 3 6 |
-| 5 | 1 T AB 2 10 | 2 F AB 1 5 |
-
-As part of a successful swap request, the exchange will credit tokens to the
-Matcher's account equal to the difference in the price ratios or the two orders.
-These tokens are considered the Matcher's profit for initiating the trade.
-
-The Matcher would initiate the following swap on the order table above:
-
-  - Row 1, To:   Investor 1 trades 2 A tokens to 8 B tokens
-  - Row 1, From: Investor 2 trades 2 A tokens from 8 B tokens
-  - Matcher takes 8 B tokens as profit
-
-Both row 1 trades are fully realized, table becomes:
-
-|Row| To          | From       |
-|---|-------------|------------|
-| 1 | 1 T AB 1 4  | 2 F AB 2 8 |
-| 2 | 1 T AB 6 6  | 2 F AB 2 7 |
-| 3 | 1 T AB 2 8  | 2 F AB 3 6 |
-| 4 | 1 T AB 2 10 | 2 F AB 1 5 |
-
-The Matcher would initiate the following swap:
-
-  - Row 1, To:   Investor 1 trades 1 A token to 4 B tokens
-  - Row 1, From: Investor 2 trades 1 A token from 4 B tokens
-  - Matcher takes 4 B tokens as profit
-
-Row 1 From is not fully realized, table becomes:
-
-|Row| To          | From       |
-|---|-------------|------------|
-| 1 | 1 T AB 6 6  | 2 F AB 1 8 |
-| 2 | 1 T AB 2 8  | 2 F AB 2 7 |
-| 3 | 1 T AB 2 10 | 2 F AB 3 6 |
-| 4 |             | 2 F AB 1 5 |
-
-The Matcher would initiate the following swap:
-
-  - Row 1, To:   Investor 1 trades 1 A token to 6 B tokens
-  - Row 1, From: Investor 2 trades 1 A token from 6 B tokens
-  - Matcher takes 2 B tokens as profit
-
-Row 1 To is now fully realized, table becomes:
-
-|Row| To          | From       |
-|---|-------------|------------|
-| 1 | 1 T AB 5 6  | 2 F AB 2 7 |
-| 2 | 1 T AB 2 8  | 2 F AB 3 5 |
-| 3 | 1 T AB 2 10 | 2 F AB 1 5 |
-
-The Matcher would initiate the following last swap:
-
-  - Row 1, To:   Investor 1 trades 2 A token to 12 B tokens
-  - Row 1, From: Investor 2 trades 2 A token from 12 B tokens
-  - Matcher takes 2 B tokens as profit
-
-Table becomes:
-
-|Row| To          | From       |
-|---|-------------|------------|
-| 1 | 1 T AB 3 6  | 2 F AB 3 5 |
-| 2 | 1 T AB 2 8  | 2 F AB 1 5 |
-| 3 | 1 T AB 2 10 |            |
-
-At this point the lowest To's price is larger than the largest From's price so
-no more swaps would be initiated until new orders came in.
-
-```rust
-pub enum ExchangeInstruction {
-    /// Trade swap request
-    /// key 0 - Signer
-    /// key 1 - Account in which to record the swap
-    /// key 2 - 'To' order
-    /// key 3 - `From` order
-    /// key 4 - Token account associated with the To Trade
-    /// key 5 - Token account associated with From trade
-    /// key 6 - Token account in which to deposit the Matcher profit from the swap.
-    SwapRequest,
-}
-
-/// Swap accounts are populated with this structure
-pub struct TradeSwapInfo {
-    /// Pair swapped
-    pub pair: TokenPair,
-    /// `To` order
-    pub to_trade_order: Pubkey,
-    /// `From` order
-    pub from_trade_order: Pubkey,
-    /// Number of primary tokens exchanged
-    pub primary_tokens: u64,
-    /// Price the primary tokens were exchanged for
-    pub primary_price: u64,
-    /// Number of secondary tokens exchanged
-    pub secondary_tokens: u64,
-    /// Price the secondary tokens were exchanged for
-    pub secondary_price: u64,
-}
-```
-
-## Exchange program operations
-
-Putting all the commands together from above, the following operations will be
-supported by the on-chain exchange program:
-
-```rust
-pub enum ExchangeInstruction {
-    /// New token account
-    /// key 0 - Signer
-    /// key 1 - New token account
-    AccountRequest,
-
-    /// Transfer tokens between two accounts
-    /// key 0 - Account to transfer tokens to
-    /// key 1 - Account to transfer tokens from.  This can be the exchange program itself,
-    ///         the exchange has a limitless number of tokens it can transfer.
-    TransferRequest(Token, u64),
-
-    /// order request
-    /// key 0 - Signer
-    /// key 1 - Account in which to record the swap
-    /// key 2 - Token account associated with this trade
-    TradeRequest(TradeRequestInfo),
-
-    /// order cancellation
-    /// key 0 - Signer
-    /// key 1 -order to cancel
-    TradeCancellation,
-
-    /// Trade swap request
-    /// key 0 - Signer
-    /// key 1 - Account in which to record the swap
-    /// key 2 - 'To' order
-    /// key 3 - `From` order
-    /// key 4 - Token account associated with the To Trade
-    /// key 5 - Token account associated with From trade
-    /// key 6 - Token account in which to deposit the Matcher profit from the swap.
-    SwapRequest,
-}
-```
-
-## Quotes and OHLCV
-
-The Matcher will provide current bid/ask price quotes based on trade actively and
-also provide OHLCV based on some time window.  The details of how the bid/ask
-price quotes are calculated are yet to be decided.
-
-## Investor strategies
-
-To make a compelling demo, the investors needs to provide interesting trade
-behavior.  Something as simple as a randomly twiddled baseline would be a
-minimum starting point.
-
-## Running the exchange
-
-The exchange bench posts trades and swaps matches as fast as it can.  
-
-You might want to bump the duration up
-to 60 seconds and the batch size to 1000 for better numbers.  You can modify those
-in client_demo/src/demo.rs::test_exchange_local_cluster.
-
-The following command runs the bench:
-
-```bash
-$ RUST_LOG=solana_bench_exchange=info cargo test --release -- --nocapture test_exchange_local_cluster
-```
-
-To also see the cluster messages:
-
-```bash
-$ RUST_LOG=solana_bench_exchange=info,solana=info cargo test --release -- --nocapture test_exchange_local_cluster
-```

+ 0 - 1028
bench-exchange/src/bench.rs

@@ -1,1028 +0,0 @@
-#![allow(clippy::useless_attribute)]
-#![allow(clippy::integer_arithmetic)]
-
-use crate::order_book::*;
-use itertools::izip;
-use log::*;
-use rand::{thread_rng, Rng};
-use rayon::prelude::*;
-use solana_client::perf_utils::{sample_txs, SampleStats};
-use solana_core::gen_keys::GenKeys;
-use solana_exchange_program::{exchange_instruction, exchange_state::*, id};
-use solana_faucet::faucet::request_airdrop_transaction;
-use solana_genesis::Base64Account;
-use solana_metrics::datapoint_info;
-use solana_sdk::{
-    client::{Client, SyncClient},
-    commitment_config::CommitmentConfig,
-    message::Message,
-    pubkey::Pubkey,
-    signature::{Keypair, Signer},
-    timing::{duration_as_ms, duration_as_s},
-    transaction::Transaction,
-    {system_instruction, system_program},
-};
-use std::{
-    cmp,
-    collections::{HashMap, VecDeque},
-    fs::File,
-    io::prelude::*,
-    mem,
-    net::SocketAddr,
-    path::Path,
-    process::exit,
-    sync::{
-        atomic::{AtomicBool, AtomicUsize, Ordering},
-        mpsc::{channel, Receiver, Sender},
-        Arc, RwLock,
-    },
-    thread::{sleep, Builder},
-    time::{Duration, Instant},
-};
-
-// TODO Chunk length as specified results in a bunch of failures, divide by 10 helps...
-// Assume 4MB network buffers, and 512 byte packets
-const FUND_CHUNK_LEN: usize = 4 * 1024 * 1024 / 512;
-
-// Maximum system transfers per transaction
-const MAX_TRANSFERS_PER_TX: u64 = 4;
-
-pub type SharedTransactions = Arc<RwLock<VecDeque<Vec<Transaction>>>>;
-
-pub struct Config {
-    pub identity: Keypair,
-    pub threads: usize,
-    pub duration: Duration,
-    pub transfer_delay: u64,
-    pub fund_amount: u64,
-    pub batch_size: usize,
-    pub chunk_size: usize,
-    pub account_groups: usize,
-    pub client_ids_and_stake_file: String,
-    pub read_from_client_file: bool,
-}
-
-impl Default for Config {
-    fn default() -> Self {
-        Self {
-            identity: Keypair::new(),
-            threads: 4,
-            duration: Duration::new(u64::max_value(), 0),
-            transfer_delay: 0,
-            fund_amount: 100_000,
-            batch_size: 10,
-            chunk_size: 10,
-            account_groups: 100,
-            client_ids_and_stake_file: String::new(),
-            read_from_client_file: false,
-        }
-    }
-}
-
-pub fn create_client_accounts_file(
-    client_ids_and_stake_file: &str,
-    batch_size: usize,
-    account_groups: usize,
-    fund_amount: u64,
-) {
-    let accounts_in_groups = batch_size * account_groups;
-    const NUM_KEYPAIR_GROUPS: u64 = 2;
-    let total_keys = accounts_in_groups as u64 * NUM_KEYPAIR_GROUPS;
-
-    let keypairs = generate_keypairs(total_keys);
-
-    let mut accounts = HashMap::new();
-    keypairs.iter().for_each(|keypair| {
-        accounts.insert(
-            serde_json::to_string(&keypair.to_bytes().to_vec()).unwrap(),
-            Base64Account {
-                balance: fund_amount,
-                executable: false,
-                owner: system_program::id().to_string(),
-                data: String::new(),
-            },
-        );
-    });
-
-    let serialized = serde_yaml::to_string(&accounts).unwrap();
-    let path = Path::new(&client_ids_and_stake_file);
-    let mut file = File::create(path).unwrap();
-    file.write_all(&serialized.into_bytes()).unwrap();
-}
-
-pub fn do_bench_exchange<T>(clients: Vec<T>, config: Config)
-where
-    T: 'static + Client + Send + Sync,
-{
-    let Config {
-        identity,
-        threads,
-        duration,
-        transfer_delay,
-        fund_amount,
-        batch_size,
-        chunk_size,
-        account_groups,
-        client_ids_and_stake_file,
-        read_from_client_file,
-    } = config;
-
-    info!(
-        "Exchange client: threads {} duration {} fund_amount {}",
-        threads,
-        duration_as_s(&duration),
-        fund_amount
-    );
-    info!(
-        "Exchange client: transfer delay {} batch size {} chunk size {}",
-        transfer_delay, batch_size, chunk_size
-    );
-
-    let accounts_in_groups = batch_size * account_groups;
-    const NUM_KEYPAIR_GROUPS: u64 = 2;
-    let total_keys = accounts_in_groups as u64 * NUM_KEYPAIR_GROUPS;
-
-    let mut signer_keypairs = if read_from_client_file {
-        let path = Path::new(&client_ids_and_stake_file);
-        let file = File::open(path).unwrap();
-
-        let accounts: HashMap<String, Base64Account> = serde_yaml::from_reader(file).unwrap();
-        accounts
-            .into_iter()
-            .map(|(keypair, _)| {
-                let bytes: Vec<u8> = serde_json::from_str(keypair.as_str()).unwrap();
-                Keypair::from_bytes(&bytes).unwrap()
-            })
-            .collect()
-    } else {
-        info!("Generating {:?} signer keys", total_keys);
-        generate_keypairs(total_keys)
-    };
-
-    let trader_signers: Vec<_> = signer_keypairs
-        .drain(0..accounts_in_groups)
-        .map(Arc::new)
-        .collect();
-    let swapper_signers: Vec<_> = signer_keypairs
-        .drain(0..accounts_in_groups)
-        .map(Arc::new)
-        .collect();
-
-    let clients: Vec<_> = clients.into_iter().map(Arc::new).collect();
-    let client = clients[0].as_ref();
-
-    if !read_from_client_file {
-        info!("Fund trader accounts");
-        fund_keys(client, &identity, &trader_signers, fund_amount);
-        info!("Fund swapper accounts");
-        fund_keys(client, &identity, &swapper_signers, fund_amount);
-    }
-
-    info!("Generating {:?} account keys", total_keys);
-    let mut account_keypairs = generate_keypairs(total_keys);
-    let src_keypairs: Vec<_> = account_keypairs.drain(0..accounts_in_groups).collect();
-    let src_pubkeys: Vec<Pubkey> = src_keypairs
-        .iter()
-        .map(|keypair| keypair.pubkey())
-        .collect();
-
-    let profit_keypairs: Vec<_> = account_keypairs.drain(0..accounts_in_groups).collect();
-    let profit_pubkeys: Vec<Pubkey> = profit_keypairs
-        .iter()
-        .map(|keypair| keypair.pubkey())
-        .collect();
-
-    info!("Create {:?} source token accounts", src_pubkeys.len());
-    create_token_accounts(client, &trader_signers, &src_keypairs);
-    info!("Create {:?} profit token accounts", profit_pubkeys.len());
-    create_token_accounts(client, &swapper_signers, &profit_keypairs);
-
-    // Collect the max transaction rate and total tx count seen (single node only)
-    let sample_stats = Arc::new(RwLock::new(Vec::new()));
-    let sample_period = 1; // in seconds
-    info!("Sampling clients for tps every {} s", sample_period);
-    info!(
-        "Requesting and swapping trades with {} ms delay per thread...",
-        transfer_delay
-    );
-
-    let exit_signal = Arc::new(AtomicBool::new(false));
-    let shared_txs: SharedTransactions = Arc::new(RwLock::new(VecDeque::new()));
-    let total_txs_sent_count = Arc::new(AtomicUsize::new(0));
-    let s_threads: Vec<_> = (0..threads)
-        .map(|_| {
-            let exit_signal = exit_signal.clone();
-            let shared_txs = shared_txs.clone();
-            let total_txs_sent_count = total_txs_sent_count.clone();
-            let client = clients[0].clone();
-            Builder::new()
-                .name("solana-exchange-transfer".to_string())
-                .spawn(move || {
-                    do_tx_transfers(&exit_signal, &shared_txs, &total_txs_sent_count, &client)
-                })
-                .unwrap()
-        })
-        .collect();
-
-    trace!("Start swapper thread");
-    let (swapper_sender, swapper_receiver) = channel();
-    let swapper_thread = {
-        let exit_signal = exit_signal.clone();
-        let shared_txs = shared_txs.clone();
-        let client = clients[0].clone();
-        Builder::new()
-            .name("solana-exchange-swapper".to_string())
-            .spawn(move || {
-                swapper(
-                    &exit_signal,
-                    &swapper_receiver,
-                    &shared_txs,
-                    &swapper_signers,
-                    &profit_pubkeys,
-                    transfer_delay,
-                    batch_size,
-                    chunk_size,
-                    account_groups,
-                    &client,
-                )
-            })
-            .unwrap()
-    };
-
-    trace!("Start trader thread");
-    let trader_thread = {
-        let exit_signal = exit_signal.clone();
-
-        let client = clients[0].clone();
-        Builder::new()
-            .name("solana-exchange-trader".to_string())
-            .spawn(move || {
-                trader(
-                    &exit_signal,
-                    &swapper_sender,
-                    &shared_txs,
-                    &trader_signers,
-                    &src_pubkeys,
-                    transfer_delay,
-                    batch_size,
-                    chunk_size,
-                    account_groups,
-                    &client,
-                )
-            })
-            .unwrap()
-    };
-
-    let sample_threads: Vec<_> = clients
-        .iter()
-        .map(|client| {
-            let exit_signal = exit_signal.clone();
-            let sample_stats = sample_stats.clone();
-            let client = client.clone();
-            Builder::new()
-                .name("solana-exchange-sample".to_string())
-                .spawn(move || sample_txs(&exit_signal, &sample_stats, sample_period, &client))
-                .unwrap()
-        })
-        .collect();
-
-    sleep(duration);
-
-    info!("Stopping threads");
-    exit_signal.store(true, Ordering::Relaxed);
-    info!("Wait for trader thread");
-    let _ = trader_thread.join();
-    info!("Waiting for swapper thread");
-    let _ = swapper_thread.join();
-    info!("Wait for tx threads");
-    for t in s_threads {
-        let _ = t.join();
-    }
-    info!("Wait for sample threads");
-    for t in sample_threads {
-        let _ = t.join();
-    }
-
-    compute_and_report_stats(
-        &sample_stats,
-        total_txs_sent_count.load(Ordering::Relaxed) as u64,
-    );
-}
-
-fn do_tx_transfers<T>(
-    exit_signal: &Arc<AtomicBool>,
-    shared_txs: &SharedTransactions,
-    total_txs_sent_count: &Arc<AtomicUsize>,
-    client: &Arc<T>,
-) where
-    T: Client,
-{
-    loop {
-        let txs;
-        {
-            let mut shared_txs_wl = shared_txs.write().unwrap();
-            txs = shared_txs_wl.pop_front();
-        }
-        if let Some(txs0) = txs {
-            let n = txs0.len();
-
-            let now = Instant::now();
-            for tx in txs0 {
-                client.async_send_transaction(tx).expect("Transfer");
-            }
-            let duration = now.elapsed();
-
-            total_txs_sent_count.fetch_add(n, Ordering::Relaxed);
-            datapoint_info!(
-                "bench-exchange-do_tx_transfers",
-                ("duration", duration_as_ms(&duration), i64),
-                ("count", n, i64)
-            );
-        }
-        if exit_signal.load(Ordering::Relaxed) {
-            return;
-        }
-    }
-}
-
-struct TradeInfo {
-    trade_account: Pubkey,
-    order_info: OrderInfo,
-}
-#[allow(clippy::too_many_arguments)]
-fn swapper<T>(
-    exit_signal: &Arc<AtomicBool>,
-    receiver: &Receiver<Vec<TradeInfo>>,
-    shared_txs: &SharedTransactions,
-    signers: &[Arc<Keypair>],
-    profit_pubkeys: &[Pubkey],
-    transfer_delay: u64,
-    batch_size: usize,
-    chunk_size: usize,
-    account_groups: usize,
-    client: &Arc<T>,
-) where
-    T: Client,
-{
-    let mut order_book = OrderBook::default();
-    let mut account_group: usize = 0;
-
-    let mut txs = 0;
-    let mut total_txs = 0;
-    let mut now = Instant::now();
-    let start_time = now;
-    let mut total_elapsed = start_time.elapsed();
-
-    // Chunks may have been dropped and we don't want to wait a long time
-    // for each time, Back-off each time we fail to confirm a chunk
-    const CHECK_TX_TIMEOUT_MAX_MS: u64 = 15000;
-    const CHECK_TX_DELAY_MS: u64 = 100;
-    let mut max_tries = CHECK_TX_TIMEOUT_MAX_MS / CHECK_TX_DELAY_MS;
-
-    // If we dump too many chunks maybe we are just waiting on a back-log
-    // rather than a series of dropped packets, reset to max waits
-    const MAX_DUMPS: u64 = 50;
-    let mut dumps = 0;
-
-    'outer: loop {
-        if let Ok(trade_infos) = receiver.try_recv() {
-            let mut tries = 0;
-            let mut trade_index = 0;
-            while client
-                .get_balance_with_commitment(
-                    &trade_infos[trade_index].trade_account,
-                    CommitmentConfig::processed(),
-                )
-                .unwrap_or(0)
-                == 0
-            {
-                tries += 1;
-                if tries >= max_tries {
-                    if exit_signal.load(Ordering::Relaxed) {
-                        break 'outer;
-                    }
-                    error!("Give up and dump batch");
-                    if dumps >= MAX_DUMPS {
-                        error!("Max batches dumped, reset wait back-off");
-                        max_tries = CHECK_TX_TIMEOUT_MAX_MS / CHECK_TX_DELAY_MS;
-                        dumps = 0;
-                    } else {
-                        dumps += 1;
-                        max_tries /= 2;
-                    }
-                    continue 'outer;
-                }
-                debug!("{} waiting for trades batch to clear", tries);
-                sleep(Duration::from_millis(CHECK_TX_DELAY_MS));
-                trade_index = thread_rng().gen_range(0, trade_infos.len());
-            }
-            max_tries = CHECK_TX_TIMEOUT_MAX_MS / CHECK_TX_DELAY_MS;
-            dumps = 0;
-
-            trade_infos.iter().for_each(|info| {
-                order_book
-                    .push(info.trade_account, info.order_info)
-                    .expect("Failed to push to order_book");
-            });
-            let mut swaps = Vec::new();
-            while let Some((to, from)) = order_book.pop() {
-                swaps.push((to, from));
-                if swaps.len() >= batch_size {
-                    break;
-                }
-            }
-            let swaps_size = swaps.len();
-
-            let mut to_swap = vec![];
-            let start = account_group * swaps_size as usize;
-            let end = account_group * swaps_size as usize + batch_size as usize;
-            for (signer, swap, profit) in izip!(
-                signers[start..end].iter(),
-                swaps,
-                profit_pubkeys[start..end].iter(),
-            ) {
-                to_swap.push((signer, swap, profit));
-            }
-            account_group = (account_group + 1) % account_groups as usize;
-
-            let (blockhash, _fee_calculator, _last_valid_slot) = client
-                .get_recent_blockhash_with_commitment(CommitmentConfig::processed())
-                .expect("Failed to get blockhash");
-            let to_swap_txs: Vec<_> = to_swap
-                .par_iter()
-                .map(|(signer, swap, profit)| {
-                    let s: &Keypair = signer;
-                    let owner = &signer.pubkey();
-                    let instruction = exchange_instruction::swap_request(
-                        owner,
-                        &swap.0.pubkey,
-                        &swap.1.pubkey,
-                        profit,
-                    );
-                    let message = Message::new(&[instruction], Some(&s.pubkey()));
-                    Transaction::new(&[s], message, blockhash)
-                })
-                .collect();
-
-            txs += to_swap_txs.len() as u64;
-            total_txs += to_swap_txs.len() as u64;
-            total_elapsed = start_time.elapsed();
-            let duration = now.elapsed();
-            if duration_as_s(&duration) >= 1_f32 {
-                now = Instant::now();
-                let tps = txs as f32 / duration_as_s(&duration);
-                info!(
-                    "Swapper {:9.2} TPS, Transactions: {:6}, Total transactions: {} over {} s",
-                    tps,
-                    txs,
-                    total_txs,
-                    total_elapsed.as_secs(),
-                );
-                txs = 0;
-            }
-
-            datapoint_info!("bench-exchange-swaps", ("count", to_swap_txs.len(), i64));
-
-            let chunks: Vec<_> = to_swap_txs.chunks(chunk_size).collect();
-            {
-                let mut shared_txs_wl = shared_txs.write().unwrap();
-                for chunk in chunks {
-                    shared_txs_wl.push_back(chunk.to_vec());
-                }
-            }
-            // Throttle the swapper so it doesn't try to catchup unbridled
-            sleep(Duration::from_millis(transfer_delay / 2));
-        }
-
-        if exit_signal.load(Ordering::Relaxed) {
-            break 'outer;
-        }
-    }
-    info!(
-        "Swapper sent {} at {:9.2} TPS",
-        total_txs,
-        total_txs as f32 / duration_as_s(&total_elapsed)
-    );
-    assert_eq!(
-        order_book.get_num_outstanding().0 + order_book.get_num_outstanding().1,
-        0
-    );
-}
-
-#[allow(clippy::too_many_arguments)]
-fn trader<T>(
-    exit_signal: &Arc<AtomicBool>,
-    sender: &Sender<Vec<TradeInfo>>,
-    shared_txs: &SharedTransactions,
-    signers: &[Arc<Keypair>],
-    srcs: &[Pubkey],
-    transfer_delay: u64,
-    batch_size: usize,
-    chunk_size: usize,
-    account_groups: usize,
-    client: &Arc<T>,
-) where
-    T: Client,
-{
-    // TODO Hard coded for now
-    let pair = AssetPair::default();
-    let tokens = 1;
-    let price = 1000;
-    let mut account_group: usize = 0;
-
-    let mut txs = 0;
-    let mut total_txs = 0;
-    let mut now = Instant::now();
-    let start_time = now;
-    let mut total_elapsed = start_time.elapsed();
-
-    loop {
-        let trade_keys = generate_keypairs(batch_size as u64);
-
-        let mut trades = vec![];
-        let mut trade_infos = vec![];
-        let start = account_group * batch_size as usize;
-        let end = account_group * batch_size as usize + batch_size as usize;
-        let mut side = OrderSide::Ask;
-        for (signer, trade, src) in izip!(
-            signers[start..end].iter(),
-            trade_keys,
-            srcs[start..end].iter(),
-        ) {
-            side = if side == OrderSide::Ask {
-                OrderSide::Bid
-            } else {
-                OrderSide::Ask
-            };
-            let order_info = OrderInfo {
-                /// Owner of the trade order
-                owner: Pubkey::default(), // don't care
-                side,
-                pair,
-                tokens,
-                price,
-                tokens_settled: 0,
-            };
-            trade_infos.push(TradeInfo {
-                trade_account: trade.pubkey(),
-                order_info,
-            });
-            trades.push((signer, trade, side, src));
-        }
-        account_group = (account_group + 1) % account_groups as usize;
-
-        let (blockhash, _fee_calculator, _last_valid_slot) = client
-            .get_recent_blockhash_with_commitment(CommitmentConfig::processed())
-            .expect("Failed to get blockhash");
-
-        trades.chunks(chunk_size).for_each(|chunk| {
-            let trades_txs: Vec<_> = chunk
-                .par_iter()
-                .map(|(owner, trade, side, src)| {
-                    let owner_pubkey = &owner.pubkey();
-                    let trade_pubkey = &trade.pubkey();
-                    let space = mem::size_of::<ExchangeState>() as u64;
-                    let instructions = [
-                        system_instruction::create_account(
-                            owner_pubkey,
-                            trade_pubkey,
-                            1,
-                            space,
-                            &id(),
-                        ),
-                        exchange_instruction::trade_request(
-                            owner_pubkey,
-                            trade_pubkey,
-                            *side,
-                            pair,
-                            tokens,
-                            price,
-                            src,
-                        ),
-                    ];
-                    let message = Message::new(&instructions, Some(owner_pubkey));
-                    Transaction::new(&[owner.as_ref(), trade], message, blockhash)
-                })
-                .collect();
-
-            {
-                txs += chunk_size as u64;
-                total_txs += chunk_size as u64;
-                total_elapsed = start_time.elapsed();
-                let duration = now.elapsed();
-                if duration_as_s(&duration) >= 1_f32 {
-                    now = Instant::now();
-                    let tps = txs as f32 / duration_as_s(&duration);
-                    info!(
-                        "Trader  {:9.2} TPS, Transactions: {:6}, Total transactions: {} over {} s",
-                        tps,
-                        txs,
-                        total_txs,
-                        total_elapsed.as_secs(),
-                    );
-                    txs = 0;
-                }
-
-                datapoint_info!("bench-exchange-trades", ("count", trades_txs.len(), i64));
-
-                {
-                    let mut shared_txs_wl = shared_txs
-                        .write()
-                        .expect("Failed to send tx to transfer threads");
-                    shared_txs_wl.push_back(trades_txs);
-                }
-            }
-            if transfer_delay > 0 {
-                sleep(Duration::from_millis(transfer_delay));
-            }
-        });
-
-        if exit_signal.load(Ordering::Relaxed) {
-            info!(
-                "Trader sent {} at {:9.2} TPS",
-                total_txs,
-                total_txs as f32 / duration_as_s(&total_elapsed)
-            );
-            return;
-        }
-
-        // TODO chunk the trade infos and send them when the batch is sent
-        sender
-            .send(trade_infos)
-            .expect("Failed to send trades to swapper");
-    }
-}
-
-fn verify_transaction<T>(sync_client: &T, tx: &Transaction) -> bool
-where
-    T: SyncClient + ?Sized,
-{
-    for s in &tx.signatures {
-        if let Ok(Some(r)) =
-            sync_client.get_signature_status_with_commitment(s, CommitmentConfig::processed())
-        {
-            match r {
-                Ok(_) => {
-                    return true;
-                }
-                Err(e) => {
-                    info!("error: {:?}", e);
-                }
-            }
-        }
-    }
-    false
-}
-
-fn verify_funding_transfer<T: SyncClient + ?Sized>(
-    client: &T,
-    tx: &Transaction,
-    amount: u64,
-) -> bool {
-    if verify_transaction(client, tx) {
-        for a in &tx.message().account_keys[1..] {
-            if client
-                .get_balance_with_commitment(a, CommitmentConfig::processed())
-                .unwrap_or(0)
-                >= amount
-            {
-                return true;
-            }
-        }
-    }
-    false
-}
-
-pub fn fund_keys<T: Client>(client: &T, source: &Keypair, dests: &[Arc<Keypair>], lamports: u64) {
-    let total = lamports * (dests.len() as u64 + 1);
-    let mut funded: Vec<(&Keypair, u64)> = vec![(source, total)];
-    let mut notfunded: Vec<&Arc<Keypair>> = dests.iter().collect();
-
-    info!(
-        "  Funding {} keys with {} lamports each",
-        dests.len(),
-        lamports
-    );
-    while !notfunded.is_empty() {
-        if funded.is_empty() {
-            panic!("No funded accounts left to fund remaining");
-        }
-        let mut new_funded: Vec<(&Keypair, u64)> = vec![];
-        let mut to_fund = vec![];
-        debug!("  Creating from... {}", funded.len());
-        for f in &mut funded {
-            let max_units = cmp::min(
-                cmp::min(notfunded.len() as u64, MAX_TRANSFERS_PER_TX),
-                (f.1 - lamports) / lamports,
-            );
-            if max_units == 0 {
-                continue;
-            }
-            let per_unit = ((f.1 - lamports) / lamports / max_units) * lamports;
-            f.1 -= per_unit * max_units;
-            let start = notfunded.len() - max_units as usize;
-            let moves: Vec<_> = notfunded[start..]
-                .iter()
-                .map(|k| (k.pubkey(), per_unit))
-                .collect();
-            notfunded[start..]
-                .iter()
-                .for_each(|k| new_funded.push((k, per_unit)));
-            notfunded.truncate(start);
-            if !moves.is_empty() {
-                to_fund.push((f.0, moves));
-            }
-        }
-
-        to_fund.chunks(FUND_CHUNK_LEN).for_each(|chunk| {
-            #[allow(clippy::clone_double_ref)] // sigh
-            let mut to_fund_txs: Vec<_> = chunk
-                .par_iter()
-                .map(|(k, m)| {
-                    let instructions = system_instruction::transfer_many(&k.pubkey(), m);
-                    let message = Message::new(&instructions, Some(&k.pubkey()));
-                    (k.clone(), Transaction::new_unsigned(message))
-                })
-                .collect();
-
-            let mut retries = 0;
-            let amount = chunk[0].1[0].1;
-            while !to_fund_txs.is_empty() {
-                let receivers: usize = to_fund_txs
-                    .iter()
-                    .map(|(_, tx)| tx.message().instructions.len())
-                    .sum();
-
-                debug!(
-                    "  {} to {} in {} txs",
-                    if retries == 0 {
-                        "  Transferring"
-                    } else {
-                        "  Retrying"
-                    },
-                    receivers,
-                    to_fund_txs.len(),
-                );
-
-                let (blockhash, _fee_calculator, _last_valid_slot) = client
-                    .get_recent_blockhash_with_commitment(CommitmentConfig::processed())
-                    .expect("blockhash");
-                to_fund_txs.par_iter_mut().for_each(|(k, tx)| {
-                    tx.sign(&[*k], blockhash);
-                });
-                to_fund_txs.iter().for_each(|(_, tx)| {
-                    client.async_send_transaction(tx.clone()).expect("transfer");
-                });
-
-                let mut waits = 0;
-                loop {
-                    sleep(Duration::from_millis(200));
-                    to_fund_txs.retain(|(_, tx)| !verify_funding_transfer(client, tx, amount));
-                    if to_fund_txs.is_empty() {
-                        break;
-                    }
-                    debug!(
-                        "    {} transactions outstanding, {:?} waits",
-                        to_fund_txs.len(),
-                        waits
-                    );
-                    waits += 1;
-                    if waits >= 5 {
-                        break;
-                    }
-                }
-                if !to_fund_txs.is_empty() {
-                    retries += 1;
-                    debug!("  Retry {:?}", retries);
-                    if retries >= 10 {
-                        error!("fund_keys: Too many retries ({}), give up", retries);
-                        exit(1);
-                    }
-                }
-            }
-        });
-        funded.append(&mut new_funded);
-        funded.retain(|(k, b)| {
-            client
-                .get_balance_with_commitment(&k.pubkey(), CommitmentConfig::processed())
-                .unwrap_or(0)
-                > lamports
-                && *b > lamports
-        });
-        debug!("  Funded: {} left: {}", funded.len(), notfunded.len());
-    }
-}
-
-pub fn create_token_accounts<T: Client>(
-    client: &T,
-    signers: &[Arc<Keypair>],
-    accounts: &[Keypair],
-) {
-    let mut notfunded: Vec<(&Arc<Keypair>, &Keypair)> = signers.iter().zip(accounts).collect();
-
-    while !notfunded.is_empty() {
-        notfunded.chunks(FUND_CHUNK_LEN).for_each(|chunk| {
-            let mut to_create_txs: Vec<_> = chunk
-                .par_iter()
-                .map(|(from_keypair, new_keypair)| {
-                    let owner_pubkey = &from_keypair.pubkey();
-                    let space = mem::size_of::<ExchangeState>() as u64;
-                    let create_ix = system_instruction::create_account(
-                        owner_pubkey,
-                        &new_keypair.pubkey(),
-                        1,
-                        space,
-                        &id(),
-                    );
-                    let request_ix =
-                        exchange_instruction::account_request(owner_pubkey, &new_keypair.pubkey());
-                    let message = Message::new(&[create_ix, request_ix], Some(owner_pubkey));
-                    (
-                        (from_keypair, new_keypair),
-                        Transaction::new_unsigned(message),
-                    )
-                })
-                .collect();
-
-            let accounts: usize = to_create_txs
-                .iter()
-                .map(|(_, tx)| tx.message().instructions.len() / 2)
-                .sum();
-
-            debug!(
-                "  Creating {} accounts in {} txs",
-                accounts,
-                to_create_txs.len(),
-            );
-
-            let mut retries = 0;
-            while !to_create_txs.is_empty() {
-                let (blockhash, _fee_calculator, _last_valid_slot) = client
-                    .get_recent_blockhash_with_commitment(CommitmentConfig::processed())
-                    .expect("Failed to get blockhash");
-                to_create_txs
-                    .par_iter_mut()
-                    .for_each(|((from_keypair, to_keypair), tx)| {
-                        tx.sign(&[from_keypair.as_ref(), to_keypair], blockhash);
-                    });
-                to_create_txs.iter().for_each(|(_, tx)| {
-                    client.async_send_transaction(tx.clone()).expect("transfer");
-                });
-
-                let mut waits = 0;
-                while !to_create_txs.is_empty() {
-                    sleep(Duration::from_millis(200));
-                    to_create_txs.retain(|(_, tx)| !verify_transaction(client, tx));
-                    if to_create_txs.is_empty() {
-                        break;
-                    }
-                    info!(
-                        "    {} transactions outstanding, waits {:?}",
-                        to_create_txs.len(),
-                        waits
-                    );
-                    waits += 1;
-                    if waits >= 5 {
-                        break;
-                    }
-                }
-
-                if !to_create_txs.is_empty() {
-                    retries += 1;
-                    info!("  Retry {:?} {} txes left", retries, to_create_txs.len());
-                    if retries >= 20 {
-                        error!(
-                            "create_token_accounts: Too many retries ({}), give up",
-                            retries
-                        );
-                        exit(1);
-                    }
-                }
-            }
-        });
-
-        let mut new_notfunded: Vec<(&Arc<Keypair>, &Keypair)> = vec![];
-        for f in &notfunded {
-            if client
-                .get_balance_with_commitment(&f.1.pubkey(), CommitmentConfig::processed())
-                .unwrap_or(0)
-                == 0
-            {
-                new_notfunded.push(*f)
-            }
-        }
-        notfunded = new_notfunded;
-        debug!("  Left: {}", notfunded.len());
-    }
-}
-
-fn compute_and_report_stats(maxes: &Arc<RwLock<Vec<(String, SampleStats)>>>, total_txs_sent: u64) {
-    let mut max_txs = 0;
-    let mut max_elapsed = Duration::new(0, 0);
-    info!("|       Max TPS | Total Transactions");
-    info!("+---------------+--------------------");
-
-    for (_sock, stats) in maxes.read().unwrap().iter() {
-        let maybe_flag = match stats.txs {
-            0 => "!!!!!",
-            _ => "",
-        };
-
-        info!("| {:13.2} | {} {}", stats.tps, stats.txs, maybe_flag);
-
-        if stats.elapsed > max_elapsed {
-            max_elapsed = stats.elapsed;
-        }
-        if stats.txs > max_txs {
-            max_txs = stats.txs;
-        }
-    }
-    info!("+---------------+--------------------");
-
-    if max_txs >= total_txs_sent {
-        info!(
-            "Warning: Average TPS might be under reported, there were no txs sent for a portion of the duration"
-        );
-        max_txs = total_txs_sent;
-    }
-    info!(
-        "{} txs outstanding when test ended (lag) ({:.2}%)",
-        total_txs_sent - max_txs,
-        (total_txs_sent - max_txs) as f64 / total_txs_sent as f64 * 100_f64
-    );
-    info!(
-        "\tAverage TPS: {:.2}",
-        max_txs as f32 / max_elapsed.as_secs() as f32
-    );
-}
-
-fn generate_keypairs(num: u64) -> Vec<Keypair> {
-    let mut seed = [0_u8; 32];
-    seed.copy_from_slice(Keypair::new().pubkey().as_ref());
-    let mut rnd = GenKeys::new(seed);
-    rnd.gen_n_keypairs(num)
-}
-
-pub fn airdrop_lamports<T: Client>(
-    client: &T,
-    faucet_addr: &SocketAddr,
-    id: &Keypair,
-    amount: u64,
-) {
-    let balance = client.get_balance_with_commitment(&id.pubkey(), CommitmentConfig::processed());
-    let balance = balance.unwrap_or(0);
-    if balance >= amount {
-        return;
-    }
-
-    let amount_to_drop = amount - balance;
-
-    info!(
-        "Airdropping {:?} lamports from {} for {}",
-        amount_to_drop,
-        faucet_addr,
-        id.pubkey(),
-    );
-
-    let mut tries = 0;
-    loop {
-        let (blockhash, _fee_calculator, _last_valid_slot) = client
-            .get_recent_blockhash_with_commitment(CommitmentConfig::processed())
-            .expect("Failed to get blockhash");
-        match request_airdrop_transaction(faucet_addr, &id.pubkey(), amount_to_drop, blockhash) {
-            Ok(transaction) => {
-                let signature = client.async_send_transaction(transaction).unwrap();
-
-                for _ in 0..30 {
-                    if let Ok(Some(_)) = client.get_signature_status_with_commitment(
-                        &signature,
-                        CommitmentConfig::processed(),
-                    ) {
-                        break;
-                    }
-                    sleep(Duration::from_millis(100));
-                }
-                if client
-                    .get_balance_with_commitment(&id.pubkey(), CommitmentConfig::processed())
-                    .unwrap_or(0)
-                    >= amount
-                {
-                    break;
-                }
-            }
-            Err(err) => {
-                panic!(
-                    "Error requesting airdrop: {:?} to addr: {:?} amount: {}",
-                    err, faucet_addr, amount
-                );
-            }
-        };
-        debug!("  Retry...");
-        tries += 1;
-        if tries > 50 {
-            error!("airdrop_lamports: Too many retries ({}), give up", tries);
-            exit(1);
-        }
-        sleep(Duration::from_secs(2));
-    }
-}

+ 0 - 221
bench-exchange/src/cli.rs

@@ -1,221 +0,0 @@
-use clap::{crate_description, crate_name, value_t, App, Arg, ArgMatches};
-use solana_core::gen_keys::GenKeys;
-use solana_faucet::faucet::FAUCET_PORT;
-use solana_sdk::signature::{read_keypair_file, Keypair};
-use std::net::SocketAddr;
-use std::process::exit;
-use std::time::Duration;
-
-pub struct Config {
-    pub entrypoint_addr: SocketAddr,
-    pub faucet_addr: SocketAddr,
-    pub identity: Keypair,
-    pub threads: usize,
-    pub num_nodes: usize,
-    pub duration: Duration,
-    pub transfer_delay: u64,
-    pub fund_amount: u64,
-    pub batch_size: usize,
-    pub chunk_size: usize,
-    pub account_groups: usize,
-    pub client_ids_and_stake_file: String,
-    pub write_to_client_file: bool,
-    pub read_from_client_file: bool,
-}
-
-impl Default for Config {
-    fn default() -> Self {
-        Self {
-            entrypoint_addr: SocketAddr::from(([127, 0, 0, 1], 8001)),
-            faucet_addr: SocketAddr::from(([127, 0, 0, 1], FAUCET_PORT)),
-            identity: Keypair::new(),
-            num_nodes: 1,
-            threads: 4,
-            duration: Duration::new(u64::max_value(), 0),
-            transfer_delay: 0,
-            fund_amount: 100_000,
-            batch_size: 100,
-            chunk_size: 100,
-            account_groups: 100,
-            client_ids_and_stake_file: String::new(),
-            write_to_client_file: false,
-            read_from_client_file: false,
-        }
-    }
-}
-
-pub fn build_args<'a, 'b>(version: &'b str) -> App<'a, 'b> {
-    App::new(crate_name!())
-        .about(crate_description!())
-        .version(version)
-        .arg(
-            Arg::with_name("entrypoint")
-                .short("n")
-                .long("entrypoint")
-                .value_name("HOST:PORT")
-                .takes_value(true)
-                .required(false)
-                .default_value("127.0.0.1:8001")
-                .help("Cluster entry point; defaults to 127.0.0.1:8001"),
-        )
-        .arg(
-            Arg::with_name("faucet")
-                .short("d")
-                .long("faucet")
-                .value_name("HOST:PORT")
-                .takes_value(true)
-                .required(false)
-                .default_value("127.0.0.1:9900")
-                .help("Location of the faucet; defaults to 127.0.0.1:9900"),
-        )
-        .arg(
-            Arg::with_name("identity")
-                .short("i")
-                .long("identity")
-                .value_name("PATH")
-                .takes_value(true)
-                .help("File containing a client identity (keypair)"),
-        )
-        .arg(
-            Arg::with_name("threads")
-                .long("threads")
-                .value_name("<threads>")
-                .takes_value(true)
-                .required(false)
-                .default_value("1")
-                .help("Number of threads submitting transactions"),
-        )
-        .arg(
-            Arg::with_name("num-nodes")
-                .long("num-nodes")
-                .value_name("NUM")
-                .takes_value(true)
-                .required(false)
-                .default_value("1")
-                .help("Wait for NUM nodes to converge"),
-        )
-        .arg(
-            Arg::with_name("duration")
-                .long("duration")
-                .value_name("SECS")
-                .takes_value(true)
-                .default_value("60")
-                .help("Seconds to run benchmark, then exit; default is forever"),
-        )
-        .arg(
-            Arg::with_name("transfer-delay")
-                .long("transfer-delay")
-                .value_name("<delay>")
-                .takes_value(true)
-                .required(false)
-                .default_value("0")
-                .help("Delay between each chunk"),
-        )
-        .arg(
-            Arg::with_name("fund-amount")
-                .long("fund-amount")
-                .value_name("<fund>")
-                .takes_value(true)
-                .required(false)
-                .default_value("100000")
-                .help("Number of lamports to fund to each signer"),
-        )
-        .arg(
-            Arg::with_name("batch-size")
-                .long("batch-size")
-                .value_name("<batch>")
-                .takes_value(true)
-                .required(false)
-                .default_value("1000")
-                .help("Number of transactions before the signer rolls over"),
-        )
-        .arg(
-            Arg::with_name("chunk-size")
-                .long("chunk-size")
-                .value_name("<cunk>")
-                .takes_value(true)
-                .required(false)
-                .default_value("500")
-                .help("Number of transactions to generate and send at a time"),
-        )
-        .arg(
-            Arg::with_name("account-groups")
-                .long("account-groups")
-                .value_name("<groups>")
-                .takes_value(true)
-                .required(false)
-                .default_value("10")
-                .help("Number of account groups to cycle for each batch"),
-        )
-        .arg(
-            Arg::with_name("write-client-keys")
-                .long("write-client-keys")
-                .value_name("FILENAME")
-                .takes_value(true)
-                .help("Generate client keys and stakes and write the list to YAML file"),
-        )
-        .arg(
-            Arg::with_name("read-client-keys")
-                .long("read-client-keys")
-                .value_name("FILENAME")
-                .takes_value(true)
-                .help("Read client keys and stakes from the YAML file"),
-        )
-}
-
-#[allow(clippy::field_reassign_with_default)]
-pub fn extract_args(matches: &ArgMatches) -> Config {
-    let mut args = Config::default();
-
-    args.entrypoint_addr = solana_net_utils::parse_host_port(
-        matches.value_of("entrypoint").unwrap(),
-    )
-    .unwrap_or_else(|e| {
-        eprintln!("failed to parse entrypoint address: {}", e);
-        exit(1)
-    });
-
-    args.faucet_addr = solana_net_utils::parse_host_port(matches.value_of("faucet").unwrap())
-        .unwrap_or_else(|e| {
-            eprintln!("failed to parse faucet address: {}", e);
-            exit(1)
-        });
-
-    if matches.is_present("identity") {
-        args.identity = read_keypair_file(matches.value_of("identity").unwrap())
-            .expect("can't read client identity");
-    } else {
-        args.identity = {
-            let seed = [42_u8; 32];
-            let mut rnd = GenKeys::new(seed);
-            rnd.gen_keypair()
-        };
-    }
-    args.threads = value_t!(matches.value_of("threads"), usize).expect("Failed to parse threads");
-    args.num_nodes =
-        value_t!(matches.value_of("num-nodes"), usize).expect("Failed to parse num-nodes");
-    let duration = value_t!(matches.value_of("duration"), u64).expect("Failed to parse duration");
-    args.duration = Duration::from_secs(duration);
-    args.transfer_delay =
-        value_t!(matches.value_of("transfer-delay"), u64).expect("Failed to parse transfer-delay");
-    args.fund_amount =
-        value_t!(matches.value_of("fund-amount"), u64).expect("Failed to parse fund-amount");
-    args.batch_size =
-        value_t!(matches.value_of("batch-size"), usize).expect("Failed to parse batch-size");
-    args.chunk_size =
-        value_t!(matches.value_of("chunk-size"), usize).expect("Failed to parse chunk-size");
-    args.account_groups = value_t!(matches.value_of("account-groups"), usize)
-        .expect("Failed to parse account-groups");
-
-    if let Some(s) = matches.value_of("write-client-keys") {
-        args.write_to_client_file = true;
-        args.client_ids_and_stake_file = s.to_string();
-    }
-
-    if let Some(s) = matches.value_of("read-client-keys") {
-        assert!(!args.write_to_client_file);
-        args.read_from_client_file = true;
-        args.client_ids_and_stake_file = s.to_string();
-    }
-    args
-}

+ 0 - 3
bench-exchange/src/lib.rs

@@ -1,3 +0,0 @@
-pub mod bench;
-pub mod cli;
-mod order_book;

+ 0 - 83
bench-exchange/src/main.rs

@@ -1,83 +0,0 @@
-#![allow(clippy::integer_arithmetic)]
-pub mod bench;
-mod cli;
-pub mod order_book;
-
-use crate::bench::{airdrop_lamports, create_client_accounts_file, do_bench_exchange, Config};
-use log::*;
-use solana_gossip::gossip_service::{discover_cluster, get_multi_client};
-use solana_sdk::signature::Signer;
-
-fn main() {
-    solana_logger::setup();
-    solana_metrics::set_panic_hook("bench-exchange");
-
-    let matches = cli::build_args(solana_version::version!()).get_matches();
-    let cli_config = cli::extract_args(&matches);
-
-    let cli::Config {
-        entrypoint_addr,
-        faucet_addr,
-        identity,
-        threads,
-        num_nodes,
-        duration,
-        transfer_delay,
-        fund_amount,
-        batch_size,
-        chunk_size,
-        account_groups,
-        client_ids_and_stake_file,
-        write_to_client_file,
-        read_from_client_file,
-        ..
-    } = cli_config;
-
-    let config = Config {
-        identity,
-        threads,
-        duration,
-        transfer_delay,
-        fund_amount,
-        batch_size,
-        chunk_size,
-        account_groups,
-        client_ids_and_stake_file,
-        read_from_client_file,
-    };
-
-    if write_to_client_file {
-        create_client_accounts_file(
-            &config.client_ids_and_stake_file,
-            config.batch_size,
-            config.account_groups,
-            config.fund_amount,
-        );
-    } else {
-        info!("Connecting to the cluster");
-        let nodes = discover_cluster(&entrypoint_addr, num_nodes).unwrap_or_else(|_| {
-            panic!("Failed to discover nodes");
-        });
-
-        let (client, num_clients) = get_multi_client(&nodes);
-
-        info!("{} nodes found", num_clients);
-        if num_clients < num_nodes {
-            panic!("Error: Insufficient nodes discovered");
-        }
-
-        if !read_from_client_file {
-            info!("Funding keypair: {}", config.identity.pubkey());
-
-            let accounts_in_groups = batch_size * account_groups;
-            const NUM_SIGNERS: u64 = 2;
-            airdrop_lamports(
-                &client,
-                &faucet_addr,
-                &config.identity,
-                fund_amount * (accounts_in_groups + 1) as u64 * NUM_SIGNERS,
-            );
-        }
-        do_bench_exchange(vec![client], config);
-    }
-}

+ 0 - 134
bench-exchange/src/order_book.rs

@@ -1,134 +0,0 @@
-use itertools::EitherOrBoth::{Both, Left, Right};
-use itertools::Itertools;
-use log::*;
-use solana_exchange_program::exchange_state::*;
-use solana_sdk::pubkey::Pubkey;
-use std::cmp::Ordering;
-use std::collections::BinaryHeap;
-use std::{error, fmt};
-
-#[derive(Clone, Debug, Eq, PartialEq)]
-pub struct ToOrder {
-    pub pubkey: Pubkey,
-    pub info: OrderInfo,
-}
-
-impl Ord for ToOrder {
-    fn cmp(&self, other: &Self) -> Ordering {
-        other.info.price.cmp(&self.info.price)
-    }
-}
-impl PartialOrd for ToOrder {
-    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
-        Some(self.cmp(other))
-    }
-}
-#[derive(Clone, Debug, Eq, PartialEq)]
-pub struct FromOrder {
-    pub pubkey: Pubkey,
-    pub info: OrderInfo,
-}
-
-impl Ord for FromOrder {
-    fn cmp(&self, other: &Self) -> Ordering {
-        self.info.price.cmp(&other.info.price)
-    }
-}
-impl PartialOrd for FromOrder {
-    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
-        Some(self.cmp(other))
-    }
-}
-
-#[derive(Default)]
-pub struct OrderBook {
-    // TODO scale to x token types
-    to_ab: BinaryHeap<ToOrder>,
-    from_ab: BinaryHeap<FromOrder>,
-}
-impl fmt::Display for OrderBook {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        writeln!(
-            f,
-            "+-Order Book--------------------------+-------------------------------------+"
-        )?;
-        for (i, it) in self
-            .to_ab
-            .iter()
-            .zip_longest(self.from_ab.iter())
-            .enumerate()
-        {
-            match it {
-                Both(to, from) => writeln!(
-                    f,
-                    "| T AB {:8} for {:8}/{:8} | F AB {:8} for {:8}/{:8} |{}",
-                    to.info.tokens,
-                    SCALER,
-                    to.info.price,
-                    from.info.tokens,
-                    SCALER,
-                    from.info.price,
-                    i
-                )?,
-                Left(to) => writeln!(
-                    f,
-                    "| T AB {:8} for {:8}/{:8} |                                     |{}",
-                    to.info.tokens, SCALER, to.info.price, i
-                )?,
-                Right(from) => writeln!(
-                    f,
-                    "|                                     | F AB {:8} for {:8}/{:8} |{}",
-                    from.info.tokens, SCALER, from.info.price, i
-                )?,
-            }
-        }
-        write!(
-            f,
-            "+-------------------------------------+-------------------------------------+"
-        )?;
-        Ok(())
-    }
-}
-
-impl OrderBook {
-    // TODO
-    // pub fn cancel(&mut self, pubkey: Pubkey) -> Result<(), Box<dyn error::Error>> {
-    //     Ok(())
-    // }
-    pub fn push(&mut self, pubkey: Pubkey, info: OrderInfo) -> Result<(), Box<dyn error::Error>> {
-        check_trade(info.side, info.tokens, info.price)?;
-        match info.side {
-            OrderSide::Ask => {
-                self.to_ab.push(ToOrder { pubkey, info });
-            }
-            OrderSide::Bid => {
-                self.from_ab.push(FromOrder { pubkey, info });
-            }
-        }
-        Ok(())
-    }
-    pub fn pop(&mut self) -> Option<(ToOrder, FromOrder)> {
-        if let Some(pair) = Self::pop_pair(&mut self.to_ab, &mut self.from_ab) {
-            return Some(pair);
-        }
-        None
-    }
-    pub fn get_num_outstanding(&self) -> (usize, usize) {
-        (self.to_ab.len(), self.from_ab.len())
-    }
-
-    fn pop_pair(
-        to_ab: &mut BinaryHeap<ToOrder>,
-        from_ab: &mut BinaryHeap<FromOrder>,
-    ) -> Option<(ToOrder, FromOrder)> {
-        let to = to_ab.peek()?;
-        let from = from_ab.peek()?;
-        if from.info.price < to.info.price {
-            debug!("Trade not viable");
-            return None;
-        }
-        let to = to_ab.pop()?;
-        let from = from_ab.pop()?;
-        Some((to, from))
-    }
-}

+ 0 - 113
bench-exchange/tests/bench_exchange.rs

@@ -1,113 +0,0 @@
-use log::*;
-use solana_bench_exchange::bench::{airdrop_lamports, do_bench_exchange, Config};
-use solana_core::validator::ValidatorConfig;
-use solana_exchange_program::{
-    exchange_processor::process_instruction, id, solana_exchange_program,
-};
-use solana_faucet::faucet::run_local_faucet_with_port;
-use solana_gossip::gossip_service::{discover_cluster, get_multi_client};
-use solana_local_cluster::{
-    local_cluster::{ClusterConfig, LocalCluster},
-    validator_configs::make_identical_validator_configs,
-};
-use solana_runtime::{bank::Bank, bank_client::BankClient};
-use solana_sdk::{
-    genesis_config::create_genesis_config,
-    signature::{Keypair, Signer},
-};
-use std::{process::exit, sync::mpsc::channel, time::Duration};
-
-#[test]
-#[ignore]
-fn test_exchange_local_cluster() {
-    solana_logger::setup();
-
-    const NUM_NODES: usize = 1;
-
-    let config = Config {
-        identity: Keypair::new(),
-        duration: Duration::from_secs(1),
-        fund_amount: 100_000,
-        threads: 1,
-        transfer_delay: 20, // 15
-        batch_size: 100,    // 1000
-        chunk_size: 10,     // 200
-        account_groups: 1,  // 10
-        ..Config::default()
-    };
-    let Config {
-        fund_amount,
-        batch_size,
-        account_groups,
-        ..
-    } = config;
-    let accounts_in_groups = batch_size * account_groups;
-
-    let cluster = LocalCluster::new(&mut ClusterConfig {
-        node_stakes: vec![100_000; NUM_NODES],
-        cluster_lamports: 100_000_000_000_000,
-        validator_configs: make_identical_validator_configs(&ValidatorConfig::default(), NUM_NODES),
-        native_instruction_processors: [solana_exchange_program!()].to_vec(),
-        ..ClusterConfig::default()
-    });
-
-    let faucet_keypair = Keypair::new();
-    cluster.transfer(
-        &cluster.funding_keypair,
-        &faucet_keypair.pubkey(),
-        2_000_000_000_000,
-    );
-
-    let (addr_sender, addr_receiver) = channel();
-    run_local_faucet_with_port(faucet_keypair, addr_sender, Some(1_000_000_000_000), 0);
-    let faucet_addr = addr_receiver
-        .recv_timeout(Duration::from_secs(2))
-        .expect("run_local_faucet")
-        .expect("faucet_addr");
-
-    info!("Connecting to the cluster");
-    let nodes =
-        discover_cluster(&cluster.entry_point_info.gossip, NUM_NODES).unwrap_or_else(|err| {
-            error!("Failed to discover {} nodes: {:?}", NUM_NODES, err);
-            exit(1);
-        });
-
-    let (client, num_clients) = get_multi_client(&nodes);
-
-    info!("clients: {}", num_clients);
-    assert!(num_clients >= NUM_NODES);
-
-    const NUM_SIGNERS: u64 = 2;
-    airdrop_lamports(
-        &client,
-        &faucet_addr,
-        &config.identity,
-        fund_amount * (accounts_in_groups + 1) as u64 * NUM_SIGNERS,
-    );
-
-    do_bench_exchange(vec![client], config);
-}
-
-#[test]
-fn test_exchange_bank_client() {
-    solana_logger::setup();
-    let (genesis_config, identity) = create_genesis_config(100_000_000_000_000);
-    let mut bank = Bank::new(&genesis_config);
-    bank.add_builtin("exchange_program", id(), process_instruction);
-    let clients = vec![BankClient::new(bank)];
-
-    do_bench_exchange(
-        clients,
-        Config {
-            identity,
-            duration: Duration::from_secs(1),
-            fund_amount: 100_000,
-            threads: 1,
-            transfer_delay: 20, // 0;
-            batch_size: 100,    // 1500;
-            chunk_size: 10,     // 1500;
-            account_groups: 1,  // 50;
-            ..Config::default()
-        },
-    );
-}

+ 0 - 1
genesis/Cargo.toml

@@ -18,7 +18,6 @@ serde_json = "1.0.64"
 serde_yaml = "0.8.17"
 serde_yaml = "0.8.17"
 solana-clap-utils = { path = "../clap-utils", version = "=1.8.0" }
 solana-clap-utils = { path = "../clap-utils", version = "=1.8.0" }
 solana-cli-config = { path = "../cli-config", version = "=1.8.0" }
 solana-cli-config = { path = "../cli-config", version = "=1.8.0" }
-solana-exchange-program = { path = "../programs/exchange", version = "=1.8.0" }
 solana-ledger = { path = "../ledger", version = "=1.8.0" }
 solana-ledger = { path = "../ledger", version = "=1.8.0" }
 solana-logger = { path = "../logger", version = "=1.8.0" }
 solana-logger = { path = "../logger", version = "=1.8.0" }
 solana-runtime = { path = "../runtime", version = "=1.8.0" }
 solana-runtime = { path = "../runtime", version = "=1.8.0" }

+ 1 - 10
genesis/src/main.rs

@@ -1,9 +1,6 @@
 //! A command-line executable for generating the chain's genesis config.
 //! A command-line executable for generating the chain's genesis config.
 #![allow(clippy::integer_arithmetic)]
 #![allow(clippy::integer_arithmetic)]
 
 
-#[macro_use]
-extern crate solana_exchange_program;
-
 use clap::{crate_description, crate_name, value_t, value_t_or_exit, App, Arg, ArgMatches};
 use clap::{crate_description, crate_name, value_t, value_t_or_exit, App, Arg, ArgMatches};
 use solana_clap_utils::{
 use solana_clap_utils::{
     input_parsers::{cluster_type_of, pubkey_of, pubkeys_of, unix_timestamp_from_rfc3339_datetime},
     input_parsers::{cluster_type_of, pubkey_of, pubkeys_of, unix_timestamp_from_rfc3339_datetime},
@@ -490,14 +487,8 @@ fn main() -> Result<(), Box<dyn error::Error>> {
         matches.is_present("enable_warmup_epochs"),
         matches.is_present("enable_warmup_epochs"),
     );
     );
 
 
-    let native_instruction_processors = if cluster_type == ClusterType::Development {
-        vec![solana_exchange_program!()]
-    } else {
-        vec![]
-    };
-
     let mut genesis_config = GenesisConfig {
     let mut genesis_config = GenesisConfig {
-        native_instruction_processors,
+        native_instruction_processors: vec![],
         ticks_per_slot,
         ticks_per_slot,
         poh_config,
         poh_config,
         fee_rate_governor,
         fee_rate_governor,

+ 0 - 1
local-cluster/Cargo.toml

@@ -21,7 +21,6 @@ solana-config-program = { path = "../programs/config", version = "=1.8.0" }
 solana-core = { path = "../core", version = "=1.8.0" }
 solana-core = { path = "../core", version = "=1.8.0" }
 solana-client = { path = "../client", version = "=1.8.0" }
 solana-client = { path = "../client", version = "=1.8.0" }
 solana-download-utils = { path = "../download-utils", version = "=1.8.0" }
 solana-download-utils = { path = "../download-utils", version = "=1.8.0" }
-solana-exchange-program = { path = "../programs/exchange", version = "=1.8.0" }
 solana-faucet = { path = "../faucet", version = "=1.8.0" }
 solana-faucet = { path = "../faucet", version = "=1.8.0" }
 solana-gossip = { path = "../gossip", version = "=1.8.0" }
 solana-gossip = { path = "../gossip", version = "=1.8.0" }
 solana-ledger = { path = "../ledger", version = "=1.8.0" }
 solana-ledger = { path = "../ledger", version = "=1.8.0" }

+ 2 - 13
net/net.sh

@@ -20,7 +20,6 @@ usage() {
                                      Valid client types are:
                                      Valid client types are:
                                          idle
                                          idle
                                          bench-tps
                                          bench-tps
-                                         bench-exchange
                                      User can optionally provide extraArgs that are transparently
                                      User can optionally provide extraArgs that are transparently
                                      supplied to the client program as command line parameters.
                                      supplied to the client program as command line parameters.
                                      For example,
                                      For example,
@@ -307,7 +306,6 @@ startBootstrapLeader() {
          \"$internalNodesLamports\" \
          \"$internalNodesLamports\" \
          $nodeIndex \
          $nodeIndex \
          ${#clientIpList[@]} \"$benchTpsExtraArgs\" \
          ${#clientIpList[@]} \"$benchTpsExtraArgs\" \
-         ${#clientIpList[@]} \"$benchExchangeExtraArgs\" \
          \"$genesisOptions\" \
          \"$genesisOptions\" \
          \"$maybeNoSnapshot $maybeSkipLedgerVerify $maybeLimitLedgerSize $maybeWaitForSupermajority\" \
          \"$maybeNoSnapshot $maybeSkipLedgerVerify $maybeLimitLedgerSize $maybeWaitForSupermajority\" \
          \"$gpuMode\" \
          \"$gpuMode\" \
@@ -379,7 +377,6 @@ startNode() {
          \"$internalNodesLamports\" \
          \"$internalNodesLamports\" \
          $nodeIndex \
          $nodeIndex \
          ${#clientIpList[@]} \"$benchTpsExtraArgs\" \
          ${#clientIpList[@]} \"$benchTpsExtraArgs\" \
-         ${#clientIpList[@]} \"$benchExchangeExtraArgs\" \
          \"$genesisOptions\" \
          \"$genesisOptions\" \
          \"$maybeNoSnapshot $maybeSkipLedgerVerify $maybeLimitLedgerSize $maybeWaitForSupermajority\" \
          \"$maybeNoSnapshot $maybeSkipLedgerVerify $maybeLimitLedgerSize $maybeWaitForSupermajority\" \
          \"$gpuMode\" \
          \"$gpuMode\" \
@@ -409,7 +406,7 @@ startClient() {
     startCommon "$ipAddress"
     startCommon "$ipAddress"
     ssh "${sshOptions[@]}" -f "$ipAddress" \
     ssh "${sshOptions[@]}" -f "$ipAddress" \
       "./solana/net/remote/remote-client.sh $deployMethod $entrypointIp \
       "./solana/net/remote/remote-client.sh $deployMethod $entrypointIp \
-      $clientToRun \"$RUST_LOG\" \"$benchTpsExtraArgs\" \"$benchExchangeExtraArgs\" $clientIndex"
+      $clientToRun \"$RUST_LOG\" \"$benchTpsExtraArgs\" $clientIndex"
   ) >> "$logFile" 2>&1 || {
   ) >> "$logFile" 2>&1 || {
     cat "$logFile"
     cat "$logFile"
     echo "^^^ +++"
     echo "^^^ +++"
@@ -421,8 +418,6 @@ startClients() {
   for ((i=0; i < "$numClients" && i < "$numClientsRequested"; i++)) do
   for ((i=0; i < "$numClients" && i < "$numClientsRequested"; i++)) do
     if [[ $i -lt "$numBenchTpsClients" ]]; then
     if [[ $i -lt "$numBenchTpsClients" ]]; then
       startClient "${clientIpList[$i]}" "solana-bench-tps" "$i"
       startClient "${clientIpList[$i]}" "solana-bench-tps" "$i"
-    elif [[ $i -lt $((numBenchTpsClients + numBenchExchangeClients)) ]]; then
-      startClient "${clientIpList[$i]}" "solana-bench-exchange" $((i-numBenchTpsClients))
     else
     else
       startClient "${clientIpList[$i]}" "idle"
       startClient "${clientIpList[$i]}" "idle"
     fi
     fi
@@ -767,9 +762,7 @@ updatePlatforms=
 nodeAddress=
 nodeAddress=
 numIdleClients=0
 numIdleClients=0
 numBenchTpsClients=0
 numBenchTpsClients=0
-numBenchExchangeClients=0
 benchTpsExtraArgs=
 benchTpsExtraArgs=
-benchExchangeExtraArgs=
 failOnValidatorBootupFailure=true
 failOnValidatorBootupFailure=true
 genesisOptions=
 genesisOptions=
 numValidatorsRequested=
 numValidatorsRequested=
@@ -977,10 +970,6 @@ while getopts "h?T:t:o:f:rc:Fn:i:d" opt "${shortArgs[@]}"; do
           numBenchTpsClients=$numClients
           numBenchTpsClients=$numClients
           benchTpsExtraArgs=$extraArgs
           benchTpsExtraArgs=$extraArgs
         ;;
         ;;
-        bench-exchange)
-          numBenchExchangeClients=$numClients
-          benchExchangeExtraArgs=$extraArgs
-        ;;
         *)
         *)
           echo "Unknown client type: $clientType"
           echo "Unknown client type: $clientType"
           exit 1
           exit 1
@@ -1013,7 +1002,7 @@ if [[ -n $numValidatorsRequested ]]; then
 fi
 fi
 
 
 numClients=${#clientIpList[@]}
 numClients=${#clientIpList[@]}
-numClientsRequested=$((numBenchTpsClients + numBenchExchangeClients + numIdleClients))
+numClientsRequested=$((numBenchTpsClients + numIdleClients))
 if [[ "$numClientsRequested" -eq 0 ]]; then
 if [[ "$numClientsRequested" -eq 0 ]]; then
   numBenchTpsClients=$numClients
   numBenchTpsClients=$numClients
   numClientsRequested=$numClients
   numClientsRequested=$numClients

+ 1 - 19
net/remote/remote-client.sh

@@ -10,8 +10,7 @@ if [[ -n $4 ]]; then
   export RUST_LOG="$4"
   export RUST_LOG="$4"
 fi
 fi
 benchTpsExtraArgs="$5"
 benchTpsExtraArgs="$5"
-benchExchangeExtraArgs="$6"
-clientIndex="$7"
+clientIndex="$6"
 
 
 missing() {
 missing() {
   echo "Error: $1 not specified"
   echo "Error: $1 not specified"
@@ -57,23 +56,6 @@ solana-bench-tps)
       --read-client-keys ./client-accounts.yml \
       --read-client-keys ./client-accounts.yml \
   "
   "
   ;;
   ;;
-solana-bench-exchange)
-  solana-keygen new --no-passphrase -fso bench.keypair
-  net/scripts/rsync-retry.sh -vPrc \
-    "$entrypointIp":~/solana/config/bench-exchange"$clientIndex".yml ./client-accounts.yml
-  clientCommand="\
-    solana-bench-exchange \
-      --entrypoint $entrypointIp:8001 \
-      --faucet $entrypointIp:9900 \
-      --threads $threadCount \
-      --batch-size 1000 \
-      --fund-amount 20000 \
-      --duration 7500 \
-      --identity bench.keypair \
-      $benchExchangeExtraArgs \
-      --read-client-keys ./client-accounts.yml \
-  "
-  ;;
 idle)
 idle)
   # Add the faucet keypair to idle clients for convenience
   # Add the faucet keypair to idle clients for convenience
   net/scripts/rsync-retry.sh -vPrc \
   net/scripts/rsync-retry.sh -vPrc \

+ 7 - 16
net/remote/remote-node.sh

@@ -20,15 +20,13 @@ internalNodesLamports="${11}"
 nodeIndex="${12}"
 nodeIndex="${12}"
 numBenchTpsClients="${13}"
 numBenchTpsClients="${13}"
 benchTpsExtraArgs="${14}"
 benchTpsExtraArgs="${14}"
-numBenchExchangeClients="${15}"
-benchExchangeExtraArgs="${16}"
-genesisOptions="${17}"
-extraNodeArgs="${18}"
-gpuMode="${19:-auto}"
-maybeWarpSlot="${20}"
-waitForNodeInit="${21}"
-extraPrimordialStakes="${22:=0}"
-tmpfsAccounts="${23:false}"
+genesisOptions="${15}"
+extraNodeArgs="${16}"
+gpuMode="${17:-auto}"
+maybeWarpSlot="${18}"
+waitForNodeInit="${19}"
+extraPrimordialStakes="${20:=0}"
+tmpfsAccounts="${21:false}"
 set +x
 set +x
 
 
 missing() {
 missing() {
@@ -194,13 +192,6 @@ EOF
         tail -n +2 -q config/bench-tps"$i".yml >> config/client-accounts.yml
         tail -n +2 -q config/bench-tps"$i".yml >> config/client-accounts.yml
         echo "" >> config/client-accounts.yml
         echo "" >> config/client-accounts.yml
       done
       done
-      for i in $(seq 0 $((numBenchExchangeClients-1))); do
-        # shellcheck disable=SC2086 # Do not want to quote $benchExchangeExtraArgs
-        solana-bench-exchange --batch-size 1000 --fund-amount 20000 \
-          --write-client-keys config/bench-exchange"$i".yml $benchExchangeExtraArgs
-        tail -n +2 -q config/bench-exchange"$i".yml >> config/client-accounts.yml
-        echo "" >> config/client-accounts.yml
-      done
       if [[ -f $externalPrimordialAccountsFile ]]; then
       if [[ -f $externalPrimordialAccountsFile ]]; then
         cat "$externalPrimordialAccountsFile" >> config/validator-balances.yml
         cat "$externalPrimordialAccountsFile" >> config/validator-balances.yml
       fi
       fi

+ 0 - 32
programs/exchange/Cargo.toml

@@ -1,32 +0,0 @@
-[package]
-name = "solana-exchange-program"
-version = "1.8.0"
-description = "Solana Exchange program"
-authors = ["Solana Maintainers <maintainers@solana.foundation>"]
-repository = "https://github.com/solana-labs/solana"
-license = "Apache-2.0"
-homepage = "https://solana.com/"
-documentation = "https://docs.rs/solana-exchange-program"
-edition = "2018"
-
-[dependencies]
-bincode = "1.3.3"
-log = "0.4.14"
-num-derive = { version = "0.3" }
-num-traits = { version = "0.2" }
-serde = "1.0.126"
-serde_derive = "1.0.103"
-solana-logger = { path = "../../logger", version = "=1.8.0" }
-solana-metrics = { path = "../../metrics", version = "=1.8.0" }
-solana-sdk = { path = "../../sdk", version = "=1.8.0" }
-thiserror = "1.0"
-
-[dev-dependencies]
-solana-runtime = { path = "../../runtime", version = "=1.8.0" }
-
-[lib]
-crate-type = ["lib", "cdylib"]
-name = "solana_exchange_program"
-
-[package.metadata.docs.rs]
-targets = ["x86_64-unknown-linux-gnu"]

+ 0 - 131
programs/exchange/src/exchange_instruction.rs

@@ -1,131 +0,0 @@
-//! Exchange program
-
-use crate::exchange_state::*;
-use crate::id;
-use serde_derive::{Deserialize, Serialize};
-use solana_sdk::instruction::{AccountMeta, Instruction};
-use solana_sdk::pubkey::Pubkey;
-
-#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
-pub struct OrderRequestInfo {
-    /// Side of market of the order (bid/ask)
-    pub side: OrderSide,
-
-    /// Token pair to trade
-    pub pair: AssetPair,
-
-    /// Number of tokens to exchange; refers to the primary or the secondary depending on the order side
-    pub tokens: u64,
-
-    /// The price ratio the primary price over the secondary price.  The primary price is fixed
-    /// and equal to the variable `SCALER`.
-    pub price: u64,
-}
-
-#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
-pub enum ExchangeInstruction {
-    /// New token account
-    /// key 0 - Signer
-    /// key 1 - New token account
-    AccountRequest,
-
-    /// Transfer tokens between two accounts
-    /// key 0 - Account to transfer tokens to
-    /// key 1 - Account to transfer tokens from.  This can be the exchange program itself,
-    ///         the exchange has a limitless number of tokens it can transfer.
-    TransferRequest(Token, u64),
-
-    /// Order request
-    /// key 0 - Signer
-    /// key 1 - Account in which to record the trade order
-    /// key 2 - Token account to source tokens from
-    OrderRequest(OrderRequestInfo),
-
-    /// Order cancellation
-    /// key 0 - Signer
-    /// key 1 - Order to cancel
-    OrderCancellation,
-
-    /// Trade swap request
-    /// key 0 - Signer
-    /// key 2 - 'To' trade order
-    /// key 3 - `From` trade order
-    /// key 6 - Token account in which to deposit the brokers profit from the swap.
-    SwapRequest,
-}
-
-pub fn account_request(owner: &Pubkey, new: &Pubkey) -> Instruction {
-    let account_metas = vec![
-        AccountMeta::new(*owner, true),
-        AccountMeta::new(*new, false),
-    ];
-    Instruction::new_with_bincode(id(), &ExchangeInstruction::AccountRequest, account_metas)
-}
-
-pub fn transfer_request(
-    owner: &Pubkey,
-    to: &Pubkey,
-    from: &Pubkey,
-    token: Token,
-    tokens: u64,
-) -> Instruction {
-    let account_metas = vec![
-        AccountMeta::new(*owner, true),
-        AccountMeta::new(*to, false),
-        AccountMeta::new(*from, false),
-    ];
-    Instruction::new_with_bincode(
-        id(),
-        &ExchangeInstruction::TransferRequest(token, tokens),
-        account_metas,
-    )
-}
-
-pub fn trade_request(
-    owner: &Pubkey,
-    trade: &Pubkey,
-    side: OrderSide,
-    pair: AssetPair,
-    tokens: u64,
-    price: u64,
-    src_account: &Pubkey,
-) -> Instruction {
-    let account_metas = vec![
-        AccountMeta::new(*owner, true),
-        AccountMeta::new(*trade, false),
-        AccountMeta::new(*src_account, false),
-    ];
-    Instruction::new_with_bincode(
-        id(),
-        &ExchangeInstruction::OrderRequest(OrderRequestInfo {
-            side,
-            pair,
-            tokens,
-            price,
-        }),
-        account_metas,
-    )
-}
-
-pub fn order_cancellation(owner: &Pubkey, order: &Pubkey) -> Instruction {
-    let account_metas = vec![
-        AccountMeta::new(*owner, true),
-        AccountMeta::new(*order, false),
-    ];
-    Instruction::new_with_bincode(id(), &ExchangeInstruction::OrderCancellation, account_metas)
-}
-
-pub fn swap_request(
-    owner: &Pubkey,
-    to_trade: &Pubkey,
-    from_trade: &Pubkey,
-    profit_account: &Pubkey,
-) -> Instruction {
-    let account_metas = vec![
-        AccountMeta::new(*owner, true),
-        AccountMeta::new(*to_trade, false),
-        AccountMeta::new(*from_trade, false),
-        AccountMeta::new(*profit_account, false),
-    ];
-    Instruction::new_with_bincode(id(), &ExchangeInstruction::SwapRequest, account_metas)
-}

+ 0 - 920
programs/exchange/src/exchange_processor.rs

@@ -1,920 +0,0 @@
-//! Config processor
-
-use crate::exchange_instruction::*;
-use crate::exchange_state::*;
-use crate::faucet;
-use log::*;
-use num_derive::{FromPrimitive, ToPrimitive};
-use serde_derive::Serialize;
-use solana_metrics::inc_new_counter_info;
-use solana_sdk::{
-    account::{ReadableAccount, WritableAccount},
-    decode_error::DecodeError,
-    instruction::InstructionError,
-    keyed_account::KeyedAccount,
-    process_instruction::InvokeContext,
-    program_utils::limited_deserialize,
-    pubkey::Pubkey,
-};
-use std::cmp;
-use thiserror::Error;
-
-#[derive(Error, Debug, Serialize, Clone, PartialEq, FromPrimitive, ToPrimitive)]
-pub enum ExchangeError {
-    #[error("Signer does not own account")]
-    SignerDoesNotOwnAccount,
-    #[error("Signer does not own order")]
-    SignerDoesNotOwnOrder,
-    #[error("The From account balance is too low")]
-    FromAccountBalanceTooLow,
-    #[error("Attmept operation on mismatched tokens")]
-    TokenMismatch,
-    #[error("From trade balance is too low")]
-    FromTradeBalanceTooLow,
-    #[error("Serialization failed")]
-    SerializeFailed,
-}
-impl<T> DecodeError<T> for ExchangeError {
-    fn type_of() -> &'static str {
-        "ExchangeError"
-    }
-}
-
-pub struct ExchangeProcessor {}
-
-impl ExchangeProcessor {
-    #[allow(clippy::needless_pass_by_value)]
-    fn map_to_invalid_arg(err: std::boxed::Box<bincode::ErrorKind>) -> InstructionError {
-        warn!("Deserialize failed, not a valid state: {:?}", err);
-        InstructionError::InvalidArgument
-    }
-
-    fn is_account_unallocated(data: &[u8]) -> Result<(), InstructionError> {
-        let state: ExchangeState = bincode::deserialize(data).map_err(Self::map_to_invalid_arg)?;
-        if let ExchangeState::Unallocated = state {
-            Ok(())
-        } else {
-            error!("New account is already in use");
-            Err(InstructionError::InvalidAccountData)
-        }
-    }
-
-    fn deserialize_account(data: &[u8]) -> Result<TokenAccountInfo, InstructionError> {
-        let state: ExchangeState = bincode::deserialize(data).map_err(Self::map_to_invalid_arg)?;
-        if let ExchangeState::Account(account) = state {
-            Ok(account)
-        } else {
-            error!("Not a valid account");
-            Err(InstructionError::InvalidAccountData)
-        }
-    }
-
-    fn deserialize_order(data: &[u8]) -> Result<OrderInfo, InstructionError> {
-        let state: ExchangeState = bincode::deserialize(data).map_err(Self::map_to_invalid_arg)?;
-        if let ExchangeState::Trade(info) = state {
-            Ok(info)
-        } else {
-            error!("Not a valid trade");
-            Err(InstructionError::InvalidAccountData)
-        }
-    }
-
-    fn serialize(state: &ExchangeState, data: &mut [u8]) -> Result<(), InstructionError> {
-        let writer = std::io::BufWriter::new(data);
-        match bincode::serialize_into(writer, state) {
-            Ok(_) => Ok(()),
-            Err(e) => {
-                error!("Serialize failed: {:?}", e);
-                Err(ExchangeError::SerializeFailed.into())
-            }
-        }
-    }
-
-    fn trade_to_token_account(trade: &OrderInfo) -> TokenAccountInfo {
-        // Turn trade order into token account
-
-        let token = match trade.side {
-            OrderSide::Ask => trade.pair.Quote,
-            OrderSide::Bid => trade.pair.Base,
-        };
-
-        let mut account = TokenAccountInfo::default().owner(&trade.owner);
-        account.tokens[token] = trade.tokens_settled;
-        account
-    }
-
-    fn calculate_swap(
-        scaler: u64,
-        to_trade: &mut OrderInfo,
-        from_trade: &mut OrderInfo,
-        profit_account: &mut TokenAccountInfo,
-    ) -> Result<(), InstructionError> {
-        if to_trade.tokens == 0 || from_trade.tokens == 0 {
-            error!("Inactive Trade, balance is zero");
-            return Err(InstructionError::InvalidArgument);
-        }
-        if to_trade.price == 0 || from_trade.price == 0 {
-            error!("Inactive Trade, price is zero");
-            return Err(InstructionError::InvalidArgument);
-        }
-
-        // Calc swap
-
-        trace!("tt {} ft {}", to_trade.tokens, from_trade.tokens);
-        trace!("tp {} fp {}", to_trade.price, from_trade.price);
-
-        let max_to_secondary = to_trade.tokens * to_trade.price / scaler;
-        let max_to_primary = from_trade.tokens * scaler / from_trade.price;
-
-        trace!("mtp {} mts {}", max_to_primary, max_to_secondary);
-
-        let max_primary = cmp::min(max_to_primary, to_trade.tokens);
-        let max_secondary = cmp::min(max_to_secondary, from_trade.tokens);
-
-        trace!("mp {} ms {}", max_primary, max_secondary);
-
-        let primary_tokens = if max_secondary < max_primary {
-            max_secondary * scaler / from_trade.price
-        } else {
-            max_primary
-        };
-        let secondary_tokens = if max_secondary < max_primary {
-            max_secondary
-        } else {
-            max_primary * to_trade.price / scaler
-        };
-
-        if primary_tokens == 0 || secondary_tokens == 0 {
-            error!("Trade quantities to low to be fulfilled");
-            return Err(InstructionError::InvalidArgument);
-        }
-
-        trace!("pt {} st {}", primary_tokens, secondary_tokens);
-
-        let primary_cost = cmp::max(primary_tokens, secondary_tokens * scaler / to_trade.price);
-        let secondary_cost = cmp::max(secondary_tokens, primary_tokens * from_trade.price / scaler);
-
-        trace!("pc {} sc {}", primary_cost, secondary_cost);
-
-        let primary_profit = primary_cost - primary_tokens;
-        let secondary_profit = secondary_cost - secondary_tokens;
-
-        trace!("pp {} sp {}", primary_profit, secondary_profit);
-
-        let primary_token = to_trade.pair.Base;
-        let secondary_token = from_trade.pair.Quote;
-
-        // Update tokens
-
-        if to_trade.tokens < primary_cost {
-            error!("Not enough tokens in to account");
-            return Err(InstructionError::InvalidArgument);
-        }
-        if from_trade.tokens < secondary_cost {
-            error!("Not enough tokens in from account");
-            return Err(InstructionError::InvalidArgument);
-        }
-        to_trade.tokens -= primary_cost;
-        to_trade.tokens_settled += secondary_tokens;
-        from_trade.tokens -= secondary_cost;
-        from_trade.tokens_settled += primary_tokens;
-
-        profit_account.tokens[primary_token] += primary_profit;
-        profit_account.tokens[secondary_token] += secondary_profit;
-
-        Ok(())
-    }
-
-    fn do_account_request(keyed_accounts: &[KeyedAccount]) -> Result<(), InstructionError> {
-        const OWNER_INDEX: usize = 0;
-        const NEW_ACCOUNT_INDEX: usize = 1;
-
-        if keyed_accounts.len() < 2 {
-            error!("Not enough accounts");
-            return Err(InstructionError::InvalidArgument);
-        }
-        Self::is_account_unallocated(keyed_accounts[NEW_ACCOUNT_INDEX].try_account_ref()?.data())?;
-        Self::serialize(
-            &ExchangeState::Account(
-                TokenAccountInfo::default()
-                    .owner(keyed_accounts[OWNER_INDEX].unsigned_key())
-                    .tokens(100_000, 100_000, 100_000, 100_000),
-            ),
-            &mut keyed_accounts[NEW_ACCOUNT_INDEX]
-                .try_account_ref_mut()?
-                .data_as_mut_slice(),
-        )
-    }
-
-    fn do_transfer_request(
-        keyed_accounts: &[KeyedAccount],
-        token: Token,
-        tokens: u64,
-    ) -> Result<(), InstructionError> {
-        const OWNER_INDEX: usize = 0;
-        const TO_ACCOUNT_INDEX: usize = 1;
-        const FROM_ACCOUNT_INDEX: usize = 2;
-
-        if keyed_accounts.len() < 3 {
-            error!("Not enough accounts");
-            return Err(InstructionError::InvalidArgument);
-        }
-
-        let mut to_account =
-            Self::deserialize_account(keyed_accounts[TO_ACCOUNT_INDEX].try_account_ref()?.data())?;
-
-        if &faucet::id() == keyed_accounts[FROM_ACCOUNT_INDEX].unsigned_key() {
-            to_account.tokens[token] += tokens;
-        } else {
-            let state: ExchangeState =
-                bincode::deserialize(keyed_accounts[FROM_ACCOUNT_INDEX].try_account_ref()?.data())
-                    .map_err(Self::map_to_invalid_arg)?;
-            match state {
-                ExchangeState::Account(mut from_account) => {
-                    if &from_account.owner != keyed_accounts[OWNER_INDEX].unsigned_key() {
-                        error!("Signer does not own from account");
-                        return Err(ExchangeError::SignerDoesNotOwnAccount.into());
-                    }
-
-                    if from_account.tokens[token] < tokens {
-                        error!("From account balance too low");
-                        return Err(ExchangeError::FromAccountBalanceTooLow.into());
-                    }
-
-                    from_account.tokens[token] -= tokens;
-                    to_account.tokens[token] += tokens;
-
-                    Self::serialize(
-                        &ExchangeState::Account(from_account),
-                        &mut keyed_accounts[FROM_ACCOUNT_INDEX]
-                            .try_account_ref_mut()?
-                            .data_as_mut_slice(),
-                    )?;
-                }
-                ExchangeState::Trade(mut from_trade) => {
-                    if &from_trade.owner != keyed_accounts[OWNER_INDEX].unsigned_key() {
-                        error!("Signer does not own from account");
-                        return Err(ExchangeError::SignerDoesNotOwnAccount.into());
-                    }
-
-                    let from_token = match from_trade.side {
-                        OrderSide::Ask => from_trade.pair.Quote,
-                        OrderSide::Bid => from_trade.pair.Base,
-                    };
-                    if token != from_token {
-                        error!("Trade to transfer from does not hold correct token");
-                        return Err(ExchangeError::TokenMismatch.into());
-                    }
-
-                    if from_trade.tokens_settled < tokens {
-                        error!("From trade balance too low");
-                        return Err(ExchangeError::FromTradeBalanceTooLow.into());
-                    }
-
-                    from_trade.tokens_settled -= tokens;
-                    to_account.tokens[token] += tokens;
-
-                    Self::serialize(
-                        &ExchangeState::Trade(from_trade),
-                        &mut keyed_accounts[FROM_ACCOUNT_INDEX]
-                            .try_account_ref_mut()?
-                            .data_as_mut_slice(),
-                    )?;
-                }
-                _ => {
-                    error!("Not a valid from account for transfer");
-                    return Err(InstructionError::InvalidArgument);
-                }
-            }
-        }
-
-        Self::serialize(
-            &ExchangeState::Account(to_account),
-            &mut keyed_accounts[TO_ACCOUNT_INDEX]
-                .try_account_ref_mut()?
-                .data_as_mut_slice(),
-        )
-    }
-
-    fn do_order_request(
-        keyed_accounts: &[KeyedAccount],
-        info: &OrderRequestInfo,
-    ) -> Result<(), InstructionError> {
-        const OWNER_INDEX: usize = 0;
-        const ORDER_INDEX: usize = 1;
-        const ACCOUNT_INDEX: usize = 2;
-
-        if keyed_accounts.len() < 3 {
-            error!("Not enough accounts");
-            return Err(InstructionError::InvalidArgument);
-        }
-
-        Self::is_account_unallocated(keyed_accounts[ORDER_INDEX].try_account_ref()?.data())?;
-
-        let mut account =
-            Self::deserialize_account(keyed_accounts[ACCOUNT_INDEX].try_account_ref_mut()?.data())?;
-
-        if &account.owner != keyed_accounts[OWNER_INDEX].unsigned_key() {
-            error!("Signer does not own account");
-            return Err(ExchangeError::SignerDoesNotOwnAccount.into());
-        }
-        let from_token = match info.side {
-            OrderSide::Ask => info.pair.Base,
-            OrderSide::Bid => info.pair.Quote,
-        };
-        if account.tokens[from_token] < info.tokens {
-            error!("From token balance is too low");
-            return Err(ExchangeError::FromAccountBalanceTooLow.into());
-        }
-
-        if let Err(e) = check_trade(info.side, info.tokens, info.price) {
-            bincode::serialize(&e).unwrap();
-        }
-
-        // Trade holds the tokens in escrow
-        account.tokens[from_token] -= info.tokens;
-
-        inc_new_counter_info!("exchange_processor-trades", 1);
-
-        Self::serialize(
-            &ExchangeState::Trade(OrderInfo {
-                owner: *keyed_accounts[OWNER_INDEX].unsigned_key(),
-                side: info.side,
-                pair: info.pair,
-                tokens: info.tokens,
-                price: info.price,
-                tokens_settled: 0,
-            }),
-            &mut keyed_accounts[ORDER_INDEX]
-                .try_account_ref_mut()?
-                .data_as_mut_slice(),
-        )?;
-        Self::serialize(
-            &ExchangeState::Account(account),
-            &mut keyed_accounts[ACCOUNT_INDEX]
-                .try_account_ref_mut()?
-                .data_as_mut_slice(),
-        )
-    }
-
-    fn do_order_cancellation(keyed_accounts: &[KeyedAccount]) -> Result<(), InstructionError> {
-        const OWNER_INDEX: usize = 0;
-        const ORDER_INDEX: usize = 1;
-
-        if keyed_accounts.len() < 2 {
-            error!("Not enough accounts");
-            return Err(InstructionError::InvalidArgument);
-        }
-
-        let order = Self::deserialize_order(keyed_accounts[ORDER_INDEX].try_account_ref()?.data())?;
-
-        if &order.owner != keyed_accounts[OWNER_INDEX].unsigned_key() {
-            error!("Signer does not own order");
-            return Err(ExchangeError::SignerDoesNotOwnOrder.into());
-        }
-
-        let token = match order.side {
-            OrderSide::Ask => order.pair.Base,
-            OrderSide::Bid => order.pair.Quote,
-        };
-
-        let mut account = TokenAccountInfo::default().owner(&order.owner);
-        account.tokens[token] = order.tokens;
-        account.tokens[token] += order.tokens_settled;
-
-        // Turn trade order into a token account
-        Self::serialize(
-            &ExchangeState::Account(account),
-            &mut keyed_accounts[ORDER_INDEX]
-                .try_account_ref_mut()?
-                .data_as_mut_slice(),
-        )
-    }
-
-    fn do_swap_request(keyed_accounts: &[KeyedAccount]) -> Result<(), InstructionError> {
-        const TO_ORDER_INDEX: usize = 1;
-        const FROM_ORDER_INDEX: usize = 2;
-        const PROFIT_ACCOUNT_INDEX: usize = 3;
-
-        if keyed_accounts.len() < 4 {
-            error!("Not enough accounts");
-            return Err(InstructionError::InvalidArgument);
-        }
-
-        let mut to_order =
-            Self::deserialize_order(keyed_accounts[TO_ORDER_INDEX].try_account_ref()?.data())?;
-        let mut from_order =
-            Self::deserialize_order(keyed_accounts[FROM_ORDER_INDEX].try_account_ref()?.data())?;
-        let mut profit_account = Self::deserialize_account(
-            keyed_accounts[PROFIT_ACCOUNT_INDEX]
-                .try_account_ref()?
-                .data(),
-        )?;
-
-        if to_order.side != OrderSide::Ask {
-            error!("To trade is not a To");
-            return Err(InstructionError::InvalidArgument);
-        }
-        if from_order.side != OrderSide::Bid {
-            error!("From trade is not a From");
-            return Err(InstructionError::InvalidArgument);
-        }
-        if to_order.pair != from_order.pair {
-            error!("Mismatched token pairs");
-            return Err(InstructionError::InvalidArgument);
-        }
-        if to_order.side == from_order.side {
-            error!("Matching trade sides");
-            return Err(InstructionError::InvalidArgument);
-        }
-
-        if let Err(e) =
-            Self::calculate_swap(SCALER, &mut to_order, &mut from_order, &mut profit_account)
-        {
-            error!(
-                "Swap calculation failed from {} for {} to {} for {}",
-                from_order.tokens, from_order.price, to_order.tokens, to_order.price,
-            );
-            return Err(e);
-        }
-
-        inc_new_counter_info!("exchange_processor-swaps", 1);
-
-        if to_order.tokens == 0 {
-            // Turn into token account
-            Self::serialize(
-                &ExchangeState::Account(Self::trade_to_token_account(&from_order)),
-                &mut keyed_accounts[TO_ORDER_INDEX]
-                    .try_account_ref_mut()?
-                    .data_as_mut_slice(),
-            )?;
-        } else {
-            Self::serialize(
-                &ExchangeState::Trade(to_order),
-                &mut keyed_accounts[TO_ORDER_INDEX]
-                    .try_account_ref_mut()?
-                    .data_as_mut_slice(),
-            )?;
-        }
-
-        if from_order.tokens == 0 {
-            // Turn into token account
-            Self::serialize(
-                &ExchangeState::Account(Self::trade_to_token_account(&from_order)),
-                &mut keyed_accounts[FROM_ORDER_INDEX]
-                    .try_account_ref_mut()?
-                    .data_as_mut_slice(),
-            )?;
-        } else {
-            Self::serialize(
-                &ExchangeState::Trade(from_order),
-                &mut keyed_accounts[FROM_ORDER_INDEX]
-                    .try_account_ref_mut()?
-                    .data_as_mut_slice(),
-            )?;
-        }
-
-        Self::serialize(
-            &ExchangeState::Account(profit_account),
-            &mut keyed_accounts[PROFIT_ACCOUNT_INDEX]
-                .try_account_ref_mut()?
-                .data_as_mut_slice(),
-        )
-    }
-}
-
-pub fn process_instruction(
-    _program_id: &Pubkey,
-    data: &[u8],
-    invoke_context: &mut dyn InvokeContext,
-) -> Result<(), InstructionError> {
-    let keyed_accounts = invoke_context.get_keyed_accounts()?;
-
-    solana_logger::setup();
-    match limited_deserialize::<ExchangeInstruction>(data)? {
-        ExchangeInstruction::AccountRequest => {
-            ExchangeProcessor::do_account_request(keyed_accounts)
-        }
-        ExchangeInstruction::TransferRequest(token, tokens) => {
-            ExchangeProcessor::do_transfer_request(keyed_accounts, token, tokens)
-        }
-        ExchangeInstruction::OrderRequest(info) => {
-            ExchangeProcessor::do_order_request(keyed_accounts, &info)
-        }
-        ExchangeInstruction::OrderCancellation => {
-            ExchangeProcessor::do_order_cancellation(keyed_accounts)
-        }
-        ExchangeInstruction::SwapRequest => ExchangeProcessor::do_swap_request(keyed_accounts),
-    }
-}
-
-#[cfg(test)]
-mod test {
-    use super::*;
-    use crate::{exchange_instruction, id};
-    use solana_runtime::bank::Bank;
-    use solana_runtime::bank_client::BankClient;
-    use solana_sdk::client::SyncClient;
-    use solana_sdk::genesis_config::create_genesis_config;
-    use solana_sdk::message::Message;
-    use solana_sdk::signature::{Keypair, Signer};
-    use solana_sdk::system_instruction;
-    use std::mem;
-
-    #[allow(clippy::too_many_arguments)]
-    fn try_calc(
-        scaler: u64,
-        primary_tokens: u64,
-        primary_price: u64,
-        secondary_tokens: u64,
-        secondary_price: u64,
-        primary_tokens_expect: u64,
-        secondary_tokens_expect: u64,
-        primary_tokens_settled_expect: u64,
-        secondary_tokens_settled_expect: u64,
-        profit_account_tokens: Tokens,
-    ) -> Result<(), InstructionError> {
-        trace!(
-            "Swap {} for {} to {} for {}",
-            primary_tokens,
-            primary_price,
-            secondary_tokens,
-            secondary_price,
-        );
-        let mut to_trade = OrderInfo::default();
-        let mut from_trade = OrderInfo::default().side(OrderSide::Bid);
-        let mut profit_account = TokenAccountInfo::default();
-
-        to_trade.tokens = primary_tokens;
-        to_trade.price = primary_price;
-        from_trade.tokens = secondary_tokens;
-        from_trade.price = secondary_price;
-        ExchangeProcessor::calculate_swap(
-            scaler,
-            &mut to_trade,
-            &mut from_trade,
-            &mut profit_account,
-        )?;
-
-        trace!(
-            "{:?} {:?} {:?} {:?}\n{:?}\n{:?}\n{:?}\n{:?}",
-            to_trade.tokens,
-            primary_tokens_expect,
-            from_trade.tokens,
-            secondary_tokens_expect,
-            primary_tokens_settled_expect,
-            secondary_tokens_settled_expect,
-            profit_account.tokens,
-            profit_account_tokens
-        );
-
-        assert_eq!(to_trade.tokens, primary_tokens_expect);
-        assert_eq!(from_trade.tokens, secondary_tokens_expect);
-        assert_eq!(to_trade.tokens_settled, primary_tokens_settled_expect);
-        assert_eq!(from_trade.tokens_settled, secondary_tokens_settled_expect);
-        assert_eq!(profit_account.tokens, profit_account_tokens);
-        Ok(())
-    }
-
-    #[test]
-    #[rustfmt::skip]
-    fn test_calculate_swap() {
-        solana_logger::setup();
-
-        try_calc(1,     50,     2,   50,    1,  0, 0, 50,   50, Tokens::new(   0, 0, 0, 0)).unwrap_err();
-        try_calc(1,     50,     1,    0,    1,  0, 0, 50,   50, Tokens::new(   0, 0, 0, 0)).unwrap_err();
-        try_calc(1,      0,     1,   50,    1,  0, 0, 50,   50, Tokens::new(   0, 0, 0, 0)).unwrap_err();
-        try_calc(1,     50,     1,   50,    0,  0, 0, 50,   50, Tokens::new(   0, 0, 0, 0)).unwrap_err();
-        try_calc(1,     50,     0,   50,    1,  0, 0, 50,   50, Tokens::new(   0, 0, 0, 0)).unwrap_err();
-        try_calc(1,       1,    2,    2,    3,  1, 2,  0,    0, Tokens::new(   0, 0, 0, 0)).unwrap_err();
-
-        try_calc(1,     50,     1,   50,    1,  0, 0, 50,   50, Tokens::new(   0, 0, 0, 0)).unwrap();
-        try_calc(1,       1,    2,    3,    3,  0, 0,  2,    1, Tokens::new(   0, 1, 0, 0)).unwrap();
-        try_calc(1,       2,    2,    3,    3,  1, 0,  2,    1, Tokens::new(   0, 1, 0, 0)).unwrap();
-        try_calc(1,       3,    2,    3,    3,  2, 0,  2,    1, Tokens::new(   0, 1, 0, 0)).unwrap();
-        try_calc(1,       3,    2,    6,    3,  1, 0,  4,    2, Tokens::new(   0, 2, 0, 0)).unwrap();
-        try_calc(1000,    1, 2000,    3, 3000,  0, 0,  2,    1, Tokens::new(   0, 1, 0, 0)).unwrap();
-        try_calc(1,       3,    2,    7,    3,  1, 1,  4,    2, Tokens::new(   0, 2, 0, 0)).unwrap();
-        try_calc(1000, 3000,  333, 1000,  500,  0, 1,999, 1998, Tokens::new(1002, 0, 0, 0)).unwrap();
-        try_calc(1000,   50,  100,   50,  101,  0,45,  5,   49, Tokens::new(   1, 0, 0, 0)).unwrap();
-    }
-
-    fn create_bank(lamports: u64) -> (Bank, Keypair) {
-        let (genesis_config, mint_keypair) = create_genesis_config(lamports);
-        let mut bank = Bank::new(&genesis_config);
-        bank.add_builtin("exchange_program", id(), process_instruction);
-        (bank, mint_keypair)
-    }
-
-    fn create_client(bank: Bank, mint_keypair: Keypair) -> (BankClient, Keypair) {
-        let owner = Keypair::new();
-        let bank_client = BankClient::new(bank);
-        bank_client
-            .transfer_and_confirm(42, &mint_keypair, &owner.pubkey())
-            .unwrap();
-
-        (bank_client, owner)
-    }
-
-    fn create_account(client: &BankClient, owner: &Keypair) -> Pubkey {
-        let new = Keypair::new();
-
-        let instruction = system_instruction::create_account(
-            &owner.pubkey(),
-            &new.pubkey(),
-            1,
-            mem::size_of::<ExchangeState>() as u64,
-            &id(),
-        );
-
-        client
-            .send_and_confirm_message(
-                &[owner, &new],
-                Message::new(&[instruction], Some(&owner.pubkey())),
-            )
-            .unwrap_or_else(|_| panic!("{}:{}", line!(), file!()));
-        new.pubkey()
-    }
-
-    fn create_token_account(client: &BankClient, owner: &Keypair) -> Pubkey {
-        let new = create_account(client, owner);
-        let instruction = exchange_instruction::account_request(&owner.pubkey(), &new);
-        client
-            .send_and_confirm_instruction(owner, instruction)
-            .unwrap_or_else(|_| panic!("{}:{}", line!(), file!()));
-        new
-    }
-
-    fn transfer(client: &BankClient, owner: &Keypair, to: &Pubkey, token: Token, tokens: u64) {
-        let instruction = exchange_instruction::transfer_request(
-            &owner.pubkey(),
-            to,
-            &faucet::id(),
-            token,
-            tokens,
-        );
-        client
-            .send_and_confirm_instruction(owner, instruction)
-            .unwrap_or_else(|_| panic!("{}:{}", line!(), file!()));
-    }
-
-    fn trade(
-        client: &BankClient,
-        owner: &Keypair,
-        side: OrderSide,
-        pair: AssetPair,
-        from_token: Token,
-        src_tokens: u64,
-        trade_tokens: u64,
-        price: u64,
-    ) -> (Pubkey, Pubkey) {
-        let trade = create_account(client, owner);
-        let src = create_token_account(client, owner);
-        transfer(client, owner, &src, from_token, src_tokens);
-
-        let instruction = exchange_instruction::trade_request(
-            &owner.pubkey(),
-            &trade,
-            side,
-            pair,
-            trade_tokens,
-            price,
-            &src,
-        );
-        client
-            .send_and_confirm_instruction(owner, instruction)
-            .unwrap_or_else(|_| panic!("{}:{}", line!(), file!()));
-        (trade, src)
-    }
-
-    #[test]
-    fn test_exchange_new_account() {
-        solana_logger::setup();
-        let (bank, mint_keypair) = create_bank(10_000);
-        let (client, owner) = create_client(bank, mint_keypair);
-
-        let new = create_token_account(&client, &owner);
-        let new_account_data = client.get_account_data(&new).unwrap().unwrap();
-
-        // Check results
-
-        assert_eq!(
-            TokenAccountInfo::default()
-                .owner(&owner.pubkey())
-                .tokens(100_000, 100_000, 100_000, 100_000),
-            ExchangeProcessor::deserialize_account(&new_account_data).unwrap()
-        );
-    }
-
-    #[test]
-    fn test_exchange_new_account_not_unallocated() {
-        solana_logger::setup();
-        let (bank, mint_keypair) = create_bank(10_000);
-        let (client, owner) = create_client(bank, mint_keypair);
-
-        let new = create_token_account(&client, &owner);
-        let instruction = exchange_instruction::account_request(&owner.pubkey(), &new);
-        client
-            .send_and_confirm_instruction(&owner, instruction)
-            .expect_err(&format!("{}:{}", line!(), file!()));
-    }
-
-    #[test]
-    fn test_exchange_new_transfer_request() {
-        solana_logger::setup();
-        let (bank, mint_keypair) = create_bank(10_000);
-        let (client, owner) = create_client(bank, mint_keypair);
-
-        let new = create_token_account(&client, &owner);
-
-        let instruction = exchange_instruction::transfer_request(
-            &owner.pubkey(),
-            &new,
-            &faucet::id(),
-            Token::A,
-            42,
-        );
-        client
-            .send_and_confirm_instruction(&owner, instruction)
-            .unwrap_or_else(|_| panic!("{}:{}", line!(), file!()));
-
-        let new_account_data = client.get_account_data(&new).unwrap().unwrap();
-
-        // Check results
-
-        assert_eq!(
-            TokenAccountInfo::default()
-                .owner(&owner.pubkey())
-                .tokens(100_042, 100_000, 100_000, 100_000),
-            ExchangeProcessor::deserialize_account(&new_account_data).unwrap()
-        );
-    }
-
-    #[test]
-    fn test_exchange_new_trade_request() {
-        solana_logger::setup();
-        let (bank, mint_keypair) = create_bank(10_000);
-        let (client, owner) = create_client(bank, mint_keypair);
-
-        let (trade, src) = trade(
-            &client,
-            &owner,
-            OrderSide::Ask,
-            AssetPair::default(),
-            Token::A,
-            42,
-            2,
-            1000,
-        );
-
-        let trade_account_data = client.get_account_data(&trade).unwrap().unwrap();
-        let src_account_data = client.get_account_data(&src).unwrap().unwrap();
-
-        // check results
-
-        assert_eq!(
-            OrderInfo {
-                owner: owner.pubkey(),
-                side: OrderSide::Ask,
-                pair: AssetPair::default(),
-                tokens: 2,
-                price: 1000,
-                tokens_settled: 0
-            },
-            ExchangeProcessor::deserialize_order(&trade_account_data).unwrap()
-        );
-        assert_eq!(
-            TokenAccountInfo::default()
-                .owner(&owner.pubkey())
-                .tokens(100_040, 100_000, 100_000, 100_000),
-            ExchangeProcessor::deserialize_account(&src_account_data).unwrap()
-        );
-    }
-
-    #[test]
-    fn test_exchange_new_swap_request() {
-        solana_logger::setup();
-        let (bank, mint_keypair) = create_bank(10_000);
-        let (client, owner) = create_client(bank, mint_keypair);
-
-        let profit = create_token_account(&client, &owner);
-        let (to_trade, _) = trade(
-            &client,
-            &owner,
-            OrderSide::Ask,
-            AssetPair::default(),
-            Token::A,
-            2,
-            2,
-            2000,
-        );
-        let (from_trade, _) = trade(
-            &client,
-            &owner,
-            OrderSide::Bid,
-            AssetPair::default(),
-            Token::B,
-            3,
-            3,
-            3000,
-        );
-
-        let instruction =
-            exchange_instruction::swap_request(&owner.pubkey(), &to_trade, &from_trade, &profit);
-        client
-            .send_and_confirm_instruction(&owner, instruction)
-            .unwrap_or_else(|_| panic!("{}:{}", line!(), file!()));
-
-        let to_trade_account_data = client.get_account_data(&to_trade).unwrap().unwrap();
-        let from_trade_account_data = client.get_account_data(&from_trade).unwrap().unwrap();
-        let profit_account_data = client.get_account_data(&profit).unwrap().unwrap();
-
-        // check results
-
-        assert_eq!(
-            OrderInfo {
-                owner: owner.pubkey(),
-                side: OrderSide::Ask,
-                pair: AssetPair::default(),
-                tokens: 1,
-                price: 2000,
-                tokens_settled: 2,
-            },
-            ExchangeProcessor::deserialize_order(&to_trade_account_data).unwrap()
-        );
-
-        assert_eq!(
-            TokenAccountInfo::default()
-                .owner(&owner.pubkey())
-                .tokens(1, 0, 0, 0),
-            ExchangeProcessor::deserialize_account(&from_trade_account_data).unwrap()
-        );
-
-        assert_eq!(
-            TokenAccountInfo::default()
-                .owner(&owner.pubkey())
-                .tokens(100_000, 100_001, 100_000, 100_000),
-            ExchangeProcessor::deserialize_account(&profit_account_data).unwrap()
-        );
-    }
-
-    #[test]
-    fn test_exchange_trade_to_token_account() {
-        solana_logger::setup();
-        let (bank, mint_keypair) = create_bank(10_000);
-        let (client, owner) = create_client(bank, mint_keypair);
-
-        let profit = create_token_account(&client, &owner);
-        let (to_trade, _) = trade(
-            &client,
-            &owner,
-            OrderSide::Ask,
-            AssetPair::default(),
-            Token::A,
-            3,
-            3,
-            2000,
-        );
-        let (from_trade, _) = trade(
-            &client,
-            &owner,
-            OrderSide::Bid,
-            AssetPair::default(),
-            Token::B,
-            3,
-            3,
-            3000,
-        );
-
-        let instruction =
-            exchange_instruction::swap_request(&owner.pubkey(), &to_trade, &from_trade, &profit);
-        client
-            .send_and_confirm_instruction(&owner, instruction)
-            .unwrap_or_else(|_| panic!("{}:{}", line!(), file!()));
-
-        let new = create_token_account(&client, &owner);
-
-        let instruction =
-            exchange_instruction::transfer_request(&owner.pubkey(), &new, &to_trade, Token::B, 1);
-        client
-            .send_and_confirm_instruction(&owner, instruction)
-            .unwrap_or_else(|_| panic!("{}:{}", line!(), file!()));
-
-        let instruction =
-            exchange_instruction::transfer_request(&owner.pubkey(), &new, &from_trade, Token::A, 1);
-        client
-            .send_and_confirm_instruction(&owner, instruction)
-            .unwrap_or_else(|_| panic!("{}:{}", line!(), file!()));
-
-        let new_account_data = client.get_account_data(&new).unwrap().unwrap();
-
-        // Check results
-
-        assert_eq!(
-            TokenAccountInfo::default()
-                .owner(&owner.pubkey())
-                .tokens(100_001, 100_001, 100_000, 100_000),
-            ExchangeProcessor::deserialize_account(&new_account_data).unwrap()
-        );
-    }
-}

+ 0 - 226
programs/exchange/src/exchange_state.rs

@@ -1,226 +0,0 @@
-use serde_derive::{Deserialize, Serialize};
-use solana_sdk::pubkey::Pubkey;
-use std::{error, fmt};
-
-/// Fixed-point scaler, 10 = one base 10 digit to the right of the decimal, 100 = 2, ...
-/// Used by both price and amount in their fixed point representation
-pub const SCALER: u64 = 1000;
-
-#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
-pub enum ExchangeError {
-    InvalidTrade(String),
-}
-impl error::Error for ExchangeError {}
-impl fmt::Display for ExchangeError {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        match self {
-            ExchangeError::InvalidTrade(s) => write!(f, "{}", s),
-        }
-    }
-}
-
-/// Supported token types
-#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
-pub enum Token {
-    A,
-    B,
-    C,
-    D,
-}
-impl Default for Token {
-    fn default() -> Self {
-        Token::A
-    }
-}
-
-// Values of tokens, could be quantities, prices, etc...
-#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
-#[allow(non_snake_case)]
-pub struct Tokens {
-    pub A: u64,
-    pub B: u64,
-    pub C: u64,
-    pub D: u64,
-}
-impl Tokens {
-    pub fn new(a: u64, b: u64, c: u64, d: u64) -> Self {
-        Self {
-            A: a,
-            B: b,
-            C: c,
-            D: d,
-        }
-    }
-}
-impl std::ops::Index<Token> for Tokens {
-    type Output = u64;
-    fn index(&self, t: Token) -> &u64 {
-        match t {
-            Token::A => &self.A,
-            Token::B => &self.B,
-            Token::C => &self.C,
-            Token::D => &self.D,
-        }
-    }
-}
-impl std::ops::IndexMut<Token> for Tokens {
-    fn index_mut(&mut self, t: Token) -> &mut u64 {
-        match t {
-            Token::A => &mut self.A,
-            Token::B => &mut self.B,
-            Token::C => &mut self.C,
-            Token::D => &mut self.D,
-        }
-    }
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
-#[allow(non_snake_case)]
-pub struct AssetPair {
-    // represents a pair of two token enums that defines a market
-    pub Base: Token,
-    // "primary" token and numerator for pricing purposes
-    pub Quote: Token,
-    // "secondary" token and denominator for pricing purposes
-}
-
-impl Default for AssetPair {
-    fn default() -> AssetPair {
-        AssetPair {
-            Base: Token::A,
-            Quote: Token::B,
-        }
-    }
-}
-
-/// Token accounts are populated with this structure
-#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
-pub struct TokenAccountInfo {
-    /// Investor who owns this account
-    pub owner: Pubkey,
-    /// Current number of tokens this account holds
-    pub tokens: Tokens,
-}
-
-impl TokenAccountInfo {
-    pub fn owner(mut self, owner: &Pubkey) -> Self {
-        self.owner = *owner;
-        self
-    }
-    pub fn tokens(mut self, a: u64, b: u64, c: u64, d: u64) -> Self {
-        self.tokens = Tokens {
-            A: a,
-            B: b,
-            C: c,
-            D: d,
-        };
-        self
-    }
-}
-
-/// side of the exchange between two tokens in a pair
-#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
-pub enum OrderSide {
-    /// Offer the Base asset and Accept the Quote asset
-    Ask, // to
-    /// Offer the Quote asset and Accept the Base asset
-    Bid, // from
-}
-impl fmt::Display for OrderSide {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        match self {
-            OrderSide::Ask => write!(f, "A")?,
-            OrderSide::Bid => write!(f, "B")?,
-        }
-        Ok(())
-    }
-}
-
-/// Trade accounts are populated with this structure
-#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
-pub struct OrderInfo {
-    /// Owner of the trade order
-    pub owner: Pubkey,
-    /// side of the order in the market (bid/ask)
-    pub side: OrderSide,
-    /// Token pair indicating two tokens to exchange, first is primary
-    pub pair: AssetPair,
-    /// Number of tokens to exchange; primary or secondary depending on side.  Once
-    /// this number goes to zero this trade order will be converted into a regular token account
-    pub tokens: u64,
-    /// Scaled price of the secondary token given the primary is equal to the scale value
-    /// If scale is 1 and price is 2 then ratio is 1:2 or 1 primary token for 2 secondary tokens
-    pub price: u64,
-    /// Number of tokens that have been settled so far.  These nay be transferred to another
-    /// token account by the owner.
-    pub tokens_settled: u64,
-}
-impl Default for OrderInfo {
-    fn default() -> Self {
-        Self {
-            owner: Pubkey::default(),
-            pair: AssetPair::default(),
-            side: OrderSide::Ask,
-            tokens: 0,
-            price: 0,
-            tokens_settled: 0,
-        }
-    }
-}
-impl OrderInfo {
-    pub fn pair(mut self, pair: AssetPair) -> Self {
-        self.pair = pair;
-        self
-    }
-    pub fn side(mut self, side: OrderSide) -> Self {
-        self.side = side;
-        self
-    }
-    pub fn tokens(mut self, tokens: u64) -> Self {
-        self.tokens = tokens;
-        self
-    }
-    pub fn price(mut self, price: u64) -> Self {
-        self.price = price;
-        self
-    }
-}
-
-pub fn check_trade(side: OrderSide, tokens: u64, price: u64) -> Result<(), ExchangeError> {
-    match side {
-        OrderSide::Ask => {
-            if tokens * price / SCALER == 0 {
-                return Err(ExchangeError::InvalidTrade(format!(
-                    "To trade of {} for {}/{} results in 0 tradeable tokens",
-                    tokens, SCALER, price
-                )));
-            }
-        }
-        OrderSide::Bid => {
-            if tokens * SCALER / price == 0 {
-                return Err(ExchangeError::InvalidTrade(format!(
-                    "From trade of {} for {}?{} results in 0 tradeable tokens",
-                    tokens, SCALER, price
-                )));
-            }
-        }
-    }
-    Ok(())
-}
-
-/// Type of exchange account, account's user data is populated with this enum
-#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
-pub enum ExchangeState {
-    /// Account's data is unallocated
-    Unallocated,
-    // Token account
-    Account(TokenAccountInfo),
-    // Trade order account
-    Trade(OrderInfo),
-    Invalid,
-}
-impl Default for ExchangeState {
-    fn default() -> Self {
-        ExchangeState::Unallocated
-    }
-}

+ 0 - 19
programs/exchange/src/lib.rs

@@ -1,19 +0,0 @@
-#![allow(clippy::integer_arithmetic)]
-pub mod exchange_instruction;
-pub mod exchange_processor;
-pub mod exchange_state;
-
-#[macro_use]
-extern crate solana_metrics;
-
-use crate::exchange_processor::process_instruction;
-
-solana_sdk::declare_program!(
-    "Exchange11111111111111111111111111111111111",
-    solana_exchange_program,
-    process_instruction
-);
-
-pub mod faucet {
-    solana_sdk::declare_id!("ExchangeFaucet11111111111111111111111111111");
-}

+ 0 - 1
scripts/cargo-install-all.sh

@@ -84,7 +84,6 @@ else
 
 
   BINS=(
   BINS=(
     solana
     solana
-    solana-bench-exchange
     solana-bench-tps
     solana-bench-tps
     solana-faucet
     solana-faucet
     solana-gossip
     solana-gossip

+ 0 - 1
scripts/coverage.sh

@@ -106,7 +106,6 @@ find target/cov -type f -name '*.gcda' -newer target/cov/before-test ! -newer ta
     --ignore bench-tps\*
     --ignore bench-tps\*
     --ignore upload-perf\*
     --ignore upload-perf\*
     --ignore bench-streamer\*
     --ignore bench-streamer\*
-    --ignore bench-exchange\*
     --ignore local-cluster\*
     --ignore local-cluster\*
   )
   )