Call private contract methods from another private contract (read-only) (#10086)

* Patch available private contracts during private transaction

* Key acl ABI added

* Work with secret store keys moved to the separate struct

* Private tx test refactored

* Revert "Private tx test refactored"

This reverts commit 476c132d692c7a886bc7b7cd7fe47b3d7692bd63.

* Test for calling private contract from another one added

* Test fixed

* Redundant tab removed

* ACL contract processing fixed, test added

* Merge with head

* Expect replaced with closure
This commit is contained in:
Anton Gavrilov 2019-02-07 12:39:04 +01:00 committed by Afri Schoedon
parent a3e39c9858
commit 45d7c60608
12 changed files with 421 additions and 45 deletions

View File

@ -0,0 +1,43 @@
[
{
"constant": true,
"inputs": [
{
"name":"user",
"type":"address"
}
],
"name": "availableKeys",
"outputs": [
{
"name": "",
"type": "bytes32[]"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant":true,
"inputs": [
{
"name":"user",
"type":"address"
},
{
"name":"document",
"type":"bytes32"
}
],
"name":"checkPermissions",
"outputs": [
{
"name":"",
"type":"bool"
}
],
"payable":false,
"type":"function"
}
]

View File

@ -34,6 +34,7 @@ use bytes::{Bytes, ToPretty};
use error::{Error, ErrorKind}; use error::{Error, ErrorKind};
use url::Url; use url::Url;
use super::find_account_password; use super::find_account_password;
use super::key_server_keys::address_to_key;
/// Initialization vector length. /// Initialization vector length.
const INIT_VEC_LEN: usize = 16; const INIT_VEC_LEN: usize = 16;
@ -188,11 +189,9 @@ impl SecretStoreEncryptor {
} }
fn sign_contract_address(&self, contract_address: &Address, accounts: &AccountProvider) -> Result<Signature, Error> { fn sign_contract_address(&self, contract_address: &Address, accounts: &AccountProvider) -> Result<Signature, Error> {
// key id in SS is H256 && we have H160 here => expand with assitional zeros
let contract_address_extended: H256 = contract_address.into();
let key_server_account = self.config.key_server_account.ok_or_else(|| ErrorKind::KeyServerAccountNotSet)?; let key_server_account = self.config.key_server_account.ok_or_else(|| ErrorKind::KeyServerAccountNotSet)?;
let password = find_account_password(&self.config.passwords, accounts, &key_server_account); let password = find_account_password(&self.config.passwords, accounts, &key_server_account);
Ok(accounts.sign(key_server_account, password, H256::from_slice(&contract_address_extended))?) Ok(accounts.sign(key_server_account, password, address_to_key(contract_address))?)
} }
} }

View File

