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:
parent
60691d03e0
commit
4ded4181a6
File diff suppressed because one or more lines are too long
@ -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"
|
||||
}
|
||||
]
|
@ -103,6 +103,12 @@ const INIT_VEC_LEN: usize = 16;
|
||||
/// Size of nonce cache
|
||||
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
|
||||
#[derive(Default, PartialEq, Debug, Clone)]
|
||||
pub struct ProviderConfig {
|
||||
@ -199,36 +205,30 @@ impl Provider where {
|
||||
bail!(ErrorKind::SignerAccountNotSet);
|
||||
}
|
||||
let tx_hash = signed_transaction.hash();
|
||||
match signed_transaction.action {
|
||||
Action::Create => {
|
||||
bail!(ErrorKind::BadTransactonType);
|
||||
}
|
||||
Action::Call(contract) => {
|
||||
let data = signed_transaction.rlp_bytes();
|
||||
let encrypted_transaction = self.encrypt(&contract, &Self::iv_from_transaction(&signed_transaction), &data)?;
|
||||
let private = PrivateTransaction::new(encrypted_transaction, contract);
|
||||
// TODO [ToDr] Using BlockId::Latest is bad here,
|
||||
// the block may change in the middle of execution
|
||||
// causing really weird stuff to happen.
|
||||
// We should retrieve hash and stick to that. IMHO
|
||||
// best would be to change the API and only allow H256 instead of BlockID
|
||||
// in private-tx to avoid such mistakes.
|
||||
let contract_nonce = self.get_contract_nonce(&contract, BlockId::Latest)?;
|
||||
let private_state = self.execute_private_transaction(BlockId::Latest, &signed_transaction)?;
|
||||
trace!(target: "privatetx", "Private transaction created, encrypted transaction: {:?}, private state: {:?}", private, private_state);
|
||||
let contract_validators = self.get_validators(BlockId::Latest, &contract)?;
|
||||
trace!(target: "privatetx", "Required validators: {:?}", contract_validators);
|
||||
let private_state_hash = self.calculate_state_hash(&private_state, contract_nonce);
|
||||
trace!(target: "privatetx", "Hashed effective private state for sender: {:?}", private_state_hash);
|
||||
self.transactions_for_signing.write().add_transaction(private.hash(), signed_transaction, contract_validators, private_state, contract_nonce)?;
|
||||
self.broadcast_private_transaction(private.hash(), private.rlp_bytes());
|
||||
Ok(Receipt {
|
||||
hash: tx_hash,
|
||||
contract_address: Some(contract),
|
||||
status_code: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
let contract = Self::contract_address_from_transaction(&signed_transaction).map_err(|_| ErrorKind::BadTransactonType)?;
|
||||
let data = signed_transaction.rlp_bytes();
|
||||
let encrypted_transaction = self.encrypt(&contract, &Self::iv_from_transaction(&signed_transaction), &data)?;
|
||||
let private = PrivateTransaction::new(encrypted_transaction, contract);
|
||||
// TODO #9825 [ToDr] Using BlockId::Latest is bad here,
|
||||
// the block may change in the middle of execution
|
||||
// causing really weird stuff to happen.
|
||||
// We should retrieve hash and stick to that. IMHO
|
||||
// best would be to change the API and only allow H256 instead of BlockID
|
||||
// in private-tx to avoid such mistakes.
|
||||
let contract_nonce = self.get_contract_nonce(&contract, BlockId::Latest)?;
|
||||
let private_state = self.execute_private_transaction(BlockId::Latest, &signed_transaction)?;
|
||||
trace!(target: "privatetx", "Private transaction created, encrypted transaction: {:?}, private state: {:?}", private, private_state);
|
||||
let contract_validators = self.get_validators(BlockId::Latest, &contract)?;
|
||||
trace!(target: "privatetx", "Required validators: {:?}", contract_validators);
|
||||
let private_state_hash = self.calculate_state_hash(&private_state, contract_nonce);
|
||||
trace!(target: "privatetx", "Hashed effective private state for sender: {:?}", private_state_hash);
|
||||
self.transactions_for_signing.write().add_transaction(private.hash(), signed_transaction, contract_validators, private_state, contract_nonce)?;
|
||||
self.broadcast_private_transaction(private.hash(), private.rlp_bytes());
|
||||
Ok(Receipt {
|
||||
hash: tx_hash,
|
||||
contract_address: Some(contract),
|
||||
status_code: 0,
|
||||
})
|
||||
}
|
||||
|
||||
/// 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());
|
||||
return Ok(());
|
||||
}
|
||||
let tx_action = transaction.transaction.action.clone();
|
||||
if let Action::Call(contract) = tx_action {
|
||||
// TODO [ToDr] Usage of BlockId::Latest
|
||||
let contract_nonce = self.get_contract_nonce(&contract, BlockId::Latest);
|
||||
if let Err(e) = contract_nonce {
|
||||
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 = Self::contract_address_from_transaction(&transaction.transaction)
|
||||
.map_err(|_| "Incorrect type of action for the transaction")?;
|
||||
// TODO #9825 [ToDr] Usage of BlockId::Latest
|
||||
let contract_nonce = self.get_contract_nonce(&contract, BlockId::Latest);
|
||||
if let Err(e) = contract_nonce {
|
||||
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());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@ -328,7 +325,7 @@ impl Provider where {
|
||||
let mut signatures = desc.received_signatures.clone();
|
||||
signatures.push(signed_tx.signature());
|
||||
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(
|
||||
desc.state.clone(),
|
||||
&desc.original_transaction,
|
||||
@ -337,7 +334,7 @@ impl Provider where {
|
||||
desc.original_transaction.gas_price
|
||||
)?;
|
||||
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 hash = public_tx.hash(chain_id);
|
||||
let signer_account = self.signer_account.ok_or_else(|| ErrorKind::SignerAccountNotSet)?;
|
||||
@ -351,13 +348,22 @@ impl Provider where {
|
||||
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) {
|
||||
warn!(target: "privatetx", "Failed to remove transaction from signing store, error: {:?}", err);
|
||||
bail!(err);
|
||||
}
|
||||
} else {
|
||||
//Add signature to the store
|
||||
// Add signature to the store
|
||||
match self.transactions_for_signing.write().add_signature(&private_hash, signed_tx.signature()) {
|
||||
Ok(_) => trace!(target: "privatetx", "Signature stored for private transaction"),
|
||||
Err(err) => {
|
||||
@ -369,6 +375,16 @@ impl Provider where {
|
||||
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> {
|
||||
if desc.received_signatures.contains(&sign) {
|
||||
return Ok(false);
|
||||
@ -474,7 +490,7 @@ impl Provider where {
|
||||
env_info.gas_limit = transaction.gas;
|
||||
|
||||
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 {
|
||||
Action::Call(ref contract_address) => {
|
||||
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
|
||||
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();
|
||||
|
||||
Ok(H256::from_slice(&contract_address_extended))
|
||||
@ -615,6 +631,21 @@ impl Provider where {
|
||||
let value = self.client.call_contract(block, *address, data)?;
|
||||
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 {
|
||||
@ -644,12 +675,12 @@ impl Importer for Arc<Provider> {
|
||||
.iter()
|
||||
.find(|address| self.validator_accounts.contains(address));
|
||||
|
||||
//extract the original transaction
|
||||
// Extract the original transaction
|
||||
let encrypted_data = private_tx.encrypted();
|
||||
let transaction_bytes = self.decrypt(&contract, &encrypted_data)?;
|
||||
let original_tx: UnverifiedTransaction = Rlp::new(&transaction_bytes).as_val()?;
|
||||
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(
|
||||
original_tx,
|
||||
validation_account.map(|&account| account),
|
||||
|
Loading…
Reference in New Issue
Block a user