From 015ccbacdab15cb0e61a585440463360844c3efd Mon Sep 17 00:00:00 2001 From: Danial Mehrjerdi Date: Mon, 9 Sep 2024 13:35:36 +0200 Subject: [PATCH 1/7] Add svm bid data extraction from transaction --- auction-server/Cargo.toml | 3 ++ auction-server/build.rs | 44 +++++++++++++++++++- auction-server/src/auction.rs | 76 +++++++++++++++++++++++++++++------ 3 files changed, 110 insertions(+), 13 deletions(-) diff --git a/auction-server/Cargo.toml b/auction-server/Cargo.toml index ece82c8ed..4af89f424 100644 --- a/auction-server/Cargo.toml +++ b/auction-server/Cargo.toml @@ -4,6 +4,9 @@ version = "0.10.0" edition = "2021" license-file = "license.txt" +[build-dependencies] +anchor-lang-idl = { version = "0.1.1", features = ["convert"] } + [dependencies] tokio = { version = "1.28", features = ["macros", "sync", "rt-multi-thread", "signal"] } tokio-stream = "0.1.14" diff --git a/auction-server/build.rs b/auction-server/build.rs index 047eb9e9f..895cdc761 100644 --- a/auction-server/build.rs +++ b/auction-server/build.rs @@ -1,4 +1,44 @@ -use std::process::Command; +use { + anchor_lang_idl::{ + convert::convert_idl, + types::IdlInstructionAccountItem, + }, + std::{ + fs, + process::Command, + }, +}; + +const SUBMIT_BID_INSTRUCTION_SVM: &str = "submit_bid"; +const PERMISSION_ACCOUNT_SVM: &str = "permission"; + +fn verify_idl() { + let idl_json = fs::read("../contracts/svm/target/idl/express_relay.json") + .expect("Failed to read IDL JSON"); + let express_relay_idl = + convert_idl(idl_json.as_slice()).expect("Failed to convert IDL to Rust"); + match express_relay_idl + .instructions + .iter() + .find(|i| i.name == SUBMIT_BID_INSTRUCTION_SVM) + { + Some(instruction) => { + if !instruction.accounts.iter().any(|a| match a { + IdlInstructionAccountItem::Single(a) => a.name == PERMISSION_ACCOUNT_SVM, + IdlInstructionAccountItem::Composite(a) => a.name == PERMISSION_ACCOUNT_SVM, + }) { + panic!( + "{} account not found in {} instruction", + PERMISSION_ACCOUNT_SVM, SUBMIT_BID_INSTRUCTION_SVM + ); + } + } + None => panic!( + "{} instruction not found in IDL", + SUBMIT_BID_INSTRUCTION_SVM + ), + } +} fn main() { let contract_setup = r#" @@ -24,4 +64,6 @@ fn main() { String::from_utf8_lossy(&output.stdout) ); } + + verify_idl(); } diff --git a/auction-server/src/auction.rs b/auction-server/src/auction.rs index 007082b26..ac4176ddb 100644 --- a/auction-server/src/auction.rs +++ b/auction-server/src/auction.rs @@ -24,8 +24,17 @@ use { }, traced_client::TracedClient, }, - ::express_relay as express_relay_svm, - anchor_lang::Discriminator, + ::express_relay::{ + self as express_relay_svm, + }, + anchor_lang::{ + AnchorDeserialize, + Discriminator, + }, + anchor_lang_idl::types::{ + Idl, + IdlInstructionAccountItem, + }, anyhow::{ anyhow, Result, @@ -82,7 +91,11 @@ use { Deserializer, Serialize, }, - solana_sdk::transaction::VersionedTransaction, + solana_sdk::{ + instruction::CompiledInstruction, + pubkey::Pubkey, + transaction::VersionedTransaction, + }, sqlx::types::time::OffsetDateTime, std::{ result, @@ -880,8 +893,8 @@ pub async fn run_tracker_loop(store: Arc, chain_id: String) -> Result<()> pub fn verify_submit_bid_instruction_svm( chain_store: &ChainStoreSvm, transaction: VersionedTransaction, -) -> Result<(), RestError> { - if transaction +) -> Result { + let submit_bid_instructions: Vec = transaction .message .instructions() .iter() @@ -895,15 +908,48 @@ pub fn verify_submit_bid_instruction_svm( .data .starts_with(&express_relay_svm::instruction::SubmitBid::discriminator()) }) - .count() - != 1 - { - return Err(RestError::BadParameters( + .cloned() + .collect(); + + match submit_bid_instructions.len() { + 1 => Ok(submit_bid_instructions[0].clone()), + _ => Err(RestError::BadParameters( "Bid has to include exactly one submit_bid instruction to Express Relay program" .to_string(), - )); + )), } - Ok(()) +} + +fn find_account_position_svm(idl: Idl, instruction: &str, account: &str) -> Option { + let instruction = idl.instructions.iter().find(|i| i.name == instruction)?; + instruction.accounts.iter().position(|a| match a { + IdlInstructionAccountItem::Single(a) => a.name == account, + IdlInstructionAccountItem::Composite(a) => a.name == account, + }) +} + +const SUBMIT_BID_INSTRUCTION_SVM: &str = "submit_bid"; +const PERMISSION_ACCOUNT_SVM: &str = "permission"; + +fn extract_bid_data_svm( + idl: Idl, + accounts: &[Pubkey], + instruction: CompiledInstruction, +) -> Result<(u64, Pubkey), RestError> { + let discriminator = express_relay_svm::instruction::SubmitBid::discriminator(); + let submit_bid_data = express_relay_svm::SubmitBidArgs::try_from_slice( + &instruction.data.as_slice()[discriminator.len()..], + ) + .map_err(|e| RestError::BadParameters(format!("Invalid submit_bid instruction data: {}", e)))?; + let permission_account_position = + find_account_position_svm(idl, SUBMIT_BID_INSTRUCTION_SVM, PERMISSION_ACCOUNT_SVM).ok_or( + RestError::BadParameters("Invalid submit_bid instruction accounts".to_string()), + )?; + let account_position = instruction.accounts[permission_account_position]; + Ok(( + submit_bid_data.bid_amount, + accounts[account_position as usize], + )) } #[tracing::instrument(skip_all)] @@ -918,7 +964,13 @@ pub async fn handle_bid_svm( .get(&bid.chain_id) .ok_or(RestError::InvalidChainId)?; - verify_submit_bid_instruction_svm(chain_store, bid.transaction.clone())?; + let submit_bid_instruction = + verify_submit_bid_instruction_svm(chain_store, bid.transaction.clone())?; + let (_bid_amount, _permission) = extract_bid_data_svm( + store.express_relay_idl.clone(), + bid.transaction.message.static_account_keys(), + submit_bid_instruction, + )?; // TODO implement this Err(RestError::NotImplemented) From 40d4c14ce325e7d1222b3dc64d16d516319fe969 Mon Sep 17 00:00:00 2001 From: Danial Mehrjerdi Date: Mon, 9 Sep 2024 17:06:26 +0200 Subject: [PATCH 2/7] Address comments --- auction-server/build.rs | 80 +++++++++++++++++++++++++---------- auction-server/src/auction.rs | 53 +++++++++++------------ auction-server/src/server.rs | 3 ++ auction-server/src/state.rs | 31 +++++++------- 4 files changed, 104 insertions(+), 63 deletions(-) diff --git a/auction-server/build.rs b/auction-server/build.rs index 895cdc761..7cb9cd968 100644 --- a/auction-server/build.rs +++ b/auction-server/build.rs @@ -9,61 +9,97 @@ use { }, }; +fn build_evm_contracts() { + let contract_setup = r#" + cd ../contracts/evm + forge build --via-ir + "#; + println!("cargo:rerun-if-changed=../contracts/evm"); + // Build the contracts and generate the ABIs. This is required for abigen! macro expansions to work. + let output = Command::new("sh") + .args(["-c", contract_setup]) + .output() + .expect("Failed to run build contracts command"); + if !output.status.success() { + panic!( + "Failed to build contracts: {}", + String::from_utf8_lossy(&output.stderr) + ); + } else { + println!( + "Built all solidity contracts {}", + String::from_utf8_lossy(&output.stdout) + ); + } +} + + const SUBMIT_BID_INSTRUCTION_SVM: &str = "submit_bid"; const PERMISSION_ACCOUNT_SVM: &str = "permission"; +const IDL_LOCATION: &str = "../contracts/svm/target/idl/express_relay.json"; -fn verify_idl() { - let idl_json = fs::read("../contracts/svm/target/idl/express_relay.json") - .expect("Failed to read IDL JSON"); +fn verify_and_extract_idl_data() { + let idl_json = fs::read(IDL_LOCATION).expect("Failed to read IDL JSON"); let express_relay_idl = convert_idl(idl_json.as_slice()).expect("Failed to convert IDL to Rust"); - match express_relay_idl + + let position = match express_relay_idl .instructions .iter() .find(|i| i.name == SUBMIT_BID_INSTRUCTION_SVM) { Some(instruction) => { - if !instruction.accounts.iter().any(|a| match a { + match instruction.accounts.iter().position(|a| match a { IdlInstructionAccountItem::Single(a) => a.name == PERMISSION_ACCOUNT_SVM, IdlInstructionAccountItem::Composite(a) => a.name == PERMISSION_ACCOUNT_SVM, }) { - panic!( + Some(position) => position, + None => panic!( "{} account not found in {} instruction", PERMISSION_ACCOUNT_SVM, SUBMIT_BID_INSTRUCTION_SVM - ); + ), } } None => panic!( "{} instruction not found in IDL", - SUBMIT_BID_INSTRUCTION_SVM + SUBMIT_BID_INSTRUCTION_SVM, ), - } + }; + println!( + "cargo:rustc-env=SUBMIT_BID_PERMISSION_ACCOUNT_POSITION={}", + position + ); } -fn main() { - let contract_setup = r#" - cd ../contracts/evm - forge build --via-ir +fn build_svm_contracts() { + let contract_setup_svm = r#" + cd ../contracts/svm/programs/express_relay + mkdir -p ../../target/idl + anchor idl build > ../../target/idl/express_relay.json "#; - println!("cargo:rerun-if-changed=../contracts/evm"); - println!("cargo:rerun-if-changed=migrations"); - - // Build the contracts and generate the ABIs. This is required for abigen! macro expansions to work. + println!("cargo:rerun-if-changed=../contracts/svm"); + // Build the svm contract and generate the IDLs. let output = Command::new("sh") - .args(["-c", contract_setup]) + .args(["-c", contract_setup_svm]) .output() - .expect("Failed to run build contracts command"); + .expect("Failed to run build svm contracts command"); if !output.status.success() { panic!( - "Failed to build contracts: {}", + "Failed to build svm contracts: {}", String::from_utf8_lossy(&output.stderr) ); } else { println!( - "Built all solidity contracts {}", + "Built all svm contracts {}", String::from_utf8_lossy(&output.stdout) ); } +} + +fn main() { + println!("cargo:rerun-if-changed=migrations"); - verify_idl(); + build_evm_contracts(); + build_svm_contracts(); + verify_and_extract_idl_data(); } diff --git a/auction-server/src/auction.rs b/auction-server/src/auction.rs index ac4176ddb..982882fca 100644 --- a/auction-server/src/auction.rs +++ b/auction-server/src/auction.rs @@ -31,10 +31,6 @@ use { AnchorDeserialize, Discriminator, }, - anchor_lang_idl::types::{ - Idl, - IdlInstructionAccountItem, - }, anyhow::{ anyhow, Result, @@ -920,19 +916,8 @@ pub fn verify_submit_bid_instruction_svm( } } -fn find_account_position_svm(idl: Idl, instruction: &str, account: &str) -> Option { - let instruction = idl.instructions.iter().find(|i| i.name == instruction)?; - instruction.accounts.iter().position(|a| match a { - IdlInstructionAccountItem::Single(a) => a.name == account, - IdlInstructionAccountItem::Composite(a) => a.name == account, - }) -} - -const SUBMIT_BID_INSTRUCTION_SVM: &str = "submit_bid"; -const PERMISSION_ACCOUNT_SVM: &str = "permission"; - fn extract_bid_data_svm( - idl: Idl, + permission_account_position: usize, accounts: &[Pubkey], instruction: CompiledInstruction, ) -> Result<(u64, Pubkey), RestError> { @@ -941,15 +926,31 @@ fn extract_bid_data_svm( &instruction.data.as_slice()[discriminator.len()..], ) .map_err(|e| RestError::BadParameters(format!("Invalid submit_bid instruction data: {}", e)))?; - let permission_account_position = - find_account_position_svm(idl, SUBMIT_BID_INSTRUCTION_SVM, PERMISSION_ACCOUNT_SVM).ok_or( - RestError::BadParameters("Invalid submit_bid instruction accounts".to_string()), - )?; - let account_position = instruction.accounts[permission_account_position]; - Ok(( - submit_bid_data.bid_amount, - accounts[account_position as usize], - )) + + if instruction.accounts.len() <= permission_account_position { + tracing::error!( + "Permission account not found in submit_bid instruction: {:?} - {}", + instruction.accounts, + permission_account_position, + ); + return Err(RestError::BadParameters( + "Permission account not found in submit_bid instruction".to_string(), + )); + } + + let account_position = instruction.accounts[permission_account_position] as usize; + if account_position >= accounts.len() { + tracing::error!( + "Permission account not found in transaction accounts: {:?} - {}", + accounts, + account_position, + ); + return Err(RestError::BadParameters( + "Permission account not found in transaction accounts".to_string(), + )); + } + + Ok((submit_bid_data.bid_amount, accounts[account_position])) } #[tracing::instrument(skip_all)] @@ -967,7 +968,7 @@ pub async fn handle_bid_svm( let submit_bid_instruction = verify_submit_bid_instruction_svm(chain_store, bid.transaction.clone())?; let (_bid_amount, _permission) = extract_bid_data_svm( - store.express_relay_idl.clone(), + store.permission_account_position, bid.transaction.message.static_account_keys(), submit_bid_instruction, )?; diff --git a/auction-server/src/server.rs b/auction-server/src/server.rs index e9f4b69e9..5b1e77b3b 100644 --- a/auction-server/src/server.rs +++ b/auction-server/src/server.rs @@ -313,6 +313,9 @@ pub async fn start_server(run_options: RunOptions) -> anyhow::Result<()> { access_tokens: RwLock::new(access_tokens), metrics_recorder: setup_metrics_recorder()?, express_relay_idl: load_express_relay_idl()?, + permission_account_position: env!("SUBMIT_BID_PERMISSION_ACCOUNT_POSITION") + .parse::() + .expect("Failed to parse permission account position"), }); tokio::join!( diff --git a/auction-server/src/state.rs b/auction-server/src/state.rs index 2c89332ea..d4c655554 100644 --- a/auction-server/src/state.rs +++ b/auction-server/src/state.rs @@ -297,21 +297,22 @@ pub struct BidStatusWithId { } pub struct Store { - pub chains: HashMap, - pub chains_svm: HashMap, - pub bids: RwLock>>, - pub event_sender: broadcast::Sender, - pub opportunity_store: OpportunityStore, - pub relayer: LocalWallet, - pub ws: WsState, - pub db: sqlx::PgPool, - pub task_tracker: TaskTracker, - pub auction_lock: Mutex>, - pub submitted_auctions: RwLock>>, - pub secret_key: String, - pub access_tokens: RwLock>, - pub metrics_recorder: PrometheusHandle, - pub express_relay_idl: Idl, + pub chains: HashMap, + pub chains_svm: HashMap, + pub bids: RwLock>>, + pub event_sender: broadcast::Sender, + pub opportunity_store: OpportunityStore, + pub relayer: LocalWallet, + pub ws: WsState, + pub db: sqlx::PgPool, + pub task_tracker: TaskTracker, + pub auction_lock: Mutex>, + pub submitted_auctions: RwLock>>, + pub secret_key: String, + pub access_tokens: RwLock>, + pub metrics_recorder: PrometheusHandle, + pub express_relay_idl: Idl, + pub permission_account_position: usize, } impl SimulatedBid { From f193260642abde1048134c3729ea25271b3e49ed Mon Sep 17 00:00:00 2001 From: Danial Mehrjerdi Date: Mon, 9 Sep 2024 17:24:28 +0200 Subject: [PATCH 3/7] Add anchor to pre-commit --- .github/workflows/ci-pre-commit.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci-pre-commit.yml b/.github/workflows/ci-pre-commit.yml index a360dc368..118c105c3 100644 --- a/.github/workflows/ci-pre-commit.yml +++ b/.github/workflows/ci-pre-commit.yml @@ -46,6 +46,8 @@ jobs: - name: Install forge dependencies 5 working-directory: contracts/evm run: forge install nomad-xyz/ExcessivelySafeCall@be417ab0c26233578b8d8f3a37b87bd1fcb4e286 --no-git --no-commit + - name: Install Anchor CLI + run: npm install -g @coral-xyz/anchor-cli@v0.30.1 - uses: pre-commit/action@v3.0.0 if: ${{ github.event_name == 'pull_request' }} with: From b80793a1b8efc4b4120d5db54569725dcdd7cac9 Mon Sep 17 00:00:00 2001 From: Danial Mehrjerdi Date: Mon, 9 Sep 2024 18:09:21 +0200 Subject: [PATCH 4/7] Fix dockerfile build --- .github/workflows/push-rust-services.yaml | 1 + Dockerfile | 26 +++-------------------- 2 files changed, 4 insertions(+), 23 deletions(-) diff --git a/.github/workflows/push-rust-services.yaml b/.github/workflows/push-rust-services.yaml index 32034e566..77e20cf5a 100644 --- a/.github/workflows/push-rust-services.yaml +++ b/.github/workflows/push-rust-services.yaml @@ -5,6 +5,7 @@ on: - v* branches: - main + - feat/svm-extract-bid-data workflow_dispatch: inputs: dispatch_description: diff --git a/Dockerfile b/Dockerfile index 8885b570f..e8a13a43b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,28 +7,11 @@ COPY contracts/evm contracts/evm WORKDIR /src/contracts/evm RUN npm install -# Build solana anchor -FROM solanalabs/solana:v1.18.18 AS solana_build -RUN apt-get update \ - && apt-get install -y \ - apt-utils \ - curl \ - gcc \ - && rm -rf /var/lib/apt/lists/* -RUN curl https://sh.rustup.rs -sSf > /tmp/rustup-init.sh \ - && chmod +x /tmp/rustup-init.sh \ - && sh /tmp/rustup-init.sh -y \ - && rm -rf /tmp/rustup-init.sh -ENV PATH="/root/.cargo/bin:${PATH}" -RUN ["/bin/bash", "-c", "source $HOME/.cargo/env"] +FROM rust:${RUST_VERSION} AS build + +# Latest version supporting anchor RUN rustup default nightly-2024-02-04 RUN cargo install --git https://github.com/coral-xyz/anchor --tag v0.30.1 anchor-cli --locked -WORKDIR /src -COPY contracts/svm contracts/svm -WORKDIR /src/contracts/svm -RUN anchor build - -FROM rust:${RUST_VERSION} AS build # Set default toolchain RUN rustup default nightly-2024-04-10 @@ -51,9 +34,6 @@ RUN forge install OpenZeppelin/openzeppelin-contracts-upgradeable@v4.9.6 --no-gi RUN forge install Uniswap/permit2@0x000000000022D473030F116dDEE9F6B43aC78BA3 --no-git --no-commit RUN forge install nomad-xyz/ExcessivelySafeCall@be417ab0c26233578b8d8f3a37b87bd1fcb4e286 --no-git --no-commit -# Add solana dependencies -COPY --from=solana_build /src/contracts/svm/target/ /src/contracts/svm/target/ - # Build auction-server WORKDIR /src COPY auction-server auction-server From d1123124efa6d70fa1942ca77bb2d48ce3c56516 Mon Sep 17 00:00:00 2001 From: Danial Mehrjerdi Date: Mon, 9 Sep 2024 19:18:05 +0200 Subject: [PATCH 5/7] Add router to permission_keyx --- .github/workflows/push-rust-services.yaml | 1 - auction-server/build.rs | 64 ++++++++++++++--------- auction-server/src/auction.rs | 58 +++++++++++++------- auction-server/src/server.rs | 15 ++++-- auction-server/src/state.rs | 38 ++++++++------ 5 files changed, 112 insertions(+), 64 deletions(-) diff --git a/.github/workflows/push-rust-services.yaml b/.github/workflows/push-rust-services.yaml index 77e20cf5a..32034e566 100644 --- a/.github/workflows/push-rust-services.yaml +++ b/.github/workflows/push-rust-services.yaml @@ -5,7 +5,6 @@ on: - v* branches: - main - - feat/svm-extract-bid-data workflow_dispatch: inputs: dispatch_description: diff --git a/auction-server/build.rs b/auction-server/build.rs index 7cb9cd968..1407dafc4 100644 --- a/auction-server/build.rs +++ b/auction-server/build.rs @@ -1,7 +1,10 @@ use { anchor_lang_idl::{ convert::convert_idl, - types::IdlInstructionAccountItem, + types::{ + Idl, + IdlInstructionAccountItem, + }, }, std::{ fs, @@ -36,38 +39,49 @@ fn build_evm_contracts() { const SUBMIT_BID_INSTRUCTION_SVM: &str = "submit_bid"; const PERMISSION_ACCOUNT_SVM: &str = "permission"; +const ROUTER_ACCOUNT_SVM: &str = "router"; const IDL_LOCATION: &str = "../contracts/svm/target/idl/express_relay.json"; +fn extract_account_position(idl: Idl, instruction_name: &str, account_name: &str) -> usize { + let instruction = idl + .instructions + .iter() + .find(|i| i.name == instruction_name) + .unwrap_or_else(|| panic!("Instruction {} not found in IDL", instruction_name)); + instruction + .accounts + .iter() + .position(|a| match a { + IdlInstructionAccountItem::Single(a) => a.name == account_name, + IdlInstructionAccountItem::Composite(a) => a.name == account_name, + }) + .unwrap_or_else(|| { + panic!( + "Account {} not found in instruction {}", + account_name, instruction_name + ) + }) +} + fn verify_and_extract_idl_data() { let idl_json = fs::read(IDL_LOCATION).expect("Failed to read IDL JSON"); let express_relay_idl = convert_idl(idl_json.as_slice()).expect("Failed to convert IDL to Rust"); - - let position = match express_relay_idl - .instructions - .iter() - .find(|i| i.name == SUBMIT_BID_INSTRUCTION_SVM) - { - Some(instruction) => { - match instruction.accounts.iter().position(|a| match a { - IdlInstructionAccountItem::Single(a) => a.name == PERMISSION_ACCOUNT_SVM, - IdlInstructionAccountItem::Composite(a) => a.name == PERMISSION_ACCOUNT_SVM, - }) { - Some(position) => position, - None => panic!( - "{} account not found in {} instruction", - PERMISSION_ACCOUNT_SVM, SUBMIT_BID_INSTRUCTION_SVM - ), - } - } - None => panic!( - "{} instruction not found in IDL", - SUBMIT_BID_INSTRUCTION_SVM, - ), - }; println!( "cargo:rustc-env=SUBMIT_BID_PERMISSION_ACCOUNT_POSITION={}", - position + extract_account_position( + express_relay_idl.clone(), + SUBMIT_BID_INSTRUCTION_SVM, + PERMISSION_ACCOUNT_SVM, + ) + ); + println!( + "cargo:rustc-env=SUBMIT_BID_ROUTER_ACCOUNT_POSITION={}", + extract_account_position( + express_relay_idl.clone(), + SUBMIT_BID_INSTRUCTION_SVM, + ROUTER_ACCOUNT_SVM, + ) ); } diff --git a/auction-server/src/auction.rs b/auction-server/src/auction.rs index 982882fca..deb8d1337 100644 --- a/auction-server/src/auction.rs +++ b/auction-server/src/auction.rs @@ -19,6 +19,8 @@ use { BidStatus, ChainStoreEvm, ChainStoreSvm, + ExpressRelaySvm, + PermissionKey, SimulatedBid, Store, }, @@ -916,32 +918,26 @@ pub fn verify_submit_bid_instruction_svm( } } -fn extract_bid_data_svm( - permission_account_position: usize, +fn extract_account_svm( accounts: &[Pubkey], instruction: CompiledInstruction, -) -> Result<(u64, Pubkey), RestError> { - let discriminator = express_relay_svm::instruction::SubmitBid::discriminator(); - let submit_bid_data = express_relay_svm::SubmitBidArgs::try_from_slice( - &instruction.data.as_slice()[discriminator.len()..], - ) - .map_err(|e| RestError::BadParameters(format!("Invalid submit_bid instruction data: {}", e)))?; - - if instruction.accounts.len() <= permission_account_position { + position: usize, +) -> Result { + if instruction.accounts.len() <= position { tracing::error!( - "Permission account not found in submit_bid instruction: {:?} - {}", - instruction.accounts, - permission_account_position, + "Account position not found in instruction: {:?} - {}", + instruction, + position, ); return Err(RestError::BadParameters( "Permission account not found in submit_bid instruction".to_string(), )); } - let account_position = instruction.accounts[permission_account_position] as usize; + let account_position = instruction.accounts[position] as usize; if account_position >= accounts.len() { tracing::error!( - "Permission account not found in transaction accounts: {:?} - {}", + "Account not found in transaction accounts: {:?} - {}", accounts, account_position, ); @@ -950,7 +946,33 @@ fn extract_bid_data_svm( )); } - Ok((submit_bid_data.bid_amount, accounts[account_position])) + Ok(accounts[account_position]) +} + +fn extract_bid_data_svm( + express_relay_svm: ExpressRelaySvm, + accounts: &[Pubkey], + instruction: CompiledInstruction, +) -> Result<(u64, PermissionKey), RestError> { + let discriminator = express_relay_svm::instruction::SubmitBid::discriminator(); + let submit_bid_data = express_relay_svm::SubmitBidArgs::try_from_slice( + &instruction.data.as_slice()[discriminator.len()..], + ) + .map_err(|e| RestError::BadParameters(format!("Invalid submit_bid instruction data: {}", e)))?; + + let permission_account = extract_account_svm( + accounts, + instruction.clone(), + express_relay_svm.permission_account_position, + )?; + let router_account = extract_account_svm( + accounts, + instruction.clone(), + express_relay_svm.router_account_position, + )?; + + let concat = [permission_account.to_bytes(), router_account.to_bytes()].concat(); + Ok((submit_bid_data.bid_amount, concat.into())) } #[tracing::instrument(skip_all)] @@ -967,8 +989,8 @@ pub async fn handle_bid_svm( let submit_bid_instruction = verify_submit_bid_instruction_svm(chain_store, bid.transaction.clone())?; - let (_bid_amount, _permission) = extract_bid_data_svm( - store.permission_account_position, + let (_bid_amount, _permission_key) = extract_bid_data_svm( + store.express_relay_svm.clone(), bid.transaction.message.static_account_keys(), submit_bid_instruction, )?; diff --git a/auction-server/src/server.rs b/auction-server/src/server.rs index 5b1e77b3b..a0b0ee244 100644 --- a/auction-server/src/server.rs +++ b/auction-server/src/server.rs @@ -27,6 +27,7 @@ use { state::{ ChainStoreEvm, ChainStoreSvm, + ExpressRelaySvm, OpportunityStore, Store, }, @@ -267,6 +268,15 @@ pub async fn start_server(run_options: RunOptions) -> anyhow::Result<()> { }; let chains_svm = setup_chain_store_svm(config_map); + let express_relay_svm = ExpressRelaySvm { + idl: load_express_relay_idl()?, + permission_account_position: env!("SUBMIT_BID_PERMISSION_ACCOUNT_POSITION") + .parse::() + .expect("Failed to parse permission account position"), + router_account_position: env!("SUBMIT_BID_ROUTER_ACCOUNT_POSITION") + .parse::() + .expect("Failed to parse router account position"), + }; let (broadcast_sender, broadcast_receiver) = tokio::sync::broadcast::channel(NOTIFICATIONS_CHAN_LEN); @@ -312,10 +322,7 @@ pub async fn start_server(run_options: RunOptions) -> anyhow::Result<()> { secret_key: run_options.secret_key.clone(), access_tokens: RwLock::new(access_tokens), metrics_recorder: setup_metrics_recorder()?, - express_relay_idl: load_express_relay_idl()?, - permission_account_position: env!("SUBMIT_BID_PERMISSION_ACCOUNT_POSITION") - .parse::() - .expect("Failed to parse permission account position"), + express_relay_svm, }); tokio::join!( diff --git a/auction-server/src/state.rs b/auction-server/src/state.rs index d4c655554..c64023612 100644 --- a/auction-server/src/state.rs +++ b/auction-server/src/state.rs @@ -296,23 +296,29 @@ pub struct BidStatusWithId { pub bid_status: BidStatus, } -pub struct Store { - pub chains: HashMap, - pub chains_svm: HashMap, - pub bids: RwLock>>, - pub event_sender: broadcast::Sender, - pub opportunity_store: OpportunityStore, - pub relayer: LocalWallet, - pub ws: WsState, - pub db: sqlx::PgPool, - pub task_tracker: TaskTracker, - pub auction_lock: Mutex>, - pub submitted_auctions: RwLock>>, - pub secret_key: String, - pub access_tokens: RwLock>, - pub metrics_recorder: PrometheusHandle, - pub express_relay_idl: Idl, +#[derive(Clone)] +pub struct ExpressRelaySvm { + pub idl: Idl, pub permission_account_position: usize, + pub router_account_position: usize, +} + +pub struct Store { + pub chains: HashMap, + pub chains_svm: HashMap, + pub bids: RwLock>>, + pub event_sender: broadcast::Sender, + pub opportunity_store: OpportunityStore, + pub relayer: LocalWallet, + pub ws: WsState, + pub db: sqlx::PgPool, + pub task_tracker: TaskTracker, + pub auction_lock: Mutex>, + pub submitted_auctions: RwLock>>, + pub secret_key: String, + pub access_tokens: RwLock>, + pub metrics_recorder: PrometheusHandle, + pub express_relay_svm: ExpressRelaySvm, } impl SimulatedBid { From d18d6432165b9b9cc4652e62c2d031b84518e5ed Mon Sep 17 00:00:00 2001 From: Danial Mehrjerdi Date: Mon, 9 Sep 2024 19:40:40 +0200 Subject: [PATCH 6/7] Address more comments --- auction-server/Cargo.toml | 1 - auction-server/src/auction.rs | 50 +++++++++++++++++++---------------- auction-server/src/server.rs | 12 --------- auction-server/src/state.rs | 2 -- 4 files changed, 27 insertions(+), 38 deletions(-) diff --git a/auction-server/Cargo.toml b/auction-server/Cargo.toml index 4af89f424..4f54cdc93 100644 --- a/auction-server/Cargo.toml +++ b/auction-server/Cargo.toml @@ -46,7 +46,6 @@ serde_path_to_error = "0.1.16" solana-sdk = "2.0.7" bincode = "1.3.3" serde_with = "3.9.0" -anchor-lang-idl = { version = "0.1.1", features = ["convert"] } anchor-lang = "0.30.1" express-relay = { path = "../contracts/svm/programs/express_relay" } diff --git a/auction-server/src/auction.rs b/auction-server/src/auction.rs index deb8d1337..512804125 100644 --- a/auction-server/src/auction.rs +++ b/auction-server/src/auction.rs @@ -923,30 +923,34 @@ fn extract_account_svm( instruction: CompiledInstruction, position: usize, ) -> Result { - if instruction.accounts.len() <= position { - tracing::error!( - "Account position not found in instruction: {:?} - {}", - instruction, - position, - ); - return Err(RestError::BadParameters( - "Permission account not found in submit_bid instruction".to_string(), - )); - } - - let account_position = instruction.accounts[position] as usize; - if account_position >= accounts.len() { - tracing::error!( - "Account not found in transaction accounts: {:?} - {}", - accounts, - account_position, - ); - return Err(RestError::BadParameters( - "Permission account not found in transaction accounts".to_string(), - )); + match instruction.accounts.get(position) { + None => { + tracing::error!( + "Account position not found in instruction: {:?} - {}", + instruction, + position, + ); + Err(RestError::BadParameters( + "Account not found in submit_bid instruction".to_string(), + )) + } + Some(account_position) => { + let account_position: usize = (*account_position).into(); + match accounts.get(position) { + None => { + tracing::error!( + "Account not found in transaction accounts: {:?} - {}", + accounts, + account_position, + ); + Err(RestError::BadParameters( + "Account not found in transaction accounts".to_string(), + )) + } + Some(account) => Ok(*account), + } + } } - - Ok(accounts[account_position]) } fn extract_bid_data_svm( diff --git a/auction-server/src/server.rs b/auction-server/src/server.rs index a0b0ee244..ccdbe0c8c 100644 --- a/auction-server/src/server.rs +++ b/auction-server/src/server.rs @@ -33,10 +33,6 @@ use { }, traced_client::TracedClient, }, - anchor_lang_idl::{ - convert::convert_idl, - types::Idl, - }, anyhow::anyhow, axum_prometheus::{ metrics_exporter_prometheus::{ @@ -69,7 +65,6 @@ use { }, std::{ collections::HashMap, - fs, sync::{ atomic::{ AtomicBool, @@ -233,12 +228,6 @@ async fn setup_chain_store( .collect() } -pub fn load_express_relay_idl() -> anyhow::Result { - let idl = fs::read("../contracts/svm/target/idl/express_relay.json")?; - convert_idl(idl.as_slice()) - .map_err(|err| anyhow!("Failed to convert express relay idl: {:?}", err)) -} - const NOTIFICATIONS_CHAN_LEN: usize = 1000; pub async fn start_server(run_options: RunOptions) -> anyhow::Result<()> { tokio::spawn(async move { @@ -269,7 +258,6 @@ pub async fn start_server(run_options: RunOptions) -> anyhow::Result<()> { let chains_svm = setup_chain_store_svm(config_map); let express_relay_svm = ExpressRelaySvm { - idl: load_express_relay_idl()?, permission_account_position: env!("SUBMIT_BID_PERMISSION_ACCOUNT_POSITION") .parse::() .expect("Failed to parse permission account position"), diff --git a/auction-server/src/state.rs b/auction-server/src/state.rs index c64023612..962c08614 100644 --- a/auction-server/src/state.rs +++ b/auction-server/src/state.rs @@ -17,7 +17,6 @@ use { models, traced_client::TracedClient, }, - anchor_lang_idl::types::Idl, axum::Json, axum_prometheus::metrics_exporter_prometheus::PrometheusHandle, base64::{ @@ -298,7 +297,6 @@ pub struct BidStatusWithId { #[derive(Clone)] pub struct ExpressRelaySvm { - pub idl: Idl, pub permission_account_position: usize, pub router_account_position: usize, } From 66d5aa0df5b0cca8dfe0bc265d654c857b7cb453 Mon Sep 17 00:00:00 2001 From: Danial Mehrjerdi Date: Mon, 9 Sep 2024 19:50:12 +0200 Subject: [PATCH 7/7] Address more comments --- auction-server/src/auction.rs | 48 +++++++++++++++-------------------- 1 file changed, 20 insertions(+), 28 deletions(-) diff --git a/auction-server/src/auction.rs b/auction-server/src/auction.rs index 512804125..520c99309 100644 --- a/auction-server/src/auction.rs +++ b/auction-server/src/auction.rs @@ -923,34 +923,26 @@ fn extract_account_svm( instruction: CompiledInstruction, position: usize, ) -> Result { - match instruction.accounts.get(position) { - None => { - tracing::error!( - "Account position not found in instruction: {:?} - {}", - instruction, - position, - ); - Err(RestError::BadParameters( - "Account not found in submit_bid instruction".to_string(), - )) - } - Some(account_position) => { - let account_position: usize = (*account_position).into(); - match accounts.get(position) { - None => { - tracing::error!( - "Account not found in transaction accounts: {:?} - {}", - accounts, - account_position, - ); - Err(RestError::BadParameters( - "Account not found in transaction accounts".to_string(), - )) - } - Some(account) => Ok(*account), - } - } - } + let account_position = instruction.accounts.get(position).ok_or_else(|| { + tracing::error!( + "Account position not found in instruction: {:?} - {}", + instruction, + position, + ); + RestError::BadParameters("Account not found in submit_bid instruction".to_string()) + })?; + + let account_position: usize = (*account_position).into(); + let account = accounts.get(account_position).ok_or_else(|| { + tracing::error!( + "Account not found in transaction accounts: {:?} - {}", + accounts, + account_position, + ); + RestError::BadParameters("Account not found in transaction accounts".to_string()) + })?; + + Ok(*account) } fn extract_bid_data_svm(