@ -0,0 +1,173 @@
// Copyright 2015-2018 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Wrapper around key server responsible for access keys processing.
use std::sync::Arc;
use parking_lot::RwLock;
use ethereum_types::{H256, Address};
use call_contract::{CallContract, RegistryInfo};
use ethcore::client::BlockId;
use ethabi::FunctionOutputDecoder;
const ACL_CHECKER_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_acl_checker";
use_contract!(keys_acl_contract, "res/keys_acl.json");
/// Returns the address (of the contract), that corresponds to the key
pub fn key_to_address(key: &H256) -> Address {
Address::from_slice(&key.to_vec()[..10])
}
/// Returns the key from the key server associated with the contract
pub fn address_to_key(contract_address: &Address) -> H256 {
// Current solution uses contract address extended with 0 as id
let contract_address_extended: H256 = contract_address.into();
H256::from_slice(&contract_address_extended)
}
/// Trait for keys server keys provider.
pub trait KeyProvider: Send + Sync + 'static {
/// Account, that is used for communication with key server
fn key_server_account(&self) -> Option<Address>;
/// List of keys available for the account
fn available_keys(&self, block: BlockId, account: &Address) -> Option<Vec<Address>>;
/// Update permissioning contract
fn update_acl_contract(&self);
}
/// Secret Store keys provider
pub struct SecretStoreKeys<C> where C: CallContract + RegistryInfo + Send + Sync + 'static {
client: Arc<C>,
key_server_account: Option<Address>,
keys_acl_contract: RwLock<Option<Address>>,
}
impl<C> SecretStoreKeys<C> where C: CallContract + RegistryInfo + Send + Sync + 'static {
/// Create provider
pub fn new(client: Arc<C>, key_server_account: Option<Address>) -> Self {
SecretStoreKeys {
client,
key_server_account,
keys_acl_contract: RwLock::new(None),
}
}
}
impl<C> KeyProvider for SecretStoreKeys<C> where C: CallContract + RegistryInfo + Send + Sync + 'static {
fn key_server_account(&self) -> Option<Address> {
self.key_server_account
}
fn available_keys(&self, block: BlockId, account: &Address) -> Option<Vec<Address>> {
match *self.keys_acl_contract.read() {
Some(acl_contract_address) => {
let (data, decoder) = keys_acl_contract::functions::available_keys::call(*account);
if let Ok(value) = self.client.call_contract(block, acl_contract_address, data) {
decoder.decode(&value).ok().map(|key_values| {
key_values.iter().map(key_to_address).collect()
})
} else {
None
}
}
None => None,
}
}
fn update_acl_contract(&self) {
let contract_address = self.client.registry_address(ACL_CHECKER_CONTRACT_REGISTRY_NAME.into(), BlockId::Latest);
if *self.keys_acl_contract.read() != contract_address {
trace!(target: "privatetx", "Configuring for ACL checker contract from address {:?}",
contract_address);
*self.keys_acl_contract.write() = contract_address;
}
}
}
/// Dummy keys provider.
pub struct StoringKeyProvider {
available_keys: RwLock<Option<Vec<Address>>>,
key_server_account: Option<Address>,
}
impl StoringKeyProvider {
/// Store available keys
pub fn set_available_keys(&self, keys: &Vec<Address>) {
*self.available_keys.write() = Some(keys.clone())
}
}
impl Default for StoringKeyProvider {
fn default() -> Self {
StoringKeyProvider {
available_keys: RwLock::new(None),
key_server_account: Some(Address::default()),
}
}
}
impl KeyProvider for StoringKeyProvider {
fn key_server_account(&self) -> Option<Address> {
self.key_server_account
}
fn available_keys(&self, _block: BlockId, _account: &Address) -> Option<Vec<Address>> {
self.available_keys.read().clone()
}
fn update_acl_contract(&self) {}
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use ethkey::{Secret, KeyPair};
use bytes::Bytes;
use super::*;
struct DummyRegistryClient {
registry_address: Option<Address>,
}
impl DummyRegistryClient {
pub fn new(registry_address: Option<Address>) -> Self {
DummyRegistryClient {
registry_address
}
}
}
impl RegistryInfo for DummyRegistryClient {
fn registry_address(&self, _name: String, _block: BlockId) -> Option<Address> { self.registry_address }
}
impl CallContract for DummyRegistryClient {
fn call_contract(&self, _id: BlockId, _address: Address, _data: Bytes) -> Result<Bytes, String> { Ok(vec![]) }
}
#[test]
fn should_update_acl_contract() {
let key = KeyPair::from_secret(Secret::from("0000000000000000000000000000000000000000000000000000000000000011")).unwrap();
let client = DummyRegistryClient::new(Some(key.address()));
let keys_data = SecretStoreKeys::new(Arc::new(client), None);
keys_data.update_acl_contract();
assert_eq!(keys_data.keys_acl_contract.read().unwrap(), key.address());
}
}

View File

