Implementing openbook_v2 place orders in rust
This commit is contained in:
parent
8890a462d5
commit
4e16fdff22
File diff suppressed because it is too large
Load Diff
|
@ -25,7 +25,11 @@ rand_chacha = "0.3.1"
|
|||
async-trait = "0.1.68"
|
||||
|
||||
solana-sdk = "1.15.2"
|
||||
solana-program = "1.15.2"
|
||||
solana-rpc-client = "1.15.2"
|
||||
derive_more = "0.99.17"
|
||||
pretty_env_logger = "0.4.0"
|
||||
tracing-subscriber = "0.3.17"
|
||||
spl-token = "3.5.0"
|
||||
|
||||
solana-client = "1.15.2"
|
|
@ -1,6 +1,11 @@
|
|||
import { AnchorProvider, Provider } from "@project-serum/anchor";
|
||||
import { AnchorProvider, BN, Program, Provider, web3 } from "@project-serum/anchor";
|
||||
import { SuccessfulTxSimulationResponse } from "@project-serum/anchor/dist/cjs/utils/rpc";
|
||||
import { Connection, PublicKey, Transaction, Signer, SendOptions, ConfirmOptions, Commitment, Keypair } from "@solana/web3.js";
|
||||
import { Command } from "./output_file";
|
||||
import { IDL, OpenbookV2 } from "./openbook-v2/openbook_v2";
|
||||
import { U64_MAX_BN } from "@blockworks-foundation/mango-v4";
|
||||
import { assert } from "console";
|
||||
import { buffer } from "stream/consumers";
|
||||
|
||||
export class TestProvider extends AnchorProvider {
|
||||
keypair: Keypair;
|
||||
|
|
|
@ -8,7 +8,7 @@ import { getKeypairFromFile } from './common_utils';
|
|||
import { deploy_programs } from './deploy_programs';
|
||||
import { User, createUser, mintUser } from './general/create_users';
|
||||
import { configure_accounts } from './general/accounts';
|
||||
import { OutputFile } from './output_file';
|
||||
import { Command, OutputFile } from './output_file';
|
||||
import { MintUtils } from './general/mint_utils';
|
||||
import { OpenbookConfigurator } from './openbook-v2/configure_openbook';
|
||||
|
||||
|
@ -142,9 +142,11 @@ async function configure(
|
|||
let programOutputData = programs.map(x => {
|
||||
|
||||
let kp = getKeypairFromFile(x.programKeyPath);
|
||||
let emptyCommands : Command[] = [];
|
||||
return {
|
||||
name: x.name,
|
||||
program_id: kp.publicKey
|
||||
program_id: kp.publicKey,
|
||||
commands: emptyCommands,
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -167,18 +169,19 @@ async function configure(
|
|||
let openbookProgramId = programOutputData[index].program_id;
|
||||
let openbookConfigurator = new OpenbookConfigurator(connection, authority, mintUtils, openbookProgramId);
|
||||
let markets = await openbookConfigurator.configureOpenbookV2(mints);
|
||||
programOutputData[index].commands = await openbookConfigurator.getCommands();
|
||||
console.log("Finished configuring openbook")
|
||||
|
||||
console.log("Creating users");
|
||||
let users = await Promise.all(Array.from(Array(nbPayers).keys()).map(_ => createUser(connection, authority, balancePerPayer)));
|
||||
let tokenAccounts = await Promise.all(users.map(
|
||||
/// user is richer than bill gates, but not as rich as certain world leaders
|
||||
user => mintUser(connection, authority, mints, mintUtils, user.publicKey, 100_000_000_000)
|
||||
async(user) => await mintUser(connection, authority, mints, mintUtils, user.publicKey, 100_000_000_000_000_000)
|
||||
))
|
||||
|
||||
let userOpenOrders = await Promise.all(users.map(
|
||||
/// user is crazy betting all his money in crypto market
|
||||
user => openbookConfigurator.configureMarketForUser(user, markets, 100_000_000_000)
|
||||
async(user) => await openbookConfigurator.configureMarketForUser(user, markets)
|
||||
))
|
||||
|
||||
let userData: User [] = users.map((user, i) => {
|
||||
|
@ -191,6 +194,14 @@ async function configure(
|
|||
|
||||
console.log("Users created");
|
||||
|
||||
console.log("Filling up orderbook");
|
||||
await Promise.all( userData.map( async(user, i) => {
|
||||
for (const market of markets) {
|
||||
await openbookConfigurator.fillOrderBook(user, users[i], market, 32);
|
||||
}
|
||||
}) )
|
||||
console.log("Orderbook filled");
|
||||
|
||||
console.log("Creating accounts")
|
||||
let accounts = await configure_accounts(connection, authority, numberOfAccountsToBeCreated, programIds);
|
||||
|
||||
|
|
|
@ -3,11 +3,13 @@ import * as web3 from "@solana/web3.js";
|
|||
import { exec } from "child_process";
|
||||
import * as fs from "fs"
|
||||
import { promisify } from "util";
|
||||
import { getKeypairFromFile } from "./common_utils";
|
||||
|
||||
export interface ProgramData {
|
||||
name: string,
|
||||
programPath: string,
|
||||
programKeyPath: string,
|
||||
idl: string,
|
||||
}
|
||||
|
||||
export async function deploy_programs(url: String, payer: string, programs: ProgramData[]) {
|
||||
|
@ -23,5 +25,24 @@ export async function deploy_programs(url: String, payer: string, programs: Prog
|
|||
if (stderr.length > 0) {
|
||||
console.log(stderr);
|
||||
}
|
||||
|
||||
if (program.idl.length > 0) {
|
||||
let programId = getKeypairFromFile(program.programKeyPath);
|
||||
console.log("deploying idl file for program " + programId.publicKey);
|
||||
let initCmd = "anchor idl init --filepath " + program.idl + " --provider.wallet " + payer + " --provider.cluster " + url + " " + programId.publicKey;
|
||||
const {stdout, stderr} = await execPromise(initCmd);
|
||||
|
||||
if (stderr.length > 0) {
|
||||
let updateCommand = "anchor idl update --filepath " + program.idl + " --provider.wallet " + payer + " --provider.cluster " + url + " " + programId.publicKey;
|
||||
const {stdout, stderr} = await execPromise(updateCommand);
|
||||
if (stdout.length > 0) {
|
||||
console.log(stdout);
|
||||
}
|
||||
|
||||
if(stderr.length > 0) {
|
||||
console.log("could not deploy idl for " + programId.publicKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,12 @@ import { Market, createMarket } from "./create_markets";
|
|||
import { MintUtils } from "../general/mint_utils";
|
||||
import { OpenbookV2 } from "./openbook_v2";
|
||||
import IDL from '../programs/openbook_v2.json'
|
||||
import { BN, Program, web3 } from "@project-serum/anchor";
|
||||
import { BN, Program, web3, IdlTypes } from "@project-serum/anchor";
|
||||
import { User } from "../general/create_users";
|
||||
import { U64_MAX_BN } from "@blockworks-foundation/mango-v4";
|
||||
import { TOKEN_PROGRAM_ID } from "@solana/spl-token";
|
||||
import { Command } from "../output_file";
|
||||
import assert from "assert";
|
||||
|
||||
export interface OpenOrders {
|
||||
market: PublicKey,
|
||||
|
@ -16,32 +21,33 @@ export class OpenbookConfigurator {
|
|||
anchorProvider: TestProvider;
|
||||
mintUtils: MintUtils;
|
||||
openbookProgramId: PublicKey;
|
||||
program: Program<OpenbookV2>;
|
||||
|
||||
constructor(connection: Connection, authority: Keypair, mintUtils: MintUtils, openbookProgramId: PublicKey) {
|
||||
this.anchorProvider = new TestProvider(connection, authority);
|
||||
this.mintUtils = mintUtils;
|
||||
this.openbookProgramId = openbookProgramId;
|
||||
this.program = new Program<OpenbookV2>(
|
||||
IDL as OpenbookV2,
|
||||
this.openbookProgramId,
|
||||
this.anchorProvider,
|
||||
);
|
||||
}
|
||||
|
||||
public async configureOpenbookV2(mints: PublicKey[]): Promise<Market[]> {
|
||||
let quoteMint = mints[0];
|
||||
let admin = Keypair.generate();
|
||||
return await Promise.all(mints.slice(1).map((mint, index) => createMarket(this.anchorProvider, this.mintUtils, admin, this.openbookProgramId, mint, quoteMint, index)))
|
||||
return await Promise.all(mints.slice(1).map((mint, index) => createMarket(this.program, this.anchorProvider, this.mintUtils, admin, this.openbookProgramId, mint, quoteMint, index)))
|
||||
}
|
||||
|
||||
public async configureMarketForUser(user: Keypair, markets: Market[], depositAmount: number) : Promise<OpenOrders[]> {
|
||||
let program = new Program<OpenbookV2>(
|
||||
IDL as OpenbookV2,
|
||||
this.openbookProgramId,
|
||||
this.anchorProvider,
|
||||
);
|
||||
public async configureMarketForUser(user: Keypair, markets: Market[],) : Promise<OpenOrders[]> {
|
||||
|
||||
const openOrders = await Promise.all(
|
||||
markets.map(async(market) => {
|
||||
let accountIndex = new BN(0);
|
||||
let [openOrders, _tmp] = PublicKey.findProgramAddressSync([Buffer.from("OpenOrders"), user.publicKey.toBuffer(), market.market_pk.toBuffer(), accountIndex.toBuffer("le", 4)], this.openbookProgramId)
|
||||
|
||||
await program.methods.initOpenOrders(
|
||||
await this.program.methods.initOpenOrders(
|
||||
0,
|
||||
64
|
||||
).accounts({
|
||||
|
@ -62,4 +68,112 @@ export class OpenbookConfigurator {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
public async fillOrderBook(user: User, userKp: Keypair, market: Market, nbOrders: number) {
|
||||
|
||||
for( let i=0; i<nbOrders; ++i) {
|
||||
|
||||
let side = {bid:{}} ;
|
||||
let placeOrder = {limit:{}};
|
||||
|
||||
await this.program.methods.placeOrder(
|
||||
side,
|
||||
new BN(1000-1-i),
|
||||
new BN(10000),
|
||||
new BN(1000000),
|
||||
new BN(i),
|
||||
placeOrder,
|
||||
false,
|
||||
U64_MAX_BN,
|
||||
255,
|
||||
).accounts({
|
||||
asks: market.asks,
|
||||
baseVault: market.base_vault,
|
||||
bids: market.bids,
|
||||
eventQueue: market.event_queue,
|
||||
market: market.market_pk,
|
||||
openOrdersAccount: user.open_orders[market.market_index].open_orders,
|
||||
oracle: market.oracle,
|
||||
owner: userKp.publicKey,
|
||||
payer: user.token_data[0].token_account,
|
||||
quoteVault: market.quote_vault,
|
||||
systemProgram: web3.SystemProgram.programId,
|
||||
tokenProgram: TOKEN_PROGRAM_ID,
|
||||
}).signers([userKp]).rpc();
|
||||
}
|
||||
|
||||
for( let i=0; i<nbOrders; ++i) {
|
||||
|
||||
let side = {ask:{}} ;
|
||||
let placeOrder = {limit:{}};
|
||||
|
||||
await this.program.methods.placeOrder(
|
||||
side,
|
||||
new BN(1000+1+i),
|
||||
new BN(10000),
|
||||
new BN(1000000),
|
||||
new BN(i),
|
||||
placeOrder,
|
||||
false,
|
||||
U64_MAX_BN,
|
||||
255,
|
||||
).accounts({
|
||||
asks: market.asks,
|
||||
baseVault: market.base_vault,
|
||||
bids: market.bids,
|
||||
eventQueue: market.event_queue,
|
||||
market: market.market_pk,
|
||||
openOrdersAccount: user.open_orders[market.market_index].open_orders,
|
||||
oracle: market.oracle,
|
||||
owner: userKp.publicKey,
|
||||
payer: user.token_data[market.market_index+1].token_account,
|
||||
quoteVault: market.quote_vault,
|
||||
systemProgram: web3.SystemProgram.programId,
|
||||
tokenProgram: TOKEN_PROGRAM_ID,
|
||||
}).signers([userKp]).rpc();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// this is a special method.
|
||||
/// It is pain to create an anchor instruction in rust
|
||||
/// so this method will create the instruction in typescript and serialize into bytes and store it into command type
|
||||
public async getCommands() : Promise<Command[]> {
|
||||
let side = {bid:{}} ;
|
||||
let placeOrder = {limit:{}};
|
||||
let placeOrderIx = await this.program.methods.placeOrder(
|
||||
side,
|
||||
new BN(0),
|
||||
new BN(0),
|
||||
new BN(0),
|
||||
new BN(0),
|
||||
placeOrder,
|
||||
false,
|
||||
U64_MAX_BN,
|
||||
255,
|
||||
).accounts({
|
||||
asks: PublicKey.default,
|
||||
baseVault: PublicKey.default,
|
||||
bids: PublicKey.default,
|
||||
eventQueue: PublicKey.default,
|
||||
market: PublicKey.default,
|
||||
openOrdersAccount: PublicKey.default,
|
||||
oracle: PublicKey.default,
|
||||
owner: PublicKey.default,
|
||||
payer: PublicKey.default,
|
||||
quoteVault: PublicKey.default,
|
||||
systemProgram: web3.SystemProgram.programId,
|
||||
tokenProgram: TOKEN_PROGRAM_ID,
|
||||
}).instruction();
|
||||
|
||||
let argument_sizes = [8, 1, 8, 8, 8, 8, 1, 1, 8, 1];
|
||||
assert(argument_sizes.reduce( (sum, current) => sum + current, 0 ) === placeOrderIx.data.length);
|
||||
let placeOrderCommand : Command = {
|
||||
name: "placeOrder",
|
||||
instruction: Array.from(placeOrderIx.data),
|
||||
argument_sizes,
|
||||
};
|
||||
|
||||
return [placeOrderCommand]
|
||||
}
|
||||
}
|
|
@ -22,12 +22,7 @@ export interface Market {
|
|||
market_index: number,
|
||||
}
|
||||
|
||||
export async function createMarket(anchorProvider: TestProvider, mintUtils: MintUtils, adminKp: Keypair, openbookProgramId: PublicKey, baseMint: PublicKey, quoteMint: PublicKey, index: number): Promise<Market> {
|
||||
let program = new Program<OpenbookV2>(
|
||||
IDL as OpenbookV2,
|
||||
openbookProgramId,
|
||||
anchorProvider,
|
||||
);
|
||||
export async function createMarket(program:Program<OpenbookV2>, anchorProvider: TestProvider, mintUtils: MintUtils, adminKp: Keypair, openbookProgramId: PublicKey, baseMint: PublicKey, quoteMint: PublicKey, index: number): Promise<Market> {
|
||||
let [oracleId, _tmp] = PublicKey.findProgramAddressSync([Buffer.from("StubOracle"), baseMint.toBytes()], openbookProgramId)
|
||||
const admin:PublicKey = adminKp.publicKey;
|
||||
|
||||
|
|
|
@ -1552,22 +1552,6 @@ export type OpenbookV2 = {
|
|||
"type": {
|
||||
"kind": "struct",
|
||||
"fields": [
|
||||
{
|
||||
"name": "basePositionLots",
|
||||
"docs": [
|
||||
"Currenlty not being used"
|
||||
],
|
||||
"type": "i64"
|
||||
},
|
||||
{
|
||||
"name": "quotePositionNative",
|
||||
"docs": [
|
||||
"Currenlty not being used"
|
||||
],
|
||||
"type": {
|
||||
"defined": "I80F48"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "quoteRunningNative",
|
||||
"docs": [
|
||||
|
@ -1589,20 +1573,6 @@ export type OpenbookV2 = {
|
|||
],
|
||||
"type": "i64"
|
||||
},
|
||||
{
|
||||
"name": "takerBaseLots",
|
||||
"docs": [
|
||||
"Amount of base lots on the EventQueue waiting to be processed"
|
||||
],
|
||||
"type": "i64"
|
||||
},
|
||||
{
|
||||
"name": "takerQuoteLots",
|
||||
"docs": [
|
||||
"Amount of quote lots on the EventQueue waiting to be processed"
|
||||
],
|
||||
"type": "i64"
|
||||
},
|
||||
{
|
||||
"name": "baseFreeNative",
|
||||
"type": {
|
||||
|
@ -4511,22 +4481,6 @@ export const IDL: OpenbookV2 = {
|
|||
"type": {
|
||||
"kind": "struct",
|
||||
"fields": [
|
||||
{
|
||||
"name": "basePositionLots",
|
||||
"docs": [
|
||||
"Currenlty not being used"
|
||||
],
|
||||
"type": "i64"
|
||||
},
|
||||
{
|
||||
"name": "quotePositionNative",
|
||||
"docs": [
|
||||
"Currenlty not being used"
|
||||
],
|
||||
"type": {
|
||||
"defined": "I80F48"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "quoteRunningNative",
|
||||
"docs": [
|
||||
|
@ -4548,20 +4502,6 @@ export const IDL: OpenbookV2 = {
|
|||
],
|
||||
"type": "i64"
|
||||
},
|
||||
{
|
||||
"name": "takerBaseLots",
|
||||
"docs": [
|
||||
"Amount of base lots on the EventQueue waiting to be processed"
|
||||
],
|
||||
"type": "i64"
|
||||
},
|
||||
{
|
||||
"name": "takerQuoteLots",
|
||||
"docs": [
|
||||
"Amount of quote lots on the EventQueue waiting to be processed"
|
||||
],
|
||||
"type": "i64"
|
||||
},
|
||||
{
|
||||
"name": "baseFreeNative",
|
||||
"type": {
|
||||
|
|
|
@ -2,9 +2,16 @@ import { PublicKey } from "@solana/web3.js";
|
|||
import { Market } from "./openbook-v2/create_markets";
|
||||
import { User } from "./general/create_users";
|
||||
|
||||
export interface Command {
|
||||
name: String,
|
||||
instruction: number[],
|
||||
argument_sizes: number[],
|
||||
}
|
||||
|
||||
export interface ProgramOutputData {
|
||||
name: String,
|
||||
program_id: PublicKey,
|
||||
commands: Command [],
|
||||
}
|
||||
|
||||
export interface OutputFile {
|
||||
|
|
|
@ -2,11 +2,13 @@
|
|||
{
|
||||
"name": "pyth_mock",
|
||||
"programPath": "programs/pyth_mock.so",
|
||||
"programKeyPath": "programs/pyth_mock.json"
|
||||
"programKeyPath": "programs/pyth_mock.json",
|
||||
"idl": ""
|
||||
},
|
||||
{
|
||||
"name": "openbook_v2",
|
||||
"programPath": "programs/openbook_v2.so",
|
||||
"programKeyPath": "programs/openbook_v2-keypair.json"
|
||||
"programKeyPath": "programs/openbook_v2-keypair.json",
|
||||
"idl": "programs/openbook_v2.json"
|
||||
}
|
||||
]
|
||||
|
|
|
@ -1552,22 +1552,6 @@
|
|||
"type": {
|
||||
"kind": "struct",
|
||||
"fields": [
|
||||
{
|
||||
"name": "basePositionLots",
|
||||
"docs": [
|
||||
"Currenlty not being used"
|
||||
],
|
||||
"type": "i64"
|
||||
},
|
||||
{
|
||||
"name": "quotePositionNative",
|
||||
"docs": [
|
||||
"Currenlty not being used"
|
||||
],
|
||||
"type": {
|
||||
"defined": "I80F48"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "quoteRunningNative",
|
||||
"docs": [
|
||||
|
@ -1589,20 +1573,6 @@
|
|||
],
|
||||
"type": "i64"
|
||||
},
|
||||
{
|
||||
"name": "takerBaseLots",
|
||||
"docs": [
|
||||
"Amount of base lots on the EventQueue waiting to be processed"
|
||||
],
|
||||
"type": "i64"
|
||||
},
|
||||
{
|
||||
"name": "takerQuoteLots",
|
||||
"docs": [
|
||||
"Amount of quote lots on the EventQueue waiting to be processed"
|
||||
],
|
||||
"type": "i64"
|
||||
},
|
||||
{
|
||||
"name": "baseFreeNative",
|
||||
"type": {
|
||||
|
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
@ -14,6 +14,8 @@
|
|||
"@project-serum/anchor": "^0.26.0",
|
||||
"@solana/spl-token": "^0.3.7",
|
||||
"@solana/web3.js": "^1.75.0",
|
||||
"cmd-ts": "^0.12.1"
|
||||
"cmd-ts": "^0.12.1",
|
||||
"loadash": "^1.0.0",
|
||||
"lodash": "^4.17.21"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,11 @@ impl Bencher {
|
|||
})
|
||||
});
|
||||
|
||||
let avg_metric = futures::future::try_join_all(futs).await?.into_iter().sum::<Metric>() / args.threads;
|
||||
let avg_metric = futures::future::try_join_all(futs)
|
||||
.await?
|
||||
.into_iter()
|
||||
.sum::<Metric>()
|
||||
/ args.threads;
|
||||
|
||||
Ok(avg_metric)
|
||||
}
|
||||
|
|
53
src/cli.rs
53
src/cli.rs
|
@ -1,13 +1,19 @@
|
|||
use std::{sync::Arc, time::Duration};
|
||||
|
||||
use clap::{command, Parser};
|
||||
use futures::StreamExt;
|
||||
use log::Log;
|
||||
use solana_client::{nonblocking::pubsub_client::PubsubClient, rpc_config::RpcTransactionLogsConfig};
|
||||
use solana_rpc_client::nonblocking::rpc_client::RpcClient;
|
||||
use tokio::sync::RwLock;
|
||||
use solana_sdk::{hash::Hash, commitment_config::{CommitmentConfig, CommitmentLevel}};
|
||||
|
||||
use crate::{
|
||||
solana_runtime::{
|
||||
accounts_fetching::AccountsFetchingTests, get_block::GetBlockTest, get_slot::GetSlotTest,
|
||||
send_and_get_status_memo::SendAndConfrimTesting,
|
||||
},
|
||||
openbook::simulate_place_orders::SimulateOpenbookV2PlaceOrder,
|
||||
test_registry::TestRegistry,
|
||||
};
|
||||
|
||||
|
@ -29,21 +35,60 @@ pub struct Args {
|
|||
#[arg(long)]
|
||||
pub get_block: bool,
|
||||
|
||||
#[arg(long)]
|
||||
pub simulate_openbook_v2: bool,
|
||||
|
||||
#[arg(short = 'a', long)]
|
||||
pub test_all: bool,
|
||||
|
||||
#[arg(short = 'r', long, default_value_t = String::from("http://127.0.0.1:8899"))]
|
||||
pub rpc_addr: String,
|
||||
|
||||
#[arg(short = 'w', long, default_value_t = String::from("ws://127.0.0.1:8900"))]
|
||||
pub rpc_ws_addr: String,
|
||||
|
||||
#[arg(short = 'd', long, default_value_t = 60)]
|
||||
pub duration_in_seconds: u64,
|
||||
|
||||
#[arg(short = 't', long, default_value_t = 4)]
|
||||
pub threads: u64,
|
||||
|
||||
#[arg(short = 'p', long)]
|
||||
pub print_logs: bool,
|
||||
}
|
||||
|
||||
impl Args {
|
||||
pub fn generate_test_registry(&self) -> TestRegistry {
|
||||
pub fn generate_test_registry(&self, block_hash: Arc<RwLock<Hash>>) -> TestRegistry {
|
||||
if self.print_logs {
|
||||
let rpc_ws_addr = self.rpc_ws_addr.clone();
|
||||
tokio::spawn(async move {
|
||||
let pubsub_client = PubsubClient::new(&rpc_ws_addr).await.unwrap();
|
||||
let res = pubsub_client.logs_subscribe(solana_client::rpc_config::RpcTransactionLogsFilter::All, RpcTransactionLogsConfig{
|
||||
commitment: None
|
||||
}).await;
|
||||
match res {
|
||||
Ok(( mut stream, _)) => {
|
||||
loop {
|
||||
let log = stream.next().await;
|
||||
match log {
|
||||
Some(log) => {
|
||||
for log_s in log.value.logs {
|
||||
println!("{}", log_s);
|
||||
}
|
||||
},
|
||||
None => {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
println!("error subscribing to the logs {}",e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let mut test_registry = TestRegistry::default();
|
||||
|
||||
if self.accounts_fetching || self.test_all {
|
||||
|
@ -51,7 +96,7 @@ impl Args {
|
|||
}
|
||||
|
||||
if self.send_and_confirm_transaction || self.test_all {
|
||||
test_registry.register(Box::new(SendAndConfrimTesting));
|
||||
test_registry.register(Box::new(SendAndConfrimTesting{block_hash: block_hash.clone()}));
|
||||
}
|
||||
|
||||
if self.get_slot || self.test_all {
|
||||
|
@ -62,6 +107,10 @@ impl Args {
|
|||
test_registry.register(Box::new(GetBlockTest));
|
||||
}
|
||||
|
||||
if self.simulate_openbook_v2 || self.test_all {
|
||||
test_registry.register(Box::new(SimulateOpenbookV2PlaceOrder{block_hash} ));
|
||||
}
|
||||
|
||||
test_registry
|
||||
}
|
||||
|
||||
|
|
|
@ -1,45 +1,78 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use solana_sdk::signature::Keypair;
|
||||
use solana_sdk::{signature::Keypair, signer::Signer};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct Command {
|
||||
pub name: String,
|
||||
pub instruction: Vec<u8>,
|
||||
pub argument_sizes: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct ProgramData {
|
||||
pub name: String,
|
||||
pub program_id: String,
|
||||
pub commands: Vec<Command>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct User {
|
||||
secret: Vec<u8>,
|
||||
token_data: Vec<TokenAccountData>,
|
||||
open_orders: Vec<OpenOrders>,
|
||||
pub secret: Vec<u8>,
|
||||
pub token_data: Vec<TokenAccountData>,
|
||||
pub open_orders: Vec<OpenOrders>,
|
||||
}
|
||||
|
||||
impl User {
|
||||
pub fn get_keypair(&self) -> Keypair {
|
||||
Keypair::from_bytes(&self.secret).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Signer for User {
|
||||
fn try_pubkey(&self) -> Result<solana_sdk::pubkey::Pubkey, solana_sdk::signer::SignerError> {
|
||||
let kp = self.get_keypair();
|
||||
Ok(kp.pubkey())
|
||||
}
|
||||
|
||||
fn try_sign_message(
|
||||
&self,
|
||||
message: &[u8],
|
||||
) -> Result<solana_sdk::signature::Signature, solana_sdk::signer::SignerError> {
|
||||
let kp = self.get_keypair();
|
||||
Ok(kp.sign_message(message))
|
||||
}
|
||||
|
||||
fn is_interactive(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct TokenAccountData {
|
||||
mint: String,
|
||||
token_account: String,
|
||||
pub mint: String,
|
||||
pub token_account: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct OpenOrders {
|
||||
market: String,
|
||||
open_orders: String,
|
||||
pub market: String,
|
||||
pub open_orders: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct Market {
|
||||
name: String,
|
||||
admin : Vec<u8>,
|
||||
market_pk: String,
|
||||
oracle: String,
|
||||
asks: String,
|
||||
bids: String,
|
||||
event_queue: String,
|
||||
base_vault: String,
|
||||
quote_vault: String,
|
||||
base_mint: String,
|
||||
quote_mint: String,
|
||||
market_index: u32,
|
||||
pub name: String,
|
||||
pub admin: Vec<u8>,
|
||||
pub market_pk: String,
|
||||
pub oracle: String,
|
||||
pub asks: String,
|
||||
pub bids: String,
|
||||
pub event_queue: String,
|
||||
pub base_vault: String,
|
||||
pub quote_vault: String,
|
||||
pub base_mint: String,
|
||||
pub quote_mint: String,
|
||||
pub market_index: usize,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
|
|
31
src/main.rs
31
src/main.rs
|
@ -2,13 +2,18 @@ pub mod bencher;
|
|||
mod cli;
|
||||
mod config;
|
||||
mod metrics;
|
||||
mod openbook;
|
||||
mod solana_runtime;
|
||||
mod test_registry;
|
||||
|
||||
use std::{sync::Arc, time::Duration};
|
||||
|
||||
use anyhow::{bail, Context};
|
||||
use clap::Parser;
|
||||
use cli::Args;
|
||||
use config::Config;
|
||||
use tokio::sync::RwLock;
|
||||
use solana_sdk::hash::Hash;
|
||||
|
||||
#[tokio::main(flavor = "multi_thread", worker_threads = 16)]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
|
@ -28,9 +33,31 @@ async fn main() -> anyhow::Result<()> {
|
|||
bail!("No payers");
|
||||
}
|
||||
|
||||
args.generate_test_registry()
|
||||
let rpc_client = args.get_rpc_client();
|
||||
let current_hash = rpc_client.get_latest_blockhash().await.unwrap();
|
||||
let block_hash: Arc<RwLock<Hash>> = Arc::new(RwLock::new(current_hash));
|
||||
// block hash updater task
|
||||
{
|
||||
let block_hash = block_hash.clone();
|
||||
let rpc_client = args.get_rpc_client();
|
||||
tokio::spawn(async move {
|
||||
loop {
|
||||
let bh = rpc_client.get_latest_blockhash().await;
|
||||
match bh {
|
||||
Ok(bh) => {
|
||||
let mut lock = block_hash.write().await;
|
||||
*lock = bh;
|
||||
}
|
||||
Err(e) => println!("blockhash update error {}", e),
|
||||
}
|
||||
tokio::time::sleep(Duration::from_millis(500)).await;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
args.generate_test_registry(block_hash)
|
||||
.start_testing(args, config_json)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,7 +90,10 @@ impl TestingTask for AccountsFetchingTests {
|
|||
for i in 0..accounts_to_fetch.len() {
|
||||
if hash_set_known.contains(&accounts_to_fetch[i]) {
|
||||
if res[i].is_none() {
|
||||
println!("unable to fetch known account {}", accounts_to_fetch[i]);
|
||||
println!(
|
||||
"unable to fetch known account {}",
|
||||
accounts_to_fetch[i]
|
||||
);
|
||||
}
|
||||
} else if res[i].is_some() {
|
||||
println!("fetched unknown account should not be possible");
|
||||
|
@ -120,7 +123,7 @@ impl TestingTask for AccountsFetchingTests {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn get_name(&self) -> &'static str {
|
||||
"Accounts Fetching"
|
||||
fn get_name(&self) -> String {
|
||||
"Accounts Fetching".to_string()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,8 +21,8 @@ impl TestingTask for GetBlockTest {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn get_name(&self) -> &'static str {
|
||||
"GetBlockTest"
|
||||
fn get_name(&self) -> String {
|
||||
"GetBlockTest".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -39,7 +39,7 @@ impl BenchFn for GetBlockBench {
|
|||
}
|
||||
|
||||
async fn bench_fn(&mut self, rpc_client: Arc<RpcClient>) -> anyhow::Result<()> {
|
||||
// self.slot += 1;
|
||||
// self.slot += 1;
|
||||
rpc_client.get_block(self.slot).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -16,8 +16,8 @@ impl TestingTask for GetSlotTest {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn get_name(&self) -> &'static str {
|
||||
"GetSlotTest"
|
||||
fn get_name(&self) -> String {
|
||||
"GetSlotTest".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
pub mod accounts_fetching;
|
||||
pub mod send_and_get_status_memo;
|
||||
pub mod get_slot;
|
||||
pub mod get_block;
|
||||
pub mod get_slot;
|
||||
pub mod send_and_get_status_memo;
|
||||
|
|
|
@ -26,7 +26,9 @@ struct Metric {
|
|||
pub number_of_unconfirmed_txs: usize,
|
||||
}
|
||||
|
||||
pub struct SendAndConfrimTesting;
|
||||
pub struct SendAndConfrimTesting {
|
||||
pub block_hash: Arc<RwLock<Hash>>
|
||||
}
|
||||
|
||||
fn create_memo_tx(msg: &[u8], payer: &Keypair, blockhash: Hash) -> Transaction {
|
||||
let memo = Pubkey::from_str(MEMO_PROGRAM_ID).unwrap();
|
||||
|
@ -117,26 +119,6 @@ impl TestingTask for SendAndConfrimTesting {
|
|||
) -> anyhow::Result<()> {
|
||||
println!("started sending and confirming memo transactions");
|
||||
let rpc_client = Arc::new(RpcClient::new(args.rpc_addr.clone()));
|
||||
let block_hash: Arc<RwLock<Hash>> = Arc::new(RwLock::new(Hash::default()));
|
||||
|
||||
// block hash updater task
|
||||
{
|
||||
let block_hash = block_hash.clone();
|
||||
let rpc_client = rpc_client.clone();
|
||||
tokio::spawn(async move {
|
||||
loop {
|
||||
let bh = rpc_client.get_latest_blockhash().await;
|
||||
match bh {
|
||||
Ok(bh) => {
|
||||
let mut lock = block_hash.write().await;
|
||||
*lock = bh;
|
||||
}
|
||||
Err(e) => println!("blockhash update error {}", e),
|
||||
}
|
||||
tokio::time::sleep(Duration::from_millis(500)).await;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let mut run_interval_ms = tokio::time::interval(Duration::from_secs(1));
|
||||
let nb_runs = args.duration_in_seconds;
|
||||
|
@ -146,6 +128,7 @@ impl TestingTask for SendAndConfrimTesting {
|
|||
let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(seed);
|
||||
let payer = payers.choose(&mut rng).unwrap();
|
||||
let payer = Keypair::from_bytes(payer.to_bytes().as_slice()).unwrap();
|
||||
let block_hash = self.block_hash.clone();
|
||||
tasks.push(tokio::spawn(send_and_confirm_transactions(
|
||||
rpc_client.clone(),
|
||||
NB_MEMO_TRANSACTIONS_SENT_PER_SECOND,
|
||||
|
@ -173,7 +156,7 @@ impl TestingTask for SendAndConfrimTesting {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn get_name(&self) -> &'static str {
|
||||
"Send and confirm memo transaction"
|
||||
fn get_name(&self) -> String {
|
||||
"Send and confirm memo transaction".to_string()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{cli::Args, config::Config};
|
||||
|
||||
#[async_trait]
|
||||
pub trait TestingTask: Send + Sync {
|
||||
async fn test(&self, args: Args, config: Config) -> anyhow::Result<()>;
|
||||
fn get_name(&self) -> &'static str;
|
||||
fn get_name(&self) -> String;
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -19,6 +18,7 @@ impl TestRegistry {
|
|||
}
|
||||
|
||||
pub async fn start_testing(self, args: Args, config: Config) {
|
||||
|
||||
let tasks = self.tests.into_iter().map(|test| {
|
||||
let args = args.clone();
|
||||
let config = config.clone();
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit b7754174bf8c8cbcbda25372fbd0f8dc8b429317
|
||||
Subproject commit 921d52c58064c3f197600b90929cedb845314a22
|
Loading…
Reference in New Issue