Skip to content

Commit 40e4e85

Browse files
committed
allow arbitrary programs as long as no invocation of user
1 parent 3d8cb04 commit 40e4e85

File tree

2 files changed

+167
-28
lines changed

2 files changed

+167
-28
lines changed

auction-server/src/api.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,6 @@ pub enum InstructionError {
111111
UnsupportedSplTokenInstruction(String),
112112
InvalidAssociatedTokenAccountInstruction(String),
113113
UnsupportedAssociatedTokenAccountInstruction(AssociatedTokenAccountInstruction),
114-
UnsupportedProgram(Pubkey),
115114
TransferInstructionNotAllowed,
116115
CloseAccountInstructionNotAllowed,
117116
InvalidTransferInstructionsCount,
@@ -132,6 +131,7 @@ pub enum InstructionError {
132131
MissingCreateAtaInstruction(Pubkey),
133132
InvalidMemoInstructionCount { expected: usize, found: usize },
134133
InvalidMemoString { expected: String, found: String },
134+
UnsupportedInvocationOfUserWallet,
135135
}
136136

137137
impl std::fmt::Display for InstructionError {
@@ -155,9 +155,6 @@ impl std::fmt::Display for InstructionError {
155155
"Unsupported associated token account instruction {:?}",
156156
instruction
157157
),
158-
InstructionError::UnsupportedProgram(program) => {
159-
write!(f, "Unsupported program {}", program)
160-
}
161158
InstructionError::TransferInstructionNotAllowed => {
162159
write!(f, "Transfer instruction is not allowed")
163160
}
@@ -278,6 +275,12 @@ impl std::fmt::Display for InstructionError {
278275
expected, found
279276
)
280277
}
278+
InstructionError::UnsupportedInvocationOfUserWallet => {
279+
write!(
280+
f,
281+
"Invocation of user wallet is not supported in this instruction"
282+
)
283+
}
281284
}
282285
}
283286
}

auction-server/src/auction/service/verification.rs