@ -21,6 +21,7 @@
#![recursion_limit="256"] #![recursion_limit="256"]
mod encryptor; mod encryptor;
mod key_server_keys;
mod private_transactions; mod private_transactions;
mod messages; mod messages;
mod error; mod error;
@ -64,6 +65,7 @@ extern crate rand;
extern crate env_logger; extern crate env_logger;
pub use encryptor::{Encryptor, SecretStoreEncryptor, EncryptorConfig, NoopEncryptor}; pub use encryptor::{Encryptor, SecretStoreEncryptor, EncryptorConfig, NoopEncryptor};
pub use key_server_keys::{KeyProvider, SecretStoreKeys, StoringKeyProvider};
pub use private_transactions::{VerifiedPrivateTransaction, VerificationStore, PrivateTransactionSigningDesc, SigningStore}; pub use private_transactions::{VerifiedPrivateTransaction, VerificationStore, PrivateTransactionSigningDesc, SigningStore};
pub use messages::{PrivateTransaction, SignedPrivateTransaction}; pub use messages::{PrivateTransaction, SignedPrivateTransaction};
pub use error::{Error, ErrorKind}; pub use error::{Error, ErrorKind};
@ -87,6 +89,7 @@ use ethcore::client::{
}; };
use ethcore::account_provider::AccountProvider; use ethcore::account_provider::AccountProvider;
use ethcore::miner::{self, Miner, MinerService, pool_client::NonceCache}; use ethcore::miner::{self, Miner, MinerService, pool_client::NonceCache};
use ethcore::{state, state_db};
use ethcore::trace::{Tracer, VMTracer}; use ethcore::trace::{Tracer, VMTracer};
use call_contract::CallContract; use call_contract::CallContract;
use rustc_hex::FromHex; use rustc_hex::FromHex;
@ -126,8 +129,8 @@ pub struct ProviderConfig {
pub struct Receipt { pub struct Receipt {
/// Private transaction hash. /// Private transaction hash.
pub hash: H256, pub hash: H256,
/// Created contract address if any. /// Contract address.
pub contract_address: Option<Address>, pub contract_address: Address,
/// Execution status. /// Execution status.
pub status_code: u8, pub status_code: u8,
} }
@ -145,13 +148,14 @@ pub struct Provider {
miner: Arc<Miner>, miner: Arc<Miner>,
accounts: Arc<AccountProvider>, accounts: Arc<AccountProvider>,
channel: IoChannel<ClientIoMessage>, channel: IoChannel<ClientIoMessage>,
keys_provider: Arc<KeyProvider>,
} }
#[derive(Debug)] #[derive(Debug)]
pub struct PrivateExecutionResult<T, V> where T: Tracer, V: VMTracer { pub struct PrivateExecutionResult<T, V> where T: Tracer, V: VMTracer {
code: Option<Bytes>, code: Option<Bytes>,
state: Bytes, state: Bytes,
contract_address: Option<Address>, contract_address: Address,
result: Executed<T::Output, V::Output>, result: Executed<T::Output, V::Output>,
} }
@ -164,7 +168,9 @@ impl Provider where {
encryptor: Box<Encryptor>, encryptor: Box<Encryptor>,
config: ProviderConfig, config: ProviderConfig,
channel: IoChannel<ClientIoMessage>, channel: IoChannel<ClientIoMessage>,
keys_provider: Arc<KeyProvider>,
) -> Self { ) -> Self {
keys_provider.update_acl_contract();
Provider { Provider {
encryptor, encryptor,
validator_accounts: config.validator_accounts.into_iter().collect(), validator_accounts: config.validator_accounts.into_iter().collect(),
@ -177,6 +183,7 @@ impl Provider where {
miner, miner,
accounts, accounts,
channel, channel,
keys_provider,
} }
} }
@ -227,7 +234,7 @@ impl Provider where {
self.broadcast_private_transaction(private.hash(), private.rlp_bytes()); self.broadcast_private_transaction(private.hash(), private.rlp_bytes());
Ok(Receipt { Ok(Receipt {
hash: tx_hash, hash: tx_hash,
contract_address: Some(contract), contract_address: contract,
status_code: 0, status_code: 0,
}) })
} }
@ -484,6 +491,14 @@ impl Provider where {
raw raw
} }
fn patch_account_state(&self, contract_address: &Address, block: BlockId, state: &mut state::State<state_db::StateDB>) -> Result<(), Error> {
let contract_code = Arc::new(self.get_decrypted_code(contract_address, block)?);
let contract_state = self.get_decrypted_state(contract_address, block)?;
trace!(target: "privatetx", "Patching contract at {:?}, code: {:?}, state: {:?}", contract_address, contract_code, contract_state);
state.patch_account(contract_address, contract_code, Self::snapshot_to_storage(contract_state))?;
Ok(())
}
pub fn execute_private<T, V>(&self, transaction: &SignedTransaction, options: TransactOptions<T, V>, block: BlockId) -> Result<PrivateExecutionResult<T, V>, Error> pub fn execute_private<T, V>(&self, transaction: &SignedTransaction, options: TransactOptions<T, V>, block: BlockId) -> Result<PrivateExecutionResult<T, V>, Error>
where where
T: Tracer, T: Tracer,
@ -496,41 +511,48 @@ impl Provider where {
// TODO #9825 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)?); // Patch current contract state
let contract_state = self.get_decrypted_state(contract_address, block)?; self.patch_account_state(contract_address, block, &mut state)?;
trace!(target: "privatetx", "Patching contract at {:?}, code: {:?}, state: {:?}", contract_address, contract_code, contract_state);
state.patch_account(contract_address, contract_code, Self::snapshot_to_storage(contract_state))?;
Some(*contract_address) Some(*contract_address)
}, },
Action::Create => None, Action::Create => None,
}; };
let engine = self.client.engine(); let engine = self.client.engine();
let contract_address = contract_address.or({
let sender = transaction.sender(); let sender = transaction.sender();
let nonce = state.nonce(&sender)?; let nonce = state.nonce(&sender)?;
let contract_address = contract_address.unwrap_or_else(|| {
let (new_address, _) = ethcore_contract_address(engine.create_address_scheme(env_info.number), &sender, &nonce, &transaction.data); let (new_address, _) = ethcore_contract_address(engine.create_address_scheme(env_info.number), &sender, &nonce, &transaction.data);
Some(new_address) new_address
}); });
// Patch other available private contracts' states as well
// TODO: #10133 patch only required for the contract states
if let Some(key_server_account) = self.keys_provider.key_server_account() {
if let Some(available_contracts) = self.keys_provider.available_keys(block, &key_server_account) {
for private_contract in available_contracts {
if private_contract == contract_address {
continue;
}
self.patch_account_state(&private_contract, block, &mut state)?;
}
}
}
let machine = engine.machine(); let machine = engine.machine();
let schedule = machine.schedule(env_info.number); let schedule = machine.schedule(env_info.number);
let result = Executive::new(&mut state, &env_info, &machine, &schedule).transact_virtual(transaction, options)?; let result = Executive::new(&mut state, &env_info, &machine, &schedule).transact_virtual(transaction, options)?;
let (encrypted_code, encrypted_storage) = match contract_address { let (encrypted_code, encrypted_storage) = {
None => bail!(ErrorKind::ContractDoesNotExist), let (code, storage) = state.into_account(&contract_address)?;
Some(address) => {
let (code, storage) = state.into_account(&address)?;
trace!(target: "privatetx", "Private contract executed. code: {:?}, state: {:?}, result: {:?}", code, storage, result.output); trace!(target: "privatetx", "Private contract executed. code: {:?}, state: {:?}, result: {:?}", code, storage, result.output);
let enc_code = match code { let enc_code = match code {
Some(c) => Some(self.encrypt(&address, &Self::iv_from_address(&address), &c)?), Some(c) => Some(self.encrypt(&contract_address, &Self::iv_from_address(&contract_address), &c)?),
None => None, None => None,
}; };
(enc_code, self.encrypt(&address, &Self::iv_from_transaction(transaction), &Self::snapshot_from_storage(&storage))?) (enc_code, self.encrypt(&contract_address, &Self::iv_from_transaction(transaction), &Self::snapshot_from_storage(&storage))?)
},
}; };
Ok(PrivateExecutionResult { Ok(PrivateExecutionResult {
code: encrypted_code, code: encrypted_code,
state: encrypted_storage, state: encrypted_storage,
contract_address, contract_address: contract_address,
result, result,
}) })
} }
@ -555,14 +577,11 @@ 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 Ok(key_server_keys::address_to_key(contract_address))
let contract_address_extended: H256 = contract_address.into();
Ok(H256::from_slice(&contract_address_extended))
} }
/// Create encrypted public contract deployment transaction. /// Create encrypted public contract deployment transaction.
pub fn public_creation_transaction(&self, block: BlockId, source: &SignedTransaction, validators: &[Address], gas_price: U256) -> Result<(Transaction, Option<Address>), Error> { pub fn public_creation_transaction(&self, block: BlockId, source: &SignedTransaction, validators: &[Address], gas_price: U256) -> Result<(Transaction, Address), Error> {
if let Action::Call(_) = source.action { if let Action::Call(_) = source.action {
bail!(ErrorKind::BadTransactonType); bail!(ErrorKind::BadTransactonType);
} }
@ -740,5 +759,6 @@ impl ChainNotify for Provider {
if let Err(err) = self.process_verification_queue() { if let Err(err) = self.process_verification_queue() {
warn!(target: "privatetx", "Cannot prune private transactions queue. error: {:?}", err); warn!(target: "privatetx", "Cannot prune private transactions queue. error: {:?}", err);
} }
self.keys_provider.update_acl_contract();
} }
} }

