#[cfg(test)]
mod tests {
#![allow(
unused_imports,
unused_mut,
unused_variables,
unused_must_use,
non_snake_case,
clippy::needless_borrow,
clippy::get_first,
clippy::explicit_auto_deref,
clippy::useless_format,
clippy::useless_conversion,
clippy::unnecessary_operation
)]
use crate::{
builder::{
init_logger, AccountSigner, BuilderError, ContractSigner, ScriptBuilder, Signer,
TransactionAttribute, TransactionBuilder, TransactionError, Witness,
},
config::{NeoConstants, TestConstants},
crypto::{KeyPair, Secp256r1PrivateKey},
neo_builder::{OracleResponse, OracleResponseCode, GAS_TOKEN_HASH},
neo_clients::{APITrait, MockClient, MockProvider, RpcClient},
neo_crypto::utils::ToHexString,
neo_protocol::{
Account, AccountTrait, ApplicationLog, NeoProtocol, NeoVersion, RawTransaction,
},
neo_types::ScriptHashExtension,
ContractParameter, InvocationResult,
};
use lazy_static::lazy_static;
use tracing::info;
use neo3::{
builder::VerificationScript,
config::{NeoConfig, NEOCONFIG},
};
use num_bigint::BigInt;
use primitive_types::{H160, H256};
use serde_json::json;
use std::{default, ops::Deref, str::FromStr, sync::Arc};
use tokio::sync::{Mutex, OnceCell};
use tracing::debug;
lazy_static! {
pub static ref ACCOUNT1: Account = Account::from_key_pair(
KeyPair::from_secret_key(
&Secp256r1PrivateKey::from_bytes(
&hex::decode(
"e6e919577dd7b8e97805151c05ae07ff4f752654d6d8797597aca989c02c4cb3"
)
.expect("Test private key 1 should be valid hex")
)
.expect("Test private key 1 should be valid bytes for Secp256r1PrivateKey")
),
None,
None
)
.expect("Failed to create ACCOUNT1 from valid key pair");
pub static ref ACCOUNT2: Account = Account::from_key_pair(
KeyPair::from_secret_key(
&Secp256r1PrivateKey::from_bytes(
&hex::decode(
"b4b2b579cac270125259f08a5f414e9235817e7637b9a66cfeb3b77d90c8e7f9"
)
.expect("Test private key 2 should be valid hex")
)
.expect("Test private key 2 should be valid bytes for Secp256r1PrivateKey")
),
None,
None
)
.expect("Failed to create ACCOUNT2 from valid key pair");
}
static CLIENT: OnceCell<RpcClient<MockProvider>> = OnceCell::const_new();
#[tokio::test]
async fn test_build_transaction_with_correct_nonce() {
let mut nonce = 1;
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
{
let mut mock_provider_guard = mock_provider.lock().await; let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"invokescript",
"invokescript_necessary_mock.json",
)
.await;
mock_provider_guard
.mock_response_with_file_ignore_param(
"calculatenetworkfee",
"calculatenetworkfee.json",
)
.await;
mock_provider_guard.mount_mocks().await;
}
let client = {
let mock_provider = mock_provider.lock().await;
Arc::new(mock_provider.into_client())
};
let mut transaction_builder = TransactionBuilder::with_client(&client);
let mut tx = transaction_builder
.valid_until_block(1)
.unwrap()
.set_script(Some(vec![1, 2, 3]))
.set_signers(vec![AccountSigner::called_by_entry(ACCOUNT1.deref()).unwrap().into()])
.unwrap()
.nonce(nonce)
.unwrap()
.get_unsigned_tx()
.await
.unwrap();
assert_eq!(*tx.nonce(), nonce);
nonce = 0;
transaction_builder = TransactionBuilder::with_client(&client);
tx = transaction_builder
.valid_until_block(1)
.unwrap()
.set_script(Some(vec![1, 2, 3]))
.set_signers(vec![AccountSigner::called_by_entry(ACCOUNT1.deref()).unwrap().into()])
.unwrap()
.nonce(nonce)
.unwrap()
.get_unsigned_tx()
.await
.unwrap();
assert_eq!(*tx.nonce(), nonce);
nonce = u32::MAX;
transaction_builder = TransactionBuilder::with_client(&client);
tx = transaction_builder
.valid_until_block(1)
.unwrap()
.set_script(Some(vec![1, 2, 3]))
.set_signers(vec![AccountSigner::called_by_entry(ACCOUNT1.deref()).unwrap().into()])
.unwrap()
.nonce(nonce)
.unwrap()
.get_unsigned_tx()
.await
.unwrap();
info!("{:?}", tx);
assert_eq!(*tx.nonce(), nonce);
}
#[tokio::test]
async fn test_build_transaction_automatically_set_nonce() {
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
{
let mut mock_provider_guard = mock_provider.lock().await; let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"invokescript",
"invokescript_necessary_mock.json",
)
.await;
let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"calculatenetworkfee",
"calculatenetworkfee.json",
)
.await;
mock_provider_guard
.mock_response_with_file_ignore_param("getblockcount", "getblockcount_1000.json")
.await;
mock_provider_guard.mount_mocks().await;
}
let client = {
let mock_provider = mock_provider.lock().await;
Arc::new(mock_provider.into_client())
};
let mut transaction_builder = TransactionBuilder::with_client(&client);
let mut tx = transaction_builder
.valid_until_block(1)
.unwrap()
.set_script(Some(vec![1, 2, 3]))
.set_signers(vec![AccountSigner::called_by_entry(ACCOUNT1.deref()).unwrap().into()])
.unwrap()
.get_unsigned_tx()
.await
.unwrap();
assert_ne!(*tx.nonce(), 0);
}
#[tokio::test]
async fn test_build_transaction_fail_building_tx_without_signer() {
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
let client = {
let mock_provider = mock_provider.lock().await;
Arc::new(mock_provider.into_client())
};
let mut transaction_builder = TransactionBuilder::with_client(&client);
let mut tx = transaction_builder
.valid_until_block(100)
.unwrap()
.set_script(Some(vec![1, 2, 3]))
.get_unsigned_tx()
.await;
assert_eq!(tx, Err(TransactionError::NoSigners));
}
#[tokio::test]
async fn test_build_transaction_fail_adding_multiple_signers_concerning_the_same_account() {
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
let client = {
let mock_provider = mock_provider.lock().await;
Arc::new(mock_provider.into_client())
};
let mut transaction_builder = TransactionBuilder::with_client(&client);
let mut tx = transaction_builder
.valid_until_block(100)
.unwrap()
.set_script(Some(vec![1, 2, 3]))
.set_signers(vec![
AccountSigner::called_by_entry(ACCOUNT1.deref()).unwrap().into(),
AccountSigner::global(ACCOUNT1.deref()).unwrap().into(),
]);
assert_eq!(
tx,
Err(TransactionError::TransactionConfiguration(
"Cannot add multiple signers concerning the same account.".to_string()
))
);
}
#[tokio::test]
async fn test_invoke_script() {
let script = ScriptBuilder::new()
.contract_call(
&ScriptHashExtension::from_hex(TestConstants::NEO_TOKEN_HASH).unwrap(),
"symbol",
&[],
None,
)
.unwrap()
.to_bytes();
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
{
let mut mock_provider_guard = mock_provider.lock().await; let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"invokescript",
"invokescript_symbol_neo.json",
)
.await;
mock_provider_guard.mount_mocks().await;
}
let client = {
let mock_provider = mock_provider.lock().await;
Arc::new(mock_provider.into_client())
};
let mut tb = TransactionBuilder::with_client(&client);
tb.set_script(Some(script));
let response = tb.call_invoke_script().await.unwrap();
assert!(!response.stack.is_empty(), "Response stack is empty");
if let Some(item) = response.stack.get(0) {
if let Some(value) = item.as_string() {
assert_eq!(value, "NEO", "Unexpected value in response");
} else {
panic!("First item in stack is not a string");
}
} else {
panic!("Response stack is empty");
}
}
#[tokio::test]
async fn test_invoke_script_without_setting_script() {
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
{
let mut mock_provider_guard = mock_provider.lock().await; let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"invokescript",
"invokescript_symbol_neo.json",
)
.await;
mock_provider_guard.mount_mocks().await;
}
let client = {
let mock_provider = mock_provider.lock().await;
Arc::new(mock_provider.into_client())
};
let mut tb = TransactionBuilder::with_client(&client);
let result = tb.call_invoke_script().await;
assert_eq!(result.unwrap_err(), TransactionError::NoScript);
}
#[tokio::test]
async fn test_build_without_setting_script() {
let client = CLIENT.get_or_init(|| async { MockClient::new().await.into_client() }).await;
let err = TransactionBuilder::with_client(&client)
.set_signers(vec![AccountSigner::called_by_entry(ACCOUNT1.deref()).unwrap().into()])
.unwrap()
.get_unsigned_tx()
.await
.err()
.unwrap();
assert_eq!(err, TransactionError::NoScript);
}
#[tokio::test]
async fn test_sign_transaction_with_additional_signers() {
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
{
let mut mock_provider_guard = mock_provider.lock().await; let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"invokescript",
"invokescript_symbol_neo.json",
)
.await;
let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"calculatenetworkfee",
"calculatenetworkfee.json",
)
.await;
let mut mock_provider_guard =
mock_provider_guard.mock_response_ignore_param("getversion", json!({})).await;
mock_provider_guard.mount_mocks().await;
}
let client = {
let mock_provider = mock_provider.lock().await;
Arc::new(mock_provider.into_client())
};
let script = vec![0x01u8, 0x02u8, 0x03u8];
let mut transaction_builder = TransactionBuilder::with_client(&client);
let tx = transaction_builder
.set_script(Some(script))
.set_signers(vec![
AccountSigner::called_by_entry(ACCOUNT1.deref()).unwrap().into(),
AccountSigner::called_by_entry(ACCOUNT2.deref()).unwrap().into(),
])
.unwrap()
.valid_until_block(1000)
.unwrap()
.sign()
.await
.unwrap();
assert_eq!(tx.witnesses().len(), 2);
let signers = tx
.witnesses()
.iter()
.map(|witness| witness.verification.get_public_keys().unwrap().first().unwrap().clone())
.collect::<Vec<_>>();
assert!(signers.contains(&ACCOUNT1.deref().clone().key_pair.clone().unwrap().public_key()));
assert!(signers.contains(&ACCOUNT2.deref().clone().key_pair.clone().unwrap().public_key()));
}
#[tokio::test]
async fn test_send_invoke_function() {
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
{
let mut mock_provider_guard = mock_provider.lock().await; let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"invokescript",
"invokescript_transfer_with_fixed_sysfee.json",
)
.await;
let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"sendrawtransaction",
"sendrawtransaction.json",
)
.await;
let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param("getblockcount", "getblockcount_1000.json")
.await;
let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"calculatenetworkfee",
"calculatenetworkfee.json",
)
.await;
let mut mock_provider_guard =
mock_provider_guard.mock_response_ignore_param("getversion", json!({})).await;
mock_provider_guard.mount_mocks().await;
}
let client = {
let mock_provider = mock_provider.lock().await;
Arc::new(mock_provider.into_client())
};
let mut tb = TransactionBuilder::with_client(&client);
let script = ScriptBuilder::new()
.contract_call(
&H160::from_str(TestConstants::NEO_TOKEN_HASH).unwrap(),
"transfer",
&[
ContractParameter::from(ACCOUNT1.address_or_scripthash().script_hash()),
ContractParameter::from(
H160::from_str("969a77db482f74ce27105f760efa139223431394").unwrap(),
),
ContractParameter::from(5),
ContractParameter::any(),
],
None,
)
.unwrap()
.to_bytes();
tb.set_script(Some(script.clone()))
.set_signers(vec![AccountSigner::none(ACCOUNT1.deref()).unwrap().into()])
.unwrap();
let mut tx = tb.sign().await.unwrap();
let response = tx.send_tx().await;
match response {
Ok(response) => {
assert_eq!(
response.hash,
H256::from_str(
"0x830816f0c801bcabf919dfa1a90d7b9a4f867482cb4d18d0631a5aa6daefab6a"
)
.unwrap()
);
},
Err(e) => {
panic!("Failed to invoke function: {:?}", e);
},
}
}
#[tokio::test]
async fn test_fail_building_transaction_with_incorrect_nonce() {
let client = CLIENT.get_or_init(|| async { MockClient::new().await.into_client() }).await;
let mut tb = TransactionBuilder::with_client(&client);
tb.valid_until_block(1)
.unwrap()
.set_script(Some(vec![1, 2, 3]))
.set_signers(vec![AccountSigner::called_by_entry(ACCOUNT1.deref()).unwrap().into()]);
assert!(tb.nonce(0).is_ok());
assert!(tb.nonce(u32::MAX).is_ok());
tb.nonce(u32::MAX).unwrap();
assert!(tb.nonce(u32::MAX).is_ok());
tb.nonce(0).unwrap();
assert!(tb.nonce((-1i32) as u32).is_ok());
}
#[tokio::test]
async fn test_fail_building_transaction_with_invalid_block_number() {
let client = CLIENT.get_or_init(|| async { MockClient::new().await.into_client() }).await;
let mut tb = TransactionBuilder::with_client(&client);
tb.set_script(Some(vec![1, 2, 3]))
.set_signers(vec![AccountSigner::called_by_entry(ACCOUNT1.deref()).unwrap().into()]);
assert!(tb.valid_until_block(-1i32 as u32).is_ok());
}
#[tokio::test]
async fn test_override_signer() {
let client = CLIENT.get_or_init(|| async { MockClient::new().await.into_client() }).await;
let mut tb = TransactionBuilder::with_client(&client);
tb.set_script(Some(vec![1, 2, 3])).set_signers(vec![AccountSigner::global(
ACCOUNT1.deref(),
)
.unwrap()
.into()]);
assert_eq!(
tb.signers()[0],
Signer::AccountSigner(AccountSigner::global(ACCOUNT1.deref()).unwrap())
);
tb.set_signers(vec![AccountSigner::called_by_entry(ACCOUNT2.deref()).unwrap().into()]);
assert_eq!(
tb.signers(),
&[AccountSigner::called_by_entry(ACCOUNT2.deref()).unwrap().into()]
);
}
#[tokio::test]
async fn test_attributes_high_priority_multisig_containing_committee_Member() {
init_logger();
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
{
let mut mock_provider_guard = mock_provider.lock().await; let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"invokescript",
"invokescript_symbol_neo.json",
)
.await;
let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"calculatenetworkfee",
"calculatenetworkfee.json",
)
.await;
mock_provider_guard
.mock_response_with_file_ignore_param("getcommittee", "getcommittee.json")
.await;
let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param("getblockcount", "getblockcount_1000.json")
.await;
mock_provider_guard.mount_mocks().await;
}
let client = {
let mock_provider = mock_provider.lock().await;
Arc::new(mock_provider.into_client())
};
let mut tb = TransactionBuilder::with_client(&client);
let multi_sig_account = Account::multi_sig_from_public_keys(
&mut [ACCOUNT2.get_public_key().unwrap(), ACCOUNT1.get_public_key().unwrap()],
1,
)
.unwrap();
tb.set_script(Some(vec![1, 2, 3]))
.add_attributes(vec![TransactionAttribute::HighPriority])
.unwrap()
.set_signers(vec![AccountSigner::none(&multi_sig_account).unwrap().into()])
.expect("Failed to set signers in test - this should never fail with valid test data");
let tx = tb.get_unsigned_tx().await;
match tx {
Ok(tx) => assert_eq!(tx.attributes()[0], TransactionAttribute::HighPriority),
Err(e) => panic!("Failed to get unsigned transaction: {:?}", e),
}
}
#[tokio::test]
async fn test_attributes_high_priority() {
init_logger();
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
{
let mut mock_provider_guard = mock_provider.lock().await; let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"invokescript",
"invokescript_symbol_neo.json",
)
.await;
let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"calculatenetworkfee",
"calculatenetworkfee.json",
)
.await;
mock_provider_guard
.mock_response_with_file_ignore_param("getcommittee", "getcommittee.json")
.await
.mock_response_with_file_ignore_param("getblockcount", "getblockcount_1000.json")
.await;
mock_provider_guard.mount_mocks().await;
}
let client = {
let mock_provider = mock_provider.lock().await;
Arc::new(mock_provider.into_client())
};
let mut tb = TransactionBuilder::with_client(&client);
tb.set_script(Some(vec![1, 2, 3]))
.add_attributes(vec![TransactionAttribute::HighPriority])
.unwrap()
.set_signers(vec![AccountSigner::none(ACCOUNT1.deref()).unwrap().into()])
.expect("Failed to set signers for high priority transaction test");
let tx = tb.get_unsigned_tx().await.unwrap();
assert_eq!(tx.attributes()[0], TransactionAttribute::HighPriority);
}
#[tokio::test]
async fn test_attributes_high_priority_not_committee_member() {
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
{
let mut mock_provider_guard = mock_provider.lock().await; let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param("getcommittee", "getcommittee.json")
.await;
mock_provider_guard.mock_get_block_count(1000).await;
mock_provider_guard.mount_mocks().await;
}
let client = {
let mock_provider = mock_provider.lock().await;
Arc::new(mock_provider.into_client())
};
let mut tb = TransactionBuilder::with_client(&client);
tb.set_script(Some(vec![1, 2, 3]))
.add_attributes(vec![TransactionAttribute::HighPriority])
.unwrap()
.set_signers(vec![AccountSigner::none(ACCOUNT2.deref()).unwrap().into()]);
assert_eq!(tb.get_unsigned_tx().await, Err(TransactionError::IllegalState("This transaction does not have a committee member as signer. Only committee members can send transactions with high priority.".to_string())));
}
#[tokio::test]
async fn test_attributes_high_priority_error_when_multiple() {
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
{
let mut mock_provider_guard = mock_provider.lock().await; let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"invokescript",
"invokescript_symbol_neo.json",
)
.await;
let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"calculatenetworkfee",
"calculatenetworkfee.json",
)
.await;
mock_provider_guard.mock_get_block_count(1000).await;
mock_provider_guard.mount_mocks().await;
}
let client = {
let mock_provider = mock_provider.lock().await;
Arc::new(mock_provider.into_client())
};
let mut tb = TransactionBuilder::with_client(&client);
tb.set_script(Some(vec![1, 2, 3]))
.add_attributes(vec![TransactionAttribute::HighPriority]);
assert_eq!(
tb.add_attributes(vec![TransactionAttribute::HighPriority]),
Err(TransactionError::TransactionConfiguration(
"A transaction can only have one HighPriority attribute.".to_string(),
))
);
}
#[tokio::test]
async fn test_attributes_not_valid_before() {
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
{
let mut mock_provider_guard = mock_provider.lock().await; let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"invokescript",
"invokescript_symbol_neo.json",
)
.await;
let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"calculatenetworkfee",
"calculatenetworkfee.json",
)
.await;
mock_provider_guard.mock_get_block_count(1000).await;
mock_provider_guard.mount_mocks().await;
}
let client = {
let mock_provider = mock_provider.lock().await;
Arc::new(mock_provider.into_client())
};
let mut tb = TransactionBuilder::with_client(&client);
tb.set_script(Some(vec![1, 2, 3]))
.add_attributes(vec![TransactionAttribute::NotValidBefore { height: 200 }])
.unwrap()
.set_signers(vec![AccountSigner::none(ACCOUNT1.deref()).unwrap().into()]);
let tx = tb.get_unsigned_tx().await.unwrap();
let attribute = tx.attributes().get(0).unwrap();
assert!(
matches!(attribute, TransactionAttribute::NotValidBefore { .. }),
"The attribute type is not NotValidBefore as expected"
);
assert_eq!(attribute.get_height().unwrap(), &200);
}
#[tokio::test]
async fn test_attributes_not_valid_before_error_when_multiple() {
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
{
let mut mock_provider_guard = mock_provider.lock().await; let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"invokescript",
"invokescript_symbol_neo.json",
)
.await;
let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"calculatenetworkfee",
"calculatenetworkfee.json",
)
.await;
mock_provider_guard.mock_get_block_count(1000).await;
mock_provider_guard.mount_mocks().await;
}
let client = {
let mock_provider = mock_provider.lock().await;
Arc::new(mock_provider.into_client())
};
let mut tb = TransactionBuilder::with_client(&client);
tb.set_script(Some(vec![1, 2, 3]))
.add_attributes(vec![TransactionAttribute::NotValidBefore { height: 200 }]);
assert_eq!(
tb.add_attributes(vec![TransactionAttribute::NotValidBefore { height: 200 }]),
Err(TransactionError::TransactionConfiguration(
"A transaction can only have one NotValidBefore attribute.".to_string(),
))
);
}
#[tokio::test]
async fn test_attributes_conflicts() {
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
{
let mut mock_provider_guard = mock_provider.lock().await; let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"invokescript",
"invokescript_symbol_neo.json",
)
.await;
let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"calculatenetworkfee",
"calculatenetworkfee.json",
)
.await;
mock_provider_guard.mock_get_block_count(1000).await;
mock_provider_guard.mount_mocks().await;
}
let client = {
let mock_provider = mock_provider.lock().await;
Arc::new(mock_provider.into_client())
};
let mut tb = TransactionBuilder::with_client(&client);
tb.set_script(Some(vec![1, 2, 3]))
.add_attributes(vec![TransactionAttribute::Conflicts {
hash: H256::from_str(
"0x8529cf7301d13cc13d85913b8367700080a6e96db045687b8db720e91e80321c",
)
.unwrap(),
}])
.unwrap()
.set_signers(vec![AccountSigner::none(ACCOUNT1.deref()).unwrap().into()]);
let tx = tb.get_unsigned_tx().await.unwrap();
assert_eq!(tx.attributes().len(), 1);
let attribute = tx.attributes().get(0).unwrap();
assert!(
matches!(attribute, TransactionAttribute::Conflicts { .. }),
"The attribute type is not Conflicts as expected"
);
assert_eq!(
attribute.get_hash().unwrap(),
&H256::from_str("0x8529cf7301d13cc13d85913b8367700080a6e96db045687b8db720e91e80321c")
.unwrap()
);
}
#[tokio::test]
async fn test_attributes_conflicts_multiple() {
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
{
let mut mock_provider_guard = mock_provider.lock().await; let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"invokescript",
"invokescript_symbol_neo.json",
)
.await;
let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"calculatenetworkfee",
"calculatenetworkfee.json",
)
.await;
mock_provider_guard.mock_get_block_count(1000).await;
mock_provider_guard.mount_mocks().await;
}
let client = {
let mock_provider = mock_provider.lock().await;
Arc::new(mock_provider.into_client())
};
let mut tb = TransactionBuilder::with_client(&client);
tb.set_script(Some(vec![1, 2, 3]))
.add_attributes(vec![TransactionAttribute::Conflicts {
hash: H256::from_str(
"0x8529cf7301d13cc13d85913b8367700080a6e96db045687b8db720e91e80321c",
)
.unwrap(),
}])
.unwrap()
.add_attributes(vec![TransactionAttribute::Conflicts {
hash: H256::from_str(
"0x8529cf7301d13cc13d85913b8367700080a6e96db045687b8db720e91e80321d",
)
.unwrap(),
}])
.unwrap()
.set_signers(vec![AccountSigner::none(ACCOUNT1.deref()).unwrap().into()]);
let tx = tb.get_unsigned_tx().await.unwrap();
assert_eq!(tx.attributes().len(), 2);
let attribute0 = tx.attributes().get(0).unwrap();
assert!(
matches!(attribute0, TransactionAttribute::Conflicts { .. }),
"The attribute type is not Conflicts as expected"
);
assert_eq!(
attribute0.get_hash().unwrap(),
&H256::from_str("0x8529cf7301d13cc13d85913b8367700080a6e96db045687b8db720e91e80321c")
.unwrap()
);
let attribute1 = tx.attributes().get(1).unwrap();
assert!(
matches!(attribute1, TransactionAttribute::Conflicts { .. }),
"The attribute type is not Conflicts as expected"
);
assert_eq!(
attribute1.get_hash().unwrap(),
&H256::from_str("0x8529cf7301d13cc13d85913b8367700080a6e96db045687b8db720e91e80321d")
.unwrap()
);
}
#[tokio::test]
async fn test_attributes_conflicts_same_exist_already() {
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
let client = {
let mock_provider = mock_provider.lock().await;
Arc::new(mock_provider.into_client())
};
let mut tb = TransactionBuilder::with_client(&client);
tb.set_script(Some(vec![1, 2, 3]))
.add_attributes(vec![TransactionAttribute::Conflicts {
hash: H256::from_str(
"fe26f525c17b58f63a4d106fba973ec34cc99bfe2501c9f672cc145b483e398b",
)
.unwrap(),
}])
.unwrap();
let result = tb.add_attributes(vec![TransactionAttribute::Conflicts {
hash: H256::from_str(
"fe26f525c17b58f63a4d106fba973ec34cc99bfe2501c9f672cc145b483e398b",
)
.unwrap(),
}]);
assert!(matches!(result, Err(TransactionError::TransactionConfiguration(_))));
if let Err(TransactionError::TransactionConfiguration(msg)) = result {
assert!(msg.contains("already exists a conflicts attribute for the hash "));
assert!(msg.contains("in this transaction"));
}
}
#[tokio::test]
async fn test_attributes_compare_not_valid_before_attributes() {
let attr1 = TransactionAttribute::NotValidBefore { height: 147 };
let attr2 = TransactionAttribute::NotValidBefore { height: 1 };
assert_ne!(attr1, attr2);
assert_eq!(attr1, TransactionAttribute::NotValidBefore { height: 147 });
}
#[tokio::test]
async fn test_fail_adding_more_than_max_attributes_to_tx_just_attributes() {
let attrs: Vec<TransactionAttribute> = (0..=NeoConstants::MAX_TRANSACTION_ATTRIBUTES)
.map(|_| TransactionAttribute::HighPriority)
.collect();
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
let client = {
let mock_provider = mock_provider.lock().await;
Arc::new(mock_provider.into_client())
};
let mut tb = TransactionBuilder::with_client(&client);
assert_eq!(
tb.add_attributes(attrs),
Err(TransactionError::TransactionConfiguration(format!(
"A transaction cannot have more than {} attributes (including signers).",
NeoConstants::MAX_TRANSACTION_ATTRIBUTES
)))
);
}
#[tokio::test]
async fn test_fail_adding_more_than_max_attributes_to_tx_attributes_and_signers() {
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
let client = {
let mock_provider = mock_provider.lock().await;
Arc::new(mock_provider.into_client())
};
let mut tb = TransactionBuilder::with_client(&client);
tb.set_signers(vec![
AccountSigner::called_by_entry(&Account::create().unwrap()).unwrap().into(),
AccountSigner::called_by_entry(&Account::create().unwrap()).unwrap().into(),
AccountSigner::called_by_entry(&Account::create().unwrap()).unwrap().into(),
])
.unwrap();
let attrs: Vec<TransactionAttribute> = (0..=NeoConstants::MAX_TRANSACTION_ATTRIBUTES - 3)
.map(|_| TransactionAttribute::HighPriority)
.collect();
assert_eq!(
tb.add_attributes(attrs),
Err(TransactionError::TransactionConfiguration(format!(
"A transaction cannot have more than {} attributes (including signers).",
NeoConstants::MAX_TRANSACTION_ATTRIBUTES
)))
);
}
#[tokio::test]
async fn test_fail_adding_more_than_max_attributes_to_tx_signers() {
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
let client = {
let mock_provider = mock_provider.lock().await;
Arc::new(mock_provider.into_client())
};
let mut tb = TransactionBuilder::with_client(&client);
tb.add_attributes(vec![TransactionAttribute::HighPriority]);
let signers: Vec<Signer> = (0..NeoConstants::MAX_TRANSACTION_ATTRIBUTES)
.map(|_| AccountSigner::called_by_entry(&Account::create().unwrap()).unwrap().into())
.collect();
assert!(signers.len() + 1 > NeoConstants::MAX_TRANSACTION_ATTRIBUTES.try_into().unwrap());
assert_eq!(
tb.set_signers(signers),
Err(TransactionError::TransactionConfiguration(format!(
"A transaction cannot have more than {} attributes (including signers).",
NeoConstants::MAX_TRANSACTION_ATTRIBUTES
)))
);
}
#[tokio::test]
async fn test_automatic_setting_of_valid_until_block_variable() {
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
{
let mut mock_provider_guard = mock_provider.lock().await; let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"invokescript",
"invokescript_symbol_neo.json",
)
.await;
let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"calculatenetworkfee",
"calculatenetworkfee.json",
)
.await;
mock_provider_guard.mock_get_block_count(1000).await;
mock_provider_guard.mount_mocks().await;
}
let client = {
let mock_provider = mock_provider.lock().await;
Arc::new(mock_provider.into_client())
};
let mut tb = TransactionBuilder::with_client(&client);
let block_count = 1000;
tb.set_script(Some(vec![1, 2, 3]))
.set_signers(vec![AccountSigner::none(ACCOUNT1.deref()).unwrap().into()]);
let tx = tb.get_unsigned_tx().await.unwrap();
assert_eq!(
*tx.valid_until_block(),
block_count + client.max_valid_until_block_increment() - 1
);
}
#[tokio::test]
async fn test_automatic_setting_of_system_fee_and_network_fee() {
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
{
let mut mock_provider_guard = mock_provider.lock().await; let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"invokescript",
"invokescript_symbol_neo.json",
)
.await;
let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"calculatenetworkfee",
"calculatenetworkfee.json",
)
.await;
let mut mock_provider_guard =
mock_provider_guard.mock_response_ignore_param("getversion", json!({})).await;
mock_provider_guard.mount_mocks().await;
}
let client = {
let mock_provider = mock_provider.lock().await;
Arc::new(mock_provider.into_client())
};
let script = vec![1, 2, 3];
let mut tb = TransactionBuilder::with_client(&client);
tb.set_script(Some(script.clone()))
.set_signers(vec![AccountSigner::none(ACCOUNT1.deref()).unwrap().into()])
.unwrap()
.valid_until_block(1000)
.unwrap();
let tx = tb.get_unsigned_tx().await.unwrap();
assert_eq!(*tx.sys_fee(), 984060);
assert_eq!(*tx.net_fee(), 1230610);
}
#[tokio::test]
async fn test_fail_trying_to_sign_transaction_with_account_missing_a_private_key() {
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
{
let mut mock_provider_guard = mock_provider.lock().await; let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"invokescript",
"invokescript_symbol_neo.json",
)
.await;
let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"calculatenetworkfee",
"calculatenetworkfee.json",
)
.await;
let mut mock_provider_guard =
mock_provider_guard.mock_response_ignore_param("getversion", json!({})).await;
mock_provider_guard.mount_mocks().await;
}
let client = {
let mock_provider = mock_provider.lock().await;
Arc::new(mock_provider.into_client())
};
let account_without_keypair =
Account::from_address(ACCOUNT1.get_address().as_str()).unwrap();
let mut tb = TransactionBuilder::with_client(&client);
tb.set_script(Some(vec![1, 2, 3]))
.set_signers(vec![AccountSigner::none(&account_without_keypair).unwrap().into()])
.unwrap()
.valid_until_block(1000)
.unwrap();
let result = tb.sign().await;
assert!(result.is_err());
assert_eq!(result, Err(BuilderError::InvalidConfiguration(
format!("Cannot create transaction signature because account {} does not hold a private key.", ACCOUNT1.get_address()),
)))
}
#[tokio::test]
async fn test_fail_automatically_signing_with_multi_sig_account_signer() {
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
{
let mut mock_provider_guard = mock_provider.lock().await; let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param("getblockcount", "getblockcount_1000.json")
.await;
let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"invokescript",
"invokescript_symbol_neo.json",
)
.await;
let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"calculatenetworkfee",
"calculatenetworkfee.json",
)
.await;
let mut mock_provider_guard =
mock_provider_guard.mock_response_ignore_param("getversion", json!({})).await;
mock_provider_guard.mount_mocks().await;
}
let client = {
let mock_provider = mock_provider.lock().await;
Arc::new(mock_provider.into_client())
};
let multi_sig_account = Account::multi_sig_from_public_keys(
vec![ACCOUNT1.get_public_key().unwrap()].as_mut(),
1,
)
.unwrap();
let mut tb = TransactionBuilder::with_client(&client);
tb.set_script(Some(vec![1, 2, 3])).set_signers(vec![AccountSigner::none(
&multi_sig_account,
)
.unwrap()
.into()]);
let result = tb.sign().await;
assert!(result.is_err());
assert_eq!(
result,
Err(BuilderError::IllegalState(
"Transactions with multi-sig signers cannot be signed automatically.".to_string(),
))
);
}
#[tokio::test]
async fn test_fail_with_no_signing_account() {
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
{
let mut mock_provider_guard = mock_provider.lock().await; let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param("getblockcount", "getblockcount_1000.json")
.await;
let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"invokescript",
"invokescript_symbol_neo.json",
)
.await;
mock_provider_guard.mount_mocks().await;
}
let client = {
let mock_provider = mock_provider.lock().await;
Arc::new(mock_provider.into_client())
};
let mut tb = TransactionBuilder::with_client(&client);
tb.set_script(Some(vec![1, 2, 3]))
.set_signers(vec![ContractSigner::called_by_entry(
ACCOUNT1.address_or_scripthash().script_hash(),
&*vec![],
)
.into()]);
let result = tb.sign().await;
assert!(result.is_err());
assert_eq!(result, Err(BuilderError::TransactionError(Box::new(TransactionError::TransactionConfiguration(format!("A transaction requires at least one signing account (i.e. an AccountSigner). None was provided."))))));
}
#[tokio::test]
async fn test_fail_signing_with_account_without_ec_keypair() {
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
{
let mut mock_provider_guard = mock_provider.lock().await; let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param("getblockcount", "getblockcount_1000.json")
.await;
let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"invokescript",
"invokescript_symbol_neo.json",
)
.await;
let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"calculatenetworkfee",
"calculatenetworkfee.json",
)
.await;
let mut mock_provider_guard =
mock_provider_guard.mock_response_ignore_param("getversion", json!({})).await;
mock_provider_guard.mount_mocks().await;
}
let client = {
let mock_provider = mock_provider.lock().await;
Arc::new(mock_provider.into_client())
};
let account_without_keypair = Account::from_verification_script(
&ACCOUNT1.clone().verification_script().clone().unwrap(),
)
.unwrap();
let mut tb = TransactionBuilder::with_client(&client);
tb.set_script(Some(vec![1, 2, 3])).set_signers(vec![AccountSigner::none(
&account_without_keypair,
)
.unwrap()
.into()]);
let result = tb.sign().await;
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains(" does not hold a private key."));
}
#[tokio::test]
async fn test_fail_sending_transaction_because_it_doesnt_contain_the_right_number_of_witnesses()
{
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
{
let mut mock_provider_guard = mock_provider.lock().await; let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"invokescript",
"invokescript_symbol_neo.json",
)
.await;
let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"calculatenetworkfee",
"calculatenetworkfee.json",
)
.await;
let mut mock_provider_guard =
mock_provider_guard.mock_response_ignore_param("getversion", json!({})).await;
mock_provider_guard.mount_mocks().await;
}
let client = {
let mock_provider = mock_provider.lock().await;
Arc::new(mock_provider.into_client())
};
let mut tb = TransactionBuilder::with_client(&client);
tb.set_script(Some(vec![1, 2, 3]))
.set_signers(vec![AccountSigner::called_by_entry(&Account::create().unwrap())
.unwrap()
.into()])
.unwrap()
.valid_until_block(1000)
.unwrap();
let mut tx = tb.get_unsigned_tx().await.unwrap();
let mut result = tx.send_tx().await;
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("The transaction does not have the same number of signers and witnesses."));
}
#[tokio::test]
async fn test_contract_witness() {
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
{
let mut mock_provider_guard = mock_provider.lock().await; let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"invokescript",
"invokescript_symbol_neo.json",
)
.await;
let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"calculatenetworkfee",
"calculatenetworkfee.json",
)
.await;
let mut mock_provider_guard =
mock_provider_guard.mock_response_ignore_param("getversion", json!({})).await;
mock_provider_guard.mount_mocks().await;
}
let client = {
let mock_provider = mock_provider.lock().await;
Arc::new(mock_provider.into_client())
};
let contract_hash = H160::from_str("e87819d005b730645050f89073a4cd7bf5f6bd3c").unwrap();
let params = vec![ContractParameter::from("iamgroot"), ContractParameter::from(2)];
let invocation_script = ScriptBuilder::new()
.push_data("iamgroot".as_bytes().to_vec())
.push_integer(BigInt::from(2))
.to_bytes();
let mut tb = TransactionBuilder::with_client(&client);
tb.set_script(Some(vec![1, 2, 3]))
.set_signers(vec![
ContractSigner::global(contract_hash, ¶ms).into(),
AccountSigner::called_by_entry(&Account::create().unwrap()).unwrap().into(),
])
.unwrap()
.valid_until_block(1000)
.unwrap();
let tx = tb.sign().await.unwrap();
assert!(tx.witnesses().contains(&Witness::from_scripts(invocation_script, vec![])));
}
#[tokio::test]
async fn test_transfer_neo_from_normal_account() {
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
{
let mut mock_provider_guard = mock_provider.lock().await; let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"invokescript",
"invokescript_transfer_with_fixed_sysfee.json",
)
.await;
let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"calculatenetworkfee",
"calculatenetworkfee.json",
)
.await;
let mut mock_provider_guard =
mock_provider_guard.mock_response_ignore_param("getversion", json!({})).await;
mock_provider_guard.mount_mocks().await;
}
let client = {
let mock_provider = mock_provider.lock().await;
Arc::new(mock_provider.into_client())
};
let script = ScriptBuilder::new()
.contract_call(
&ScriptHashExtension::from_hex(TestConstants::NEO_TOKEN_HASH).unwrap(),
"transfer",
&[
ContractParameter::from(ACCOUNT1.address_or_scripthash().script_hash()),
ContractParameter::from(
H160::from_str("969a77db482f74ce27105f760efa139223431394").unwrap(),
),
ContractParameter::from(5),
ContractParameter::any(),
],
None,
)
.unwrap()
.to_bytes();
let binding = ACCOUNT1.verification_script().clone().unwrap();
let expected_verification_script = binding.script();
let mut tb = TransactionBuilder::with_client(&client);
tb.set_script(Some(script.clone()))
.set_signers(vec![AccountSigner::none(ACCOUNT1.deref()).unwrap().into()])
.unwrap()
.valid_until_block(100)
.unwrap();
let tx = tb.sign().await.unwrap();
assert_eq!(tx.script(), &script);
assert_eq!(tx.witnesses().len(), 1);
assert_eq!(
tx.witnesses().first().unwrap().verification.script(),
expected_verification_script
);
}
#[tokio::test]
async fn test_extend_script() {
let client = CLIENT.get_or_init(|| async { MockClient::new().await.into_client() }).await;
let script1 = ScriptBuilder::new()
.contract_call(
&ScriptHashExtension::from_hex(TestConstants::NEO_TOKEN_HASH).unwrap(),
"transfer",
&[
ContractParameter::from(ACCOUNT1.address_or_scripthash().script_hash()),
ContractParameter::from(
H160::from_str("969a77db482f74ce27105f760efa139223431394").unwrap(),
),
ContractParameter::from(11),
ContractParameter::any(),
],
None,
)
.unwrap()
.to_bytes();
let script2 = ScriptBuilder::new()
.contract_call(
&ScriptHashExtension::from_hex(TestConstants::NEO_TOKEN_HASH).unwrap(),
"transfer",
&[
ContractParameter::from(ACCOUNT1.address_or_scripthash().script_hash()),
ContractParameter::from(ACCOUNT2.address_or_scripthash().script_hash()),
ContractParameter::from(22),
ContractParameter::any(),
],
None,
)
.unwrap()
.to_bytes();
let mut tb = TransactionBuilder::with_client(&client);
tb.set_script(Some(script1.clone()));
assert_eq!(tb.script().clone().unwrap(), script1);
tb.extend_script(script2.clone());
assert_eq!(tb.script().clone().unwrap(), [script1, script2].concat());
}
#[tokio::test]
async fn test_invoking_with_params_should_produce_the_correct_request() {
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
{
let mut mock_provider_guard = mock_provider.lock().await; let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file(
"invokefunction",
"invokefunction_transfer_neo.json",
json!([
TestConstants::NEO_TOKEN_HASH,
"transfer",
vec![
ContractParameter::from(ACCOUNT1.address_or_scripthash().script_hash()),
ContractParameter::from(
H160::from_str("969a77db482f74ce27105f760efa139223431394").unwrap(),
),
ContractParameter::from(5),
ContractParameter::any(),
],
]),
)
.await;
mock_provider_guard.mount_mocks().await;
}
let client = {
let mock_provider = mock_provider.lock().await;
Arc::new(mock_provider.into_client())
};
let response = client
.invoke_function(
&H160::from_str(TestConstants::NEO_TOKEN_HASH).unwrap(),
"transfer".to_string(),
vec![
ContractParameter::from(ACCOUNT1.address_or_scripthash().script_hash()),
ContractParameter::from(
H160::from_str("969a77db482f74ce27105f760efa139223431394").unwrap(),
),
ContractParameter::from(5),
ContractParameter::any(),
],
None,
)
.await
.unwrap();
let script_In_Response = "CxUMFJQTQyOSE/oOdl8QJ850L0jbd5qWDBQGSl3MDxYsg0c9Aok46V+3dhMechTAHwwIdHJhbnNmZXIMFIOrBnmtVcBQoTrUP1k26nP16x72QWJ9W1I=".to_string();
assert_eq!(response.script, script_In_Response);
}
#[tokio::test]
async fn test_fail_signing_with_account_without_ec_key_pair() {
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
let client = {
let mut mock_provider = mock_provider.lock().await;
mock_provider.mock_default_responses().await.mount_mocks().await;
Arc::new(mock_provider.into_client())
};
let account =
Account::from_verification_script(&VerificationScript::from(vec![1, 2, 3])).unwrap();
let mut tx_builder = TransactionBuilder::with_client(client.as_ref());
tx_builder
.set_script(Some(vec![1, 2, 3]))
.set_signers(vec![AccountSigner::none(&account).unwrap().into()]);
assert!(tx_builder.sign().await.is_err());
}
#[tokio::test]
async fn test_do_if_sender_cannot_cover_fees() {
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
{
let mut mock_provider_guard = mock_provider.lock().await; let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"invokescript",
"invokescript_transfer_with_fixed_sysfee.json",
)
.await;
let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"calculatenetworkfee",
"calculatenetworkfee.json",
)
.await;
let mut mock_provider_guard = mock_provider_guard
.mock_response_for_balance_of(
&hex::encode(GAS_TOKEN_HASH.deref().as_bytes()),
&hex::encode(ACCOUNT1.address_or_scripthash().script_hash().as_bytes()),
"invokefunction_balanceOf_1000000.json",
)
.await;
mock_provider_guard.mount_mocks().await;
}
let client = {
let mock_provider = mock_provider.lock().await;
Arc::new(mock_provider.into_client())
};
let recipient = H160::from_str("969a77db482f74ce27105f760efa139223431394").unwrap();
let script = ScriptBuilder::new()
.contract_call(
&GAS_TOKEN_HASH,
"transfer",
&[
ContractParameter::h160(&ACCOUNT1.address_or_scripthash().script_hash()),
ContractParameter::h160(&recipient),
ContractParameter::integer(2_000_000),
ContractParameter::any(),
],
None,
)
.unwrap()
.to_bytes();
let tested = Arc::new(std::sync::atomic::AtomicBool::new(false));
let tested_clone = tested.clone();
let mut tx_builder = TransactionBuilder::with_client(client.as_ref());
let _ = tx_builder
.set_script(Some(script))
.set_signers(vec![AccountSigner::called_by_entry(&ACCOUNT1).unwrap().into()])
.unwrap()
.valid_until_block(2000000)
.unwrap()
.do_if_sender_cannot_cover_fees(Box::new(move |fee, balance| {
assert_eq!(fee, 1230610 + 9999510);
assert_eq!(balance, 1000000);
tested_clone.store(true, std::sync::atomic::Ordering::SeqCst);
}));
let _ = tx_builder.get_unsigned_tx().await.unwrap();
assert!(tested.load(std::sync::atomic::Ordering::SeqCst));
}
#[tokio::test]
async fn test_do_if_sender_cannot_cover_fees_already_specified_a_supplier() {
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
let client = {
let mut mock_provider = mock_provider.lock().await;
mock_provider.mock_default_responses().await.mount_mocks().await;
Arc::new(mock_provider.into_client())
};
let mut tx_builder = TransactionBuilder::with_client(&client);
tx_builder
.throw_if_sender_cannot_cover_fees(TransactionError::IllegalState(Default::default()));
let result = tx_builder.do_if_sender_cannot_cover_fees(Box::new(|_, _| {}));
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("Cannot handle a consumer for this case, since an exception "));
}
#[tokio::test]
async fn test_throw_if_sender_cannot_cover_fees() {
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
{
let mut mock_provider_guard = mock_provider.lock().await; let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"invokescript",
"invokescript_transfer_with_fixed_sysfee.json",
)
.await;
let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"calculatenetworkfee",
"calculatenetworkfee.json",
)
.await;
let mut mock_provider_guard = mock_provider_guard
.mock_response_for_balance_of(
&hex::encode(GAS_TOKEN_HASH.deref().as_bytes()),
&hex::encode(ACCOUNT1.address_or_scripthash().script_hash().as_bytes()),
"invokefunction_balanceOf_1000000.json",
)
.await;
mock_provider_guard.mount_mocks().await;
}
let client = {
let mock_provider = mock_provider.lock().await;
Arc::new(mock_provider.into_client())
};
let recipient = H160::from_str("969a77db482f74ce27105f760efa139223431394").unwrap();
let script = ScriptBuilder::new()
.contract_call(
&H160::from_str(TestConstants::NEO_TOKEN_HASH).unwrap(),
"transfer",
&[
ContractParameter::h160(&ACCOUNT1.address_or_scripthash().script_hash()),
ContractParameter::h160(&recipient),
ContractParameter::integer(5),
ContractParameter::any(),
],
None,
)
.unwrap()
.to_bytes();
let mut tx_builder = TransactionBuilder::with_client(&client);
let _ = tx_builder
.set_script(Some(script))
.valid_until_block(2000000)
.unwrap()
.set_signers(vec![AccountSigner::called_by_entry(&ACCOUNT1).unwrap().into()])
.unwrap()
.throw_if_sender_cannot_cover_fees(TransactionError::IllegalState(
"test throwIfSenderCannotCoverFees".to_string(),
));
let result = tx_builder.get_unsigned_tx().await;
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("test throwIfSenderCannotCoverFees"));
}
#[tokio::test]
async fn test_throw_if_sender_cannot_cover_fees_already_specified_a_consumer() {
let client = CLIENT.get_or_init(|| async { MockClient::new().await.into_client() }).await;
let mut tx_builder = TransactionBuilder::with_client(&client);
let _ = tx_builder.do_if_sender_cannot_cover_fees(Box::new(|_, _| {}));
let result =
tx_builder.throw_if_sender_cannot_cover_fees(TransactionError::InsufficientFunds);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("Cannot handle a supplier for this case, since a consumer "));
}
#[tokio::test]
async fn test_build_with_invalid_script() {
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
{
let mut mock_provider_guard = mock_provider.lock().await; let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file(
"invokescript",
"invokescript_invalidscript.json",
json!(["DAASDBSTrRVy"]),
)
.await;
let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param("getblockcount", "getblockcount_1000.json")
.await;
mock_provider_guard.mount_mocks().await;
}
let client = {
let mock_provider = mock_provider.lock().await;
Arc::new(mock_provider.into_client())
};
let account1 =
Account::from_wif("L1WMhxazScMhUrdv34JqQb1HFSQmWeN2Kpc1R9JGKwL7CDNP21uR").unwrap();
let mut tx_builder = TransactionBuilder::with_client(&client);
tx_builder
.set_script(Some(hex::decode("0c00120c1493ad1572").unwrap()))
.set_signers(vec![AccountSigner::called_by_entry(&account1).unwrap().into()]);
let result = tx_builder.get_unsigned_tx().await;
assert!(result.is_err());
if let TransactionError::TransactionConfiguration(error) = result.unwrap_err() {
assert!(error.contains("Instruction out of bounds"));
}
}
#[tokio::test]
async fn test_build_with_script_vm_faults() {
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
{
let mut mock_provider_guard = mock_provider.lock().await; let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file(
"invokescript",
"invokescript_exception.json",
json!(["DA5PcmFjbGVDb250cmFjdEEa93tn"]),
)
.await;
let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param("getblockcount", "getblockcount_1000.json")
.await;
mock_provider_guard.mount_mocks().await;
}
let client = {
let mock_provider = mock_provider.lock().await;
Arc::new(mock_provider.into_client())
};
let account1 =
Account::from_wif("L1WMhxazScMhUrdv34JqQb1HFSQmWeN2Kpc1R9JGKwL7CDNP21uR").unwrap();
let mut tx_builder = TransactionBuilder::with_client(&client);
tx_builder
.set_script(Some(hex::decode("0c0e4f7261636c65436f6e7472616374411af77b67").unwrap()))
.set_signers(vec![AccountSigner::called_by_entry(&account1).unwrap().into()]);
let result = tx_builder.get_unsigned_tx().await;
assert!(result.is_err());
if let TransactionError::TransactionConfiguration(error) = result.unwrap_err() {
assert_eq!(
error,
"The vm exited due to the following exception: Value was either too large or too small for an Int32."
);
}
}
#[tokio::test]
async fn test_get_unsigned_transaction() {
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
{
let mut mock_provider_guard = mock_provider.lock().await; let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"invokescript",
"invokescript_symbol_neo.json",
)
.await;
let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param("getblockcount", "getblockcount_1000.json")
.await;
let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"calculatenetworkfee",
"calculatenetworkfee.json",
)
.await;
mock_provider_guard.mount_mocks().await;
}
let client = {
let mock_provider = mock_provider.lock().await;
Arc::new(mock_provider.into_client())
};
let account1 =
Account::from_wif("L1WMhxazScMhUrdv34JqQb1HFSQmWeN2Kpc1R9JGKwL7CDNP21uR").unwrap();
let mut tx_builder = TransactionBuilder::with_client(&client);
tx_builder
.set_script(Some(vec![1, 2, 3]))
.set_signers(vec![AccountSigner::called_by_entry(&account1).unwrap().into()]);
let tx = match tx_builder.get_unsigned_tx().await {
Ok(tx) => tx,
Err(e) => panic!("Error: {}", e),
};
assert_eq!(tx.version, 0);
assert_eq!(tx.signers().len(), 1);
assert_eq!(
tx.signers[0].as_account_signer().unwrap(),
&AccountSigner::called_by_entry(&account1).unwrap()
);
assert!(tx.witnesses.is_empty());
}
#[tokio::test]
async fn test_version() {
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
{
let mut mock_provider_guard = mock_provider.lock().await; let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"invokescript",
"invokescript_symbol_neo.json",
)
.await;
let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param("getblockcount", "getblockcount_1000.json")
.await;
let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"calculatenetworkfee",
"calculatenetworkfee.json",
)
.await;
mock_provider_guard.mount_mocks().await;
}
let client = {
let mock_provider = mock_provider.lock().await;
Arc::new(mock_provider.into_client())
};
let account1 =
Account::from_wif("L1WMhxazScMhUrdv34JqQb1HFSQmWeN2Kpc1R9JGKwL7CDNP21uR").unwrap();
let mut tx_builder = TransactionBuilder::with_client(&client);
tx_builder
.version(1)
.set_script(Some(vec![1, 2, 3]))
.set_signers(vec![AccountSigner::called_by_entry(&account1).unwrap().into()]);
let tx = match tx_builder.get_unsigned_tx().await {
Ok(tx) => tx,
Err(e) => panic!("Error: {}", e),
};
assert_eq!(tx.version, 1);
}
#[tokio::test]
async fn test_additional_network_fee() {
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
{
let mut mock_provider_guard = mock_provider.lock().await; let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"invokescript",
"invokescript_symbol_neo.json",
)
.await;
let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param("getblockcount", "getblockcount_1000.json")
.await;
let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"calculatenetworkfee",
"calculatenetworkfee.json",
)
.await;
mock_provider_guard.mount_mocks().await;
}
let client = {
let mock_provider = mock_provider.lock().await;
Arc::new(mock_provider.into_client())
};
let account = Account::create().unwrap();
let mut tx_builder = TransactionBuilder::with_client(&client);
tx_builder
.set_script(Some(vec![1, 2, 3]))
.set_signers(vec![AccountSigner::none(&account).unwrap().into()]);
let tx = match tx_builder.get_unsigned_tx().await {
Ok(tx) => tx,
Err(e) => panic!("Error: {}", e),
};
assert_eq!(tx.net_fee, 1230610);
let mut tx_builder = TransactionBuilder::with_client(&client);
tx_builder
.set_script(Some(vec![1, 2, 3]))
.set_signers(vec![AccountSigner::none(&account).unwrap().into()])
.unwrap()
.set_additional_network_fee(2000);
let tx = match tx_builder.get_unsigned_tx().await {
Ok(tx) => tx,
Err(e) => panic!("Error: {}", e),
};
assert_eq!(tx.net_fee, 1230610 + 2000);
}
#[tokio::test]
async fn test_additional_system_fee() {
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
{
let mut mock_provider_guard = mock_provider.lock().await; let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"invokescript",
"invokescript_symbol_neo.json",
)
.await;
let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param("getblockcount", "getblockcount_1000.json")
.await;
let mut mock_provider_guard = mock_provider_guard
.mock_response_with_file_ignore_param(
"calculatenetworkfee",
"calculatenetworkfee.json",
)
.await;
mock_provider_guard.mount_mocks().await;
}
let client = {
let mock_provider = mock_provider.lock().await;
Arc::new(mock_provider.into_client())
};
let account = Account::create().unwrap();
let mut tx_builder = TransactionBuilder::with_client(&client);
tx_builder
.set_script(Some(vec![1, 2, 3]))
.set_signers(vec![AccountSigner::none(&account).unwrap().into()])
.expect("Failed to set signers in test - this should never fail with valid test data");
let tx = match tx_builder.get_unsigned_tx().await {
Ok(tx) => tx,
Err(e) => panic!("Error: {}", e),
};
assert_eq!(tx.sys_fee, 984060);
let mut tx_builder = TransactionBuilder::with_client(&client);
tx_builder
.set_script(Some(vec![1, 2, 3]))
.set_signers(vec![AccountSigner::none(&account).unwrap().into()])
.unwrap()
.set_additional_system_fee(3000);
let tx = match tx_builder.get_unsigned_tx().await {
Ok(tx) => tx,
Err(e) => panic!("Error: {}", e),
};
assert_eq!(tx.sys_fee, 984060 + 3000);
}
#[tokio::test]
async fn test_set_first_signer() {
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
let client = {
let mut mock_provider = mock_provider.lock().await;
mock_provider.mock_default_responses().await.mount_mocks().await;
Arc::new(mock_provider.into_client())
};
let account1 =
Account::from_wif("L1WMhxazScMhUrdv34JqQb1HFSQmWeN2Kpc1R9JGKwL7CDNP21uR").unwrap();
let account2 =
Account::from_wif("KysNqEuLb3wmZJ6PsxbA9Bh6ewTybEda4dEiN9X7X48dJPkLWZ5a").unwrap();
let s1 = AccountSigner::global(&account1.clone()).unwrap();
let s2 = AccountSigner::called_by_entry(&account2.clone()).unwrap();
let mut tx_builder = TransactionBuilder::with_client(&client);
&tx_builder
.set_script(Some(vec![1, 2, 3]))
.set_signers(vec![s1.clone().into(), s2.clone().into()]);
assert_eq!(tx_builder.clone().signers, vec![s1.clone().into(), s2.clone().into()]);
tx_builder.first_signer(&s2.account).unwrap();
assert_eq!(tx_builder.clone().signers, vec![s2.clone().into(), s1.clone().into()]);
&tx_builder.first_signer(&account1).unwrap();
assert_eq!(tx_builder.clone().signers, vec![s1.clone().into(), s2.clone().into()]);
}
#[tokio::test]
async fn test_set_first_signer_fee_only_present() {
let client = CLIENT.get_or_init(|| async { MockClient::new().await.into_client() }).await;
let account1 =
Account::from_wif("L1WMhxazScMhUrdv34JqQb1HFSQmWeN2Kpc1R9JGKwL7CDNP21uR").unwrap();
let account2 =
Account::from_wif("KysNqEuLb3wmZJ6PsxbA9Bh6ewTybEda4dEiN9X7X48dJPkLWZ5a").unwrap();
let s1 = AccountSigner::none(&account1.clone()).unwrap();
let s2 = AccountSigner::called_by_entry(&account2.clone()).unwrap();
let mut tx_builder = TransactionBuilder::with_client(&client);
tx_builder
.set_script(Some(vec![1, 2, 3]))
.set_signers(vec![s1.clone().into(), s2.clone().into()]);
assert_eq!(tx_builder.signers, vec![s1.clone().into(), s2.clone().into()]);
let result = tx_builder.first_signer(&account2);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("contains a signer with fee-only witness scope"));
}
#[tokio::test]
async fn test_set_first_signer_not_present() {
let client = CLIENT.get_or_init(|| async { MockClient::new().await.into_client() }).await;
let account1 =
Account::from_wif("L1WMhxazScMhUrdv34JqQb1HFSQmWeN2Kpc1R9JGKwL7CDNP21uR").unwrap();
let account2 =
Account::from_wif("KysNqEuLb3wmZJ6PsxbA9Bh6ewTybEda4dEiN9X7X48dJPkLWZ5a").unwrap();
let s1 = AccountSigner::global(&account1.clone()).unwrap();
let mut tx_builder = TransactionBuilder::with_client(&client);
tx_builder.set_script(Some(vec![1, 2, 3])).set_signers(vec![s1.clone().into()]);
assert_eq!(tx_builder.signers[0], s1.clone().into());
let result = tx_builder.first_signer(&account2);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("Could not find a signer with script hash "));
}
#[tokio::test]
async fn test_tracking_transaction_should_return_correct_block() {
init_logger();
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
{
let mut mock_provider = mock_provider.lock().await;
mock_provider
.mock_response_with_file_ignore_param(
"invokescript",
"invokescript_transfer_with_fixed_sysfee.json",
)
.await
.mock_response_with_file_ignore_param(
"calculatenetworkfee",
"calculatenetworkfee.json",
)
.await
.mock_response_ignore_param("getversion", json!({}))
.await
.mount_mocks()
.await;
}
let client = {
let mock_provider = mock_provider.lock().await;
Arc::new(mock_provider.into_client())
};
let account1 =
Account::from_wif("L1WMhxazScMhUrdv34JqQb1HFSQmWeN2Kpc1R9JGKwL7CDNP21uR").unwrap();
let recipient = H160::from_str("969a77db482f74ce27105f760efa139223431394").unwrap();
let script = ScriptBuilder::new()
.contract_call(
&H160::from_str(TestConstants::NEO_TOKEN_HASH).unwrap(),
"transfer",
&[
ContractParameter::h160(&account1.address_or_scripthash().script_hash()),
ContractParameter::h160(&recipient),
ContractParameter::integer(5),
ContractParameter::any(),
],
None,
)
.unwrap()
.to_bytes();
let mut tx_builder = TransactionBuilder::with_client(&client);
tx_builder
.set_script(Some(script))
.nonce(0)
.unwrap()
.set_signers(vec![AccountSigner::called_by_entry(&account1).unwrap().into()])
.expect("Failed to set signers in test - this should never fail with valid test data")
.valid_until_block(1000)
.unwrap();
let mut tx = tx_builder.sign().await.unwrap();
tx.block_count_when_sent = Some(999);
let tx_id = tx.tx_id().unwrap();
let tx_id_str = format!("{:?}", tx_id);
let block_hash_str = "0x1111111111111111111111111111111111111111111111111111111111111111";
{
let mut mock_provider = mock_provider.lock().await;
mock_provider
.mock_response_with_file_ignore_param("getblockcount", "getblockcount_1000.json")
.await
.mock_response_ignore_param("getblockhash", json!(block_hash_str))
.await
.mock_response_ignore_param(
"getblock",
json!({
"hash": block_hash_str,
"size": 1,
"version": 0,
"previousblockhash": block_hash_str,
"merkleroot": block_hash_str,
"time": 0,
"nonce": "0",
"index": 999,
"primary": 0,
"nextconsensus": "0",
"tx": [
{
"hash": tx_id_str,
"size": 0,
"version": 0,
"nonce": 0,
"sender": "0",
"sysfee": "0",
"netfee": "0",
"validuntilblock": 0,
"script": "",
}
],
"confirmations": 1,
"nextblockhash": null,
}),
)
.await
.mount_mocks()
.await;
}
tx.track_tx(1).await.unwrap();
}
#[tokio::test]
async fn test_tracking_transaction_tx_not_sent() {
init_logger();
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
let client = {
let mut mock_provider = mock_provider.lock().await;
mock_provider
.mock_invoke_script(InvocationResult::default())
.await
.mock_response_with_file_ignore_param("getblockcount", "getblockcount_1000.json")
.await
.mock_response_with_file_ignore_param(
"calculatenetworkfee",
"calculatenetworkfee.json",
)
.await
.mock_response_ignore_param("getversion", json!({}))
.await
.mount_mocks()
.await;
Arc::new(mock_provider.into_client())
};
let account1 =
Account::from_wif("L1WMhxazScMhUrdv34JqQb1HFSQmWeN2Kpc1R9JGKwL7CDNP21uR").unwrap();
let recipient = H160::from_str("969a77db482f74ce27105f760efa139223431394").unwrap();
let script = ScriptBuilder::new()
.contract_call(
&H160::from_str(TestConstants::NEO_TOKEN_HASH).unwrap(),
"transfer",
&[
ContractParameter::h160(&account1.address_or_scripthash().script_hash()),
ContractParameter::h160(&recipient),
ContractParameter::integer(5),
ContractParameter::any(),
],
None,
)
.unwrap()
.to_bytes();
let mut tx_builder = TransactionBuilder::with_client(&client);
tx_builder
.set_script(Some(script))
.nonce(0)
.unwrap()
.set_signers(vec![AccountSigner::called_by_entry(&account1).unwrap().into()])
.expect("Failed to set signers in test - this should never fail with valid test data");
let tx = tx_builder.sign().await.unwrap();
let result = tx.track_tx(1).await;
assert!(
matches!(result, Err(TransactionError::IllegalState(msg)) if msg.contains("Cannot track transaction before it has been sent"))
);
}
#[tokio::test]
async fn test_get_application_log() {
init_logger();
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
let client = {
let mut mock_provider = mock_provider.lock().await;
mock_provider
.mock_invoke_script(InvocationResult::default())
.await
.mock_response_with_file_ignore_param("getblockcount", "getblockcount_1000.json")
.await
.mock_response_with_file_ignore_param(
"calculatenetworkfee",
"calculatenetworkfee.json",
)
.await
.mock_response_ignore_param("getversion", json!({}))
.await
.mock_send_raw_transaction(RawTransaction { hash: H256::zero() })
.await
.mock_get_application_log(Some(ApplicationLog {
transaction_id: H256::from_str(
"0xeb52f99ae5cf923d8905bdd91c4160e2207d20c0cb42f8062f31c6743770e4d1",
)
.unwrap(),
..Default::default()
}))
.await
.mount_mocks()
.await;
Arc::new(mock_provider.into_client())
};
let account1 =
Account::from_wif("L1WMhxazScMhUrdv34JqQb1HFSQmWeN2Kpc1R9JGKwL7CDNP21uR").unwrap();
let script = ScriptBuilder::new()
.contract_call(
&H160::from_str(TestConstants::NEO_TOKEN_HASH).unwrap(),
"transfer",
&[
ContractParameter::h160(&account1.address_or_scripthash().script_hash()),
ContractParameter::h160(&account1.address_or_scripthash().script_hash()),
ContractParameter::integer(1),
ContractParameter::any(),
],
None,
)
.unwrap()
.to_bytes();
let mut tx_builder = TransactionBuilder::with_client(client.as_ref());
tx_builder
.set_script(Some(script))
.set_signers(vec![AccountSigner::called_by_entry(&account1).unwrap().into()])
.expect("Failed to set signers in test - this should never fail with valid test data");
let mut tx = tx_builder.sign().await.map_err(TransactionError::BuilderError).unwrap();
let _ = tx.send_tx().await.map_err(TransactionError::from).unwrap();
let application_log = tx
.get_application_log(client.as_ref())
.await
.map_err(TransactionError::from)
.unwrap();
assert_eq!(
application_log.transaction_id,
H256::from_str("0xeb52f99ae5cf923d8905bdd91c4160e2207d20c0cb42f8062f31c6743770e4d1")
.unwrap()
);
}
#[tokio::test]
async fn test_get_application_log_tx_not_sent() {
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
let client = {
let mut mock_provider = mock_provider.lock().await;
mock_provider
.mock_invoke_script(InvocationResult {
gas_consumed: "984060".to_string(),
..Default::default()
})
.await
.mock_response_with_file_ignore_param("getblockcount", "getblockcount_1000.json")
.await
.mock_response_with_file_ignore_param(
"calculatenetworkfee",
"calculatenetworkfee.json",
)
.await
.mock_response_ignore_param("getversion", json!({}))
.await
.mount_mocks()
.await;
Arc::new(mock_provider.into_client())
};
let account1 =
Account::from_wif("L1WMhxazScMhUrdv34JqQb1HFSQmWeN2Kpc1R9JGKwL7CDNP21uR").unwrap();
let script = ScriptBuilder::new()
.contract_call(
&H160::from_str(TestConstants::NEO_TOKEN_HASH).unwrap(),
"transfer",
&[
ContractParameter::h160(&account1.address_or_scripthash().script_hash()),
ContractParameter::h160(&account1.address_or_scripthash().script_hash()),
ContractParameter::integer(1),
ContractParameter::any(),
],
None,
)
.unwrap()
.to_bytes();
let mut tx_builder = TransactionBuilder::with_client(&client);
tx_builder
.set_script(Some(script))
.set_signers(vec![AccountSigner::called_by_entry(&account1).unwrap().into()]);
let tx = tx_builder.sign().await.unwrap();
assert!(tx.get_application_log(&client.as_ref()).await.is_err());
}
#[tokio::test]
async fn test_get_application_log_not_existing() {
init_logger();
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
let client = {
let mut mock_provider = mock_provider.lock().await;
mock_provider
.mock_invoke_script(InvocationResult::default())
.await
.mock_response_with_file_ignore_param("getblockcount", "getblockcount_1000.json")
.await
.mock_response_with_file_ignore_param(
"calculatenetworkfee",
"calculatenetworkfee.json",
)
.await
.mock_response_ignore_param("getversion", json!({}))
.await
.mock_send_raw_transaction(RawTransaction { hash: H256::zero() })
.await
.mock_get_application_log(Default::default())
.await
.mount_mocks()
.await;
Arc::new(mock_provider.into_client())
};
let account1 =
Account::from_wif("L1WMhxazScMhUrdv34JqQb1HFSQmWeN2Kpc1R9JGKwL7CDNP21uR").unwrap();
let script = ScriptBuilder::new()
.contract_call(
&H160::from_str(TestConstants::NEO_TOKEN_HASH).unwrap(),
"transfer",
&[
ContractParameter::h160(&account1.address_or_scripthash().script_hash()),
ContractParameter::h160(&account1.address_or_scripthash().script_hash()),
ContractParameter::integer(1),
ContractParameter::any(),
],
None,
)
.unwrap()
.to_bytes();
let mut tx_builder = TransactionBuilder::with_client(&client);
tx_builder
.set_script(Some(script))
.set_signers(vec![AccountSigner::called_by_entry(&account1).unwrap().into()]);
let mut tx = tx_builder.sign().await.unwrap();
let _ = tx.send_tx().await.map_err(TransactionError::from).unwrap();
let log = tx.get_application_log(&client.as_ref()).await;
assert!(log.is_err());
}
#[tokio::test]
async fn test_transmission_on_fault() {
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
let client = {
let mut mock_provider = mock_provider.lock().await;
mock_provider
.mock_response_with_file_ignore_param("invokescript", "invokescript_fault.json")
.await
.mock_response_with_file_ignore_param("getblockcount", "getblockcount_1000.json")
.await
.mock_response_with_file_ignore_param(
"calculatenetworkfee",
"calculatenetworkfee.json",
)
.await
.mock_response_ignore_param("getversion", json!({}))
.await
.mount_mocks()
.await;
Arc::new(mock_provider.into_client())
};
let account = Account::create().unwrap();
let mut tx_builder = TransactionBuilder::with_client(&client);
tx_builder
.set_script(Some(vec![1, 2, 3]))
.set_signers(vec![AccountSigner::none(&account).unwrap().into()])
.expect("Failed to set signers in test - this should never fail with valid test data");
tx_builder.allow_transmission_on_fault();
let result = tx_builder.call_invoke_script().await.unwrap();
assert!(result.has_state_fault());
let gas_comsumed: i64 = result.gas_consumed.parse().unwrap();
let tx = match tx_builder.get_unsigned_tx().await {
Ok(tx) => tx,
Err(e) => panic!("Error: {}", e),
};
assert_eq!(tx.sys_fee, gas_comsumed);
}
#[tokio::test]
async fn test_prevent_transmission_on_fault() {
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
let client = {
let mut mock_provider = mock_provider.lock().await;
mock_provider
.mock_response_with_file_ignore_param("invokescript", "invokescript_fault.json")
.await
.mock_response_with_file_ignore_param("getblockcount", "getblockcount_1000.json")
.await
.mock_response_with_file_ignore_param(
"calculatenetworkfee",
"calculatenetworkfee.json",
)
.await
.mock_response_ignore_param("getversion", json!({}))
.await
.mount_mocks()
.await;
Arc::new(mock_provider.into_client())
};
let account = Account::create().unwrap();
let mut tx_builder = TransactionBuilder::with_client(&client);
tx_builder
.set_script(Some(vec![1, 2, 3]))
.set_signers(vec![AccountSigner::none(&account).unwrap().into()])
.expect("Failed to set signers in test - this should never fail with valid test data");
tx_builder.disallow_transmission_on_fault();
let result = tx_builder.call_invoke_script().await.unwrap();
assert!(result.has_state_fault());
let tx = tx_builder.get_unsigned_tx().await;
assert!(matches!(tx, Err(TransactionError::TransactionConfiguration(_))));
if let Err(TransactionError::TransactionConfiguration(msg)) = tx {
assert!(msg.contains("The vm exited due to the following exception: "));
}
}
#[tokio::test]
async fn test_get_unsigned_tx_rejects_invalid_oracle_response_attribute() {
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
let client = {
let mut mock_provider = mock_provider.lock().await;
mock_provider
.mock_response_with_file_ignore_param(
"invokescript",
"invokescript_symbol_neo.json",
)
.await
.mock_response_with_file_ignore_param(
"calculatenetworkfee",
"calculatenetworkfee.json",
)
.await
.mock_response_ignore_param("getversion", json!({}))
.await
.mount_mocks()
.await;
Arc::new(mock_provider.into_client())
};
let mut tb = TransactionBuilder::with_client(&client);
tb.set_script(Some(vec![1, 2, 3]))
.set_signers(vec![AccountSigner::none(ACCOUNT1.deref()).unwrap().into()])
.unwrap()
.valid_until_block(1000)
.unwrap();
tb.add_attributes(vec![TransactionAttribute::OracleResponse(OracleResponse {
id: 7,
response_code: OracleResponseCode::Success,
result: "not-valid-base64".to_string(),
})])
.unwrap();
let result = tb.get_unsigned_tx().await;
assert!(matches!(result, Err(TransactionError::TransactionConfiguration(_))));
if let Err(TransactionError::TransactionConfiguration(message)) = result {
assert!(message.contains("OracleResponse.result must be valid base64"));
}
}
#[tokio::test]
async fn test_sign_with_multiple_accounts() {
init_logger();
let mock_provider = Arc::new(Mutex::new(MockClient::new().await));
let client = {
let mut mock_provider = mock_provider.lock().await;
mock_provider.mock_default_responses().await.mount_mocks().await;
Arc::new(mock_provider.into_client())
};
let account1 =
Account::from_wif("L1WMhxazScMhUrdv34JqQb1HFSQmWeN2Kpc1R9JGKwL7CDNP21uR").unwrap();
let account2 =
Account::from_wif("KysNqEuLb3wmZJ6PsxbA9Bh6ewTybEda4dEiN9X7X48dJPkLWZ5a").unwrap();
let mut tx_builder = TransactionBuilder::with_client(&client);
tx_builder
.set_script(Some(vec![1, 2, 3]))
.set_signers(vec![
AccountSigner::called_by_entry(&account1).unwrap().into(),
AccountSigner::called_by_entry(&account2).unwrap().into(),
])
.expect("Failed to set signers in test - this should never fail with valid test data");
let tx = tx_builder.sign().await.unwrap();
assert_eq!(tx.witnesses.len(), 2);
assert!(tx
.witnesses
.iter()
.any(|w| w.verification == account1.verification_script().clone().unwrap()));
assert!(tx
.witnesses
.iter()
.any(|w| w.verification == account2.verification_script().clone().unwrap()));
}
}