-
Notifications
You must be signed in to change notification settings - Fork 677
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
refactor: call_main
utility for integration test
#7217
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
91f2588
refactor: `call_main` utility for integration test
jakmeier 6bdfc46
Split `execute_sir_tx` in two.
jakmeier af6f761
de-duplicate code in test_deploy_cost_increased
jakmeier 8c19dac
Merge branch 'master' into call-main-in-process-blocks
jakmeier File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -228,6 +228,23 @@ fn prepare_env_with_congestion( | |
(env, tx_hashes) | ||
} | ||
|
||
/// Create a `TestEnv` with an account and a contract deployed to that account. | ||
fn prepare_env_with_contract( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would be great to make it just a method of the builder object! But, with the way real and kv runtimes are setup, that's not going to be trivial, so lgtm. |
||
epoch_length: u64, | ||
protocol_version: u32, | ||
account: AccountId, | ||
contract: Vec<u8>, | ||
) -> TestEnv { | ||
let mut genesis = Genesis::test(vec![account.clone()], 1); | ||
genesis.config.epoch_length = epoch_length; | ||
genesis.config.protocol_version = protocol_version; | ||
let mut env = TestEnv::builder(ChainGenesis::new(&genesis)) | ||
.runtime_adapters(create_nightshade_runtimes(&genesis, 1)) | ||
.build(); | ||
deploy_test_contract(&mut env, account, &contract, epoch_length.clone(), 1); | ||
env | ||
} | ||
|
||
/// Runs block producing client and stops after network mock received two blocks. | ||
#[test] | ||
fn produce_two_blocks() { | ||
|
@@ -2709,32 +2726,7 @@ fn test_execution_metadata() { | |
}; | ||
|
||
// Call the contract and get the execution outcome. | ||
let execution_outcome = { | ||
let tip = env.clients[0].chain.head().unwrap(); | ||
let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); | ||
let tx = Transaction { | ||
signer_id: "test0".parse().unwrap(), | ||
receiver_id: "test0".parse().unwrap(), | ||
public_key: signer.public_key(), | ||
actions: vec![Action::FunctionCall(FunctionCallAction { | ||
method_name: "main".to_string(), | ||
args: Vec::new(), | ||
gas: 100_000_000_000_000, | ||
deposit: 0, | ||
})], | ||
|
||
nonce: 10, | ||
block_hash: tip.last_block_hash, | ||
} | ||
.sign(&signer); | ||
let tx_hash = tx.get_hash(); | ||
|
||
env.clients[0].process_tx(tx, false, false); | ||
for i in 0..3 { | ||
env.produce_block(0, tip.height + i + 1); | ||
} | ||
env.query_transaction_status(&tx_hash) | ||
}; | ||
let execution_outcome = env.call_main(&"test0".parse().unwrap()); | ||
|
||
// Now, let's assert that we get the cost breakdown we expect. | ||
let config = RuntimeConfigStore::test().get_config(PROTOCOL_VERSION).clone(); | ||
|
@@ -3358,49 +3350,12 @@ fn verify_contract_limits_upgrade( | |
env | ||
}; | ||
|
||
let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); | ||
let tx = Transaction { | ||
signer_id: "test0".parse().unwrap(), | ||
receiver_id: "test0".parse().unwrap(), | ||
public_key: signer.public_key(), | ||
actions: vec![Action::FunctionCall(FunctionCallAction { | ||
method_name: "main".to_string(), | ||
args: Vec::new(), | ||
gas: 100_000_000_000_000, | ||
deposit: 0, | ||
})], | ||
|
||
nonce: 0, | ||
block_hash: CryptoHash::default(), | ||
}; | ||
|
||
// Run the transaction & get tx outcome. | ||
let old_outcome = { | ||
let tip = env.clients[0].chain.head().unwrap(); | ||
let signed_transaction = | ||
Transaction { nonce: 10, block_hash: tip.last_block_hash, ..tx.clone() }.sign(&signer); | ||
let tx_hash = signed_transaction.get_hash(); | ||
env.clients[0].process_tx(signed_transaction, false, false); | ||
for i in 0..3 { | ||
env.produce_block(0, tip.height + i + 1); | ||
} | ||
env.clients[0].chain.get_final_transaction_result(&tx_hash).unwrap() | ||
}; | ||
let account = "test0".parse().unwrap(); | ||
let old_outcome = env.call_main(&account); | ||
|
||
env.upgrade_protocol(new_protocol_version); | ||
|
||
// Re-run the transaction & get tx outcome. | ||
let new_outcome = { | ||
let tip = env.clients[0].chain.head().unwrap(); | ||
let signed_transaction = | ||
Transaction { nonce: 11, block_hash: tip.last_block_hash, ..tx }.sign(&signer); | ||
let tx_hash = signed_transaction.get_hash(); | ||
env.clients[0].process_tx(signed_transaction, false, false); | ||
for i in 0..3 { | ||
env.produce_block(0, tip.height + i + 1); | ||
} | ||
env.clients[0].chain.get_final_transaction_result(&tx_hash).unwrap() | ||
}; | ||
let new_outcome = env.call_main(&account); | ||
|
||
assert_matches!(old_outcome.status, FinalExecutionStatus::SuccessValue(_)); | ||
let e = match new_outcome.status { | ||
|
@@ -3494,33 +3449,15 @@ fn test_deploy_cost_increased() { | |
}; | ||
|
||
let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); | ||
let tx = Transaction { | ||
signer_id: "test0".parse().unwrap(), | ||
receiver_id: "test0".parse().unwrap(), | ||
public_key: signer.public_key(), | ||
actions: vec![Action::DeployContract(DeployContractAction { code: test_contract })], | ||
nonce: 0, | ||
block_hash: CryptoHash::default(), | ||
}; | ||
let actions = vec![Action::DeployContract(DeployContractAction { code: test_contract })]; | ||
|
||
// Run the transaction & get tx outcome in a closure. | ||
let deploy_contract = |env: &mut TestEnv, nonce: u64| { | ||
let tip = env.clients[0].chain.head().unwrap(); | ||
let signed_transaction = | ||
Transaction { nonce, block_hash: tip.last_block_hash, ..tx.clone() }.sign(&signer); | ||
let tx_hash = signed_transaction.get_hash(); | ||
env.clients[0].process_tx(signed_transaction, false, false); | ||
for i in 0..epoch_length { | ||
env.produce_block(0, tip.height + i + 1); | ||
} | ||
env.clients[0].chain.get_final_transaction_result(&tx_hash).unwrap() | ||
}; | ||
|
||
let old_outcome = deploy_contract(&mut env, 10); | ||
let tx = env.tx_from_actions(actions.clone(), &signer, signer.account_id.clone()); | ||
let old_outcome = env.execute_tx(tx); | ||
|
||
env.upgrade_protocol(new_protocol_version); | ||
|
||
let new_outcome = deploy_contract(&mut env, 11); | ||
let tx = env.tx_from_actions(actions, &signer, signer.account_id.clone()); | ||
let new_outcome = env.execute_tx(tx); | ||
|
||
assert_matches!(old_outcome.status, FinalExecutionStatus::SuccessValue(_)); | ||
assert_matches!(new_outcome.status, FinalExecutionStatus::SuccessValue(_)); | ||
|
@@ -4990,7 +4927,6 @@ mod lower_storage_key_limit_test { | |
|
||
mod new_contract_loading_cost { | ||
use super::*; | ||
use near_primitives::views::FinalExecutionOutcomeView; | ||
|
||
/// Check that normal execution has the same gas cost after FixContractLoadingCost. | ||
#[test] | ||
|
@@ -5005,18 +4941,20 @@ mod new_contract_loading_cost { | |
let epoch_length: BlockHeight = 5; | ||
|
||
let account: AccountId = "test0".parse().unwrap(); | ||
let mut env = | ||
test_env_with_contract(epoch_length, old_protocol_version, account.clone(), contract); | ||
|
||
let signer = InMemorySigner::from_seed(account.clone(), KeyType::ED25519, account.as_str()); | ||
let mut env = prepare_env_with_contract( | ||
epoch_length, | ||
old_protocol_version, | ||
account.clone(), | ||
contract, | ||
); | ||
|
||
let old_result = call_main(&mut env, &signer, epoch_length); | ||
let old_result = env.call_main(&account); | ||
let old_gas = old_result.receipts_outcome[0].outcome.gas_burnt; | ||
assert_matches!(old_result.status, FinalExecutionStatus::SuccessValue(_)); | ||
|
||
env.upgrade_protocol(new_protocol_version); | ||
|
||
let new_result = call_main(&mut env, &signer, epoch_length); | ||
let new_result = env.call_main(&account); | ||
let new_gas = new_result.receipts_outcome[0].outcome.gas_burnt; | ||
assert_matches!(new_result.status, FinalExecutionStatus::SuccessValue(_)); | ||
|
||
|
@@ -5036,22 +4974,20 @@ mod new_contract_loading_cost { | |
let epoch_length: BlockHeight = 5; | ||
|
||
let account: AccountId = "test0".parse().unwrap(); | ||
let mut env = test_env_with_contract( | ||
let mut env = prepare_env_with_contract( | ||
epoch_length, | ||
old_protocol_version, | ||
account.clone(), | ||
bad_contract, | ||
); | ||
|
||
let signer = InMemorySigner::from_seed(account.clone(), KeyType::ED25519, account.as_str()); | ||
|
||
let old_result = call_main(&mut env, &signer, epoch_length); | ||
let old_result = env.call_main(&account); | ||
let old_gas = old_result.receipts_outcome[0].outcome.gas_burnt; | ||
assert_matches!(old_result.status, FinalExecutionStatus::Failure(_)); | ||
|
||
env.upgrade_protocol(new_protocol_version); | ||
|
||
let new_result = call_main(&mut env, &signer, epoch_length); | ||
let new_result = env.call_main(&account); | ||
let new_gas = new_result.receipts_outcome[0].outcome.gas_burnt; | ||
assert_matches!(new_result.status, FinalExecutionStatus::Failure(_)); | ||
|
||
|
@@ -5063,61 +4999,4 @@ mod new_contract_loading_cost { | |
let loading_cost = loading_base + contract_size as u64 * loading_byte; | ||
assert_eq!(old_gas + loading_cost, new_gas); | ||
} | ||
|
||
/// Create a `TestEnv` with a contract deployed for an account. | ||
fn test_env_with_contract( | ||
epoch_length: u64, | ||
protocol_version: u32, | ||
account: AccountId, | ||
contract: Vec<u8>, | ||
) -> TestEnv { | ||
let mut genesis = Genesis::test(vec![account], 1); | ||
genesis.config.epoch_length = epoch_length; | ||
genesis.config.protocol_version = protocol_version; | ||
let mut env = TestEnv::builder(ChainGenesis::new(&genesis)) | ||
.runtime_adapters(create_nightshade_runtimes(&genesis, 1)) | ||
.build(); | ||
deploy_test_contract( | ||
&mut env, | ||
"test0".parse().unwrap(), | ||
&contract, | ||
epoch_length.clone(), | ||
1, | ||
); | ||
env | ||
} | ||
|
||
/// Execute a function call transaction that calls main on the `TestEnv`. | ||
fn call_main( | ||
env: &mut TestEnv, | ||
signer: &InMemorySigner, | ||
epoch_length: u64, | ||
) -> FinalExecutionOutcomeView { | ||
let tx = Transaction { | ||
signer_id: "test0".parse().unwrap(), | ||
receiver_id: "test0".parse().unwrap(), | ||
public_key: signer.public_key(), | ||
actions: vec![Action::FunctionCall(FunctionCallAction { | ||
method_name: "main".to_string(), | ||
args: vec![], | ||
gas: 3 * 10u64.pow(14), | ||
deposit: 0, | ||
})], | ||
|
||
nonce: 0, | ||
block_hash: CryptoHash::default(), | ||
}; | ||
|
||
let tip = env.clients[0].chain.head().unwrap(); | ||
let signed_tx = | ||
Transaction { nonce: tip.height + 1, block_hash: tip.last_block_hash, ..tx.clone() } | ||
.sign(signer); | ||
let tx_hash = signed_tx.get_hash().clone(); | ||
env.clients[0].process_tx(signed_tx, false, false); | ||
for i in 0..epoch_length { | ||
let block = env.clients[0].produce_block(tip.height + i + 1).unwrap().unwrap(); | ||
env.process_block(0, block.clone(), Provenance::PRODUCED); | ||
} | ||
env.clients[0].chain.get_final_transaction_result(&tx_hash).unwrap() | ||
} | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One thing I don't like here is that the API is non-compistional. If at some point we introduce some kind of transaction builder which returns a Tx, we'd have to mirror the API here, as there isn't a function which takes a Tx.
This is a non-trivial problem: tx has a nonce, and a block hash, and has to be signed, and we don't want the caller to get the nonce.
So perhaps we need the following, slightly hacky API?
The caller would then basically specify garbage nonce & hash
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, I was really hoping to get rid of the caller-site giving garbage nonce and hash.
I have a different suggestion how to make it more compositional, basically split the "create tx" part from the "execute tx" into two functions.
This also opened up more removal of duplicated code in
test_deploy_cost_increased
. The new interface and this additional refactor are included in the latest two commits.@matklad Do you think this is better now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I definitelly like the new API better, in particular,
execute_tx(&mut self, tx: SignedTransaction)
is very much the right boundary.I think I'd still prefer garbage nonce approach, but that's debatable. In particular, I feel like that's more of a problem of the split between Tx and SignedTx -- ideally, "what the TX logically is" (list of actions, sender, receiver) should be separate from stuff we need to make cryptograhpy work (nonce, block hash, signature), at least in the testing API. Ideally, we'd have some kind of TxBuilder, but a Tx with
Default
nonce&block hash can stand in its place. But either of these is a bigger change than warranted in this PR, even if they are good ideas.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the logical piece of work that we generally care about is just a list of actions, not a transaction.
What you describe as
in my opinion is not a
Transaction
. The crypto stuff to define valid placement in the chain very much belongs to a valid transaction definition. The difference betweenTransaction
andSignedTransaction
is just how we represent it in memory but logically they describe the same thing.But I support the idea of a TX builder that prepares actions/signer/receiver and builds into a full TX. But
fn tx_from_actions
is already quite simple to build a TX. I think to make a builder valuable, it would need to do more than just that. For example, track accounts included in genesis, or help somehow with preparing the receiver with a deployed contract first etc.