View File

@ -29,7 +29,7 @@ extern crate rustc_hex;
extern crate log; extern crate log;
use std::sync::Arc; use std::sync::Arc;
use rustc_hex::FromHex; use rustc_hex::{FromHex, ToHex};
use types::ids::BlockId; use types::ids::BlockId;
use types::transaction::{Transaction, Action}; use types::transaction::{Transaction, Action};
@ -42,7 +42,7 @@ use ethcore::test_helpers::{generate_dummy_client, push_block_with_transactions}
use ethkey::{Secret, KeyPair, Signature}; use ethkey::{Secret, KeyPair, Signature};
use hash::keccak; use hash::keccak;
use ethcore_private_tx::{NoopEncryptor, Provider, ProviderConfig}; use ethcore_private_tx::{NoopEncryptor, Provider, ProviderConfig, StoringKeyProvider};
#[test] #[test]
fn private_contract() { fn private_contract() {
@ -67,6 +67,7 @@ fn private_contract() {
let io = ethcore_io::IoChannel::disconnected(); let io = ethcore_io::IoChannel::disconnected();
let miner = Arc::new(Miner::new_for_tests(&::ethcore::spec::Spec::new_test(), None)); let miner = Arc::new(Miner::new_for_tests(&::ethcore::spec::Spec::new_test(), None));
let private_keys = Arc::new(StoringKeyProvider::default());
let pm = Arc::new(Provider::new( let pm = Arc::new(Provider::new(
client.clone(), client.clone(),
miner, miner,
@ -74,6 +75,7 @@ fn private_contract() {
Box::new(NoopEncryptor::default()), Box::new(NoopEncryptor::default()),
config, config,
io, io,
private_keys,
)); ));
let (address, _) = contract_address(CreateContractAddress::FromSenderAndNonce, &key1.address(), &0.into(), &[]); let (address, _) = contract_address(CreateContractAddress::FromSenderAndNonce, &key1.address(), &0.into(), &[]);
@ -155,3 +157,127 @@ fn private_contract() {
let result = pm.private_call(BlockId::Latest, &query_tx).unwrap(); let result = pm.private_call(BlockId::Latest, &query_tx).unwrap();
assert_eq!(result.output, "2a00000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap()); assert_eq!(result.output, "2a00000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap());
} }
#[test]
fn call_other_private_contract() {
// This test verifies calls private contract methods from another one
// Two contract will be deployed
// The same contract A:
// contract Test1 {
// bytes32 public x;
// function setX(bytes32 _x) {
// x = _x;
// }
// }
// And the following contract B:
// contract Deployed {
// function setX(uint) {}
// function x() returns (uint) {}
//}
// contract Existing {
// Deployed dc;
// function Existing(address t) {
// dc = Deployed(t);
// }
// function getX() returns (uint) {
// return dc.x();
// }
// }
//ethcore_logger::init_log();
// Create client and provider
let client = generate_dummy_client(0);
let chain_id = client.signing_chain_id();
let key1 = KeyPair::from_secret(Secret::from("0000000000000000000000000000000000000000000000000000000000000011")).unwrap();
let _key2 = KeyPair::from_secret(Secret::from("0000000000000000000000000000000000000000000000000000000000000012")).unwrap();
let key3 = KeyPair::from_secret(Secret::from("0000000000000000000000000000000000000000000000000000000000000013")).unwrap();
let key4 = KeyPair::from_secret(Secret::from("0000000000000000000000000000000000000000000000000000000000000014")).unwrap();
let ap = Arc::new(AccountProvider::transient_provider());
ap.insert_account(key1.secret().clone(), &"".into()).unwrap();
ap.insert_account(key3.secret().clone(), &"".into()).unwrap();
ap.insert_account(key4.secret().clone(), &"".into()).unwrap();
let config = ProviderConfig{
validator_accounts: vec![key3.address(), key4.address()],
signer_account: None,
passwords: vec!["".into()],
};
let io = ethcore_io::IoChannel::disconnected();
let miner = Arc::new(Miner::new_for_tests(&::ethcore::spec::Spec::new_test(), None));
let private_keys = Arc::new(StoringKeyProvider::default());
let pm = Arc::new(Provider::new(
client.clone(),
miner,
ap.clone(),
Box::new(NoopEncryptor::default()),
config,
io,
private_keys.clone(),
));
// Deploy contract A
let (address_a, _) = contract_address(CreateContractAddress::FromSenderAndNonce, &key1.address(), &0.into(), &[]);
trace!("Creating private contract A");
let private_contract_a_test = "6060604052341561000f57600080fd5b60d88061001d6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630c55699c146046578063bc64b76d14607457600080fd5b3415605057600080fd5b60566098565b60405180826000191660001916815260200191505060405180910390f35b3415607e57600080fd5b6096600480803560001916906020019091905050609e565b005b60005481565b8060008160001916905550505600a165627a7a723058206acbdf4b15ca4c2d43e1b1879b830451a34f1e9d02ff1f2f394d8d857e79d2080029".from_hex().unwrap();
let mut private_create_tx1 = Transaction::default();
private_create_tx1.action = Action::Create;
private_create_tx1.data = private_contract_a_test;
private_create_tx1.gas = 200000.into();
private_create_tx1.nonce = 0.into();
let private_create_tx_signed = private_create_tx1.sign(&key1.secret(), None);
let validators = vec![key3.address(), key4.address()];
let (public_tx1, _) = pm.public_creation_transaction(BlockId::Latest, &private_create_tx_signed, &validators, 0.into()).unwrap();
let public_tx1 = public_tx1.sign(&key1.secret(), chain_id);
trace!("Transaction created. Pushing block");
push_block_with_transactions(&client, &[public_tx1]);
// Deploy contract B
let (address_b, _) = contract_address(CreateContractAddress::FromSenderAndNonce, &key1.address(), &1.into(), &[]);
trace!("Creating private contract B");
// Build constructor data
let mut deploy_data = "6060604052341561000f57600080fd5b6040516020806101c583398101604052808051906020019091905050806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505061014a8061007b6000396000f300606060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680635197c7aa14610046575b600080fd5b341561005157600080fd5b61005961006f565b6040518082815260200191505060405180910390f35b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16630c55699c6000604051602001526040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401602060405180830381600087803b15156100fe57600080fd5b6102c65a03f1151561010f57600080fd5b505050604051805190509050905600a165627a7a723058207f8994e02725b47d76ec73e5c54a338d27b306dd1c830276bff2d75fcd1a5c920029000000000000000000000000".to_string();
deploy_data.push_str(&address_a.to_vec().to_hex());
let private_contract_b_test = deploy_data.from_hex().unwrap();
let mut private_create_tx2 = Transaction::default();
private_create_tx2.action = Action::Create;
private_create_tx2.data = private_contract_b_test;
private_create_tx2.gas = 200000.into();
private_create_tx2.nonce = 1.into();
let private_create_tx_signed = private_create_tx2.sign(&key1.secret(), None);
let (public_tx2, _) = pm.public_creation_transaction(BlockId::Latest, &private_create_tx_signed, &validators, 0.into()).unwrap();
let public_tx2 = public_tx2.sign(&key1.secret(), chain_id);
trace!("Transaction created. Pushing block");
push_block_with_transactions(&client, &[public_tx2]);
// Let provider know, that it has access to both keys for A and B
private_keys.set_available_keys(&vec![address_a, address_b]);
// Call A.setx(42)
trace!("Modifying private state");
let mut private_tx = Transaction::default();
private_tx.action = Action::Call(address_a.clone());
private_tx.data = "bc64b76d2a00000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap(); //setX(42)
private_tx.gas = 120000.into();
private_tx.nonce = 2.into();
let private_tx = private_tx.sign(&key1.secret(), None);
let private_contract_nonce = pm.get_contract_nonce(&address_b, BlockId::Latest).unwrap();
let private_state = pm.execute_private_transaction(BlockId::Latest, &private_tx).unwrap();
let nonced_state_hash = pm.calculate_state_hash(&private_state, private_contract_nonce);
let signatures: Vec<_> = [&key3, &key4].iter().map(|k|
Signature::from(::ethkey::sign(&k.secret(), &nonced_state_hash).unwrap().into_electrum())).collect();
let public_tx = pm.public_transaction(private_state, &private_tx, &signatures, 2.into(), 0.into()).unwrap();
let public_tx = public_tx.sign(&key1.secret(), chain_id);
push_block_with_transactions(&client, &[public_tx]);
// Call B.getX()
trace!("Querying private state");
let mut query_tx = Transaction::default();
query_tx.action = Action::Call(address_b.clone());
query_tx.data = "5197c7aa".from_hex().unwrap(); // getX
query_tx.gas = 50000.into();
query_tx.nonce = 3.into();
let query_tx = query_tx.sign(&key1.secret(), chain_id);
let result = pm.private_call(BlockId::Latest, &query_tx).unwrap();
assert_eq!(&result.output[..], &("2a00000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap()[..]));
}

View File

@ -99,6 +99,7 @@ impl ClientService {
account_provider: Arc<AccountProvider>, account_provider: Arc<AccountProvider>,
encryptor: Box<ethcore_private_tx::Encryptor>, encryptor: Box<ethcore_private_tx::Encryptor>,
private_tx_conf: ethcore_private_tx::ProviderConfig, private_tx_conf: ethcore_private_tx::ProviderConfig,
private_encryptor_conf: ethcore_private_tx::EncryptorConfig,
) -> Result<ClientService, Error> ) -> Result<ClientService, Error>
{ {
let io_service = IoService::<ClientIoMessage>::start()?; let io_service = IoService::<ClientIoMessage>::start()?;
@ -127,6 +128,10 @@ impl ClientService {
}; };
let snapshot = Arc::new(SnapshotService::new(snapshot_params)?); let snapshot = Arc::new(SnapshotService::new(snapshot_params)?);
let private_keys = Arc::new(ethcore_private_tx::SecretStoreKeys::new(
client.clone(),
private_encryptor_conf.key_server_account,
));
let provider = Arc::new(ethcore_private_tx::Provider::new( let provider = Arc::new(ethcore_private_tx::Provider::new(
client.clone(), client.clone(),
miner, miner,
@ -134,6 +139,7 @@ impl ClientService {
encryptor, encryptor,
private_tx_conf, private_tx_conf,
io_service.channel(), io_service.channel(),
private_keys,
)); ));
let private_tx = Arc::new(PrivateTxService::new(provider)); let private_tx = Arc::new(PrivateTxService::new(provider));
@ -314,6 +320,7 @@ mod tests {
Arc::new(AccountProvider::transient_provider()), Arc::new(AccountProvider::transient_provider()),
Box::new(ethcore_private_tx::NoopEncryptor), Box::new(ethcore_private_tx::NoopEncryptor),
Default::default(), Default::default(),
Default::default(),
); );
assert!(service.is_ok()); assert!(service.is_ok());
drop(service.unwrap()); drop(service.unwrap());

View File

@ -24,7 +24,7 @@ use ethcore::CreateContractAddress;
use types::transaction::{Transaction, Action}; use types::transaction::{Transaction, Action};
use ethcore::executive::{contract_address}; use ethcore::executive::{contract_address};
use ethcore::test_helpers::{push_block_with_transactions}; use ethcore::test_helpers::{push_block_with_transactions};
use ethcore_private_tx::{Provider, ProviderConfig, NoopEncryptor, Importer, SignedPrivateTransaction}; use ethcore_private_tx::{Provider, ProviderConfig, NoopEncryptor, Importer, SignedPrivateTransaction, StoringKeyProvider};
use ethcore::account_provider::AccountProvider; use ethcore::account_provider::AccountProvider;
use ethkey::{KeyPair}; use ethkey::{KeyPair};
use tests::helpers::{TestNet, TestIoHandler}; use tests::helpers::{TestNet, TestIoHandler};
@ -78,6 +78,8 @@ fn send_private_transaction() {
passwords: vec!["".into()], passwords: vec!["".into()],
}; };
let private_keys = Arc::new(StoringKeyProvider::default());
let pm0 = Arc::new(Provider::new( let pm0 = Arc::new(Provider::new(
client0.clone(), client0.clone(),
net.peer(0).miner.clone(), net.peer(0).miner.clone(),
@ -85,6 +87,7 @@ fn send_private_transaction() {
Box::new(NoopEncryptor::default()), Box::new(NoopEncryptor::default()),
signer_config, signer_config,
IoChannel::to_handler(Arc::downgrade(&io_handler0)), IoChannel::to_handler(Arc::downgrade(&io_handler0)),
private_keys.clone(),
)); ));
pm0.add_notify(net.peers[0].clone()); pm0.add_notify(net.peers[0].clone());
@ -95,6 +98,7 @@ fn send_private_transaction() {
Box::new(NoopEncryptor::default()), Box::new(NoopEncryptor::default()),
validator_config, validator_config,
IoChannel::to_handler(Arc::downgrade(&io_handler1)), IoChannel::to_handler(Arc::downgrade(&io_handler1)),
private_keys.clone(),
)); ));
pm1.add_notify(net.peers[1].clone()); pm1.add_notify(net.peers[1].clone());

View File

@ -398,6 +398,7 @@ fn execute_import(cmd: ImportBlockchain) -> Result<(), String> {
Arc::new(AccountProvider::transient_provider()), Arc::new(AccountProvider::transient_provider()),
Box::new(ethcore_private_tx::NoopEncryptor), Box::new(ethcore_private_tx::NoopEncryptor),
Default::default(), Default::default(),
Default::default(),
).map_err(|e| format!("Client service error: {:?}", e))?; ).map_err(|e| format!("Client service error: {:?}", e))?;
// free up the spec in memory. // free up the spec in memory.
@ -589,6 +590,7 @@ fn start_client(
Arc::new(AccountProvider::transient_provider()), Arc::new(AccountProvider::transient_provider()),
Box::new(ethcore_private_tx::NoopEncryptor), Box::new(ethcore_private_tx::NoopEncryptor),
Default::default(), Default::default(),
Default::default(),
).map_err(|e| format!("Client service error: {:?}", e))?; ).map_err(|e| format!("Client service error: {:?}", e))?;
drop(spec); drop(spec);

