Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SVM - requirement of load_and_execute_sanitized_transaction #228

Open
apfitzge opened this issue Mar 13, 2024 · 1 comment
Open

SVM - requirement of load_and_execute_sanitized_transaction #228

apfitzge opened this issue Mar 13, 2024 · 1 comment

Comments

@apfitzge
Copy link

Problem

  • load_and_execute_sanitized_transactions has a undocumented requirement that sanitized_txs has no conflicting transactions.
  • If conflicting transactions are passed in, it will not crash, but may give incorrect results
    • The validation and leader code currently check before calling, so this is not happening in our production environment
    • However, this function requirement is not checked nor documented in this new publicly exposed interface
    • If other groups begin using this interface, it is a non-obvious requirement

Proposed Solution

@apfitzge
Copy link
Author

Example test which gives incorrect results but does not fail:

#[test]
fn test_conflicting_simple_transfers() {
    let kp1 = Keypair::new();

    let mut account_data = AccountSharedData::default();
    account_data.set_lamports(1_000_000_000_000);

    let mut mock_bank = mock_bank::MockBankCallback::default();

    mock_bank
        .account_shared_data
        .insert(kp1.pubkey(), account_data.clone());

    let tx1 = SanitizedTransaction::from_transaction_for_tests(system_transaction::transfer(
        &kp1,
        &Pubkey::new_unique(),
        1_000_000,
        Hash::default(),
    ));
    let tx2 = SanitizedTransaction::from_transaction_for_tests(system_transaction::transfer(
        &kp1,
        &Pubkey::new_unique(),
        1_000_000,
        Hash::default(),
    ));

    let batch_processor = TransactionBatchProcessor::<MockForkGraph>::default();
    batch_processor
        .loaded_programs_cache
        .write()
        .unwrap()
        .fork_graph = Some(Arc::new(RwLock::new(MockForkGraph {})));

    let mut check_results = [(Ok(()), None, Some(5000)), (Ok(()), None, Some(5000))];
    let result = batch_processor.load_and_execute_sanitized_transactions(
        &mock_bank,
        &[tx1, tx2],
        &mut check_results,
        &mut TransactionErrorMetrics::default(),
        ExecutionRecordingConfig::new_single_setting(false),
        &mut ExecuteTimings::default(),
        None,
        [solana_system_program::id()].iter(),
        None,
        false,
    );

    let (ltx1, _nonce) = &result.loaded_transactions[0];
    let (ltx2, _nonce) = &result.loaded_transactions[1];
    let ltx1 = ltx1.as_ref().unwrap();
    let ltx2 = ltx2.as_ref().unwrap();

    assert!(result.execution_results[0].was_executed());
    assert!(result.execution_results[1].was_executed());

    assert!(!result.execution_results[0].was_executed_successfully());
    assert!(!result.execution_results[1].was_executed_successfully());

    // Fees drained from the account. This should be 999_999_990_000 since 2 fees
    // were charged for the same account.
    assert_eq!(ltx1.accounts[0].0, kp1.pubkey());
    assert_eq!(ltx2.accounts[0].0, kp1.pubkey());
    assert_eq!(ltx1.accounts[0].1.lamports(), 999_999_995_000);
    assert_eq!(ltx2.accounts[0].1.lamports(), 999_999_995_000); // This is a bug
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant