Version and notification for private contract wrapper added (#9761)

* Version and notification for private contract wrapper added

* Error handling improved

* Style for comments in file fixed

* TODO issue added into comments
This commit is contained in:
Anton Gavrilov 2018-12-03 20:44:36 +01:00 committed by André Silva
parent 60691d03e0
commit 4ded4181a6
3 changed files with 267 additions and 66 deletions

File diff suppressed because one or more lines are too long

View File

@ -1 +1,171 @@
[{"constant": false,"inputs": [{"name": "newState","type": "bytes"},{"name": "v","type": "uint8[]"},{"name": "r","type": "bytes32[]"},{"name": "s","type": "bytes32[]"}],"name": "setState","outputs": [],"payable": false,"stateMutability": "nonpayable","type": "function"},{"constant": true,"inputs": [],"name": "code","outputs": [{"name": "","type": "bytes"}],"payable": false,"stateMutability": "view","type": "function"},{"constant": true,"inputs": [{"name": "","type": "uint256"}],"name": "validators","outputs": [{"name": "","type": "address"}],"payable": false,"stateMutability": "view","type": "function"},{"constant": true,"inputs": [],"name": "nonce","outputs": [{"name": "","type": "uint256"}],"payable": false,"stateMutability": "view","type": "function"},{"constant": true,"inputs": [],"name": "getValidators","outputs": [{"name": "","type": "address[]"}],"payable": false,"stateMutability": "view","type": "function"},{"constant": true,"inputs": [],"name": "state","outputs": [{"name": "","type": "bytes"}],"payable": false,"stateMutability": "view","type": "function"},{"inputs": [{"name": "initialValidators","type": "address[]"},{"name": "initialCode","type": "bytes"},{"name": "initialState","type": "bytes"}],"payable": false,"stateMutability": "nonpayable","type": "constructor"}] [
{
"constant": true,
"inputs": [],
"name": "getVersion",
"outputs": [
{
"name": "",
"type": "uint8"
}
],
"payable": false,
"stateMutability": "pure",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "newState",
"type": "bytes"
},
{
"name": "v",
"type": "uint8[]"
},
{
"name": "r",
"type": "bytes32[]"
},
{
"name": "s",
"type": "bytes32[]"
}
],
"name": "setState",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "code",
"outputs": [
{
"name": "",
"type": "bytes"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "",
"type": "uint256"
}
],
"name": "validators",
"outputs": [
{
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "changesOriginator",
"type": "address"
},
{
"name": "originalTransactionHash",
"type": "bytes"
}
],
"name": "notifyChanges",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "nonce",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "getValidators",
"outputs": [
{
"name": "",
"type": "address[]"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "state",
"outputs": [
{
"name": "",
"type": "bytes"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"name": "initialValidators",
"type": "address[]"
},
{
"name": "initialCode",
"type": "bytes"
},
{
"name": "initialState",
"type": "bytes"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"name": "changesOriginator",
"type": "address"
},
{
"indexed": false,
"name": "originalTransactionHash",
"type": "bytes"
}
],
"name": "PrivateStateChanged",
"type": "event"
}
]

View File

@ -103,6 +103,12 @@ const INIT_VEC_LEN: usize = 16;
/// Size of nonce cache /// Size of nonce cache
const NONCE_CACHE_SIZE: usize = 128; const NONCE_CACHE_SIZE: usize = 128;
/// Version for the initial private contract wrapper
const INITIAL_PRIVATE_CONTRACT_VER: usize = 1;
/// Version for the private contract notification about private state changes added
const PRIVATE_CONTRACT_WITH_NOTIFICATION_VER: usize = 2;
/// Configurtion for private transaction provider /// Configurtion for private transaction provider
#[derive(Default, PartialEq, Debug, Clone)] #[derive(Default, PartialEq, Debug, Clone)]
pub struct ProviderConfig { pub struct ProviderConfig {
@ -199,36 +205,30 @@ impl Provider where {
bail!(ErrorKind::SignerAccountNotSet); bail!(ErrorKind::SignerAccountNotSet);
} }
let tx_hash = signed_transaction.hash(); let tx_hash = signed_transaction.hash();
match signed_transaction.action { let contract = Self::contract_address_from_transaction(&signed_transaction).map_err(|_| ErrorKind::BadTransactonType)?;
Action::Create => { let data = signed_transaction.rlp_bytes();
bail!(ErrorKind::BadTransactonType); let encrypted_transaction = self.encrypt(&contract, &Self::iv_from_transaction(&signed_transaction), &data)?;
} let private = PrivateTransaction::new(encrypted_transaction, contract);
Action::Call(contract) => { // TODO #9825 [ToDr] Using BlockId::Latest is bad here,
let data = signed_transaction.rlp_bytes(); // the block may change in the middle of execution
let encrypted_transaction = self.encrypt(&contract, &Self::iv_from_transaction(&signed_transaction), &data)?; // causing really weird stuff to happen.
let private = PrivateTransaction::new(encrypted_transaction, contract); // We should retrieve hash and stick to that. IMHO
// TODO [ToDr] Using BlockId::Latest is bad here, // best would be to change the API and only allow H256 instead of BlockID
// the block may change in the middle of execution // in private-tx to avoid such mistakes.
// causing really weird stuff to happen. let contract_nonce = self.get_contract_nonce(&contract, BlockId::Latest)?;
// We should retrieve hash and stick to that. IMHO let private_state = self.execute_private_transaction(BlockId::Latest, &signed_transaction)?;
// best would be to change the API and only allow H256 instead of BlockID trace!(target: "privatetx", "Private transaction created, encrypted transaction: {:?}, private state: {:?}", private, private_state);
// in private-tx to avoid such mistakes. let contract_validators = self.get_validators(BlockId::Latest, &contract)?;
let contract_nonce = self.get_contract_nonce(&contract, BlockId::Latest)?; trace!(target: "privatetx", "Required validators: {:?}", contract_validators);
let private_state = self.execute_private_transaction(BlockId::Latest, &signed_transaction)?; let private_state_hash = self.calculate_state_hash(&private_state, contract_nonce);
trace!(target: "privatetx", "Private transaction created, encrypted transaction: {:?}, private state: {:?}", private, private_state); trace!(target: "privatetx", "Hashed effective private state for sender: {:?}", private_state_hash);
let contract_validators = self.get_validators(BlockId::Latest, &contract)?; self.transactions_for_signing.write().add_transaction(private.hash(), signed_transaction, contract_validators, private_state, contract_nonce)?;
trace!(target: "privatetx", "Required validators: {:?}", contract_validators); self.broadcast_private_transaction(private.hash(), private.rlp_bytes());
let private_state_hash = self.calculate_state_hash(&private_state, contract_nonce); Ok(Receipt {
trace!(target: "privatetx", "Hashed effective private state for sender: {:?}", private_state_hash); hash: tx_hash,
self.transactions_for_signing.write().add_transaction(private.hash(), signed_transaction, contract_validators, private_state, contract_nonce)?; contract_address: Some(contract),
self.broadcast_private_transaction(private.hash(), private.rlp_bytes()); status_code: 0,
Ok(Receipt { })
hash: tx_hash,
contract_address: Some(contract),
status_code: 0,
})
}
}
} }
/// Calculate hash from united private state and contract nonce /// Calculate hash from united private state and contract nonce
@ -269,33 +269,30 @@ impl Provider where {
self.broadcast_private_transaction(private_hash, transaction.private_transaction.rlp_bytes()); self.broadcast_private_transaction(private_hash, transaction.private_transaction.rlp_bytes());
return Ok(()); return Ok(());
} }
let tx_action = transaction.transaction.action.clone(); let contract = Self::contract_address_from_transaction(&transaction.transaction)
if let Action::Call(contract) = tx_action { .map_err(|_| "Incorrect type of action for the transaction")?;
// TODO [ToDr] Usage of BlockId::Latest // TODO #9825 [ToDr] Usage of BlockId::Latest
let contract_nonce = self.get_contract_nonce(&contract, BlockId::Latest); let contract_nonce = self.get_contract_nonce(&contract, BlockId::Latest);
if let Err(e) = contract_nonce { if let Err(e) = contract_nonce {
bail!("Cannot retrieve contract nonce: {:?}", e); bail!("Cannot retrieve contract nonce: {:?}", e);
}
let contract_nonce = contract_nonce.expect("Error was checked before");
let private_state = self.execute_private_transaction(BlockId::Latest, &transaction.transaction);
if let Err(e) = private_state {
bail!("Cannot retrieve private state: {:?}", e);
}
let private_state = private_state.expect("Error was checked before");
let private_state_hash = self.calculate_state_hash(&private_state, contract_nonce);
trace!(target: "privatetx", "Hashed effective private state for validator: {:?}", private_state_hash);
let password = find_account_password(&self.passwords, &*self.accounts, &validator_account);
let signed_state = self.accounts.sign(validator_account, password, private_state_hash);
if let Err(e) = signed_state {
bail!("Cannot sign the state: {:?}", e);
}
let signed_state = signed_state.expect("Error was checked before");
let signed_private_transaction = SignedPrivateTransaction::new(private_hash, signed_state, None);
trace!(target: "privatetx", "Sending signature for private transaction: {:?}", signed_private_transaction);
self.broadcast_signed_private_transaction(signed_private_transaction.hash(), signed_private_transaction.rlp_bytes());
} else {
bail!("Incorrect type of action for the transaction");
} }
let contract_nonce = contract_nonce.expect("Error was checked before");
let private_state = self.execute_private_transaction(BlockId::Latest, &transaction.transaction);
if let Err(e) = private_state {
bail!("Cannot retrieve private state: {:?}", e);
}
let private_state = private_state.expect("Error was checked before");
let private_state_hash = self.calculate_state_hash(&private_state, contract_nonce);
trace!(target: "privatetx", "Hashed effective private state for validator: {:?}", private_state_hash);
let password = find_account_password(&self.passwords, &*self.accounts, &validator_account);
let signed_state = self.accounts.sign(validator_account, password, private_state_hash);
if let Err(e) = signed_state {
bail!("Cannot sign the state: {:?}", e);
}
let signed_state = signed_state.expect("Error was checked before");
let signed_private_transaction = SignedPrivateTransaction::new(private_hash, signed_state, None);
trace!(target: "privatetx", "Sending signature for private transaction: {:?}", signed_private_transaction);
self.broadcast_signed_private_transaction(signed_private_transaction.hash(), signed_private_transaction.rlp_bytes());
} }
} }
Ok(()) Ok(())
@ -328,7 +325,7 @@ impl Provider where {
let mut signatures = desc.received_signatures.clone(); let mut signatures = desc.received_signatures.clone();
signatures.push(signed_tx.signature()); signatures.push(signed_tx.signature());
let rsv: Vec<Signature> = signatures.into_iter().map(|sign| sign.into_electrum().into()).collect(); let rsv: Vec<Signature> = signatures.into_iter().map(|sign| sign.into_electrum().into()).collect();
//Create public transaction // Create public transaction
let public_tx = self.public_transaction( let public_tx = self.public_transaction(
desc.state.clone(), desc.state.clone(),
&desc.original_transaction, &desc.original_transaction,
@ -337,7 +334,7 @@ impl Provider where {
desc.original_transaction.gas_price desc.original_transaction.gas_price
)?; )?;
trace!(target: "privatetx", "Last required signature received, public transaction created: {:?}", public_tx); trace!(target: "privatetx", "Last required signature received, public transaction created: {:?}", public_tx);
//Sign and add it to the queue // Sign and add it to the queue
let chain_id = desc.original_transaction.chain_id(); let chain_id = desc.original_transaction.chain_id();
let hash = public_tx.hash(chain_id); let hash = public_tx.hash(chain_id);
let signer_account = self.signer_account.ok_or_else(|| ErrorKind::SignerAccountNotSet)?; let signer_account = self.signer_account.ok_or_else(|| ErrorKind::SignerAccountNotSet)?;
@ -351,13 +348,22 @@ impl Provider where {
bail!(err); bail!(err);
} }
} }
//Remove from store for signing // Notify about state changes
let contract = Self::contract_address_from_transaction(&desc.original_transaction)?;
// TODO #9825 Usage of BlockId::Latest
if self.get_contract_version(BlockId::Latest, &contract) >= PRIVATE_CONTRACT_WITH_NOTIFICATION_VER {
match self.state_changes_notify(BlockId::Latest, &contract, &desc.original_transaction.sender(), desc.original_transaction.hash()) {
Ok(_) => trace!(target: "privatetx", "Notification about private state changes sent"),
Err(err) => warn!(target: "privatetx", "Failed to send private state changed notification, error: {:?}", err),
}
}
// Remove from store for signing
if let Err(err) = self.transactions_for_signing.write().remove(&private_hash) { if let Err(err) = self.transactions_for_signing.write().remove(&private_hash) {
warn!(target: "privatetx", "Failed to remove transaction from signing store, error: {:?}", err); warn!(target: "privatetx", "Failed to remove transaction from signing store, error: {:?}", err);
bail!(err); bail!(err);
} }
} else { } else {
//Add signature to the store // Add signature to the store
match self.transactions_for_signing.write().add_signature(&private_hash, signed_tx.signature()) { match self.transactions_for_signing.write().add_signature(&private_hash, signed_tx.signature()) {
Ok(_) => trace!(target: "privatetx", "Signature stored for private transaction"), Ok(_) => trace!(target: "privatetx", "Signature stored for private transaction"),
Err(err) => { Err(err) => {
@ -369,6 +375,16 @@ impl Provider where {
Ok(()) Ok(())
} }
fn contract_address_from_transaction(transaction: &SignedTransaction) -> Result<Address, Error> {
match transaction.action {
Action::Call(contract) => Ok(contract),
_ => {
warn!(target: "privatetx", "Incorrect type of action for the transaction");
bail!(ErrorKind::BadTransactonType);
}
}
}
fn last_required_signature(&self, desc: &PrivateTransactionSigningDesc, sign: Signature) -> Result<bool, Error> { fn last_required_signature(&self, desc: &PrivateTransactionSigningDesc, sign: Signature) -> Result<bool, Error> {
if desc.received_signatures.contains(&sign) { if desc.received_signatures.contains(&sign) {
return Ok(false); return Ok(false);
@ -474,7 +490,7 @@ impl Provider where {
env_info.gas_limit = transaction.gas; env_info.gas_limit = transaction.gas;
let mut state = self.client.state_at(block).ok_or(ErrorKind::StatePruned)?; let mut state = self.client.state_at(block).ok_or(ErrorKind::StatePruned)?;
// TODO: in case of BlockId::Latest these need to operate on the same state // TODO #9825 in case of BlockId::Latest these need to operate on the same state
let contract_address = match transaction.action { let contract_address = match transaction.action {
Action::Call(ref contract_address) => { Action::Call(ref contract_address) => {
let contract_code = Arc::new(self.get_decrypted_code(contract_address, block)?); let contract_code = Arc::new(self.get_decrypted_code(contract_address, block)?);
@ -536,7 +552,7 @@ impl Provider where {
/// Returns the key from the key server associated with the contract /// Returns the key from the key server associated with the contract
pub fn contract_key_id(&self, contract_address: &Address) -> Result<H256, Error> { pub fn contract_key_id(&self, contract_address: &Address) -> Result<H256, Error> {
//current solution uses contract address extended with 0 as id // Current solution uses contract address extended with 0 as id
let contract_address_extended: H256 = contract_address.into(); let contract_address_extended: H256 = contract_address.into();
Ok(H256::from_slice(&contract_address_extended)) Ok(H256::from_slice(&contract_address_extended))
@ -615,6 +631,21 @@ impl Provider where {
let value = self.client.call_contract(block, *address, data)?; let value = self.client.call_contract(block, *address, data)?;
decoder.decode(&value).map_err(|e| ErrorKind::Call(format!("Contract call failed {:?}", e)).into()) decoder.decode(&value).map_err(|e| ErrorKind::Call(format!("Contract call failed {:?}", e)).into())
} }
fn get_contract_version(&self, block: BlockId, address: &Address) -> usize {
let (data, decoder) = private_contract::functions::get_version::call();
match self.client.call_contract(block, *address, data)
.and_then(|value| decoder.decode(&value).map_err(|e| e.to_string())) {
Ok(version) => version.low_u64() as usize,
Err(_) => INITIAL_PRIVATE_CONTRACT_VER,
}
}
fn state_changes_notify(&self, block: BlockId, address: &Address, originator: &Address, transaction_hash: H256) -> Result<(), Error> {
let (data, _) = private_contract::functions::notify_changes::call(*originator, transaction_hash.0.to_vec());
let _value = self.client.call_contract(block, *address, data)?;
Ok(())
}
} }
pub trait Importer { pub trait Importer {
@ -644,12 +675,12 @@ impl Importer for Arc<Provider> {
.iter() .iter()
.find(|address| self.validator_accounts.contains(address)); .find(|address| self.validator_accounts.contains(address));
//extract the original transaction // Extract the original transaction
let encrypted_data = private_tx.encrypted(); let encrypted_data = private_tx.encrypted();
let transaction_bytes = self.decrypt(&contract, &encrypted_data)?; let transaction_bytes = self.decrypt(&contract, &encrypted_data)?;
let original_tx: UnverifiedTransaction = Rlp::new(&transaction_bytes).as_val()?; let original_tx: UnverifiedTransaction = Rlp::new(&transaction_bytes).as_val()?;
let nonce_cache = NonceCache::new(NONCE_CACHE_SIZE); let nonce_cache = NonceCache::new(NONCE_CACHE_SIZE);
//add to the queue for further verification // Add to the queue for further verification
self.transactions_for_verification.add_transaction( self.transactions_for_verification.add_transaction(
original_tx, original_tx,
validation_account.map(|&account| account), validation_account.map(|&account| account),