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 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))?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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"]
|
#![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) => {
|
trace!(target: "privatetx", "Private contract executed. code: {:?}, state: {:?}, result: {:?}", code, storage, result.output);
|
||||||
let (code, storage) = state.into_account(&address)?;
|
let enc_code = match code {
|
||||||
trace!(target: "privatetx", "Private contract executed. code: {:?}, state: {:?}, result: {:?}", code, storage, result.output);
|
Some(c) => Some(self.encrypt(&contract_address, &Self::iv_from_address(&contract_address), &c)?),
|
||||||
let enc_code = match code {
|
None => None,
|
||||||
Some(c) => Some(self.encrypt(&address, &Self::iv_from_address(&address), &c)?),
|
};
|
||||||
None => None,
|
(enc_code, self.encrypt(&contract_address, &Self::iv_from_transaction(transaction), &Self::snapshot_from_storage(&storage))?)
|
||||||
};
|
|
||||||
(enc_code, self.encrypt(&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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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()[..]));
|
||||||
|
}
|
@ -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,13 +128,18 @@ 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,
|
||||||
account_provider,
|
account_provider,
|
||||||
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());
|
||||||
|
@ -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());
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -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)
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -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(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user