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:
parent
a3e39c9858
commit
45d7c60608
43
ethcore/private-tx/res/keys_acl.json
Normal file
43
ethcore/private-tx/res/keys_acl.json
Normal 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"
|
||||
}
|
||||
]
|
@ -34,6 +34,7 @@ use bytes::{Bytes, ToPretty};
|
||||
use error::{Error, ErrorKind};
|
||||
use url::Url;
|
||||
use super::find_account_password;
|
||||
use super::key_server_keys::address_to_key;
|
||||
|
||||
/// Initialization vector length.
|
||||
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> {
|
||||
// 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 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))?)
|
||||
}
|
||||
}
|
||||
|
||||
|
173
ethcore/private-tx/src/key_server_keys.rs
Normal file
173
ethcore/private-tx/src/key_server_keys.rs
Normal 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());
|
||||
}
|
||||
}
|
@ -21,6 +21,7 @@
|
||||
#![recursion_limit="256"]
|
||||
|
||||
mod encryptor;
|
||||
mod key_server_keys;
|
||||
mod private_transactions;
|
||||
mod messages;
|
||||
mod error;
|
||||
@ -64,6 +65,7 @@ extern crate rand;
|
||||
extern crate env_logger;
|
||||
|
||||
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 messages::{PrivateTransaction, SignedPrivateTransaction};
|
||||
pub use error::{Error, ErrorKind};
|
||||
@ -87,6 +89,7 @@ use ethcore::client::{
|
||||
};
|
||||
use ethcore::account_provider::AccountProvider;
|
||||
use ethcore::miner::{self, Miner, MinerService, pool_client::NonceCache};
|
||||
use ethcore::{state, state_db};
|
||||
use ethcore::trace::{Tracer, VMTracer};
|
||||
use call_contract::CallContract;
|
||||
use rustc_hex::FromHex;
|
||||
@ -126,8 +129,8 @@ pub struct ProviderConfig {
|
||||
pub struct Receipt {
|
||||
/// Private transaction hash.
|
||||
pub hash: H256,
|
||||
/// Created contract address if any.
|
||||
pub contract_address: Option<Address>,
|
||||
/// Contract address.
|
||||
pub contract_address: Address,
|
||||
/// Execution status.
|
||||
pub status_code: u8,
|
||||
}
|
||||
@ -145,13 +148,14 @@ pub struct Provider {
|
||||
miner: Arc<Miner>,
|
||||
accounts: Arc<AccountProvider>,
|
||||
channel: IoChannel<ClientIoMessage>,
|
||||
keys_provider: Arc<KeyProvider>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PrivateExecutionResult<T, V> where T: Tracer, V: VMTracer {
|
||||
code: Option<Bytes>,
|
||||
state: Bytes,
|
||||
contract_address: Option<Address>,
|
||||
contract_address: Address,
|
||||
result: Executed<T::Output, V::Output>,
|
||||
}
|
||||
|
||||
@ -164,7 +168,9 @@ impl Provider where {
|
||||
encryptor: Box<Encryptor>,
|
||||
config: ProviderConfig,
|
||||
channel: IoChannel<ClientIoMessage>,
|
||||
keys_provider: Arc<KeyProvider>,
|
||||
) -> Self {
|
||||
keys_provider.update_acl_contract();
|
||||
Provider {
|
||||
encryptor,
|
||||
validator_accounts: config.validator_accounts.into_iter().collect(),
|
||||
@ -177,6 +183,7 @@ impl Provider where {
|
||||
miner,
|
||||
accounts,
|
||||
channel,
|
||||
keys_provider,
|
||||
}
|
||||
}
|
||||
|
||||
@ -227,7 +234,7 @@ impl Provider where {
|
||||
self.broadcast_private_transaction(private.hash(), private.rlp_bytes());
|
||||
Ok(Receipt {
|
||||
hash: tx_hash,
|
||||
contract_address: Some(contract),
|
||||
contract_address: contract,
|
||||
status_code: 0,
|
||||
})
|
||||
}
|
||||
@ -484,6 +491,14 @@ impl Provider where {
|
||||
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>
|
||||
where
|
||||
T: Tracer,
|
||||
@ -496,41 +511,48 @@ impl Provider where {
|
||||
// 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)?);
|
||||
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))?;
|
||||
// Patch current contract state
|
||||
self.patch_account_state(contract_address, block, &mut state)?;
|
||||
Some(*contract_address)
|
||||
},
|
||||
Action::Create => None,
|
||||
};
|
||||
|
||||
let engine = self.client.engine();
|
||||
let contract_address = contract_address.or({
|
||||
let sender = transaction.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);
|
||||
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 schedule = machine.schedule(env_info.number);
|
||||
let result = Executive::new(&mut state, &env_info, &machine, &schedule).transact_virtual(transaction, options)?;
|
||||
let (encrypted_code, encrypted_storage) = match contract_address {
|
||||
None => bail!(ErrorKind::ContractDoesNotExist),
|
||||
Some(address) => {
|
||||
let (code, storage) = state.into_account(&address)?;
|
||||
let (encrypted_code, encrypted_storage) = {
|
||||
let (code, storage) = state.into_account(&contract_address)?;
|
||||
trace!(target: "privatetx", "Private contract executed. code: {:?}, state: {:?}, result: {:?}", code, storage, result.output);
|
||||
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,
|
||||
};
|
||||
(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 {
|
||||
code: encrypted_code,
|
||||
state: encrypted_storage,
|
||||
contract_address,
|
||||
contract_address: contract_address,
|
||||
result,
|
||||
})
|
||||
}
|
||||
@ -555,14 +577,11 @@ 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
|
||||
let contract_address_extended: H256 = contract_address.into();
|
||||
|
||||
Ok(H256::from_slice(&contract_address_extended))
|
||||
Ok(key_server_keys::address_to_key(contract_address))
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
bail!(ErrorKind::BadTransactonType);
|
||||
}
|
||||
@ -740,5 +759,6 @@ impl ChainNotify for Provider {
|
||||
if let Err(err) = self.process_verification_queue() {
|
||||
warn!(target: "privatetx", "Cannot prune private transactions queue. error: {:?}", err);
|
||||
}
|
||||
self.keys_provider.update_acl_contract();
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ extern crate rustc_hex;
|
||||
extern crate log;
|
||||
|
||||
use std::sync::Arc;
|
||||
use rustc_hex::FromHex;
|
||||
use rustc_hex::{FromHex, ToHex};
|
||||
|
||||
use types::ids::BlockId;
|
||||
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 hash::keccak;
|
||||
|
||||
use ethcore_private_tx::{NoopEncryptor, Provider, ProviderConfig};
|
||||
use ethcore_private_tx::{NoopEncryptor, Provider, ProviderConfig, StoringKeyProvider};
|
||||
|
||||
#[test]
|
||||
fn private_contract() {
|
||||
@ -67,6 +67,7 @@ fn private_contract() {
|
||||
|
||||
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,
|
||||
@ -74,6 +75,7 @@ fn private_contract() {
|
||||
Box::new(NoopEncryptor::default()),
|
||||
config,
|
||||
io,
|
||||
private_keys,
|
||||
));
|
||||
|
||||
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();
|
||||
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()[..]));
|
||||
}
|
@ -99,6 +99,7 @@ impl ClientService {
|
||||
account_provider: Arc<AccountProvider>,
|
||||
encryptor: Box<ethcore_private_tx::Encryptor>,
|
||||
private_tx_conf: ethcore_private_tx::ProviderConfig,
|
||||
private_encryptor_conf: ethcore_private_tx::EncryptorConfig,
|
||||
) -> Result<ClientService, Error>
|
||||
{
|
||||
let io_service = IoService::<ClientIoMessage>::start()?;
|
||||
@ -127,6 +128,10 @@ impl ClientService {
|
||||
};
|
||||
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(
|
||||
client.clone(),
|
||||
miner,
|
||||
@ -134,6 +139,7 @@ impl ClientService {
|
||||
encryptor,
|
||||
private_tx_conf,
|
||||
io_service.channel(),
|
||||
private_keys,
|
||||
));
|
||||
let private_tx = Arc::new(PrivateTxService::new(provider));
|
||||
|
||||
@ -314,6 +320,7 @@ mod tests {
|
||||
Arc::new(AccountProvider::transient_provider()),
|
||||
Box::new(ethcore_private_tx::NoopEncryptor),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
);
|
||||
assert!(service.is_ok());
|
||||
drop(service.unwrap());
|
||||
|
@ -24,7 +24,7 @@ use ethcore::CreateContractAddress;
|
||||
use types::transaction::{Transaction, Action};
|
||||
use ethcore::executive::{contract_address};
|
||||
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 ethkey::{KeyPair};
|
||||
use tests::helpers::{TestNet, TestIoHandler};
|
||||
@ -78,6 +78,8 @@ fn send_private_transaction() {
|
||||
passwords: vec!["".into()],
|
||||
};
|
||||
|
||||
let private_keys = Arc::new(StoringKeyProvider::default());
|
||||
|
||||
let pm0 = Arc::new(Provider::new(
|
||||
client0.clone(),
|
||||
net.peer(0).miner.clone(),
|
||||
@ -85,6 +87,7 @@ fn send_private_transaction() {
|
||||
Box::new(NoopEncryptor::default()),
|
||||
signer_config,
|
||||
IoChannel::to_handler(Arc::downgrade(&io_handler0)),
|
||||
private_keys.clone(),
|
||||
));
|
||||
pm0.add_notify(net.peers[0].clone());
|
||||
|
||||
@ -95,6 +98,7 @@ fn send_private_transaction() {
|
||||
Box::new(NoopEncryptor::default()),
|
||||
validator_config,
|
||||
IoChannel::to_handler(Arc::downgrade(&io_handler1)),
|
||||
private_keys.clone(),
|
||||
));
|
||||
pm1.add_notify(net.peers[1].clone());
|
||||
|
||||
|
@ -398,6 +398,7 @@ fn execute_import(cmd: ImportBlockchain) -> Result<(), String> {
|
||||
Arc::new(AccountProvider::transient_provider()),
|
||||
Box::new(ethcore_private_tx::NoopEncryptor),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
).map_err(|e| format!("Client service error: {:?}", e))?;
|
||||
|
||||
// free up the spec in memory.
|
||||
@ -589,6 +590,7 @@ fn start_client(
|
||||
Arc::new(AccountProvider::transient_provider()),
|
||||
Box::new(ethcore_private_tx::NoopEncryptor),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
).map_err(|e| format!("Client service error: {:?}", e))?;
|
||||
|
||||
drop(spec);
|
||||
|
@ -582,8 +582,9 @@ fn execute_impl<Cr, Rr>(cmd: RunCmd, logger: Arc<RotatingLogger>, on_client_rq:
|
||||
&cmd.dirs.ipc_path(),
|
||||
miner.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_encryptor_conf,
|
||||
).map_err(|e| format!("Client service error: {:?}", e))?;
|
||||
|
||||
let connection_filter_address = spec.params().node_permission_contract;
|
||||
|
@ -202,6 +202,7 @@ impl SnapshotCommand {
|
||||
Arc::new(AccountProvider::transient_provider()),
|
||||
Box::new(ethcore_private_tx::NoopEncryptor),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
).map_err(|e| format!("Client service error: {:?}", e))?;
|
||||
|
||||
Ok(service)
|
||||
|
@ -94,7 +94,7 @@ impl Private for PrivateClient {
|
||||
transaction: request,
|
||||
receipt: PrivateTransactionReceipt {
|
||||
transaction_hash: tx_hash.into(),
|
||||
contract_address: contract_address.map(|address| address.into()),
|
||||
contract_address: contract_address.into(),
|
||||
status_code: 0,
|
||||
}
|
||||
})
|
||||
|
@ -24,7 +24,7 @@ pub struct PrivateTransactionReceipt {
|
||||
/// Transaction Hash
|
||||
pub transaction_hash: H256,
|
||||
/// Private contract address
|
||||
pub contract_address: Option<H160>,
|
||||
pub contract_address: H160,
|
||||
/// Status code
|
||||
#[serde(rename = "status")]
|
||||
pub status_code: u8,
|
||||
@ -34,7 +34,7 @@ impl From<EthPrivateReceipt> for PrivateTransactionReceipt {
|
||||
fn from(r: EthPrivateReceipt) -> Self {
|
||||
PrivateTransactionReceipt {
|
||||
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(),
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user