View File

@ -582,8 +582,9 @@ fn execute_impl<Cr, Rr>(cmd: RunCmd, logger: Arc<RotatingLogger>, on_client_rq:
&cmd.dirs.ipc_path(), &cmd.dirs.ipc_path(),
miner.clone(), miner.clone(),
account_provider.clone(), account_provider.clone(),
Box::new(SecretStoreEncryptor::new(cmd.private_encryptor_conf, fetch.clone()).map_err(|e| e.to_string())?), Box::new(SecretStoreEncryptor::new(cmd.private_encryptor_conf.clone(), fetch.clone()).map_err(|e| e.to_string())?),
cmd.private_provider_conf, cmd.private_provider_conf,
cmd.private_encryptor_conf,
).map_err(|e| format!("Client service error: {:?}", e))?; ).map_err(|e| format!("Client service error: {:?}", e))?;
let connection_filter_address = spec.params().node_permission_contract; let connection_filter_address = spec.params().node_permission_contract;

View File

@ -202,6 +202,7 @@ impl SnapshotCommand {
Arc::new(AccountProvider::transient_provider()), Arc::new(AccountProvider::transient_provider()),
Box::new(ethcore_private_tx::NoopEncryptor), Box::new(ethcore_private_tx::NoopEncryptor),
Default::default(), Default::default(),
Default::default(),
).map_err(|e| format!("Client service error: {:?}", e))?; ).map_err(|e| format!("Client service error: {:?}", e))?;
Ok(service) Ok(service)