Lines changed: 160 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,9 @@ impl Service {
251251
fn validate_swap_transaction_instructions(
252252
&self,
253253
tx: &VersionedTransaction,
254+
user_wallet: &Pubkey,
254255
) -> Result<(), RestError> {
256+
let accounts = tx.message.static_account_keys();
255257
tx.message
256258
.instructions()
257259
.iter()
@@ -260,6 +262,8 @@ impl Service {
260262
self.validate_swap_transaction_instruction(
261263
ix.program_id(tx.message.static_account_keys()),
262264
ix,
265+
user_wallet,
266+
accounts,
263267
)
264268
.map_err(|e| RestError::InvalidInstruction(Some(index), e))
265269
})?;
@@ -271,6 +275,8 @@ impl Service {
271275
&self,
272276
program_id: &Pubkey,
273277
ix: &CompiledInstruction,
278+
user_wallet: &Pubkey,
279+
accounts: &[Pubkey],
274280
) -> Result<(), InstructionError> {
275281
if *program_id == system_program::id() {
276282
if matches!(
@@ -308,7 +314,18 @@ impl Service {
308314
{
309315
Ok(())
310316
} else {
311-
Err(InstructionError::UnsupportedProgram(*program_id))
317+
ix.accounts.iter().try_for_each(|i| {
318+
if accounts
319+
.get(*i as usize)
320+
.expect("account index out of bounds")
321+
== user_wallet
322+
{
323+
return Err(InstructionError::UnsupportedInvocationOfUserWallet);
324+
}
325+
Ok(())
326+
})?;
327+
328+
Ok(())
312329
}
313330
}
314331

@@ -1020,13 +1037,6 @@ impl Service {
10201037
})
10211038
.await
10221039
.ok_or(RestError::SwapOpportunityNotFound)?;
1023-
self.validate_swap_transaction_instructions(
1024-
bid_chain_data_create_svm.get_transaction(),
1025-
)?;
1026-
let quote_tokens = get_swap_quote_tokens(&opp);
1027-
let opportunity_swap_data = get_opportunity_swap_data(&opp);
1028-
self.check_svm_swap_bid_fields(bid_data, opportunity_swap_data, &quote_tokens)
1029-
.await?;
10301040

10311041
let transaction_data = self
10321042
.get_bid_transaction_data_swap(bid_data.transaction.clone())
@@ -1042,6 +1052,15 @@ impl Service {
10421052
..
10431053
} = transaction_data.accounts;
10441054

1055+
self.validate_swap_transaction_instructions(
1056+
bid_chain_data_create_svm.get_transaction(),
1057+
&user_wallet,
1058+
)?;
1059+
let quote_tokens = get_swap_quote_tokens(&opp);
1060+
let opportunity_swap_data = get_opportunity_swap_data(&opp);
1061+
self.check_svm_swap_bid_fields(bid_data, opportunity_swap_data, &quote_tokens)
1062+
.await?;
1063+
10451064
Self::check_memo_instructions(&bid_data.transaction, &opportunity_swap_data.memo)
10461065
.await?;
10471066

@@ -2266,6 +2285,18 @@ mod tests {
22662285
#[tokio::test]
22672286
async fn test_verify_bid_when_unsupported_system_program_instruction() {
22682287
let (service, opportunities) = get_service(true);
2288+
let opportunity = opportunities.user_token_specified.clone();
2289+
let bid_amount = 1;
2290+
let searcher = Keypair::new();
2291+
let swap_instruction = svm::Svm::get_swap_instruction(GetSwapInstructionParams {
2292+
searcher: searcher.pubkey(),
2293+
opportunity_params: get_opportunity_params(opportunity.clone()),
2294+
bid_amount,
2295+
deadline: (OffsetDateTime::now_utc() + Duration::seconds(30)).unix_timestamp(),
2296+
fee_receiver_relayer: Pubkey::new_unique(),
2297+
relayer_signer: service.config.chain_config.express_relay.relayer.pubkey(),
2298+
})
2299+
.unwrap();
22692300
let instructions = vec![
22702301
system_instruction::advance_nonce_account(&Pubkey::new_unique(), &Pubkey::new_unique()),
22712302
system_instruction::create_account(
@@ -2288,11 +2319,10 @@ mod tests {
22882319
),
22892320
];
22902321
for instruction in instructions.into_iter() {
2291-
let searcher = Keypair::new();
22922322
let result = get_verify_bid_result(
22932323
service.clone(),
2294-
searcher,
2295-
vec![instruction],
2324+
searcher.insecure_clone(),
2325+
vec![instruction, swap_instruction.clone()],
22962326
opportunities.user_token_specified.clone(),
22972327
)
22982328
.await;
@@ -2309,6 +2339,18 @@ mod tests {
23092339
#[tokio::test]
23102340
async fn test_verify_bid_when_unsupported_token_instruction() {
23112341
let (service, opportunities) = get_service(true);
2342+
let opportunity = opportunities.user_token_specified.clone();
2343+
let bid_amount = 1;
2344+
let searcher = Keypair::new();
2345+
let swap_instruction = svm::Svm::get_swap_instruction(GetSwapInstructionParams {
2346+
searcher: searcher.pubkey(),
2347+
opportunity_params: get_opportunity_params(opportunity.clone()),
2348+
bid_amount,
2349+
deadline: (OffsetDateTime::now_utc() + Duration::seconds(30)).unix_timestamp(),
2350+
fee_receiver_relayer: Pubkey::new_unique(),
2351+
relayer_signer: service.config.chain_config.express_relay.relayer.pubkey(),
2352+
})
2353+
.unwrap();
23122354
let instructions = vec![
23132355
spl_token::instruction::initialize_account(
23142356
&spl_token::id(),
@@ -2403,11 +2445,10 @@ mod tests {
24032445
for instruction in instructions.into_iter() {
24042446
let data = instruction.data.clone();
24052447
let ix_parsed = TokenInstruction::unpack(&data).unwrap();
2406-
let searcher = Keypair::new();
24072448
let result = get_verify_bid_result(
24082449
service.clone(),
2409-
searcher,
2410-
vec![instruction],
2450+
searcher.insecure_clone(),
2451+
vec![instruction, swap_instruction.clone()],
24112452
opportunities.user_token_specified.clone(),
24122453
)
24132454
.await;
@@ -2424,6 +2465,18 @@ mod tests {
24242465
#[tokio::test]
24252466
async fn test_verify_bid_when_unsupported_associated_token_account_instruction() {
24262467
let (service, opportunities) = get_service(true);
2468+
let opportunity = opportunities.user_token_specified.clone();
2469+
let bid_amount = 1;
2470+
let searcher = Keypair::new();
2471+
let swap_instruction = svm::Svm::get_swap_instruction(GetSwapInstructionParams {
2472+
searcher: searcher.pubkey(),
2473+
opportunity_params: get_opportunity_params(opportunity.clone()),
2474+
bid_amount,
2475+
deadline: (OffsetDateTime::now_utc() + Duration::seconds(30)).unix_timestamp(),
2476+
fee_receiver_relayer: Pubkey::new_unique(),
2477+
relayer_signer: service.config.chain_config.express_relay.relayer.pubkey(),
2478+
})
2479+
.unwrap();
24272480
let instructions = vec![recover_nested(
24282481
&Pubkey::new_unique(),
24292482
&Pubkey::new_unique(),
@@ -2437,11 +2490,10 @@ mod tests {
24372490
InstructionError::InvalidAssociatedTokenAccountInstruction(e.to_string())
24382491
})
24392492
.unwrap();
2440-
let searcher = Keypair::new();
24412493
let result = get_verify_bid_result(
24422494
service.clone(),
2443-
searcher,
2444-
vec![instruction],
2495+
searcher.insecure_clone(),
2496+
vec![instruction, swap_instruction.clone()],
24452497
opportunities.user_token_specified.clone(),
24462498
)
24472499
.await;
@@ -2456,29 +2508,113 @@ mod tests {
24562508
}
24572509

24582510
#[tokio::test]
2459-
async fn test_verify_bid_when_unsupported_program() {
2511+
async fn test_verify_bid_when_arbitrary_program_invokes_user() {
24602512
let (service, opportunities) = get_service(true);
2461-
let program_id = Pubkey::new_unique();
2462-
let instructions = vec![Instruction::new_with_bincode(program_id, &"", vec![])];
2513+
let opportunity = opportunities.user_token_specified.clone();
2514+
let bid_amount = 1;
2515+
let searcher = Keypair::new();
2516+
let swap_instruction = svm::Svm::get_swap_instruction(GetSwapInstructionParams {
2517+
searcher: searcher.pubkey(),
2518+
opportunity_params: get_opportunity_params(opportunity.clone()),
2519+
bid_amount,
2520+
deadline: (OffsetDateTime::now_utc() + Duration::seconds(30)).unix_timestamp(),
2521+
fee_receiver_relayer: Pubkey::new_unique(),
2522+
relayer_signer: service.config.chain_config.express_relay.relayer.pubkey(),
2523+
})
2524+
.unwrap();
2525+
let SwapParams {
2526+
user_wallet_address,
2527+
router_account: _,
2528+
permission_account: _,
2529+
minimum_deadline: _,
2530+
} = get_opportunity_swap_params(opportunity);
2531+
let instructions = vec![
2532+
Instruction::new_with_bincode(
2533+
Pubkey::new_unique(),
2534+
&"",
2535+
vec![AccountMeta {
2536+
pubkey: user_wallet_address,
2537+
is_signer: false,
2538+
is_writable: true,
2539+
}],
2540+
),
2541+
Instruction::new_with_bincode(
2542+
Pubkey::new_unique(),
2543+
&"",
2544+
vec![AccountMeta {
2545+
pubkey: user_wallet_address,
2546+
is_signer: true,
2547+
is_writable: true,
2548+
}],
2549+
),
2550+
];
24632551
for instruction in instructions.into_iter() {
2464-
let searcher = Keypair::new();
24652552
let result = get_verify_bid_result(
24662553
service.clone(),
2467-
searcher,
2468-
vec![instruction],
2554+
searcher.insecure_clone(),
2555+
vec![instruction, swap_instruction.clone()],
24692556
opportunities.user_token_specified.clone(),
24702557
)
24712558
.await;
24722559
assert_eq!(
24732560
result.unwrap_err(),
24742561
RestError::InvalidInstruction(
24752562
Some(0),
2476-
InstructionError::UnsupportedProgram(program_id)
2563+
InstructionError::UnsupportedInvocationOfUserWallet
24772564
)
24782565
);
24792566
}
24802567
}
24812568

2569+
#[tokio::test]
2570+
async fn test_verify_bid_when_arbitrary_program_does_not_invoke_user() {
2571+
let (service, opportunities) = get_service(true);
2572+
let opportunity = opportunities.user_token_specified.clone();
2573+
let bid_amount = 1;
2574+
let searcher = Keypair::new();
2575+
let swap_instruction = svm::Svm::get_swap_instruction(GetSwapInstructionParams {
2576+
searcher: searcher.pubkey(),
2577+
opportunity_params: get_opportunity_params(opportunity.clone()),
2578+
bid_amount,
2579+
deadline: (OffsetDateTime::now_utc() + Duration::seconds(30)).unix_timestamp(),
2580+
fee_receiver_relayer: Pubkey::new_unique(),
2581+
relayer_signer: service.config.chain_config.express_relay.relayer.pubkey(),
2582+
})
2583+
.unwrap();
2584+
let instructions = vec![Instruction::new_with_bincode(
2585+
Pubkey::new_unique(),
2586+
&"",
2587+
vec![],
2588+
)];
2589+
let swap_params = get_opportunity_swap_params(opportunity);
2590+
2591+
for instruction in instructions.into_iter() {
2592+
let instructions = vec![instruction, swap_instruction.clone()];
2593+
let mut transaction =
2594+
Transaction::new_with_payer(&instructions, Some(&searcher.pubkey()));
2595+
transaction.partial_sign(&[searcher.insecure_clone()], Hash::default());
2596+
let result = get_verify_bid_result(
2597+
service.clone(),
2598+
searcher.insecure_clone(),
2599+
instructions,
2600+
opportunities.user_token_specified.clone(),
2601+
)
2602+
.await
2603+
.unwrap();
2604+
2605+
assert_eq!(
2606+
result.0,
2607+
BidChainDataSvm {
2608+
transaction: transaction.into(),
2609+
permission_account: swap_params.permission_account,
2610+
router: swap_params.router_account,
2611+
bid_payment_instruction_type: BidPaymentInstructionType::Swap,
2612+
}
2613+
);
2614+
assert_eq!(result.1, bid_amount);
2615+
}
2616+
}
2617+
24822618
#[tokio::test]
24832619
async fn test_verify_bid_when_multiple_express_relay_instructions() {
24842620
let (service, opportunities) = get_service(true);

0 commit comments

Comments
 (0)