Explorar o código

Remove unused exchange program and bench client (#18463)

Justin Starry %!s(int64=4) %!d(string=hai) anos
pai
achega
cfece66403

+ 0 - 48
Cargo.lock

@@ -4295,35 +4295,6 @@ dependencies = [
  "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]]
 name = "solana-bench-streamer"
 version = "1.8.0"
@@ -4685,23 +4656,6 @@ dependencies = [
  "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]]
 name = "solana-failure-program"
 version = "1.8.0"
@@ -4803,7 +4757,6 @@ dependencies = [
  "serde_yaml",
  "solana-clap-utils",
  "solana-cli-config",
- "solana-exchange-program",
  "solana-ledger",
  "solana-logger 1.8.0",
  "solana-runtime",
@@ -5022,7 +4975,6 @@ dependencies = [
  "solana-config-program",
  "solana-core",
  "solana-download-utils",
- "solana-exchange-program",
  "solana-faucet",
  "solana-gossip",
  "solana-ledger",

+ 0 - 2
Cargo.toml

@@ -1,7 +1,6 @@
 [workspace]
 members = [
     "accounts-cluster-bench",
-    "bench-exchange",
     "bench-streamer",
     "bench-tps",
     "accounts-bench",
@@ -44,7 +43,6 @@ members = [
     "program-test",
     "programs/bpf_loader",
     "programs/config",
-    "programs/exchange",
     "programs/failure",
     "programs/noop",
     "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"
 solana-clap-utils = { path = "../clap-utils", 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-logger = { path = "../logger", 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.
 #![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 solana_clap_utils::{
     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"),
     );
 
-    let native_instruction_processors = if cluster_type == ClusterType::Development {
-        vec![solana_exchange_program!()]
-    } else {
-        vec![]
-    };
-
     let mut genesis_config = GenesisConfig {
-        native_instruction_processors,
+        native_instruction_processors: vec![],
         ticks_per_slot,
         poh_config,
         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-client = { path = "../client", 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-gossip = { path = "../gossip", 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:
                                          idle
                                          bench-tps
-                                         bench-exchange
                                      User can optionally provide extraArgs that are transparently
                                      supplied to the client program as command line parameters.
                                      For example,
@@ -307,7 +306,6 @@ startBootstrapLeader() {
          \"$internalNodesLamports\" \
          $nodeIndex \
          ${#clientIpList[@]} \"$benchTpsExtraArgs\" \
-         ${#clientIpList[@]} \"$benchExchangeExtraArgs\" \
          \"$genesisOptions\" \
          \"$maybeNoSnapshot $maybeSkipLedgerVerify $maybeLimitLedgerSize $maybeWaitForSupermajority\" \
          \"$gpuMode\" \
@@ -379,7 +377,6 @@ startNode() {
          \"$internalNodesLamports\" \
          $nodeIndex \
          ${#clientIpList[@]} \"$benchTpsExtraArgs\" \
-         ${#clientIpList[@]} \"$benchExchangeExtraArgs\" \
          \"$genesisOptions\" \
          \"$maybeNoSnapshot $maybeSkipLedgerVerify $maybeLimitLedgerSize $maybeWaitForSupermajority\" \
          \"$gpuMode\" \
@@ -409,7 +406,7 @@ startClient() {
     startCommon "$ipAddress"
     ssh "${sshOptions[@]}" -f "$ipAddress" \
       "./solana/net/remote/remote-client.sh $deployMethod $entrypointIp \
-      $clientToRun \"$RUST_LOG\" \"$benchTpsExtraArgs\" \"$benchExchangeExtraArgs\" $clientIndex"
+      $clientToRun \"$RUST_LOG\" \"$benchTpsExtraArgs\" $clientIndex"
   ) >> "$logFile" 2>&1 || {
     cat "$logFile"
     echo "^^^ +++"
@@ -421,8 +418,6 @@ startClients() {
   for ((i=0; i < "$numClients" && i < "$numClientsRequested"; i++)) do
     if [[ $i -lt "$numBenchTpsClients" ]]; then
       startClient "${clientIpList[$i]}" "solana-bench-tps" "$i"
-    elif [[ $i -lt $((numBenchTpsClients + numBenchExchangeClients)) ]]; then
-      startClient "${clientIpList[$i]}" "solana-bench-exchange" $((i-numBenchTpsClients))
     else
       startClient "${clientIpList[$i]}" "idle"
     fi
@@ -767,9 +762,7 @@ updatePlatforms=
 nodeAddress=
 numIdleClients=0
 numBenchTpsClients=0
-numBenchExchangeClients=0
 benchTpsExtraArgs=
-benchExchangeExtraArgs=
 failOnValidatorBootupFailure=true
 genesisOptions=
 numValidatorsRequested=
@@ -977,10 +970,6 @@ while getopts "h?T:t:o:f:rc:Fn:i:d" opt "${shortArgs[@]}"; do
           numBenchTpsClients=$numClients
           benchTpsExtraArgs=$extraArgs
         ;;
-        bench-exchange)
-          numBenchExchangeClients=$numClients
-          benchExchangeExtraArgs=$extraArgs
-        ;;
         *)
           echo "Unknown client type: $clientType"
           exit 1
@@ -1013,7 +1002,7 @@ if [[ -n $numValidatorsRequested ]]; then
 fi
 
 numClients=${#clientIpList[@]}
-numClientsRequested=$((numBenchTpsClients + numBenchExchangeClients + numIdleClients))
+numClientsRequested=$((numBenchTpsClients + numIdleClients))
 if [[ "$numClientsRequested" -eq 0 ]]; then
   numBenchTpsClients=$numClients
   numClientsRequested=$numClients

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

@@ -10,8 +10,7 @@ if [[ -n $4 ]]; then
   export RUST_LOG="$4"
 fi
 benchTpsExtraArgs="$5"
-benchExchangeExtraArgs="$6"
-clientIndex="$7"
+clientIndex="$6"
 
 missing() {
   echo "Error: $1 not specified"
@@ -57,23 +56,6 @@ solana-bench-tps)
       --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)
   # Add the faucet keypair to idle clients for convenience
   net/scripts/rsync-retry.sh -vPrc \

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

@@ -20,15 +20,13 @@ internalNodesLamports="${11}"
 nodeIndex="${12}"
 numBenchTpsClients="${13}"
 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
 
 missing() {
@@ -194,13 +192,6 @@ EOF
         tail -n +2 -q config/bench-tps"$i".yml >> config/client-accounts.yml
         echo "" >> config/client-accounts.yml
       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
         cat "$externalPrimordialAccountsFile" >> config/validator-balances.yml
       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=(
     solana
-    solana-bench-exchange
     solana-bench-tps
     solana-faucet
     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 upload-perf\*
     --ignore bench-streamer\*
-    --ignore bench-exchange\*
     --ignore local-cluster\*
   )