View File

@ -94,7 +94,7 @@ impl Private for PrivateClient {
transaction: request, transaction: request,
receipt: PrivateTransactionReceipt { receipt: PrivateTransactionReceipt {
transaction_hash: tx_hash.into(), transaction_hash: tx_hash.into(),
contract_address: contract_address.map(|address| address.into()), contract_address: contract_address.into(),
status_code: 0, status_code: 0,
} }
}) })

View File

@ -24,7 +24,7 @@ pub struct PrivateTransactionReceipt {
/// Transaction Hash /// Transaction Hash
pub transaction_hash: H256, pub transaction_hash: H256,
/// Private contract address /// Private contract address
pub contract_address: Option<H160>, pub contract_address: H160,
/// Status code /// Status code
#[serde(rename = "status")] #[serde(rename = "status")]
pub status_code: u8, pub status_code: u8,
@ -34,7 +34,7 @@ impl From<EthPrivateReceipt> for PrivateTransactionReceipt {
fn from(r: EthPrivateReceipt) -> Self { fn from(r: EthPrivateReceipt) -> Self {
PrivateTransactionReceipt { PrivateTransactionReceipt {
transaction_hash: r.hash.into(), transaction_hash: r.hash.into(),
contract_address: r.contract_address.map(Into::into), contract_address: r.contract_address.into(),
status_code: r.status_code.into(), status_code: r.status_code.into(),
} }
} }