Basic Authority (#991)
* Firt commit. * First non-functional but correct implementation of BasicAuthority. Still needs: - Sealing infrastructure. * Punch a hole to give miner access to key store. * Fix test built. * Basic version of synchronous mining. This will seal a block whenever a new transaction comes through. To be made better we need a timer which will wait for one second after the last block before sealing a new one - better still would be to cooperatively interleave blocks with other sealing nodes. * Add tests. * Fix minor issues from repotting. * Address grumbles.
This commit is contained in:
parent
1583f7d434
commit
ac73b2628a
@ -3,19 +3,18 @@
|
|||||||
"engine": {
|
"engine": {
|
||||||
"Ethash": {
|
"Ethash": {
|
||||||
"params": {
|
"params": {
|
||||||
"tieBreakingGas": false,
|
|
||||||
"gasLimitBoundDivisor": "0x0400",
|
"gasLimitBoundDivisor": "0x0400",
|
||||||
"minimumDifficulty": "0x020000",
|
"minimumDifficulty": "0x020000",
|
||||||
"difficultyBoundDivisor": "0x0800",
|
"difficultyBoundDivisor": "0x0800",
|
||||||
"durationLimit": "0x0d",
|
"durationLimit": "0x0d",
|
||||||
"blockReward": "0x4563918244F40000",
|
"blockReward": "0x4563918244F40000",
|
||||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
|
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||||
|
"frontierCompatibilityModeLimit": "0x118c30"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"params": {
|
"params": {
|
||||||
"accountStartNonce": "0x00",
|
"accountStartNonce": "0x00",
|
||||||
"frontierCompatibilityModeLimit": "0x118c30",
|
|
||||||
"maximumExtraDataSize": "0x20",
|
"maximumExtraDataSize": "0x20",
|
||||||
"minGasLimit": "0x1388",
|
"minGasLimit": "0x1388",
|
||||||
"networkID" : "0x1"
|
"networkID" : "0x1"
|
||||||
|
@ -3,19 +3,18 @@
|
|||||||
"engine": {
|
"engine": {
|
||||||
"Ethash": {
|
"Ethash": {
|
||||||
"params": {
|
"params": {
|
||||||
"tieBreakingGas": false,
|
|
||||||
"gasLimitBoundDivisor": "0x0400",
|
"gasLimitBoundDivisor": "0x0400",
|
||||||
"minimumDifficulty": "0x020000",
|
"minimumDifficulty": "0x020000",
|
||||||
"difficultyBoundDivisor": "0x0800",
|
"difficultyBoundDivisor": "0x0800",
|
||||||
"durationLimit": "0x0d",
|
"durationLimit": "0x0d",
|
||||||
"blockReward": "0x4563918244F40000",
|
"blockReward": "0x4563918244F40000",
|
||||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
|
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||||
|
"frontierCompatibilityModeLimit": "0x118c30"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"params": {
|
"params": {
|
||||||
"accountStartNonce": "0x00",
|
"accountStartNonce": "0x00",
|
||||||
"frontierCompatibilityModeLimit": "0x118c30",
|
|
||||||
"maximumExtraDataSize": "0x20",
|
"maximumExtraDataSize": "0x20",
|
||||||
"minGasLimit": "0x1388",
|
"minGasLimit": "0x1388",
|
||||||
"networkID" : "0x1"
|
"networkID" : "0x1"
|
||||||
|
@ -3,19 +3,18 @@
|
|||||||
"engine": {
|
"engine": {
|
||||||
"Ethash": {
|
"Ethash": {
|
||||||
"params": {
|
"params": {
|
||||||
"tieBreakingGas": false,
|
|
||||||
"gasLimitBoundDivisor": "0x0400",
|
"gasLimitBoundDivisor": "0x0400",
|
||||||
"minimumDifficulty": "0x020000",
|
"minimumDifficulty": "0x020000",
|
||||||
"difficultyBoundDivisor": "0x0800",
|
"difficultyBoundDivisor": "0x0800",
|
||||||
"durationLimit": "0x0d",
|
"durationLimit": "0x0d",
|
||||||
"blockReward": "0x4563918244F40000",
|
"blockReward": "0x4563918244F40000",
|
||||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
|
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||||
|
"frontierCompatibilityModeLimit": "0xffffffffffffffff"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"params": {
|
"params": {
|
||||||
"accountStartNonce": "0x00",
|
"accountStartNonce": "0x00",
|
||||||
"frontierCompatibilityModeLimit": "0xffffffffffffffff",
|
|
||||||
"maximumExtraDataSize": "0x20",
|
"maximumExtraDataSize": "0x20",
|
||||||
"minGasLimit": "0x1388",
|
"minGasLimit": "0x1388",
|
||||||
"networkID" : "0x1"
|
"networkID" : "0x1"
|
||||||
|
@ -3,19 +3,18 @@
|
|||||||
"engine": {
|
"engine": {
|
||||||
"Ethash": {
|
"Ethash": {
|
||||||
"params": {
|
"params": {
|
||||||
"tieBreakingGas": false,
|
|
||||||
"gasLimitBoundDivisor": "0x0400",
|
"gasLimitBoundDivisor": "0x0400",
|
||||||
"minimumDifficulty": "0x020000",
|
"minimumDifficulty": "0x020000",
|
||||||
"difficultyBoundDivisor": "0x0800",
|
"difficultyBoundDivisor": "0x0800",
|
||||||
"durationLimit": "0x0d",
|
"durationLimit": "0x0d",
|
||||||
"blockReward": "0x4563918244F40000",
|
"blockReward": "0x4563918244F40000",
|
||||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
|
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||||
|
"frontierCompatibilityModeLimit": 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"params": {
|
"params": {
|
||||||
"accountStartNonce": "0x00",
|
"accountStartNonce": "0x00",
|
||||||
"frontierCompatibilityModeLimit": 0,
|
|
||||||
"maximumExtraDataSize": "0x20",
|
"maximumExtraDataSize": "0x20",
|
||||||
"minGasLimit": "0x1388",
|
"minGasLimit": "0x1388",
|
||||||
"networkID" : "0x1"
|
"networkID" : "0x1"
|
||||||
|
@ -3,19 +3,18 @@
|
|||||||
"engine": {
|
"engine": {
|
||||||
"Ethash": {
|
"Ethash": {
|
||||||
"params": {
|
"params": {
|
||||||
"tieBreakingGas": false,
|
|
||||||
"gasLimitBoundDivisor": "0x0400",
|
"gasLimitBoundDivisor": "0x0400",
|
||||||
"minimumDifficulty": "0x020000",
|
"minimumDifficulty": "0x020000",
|
||||||
"difficultyBoundDivisor": "0x0800",
|
"difficultyBoundDivisor": "0x0800",
|
||||||
"durationLimit": "0x0d",
|
"durationLimit": "0x0d",
|
||||||
"blockReward": "0x4563918244F40000",
|
"blockReward": "0x4563918244F40000",
|
||||||
"registrar": ""
|
"registrar": "",
|
||||||
|
"frontierCompatibilityModeLimit": "0x789b0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"params": {
|
"params": {
|
||||||
"accountStartNonce": "0x0100000",
|
"accountStartNonce": "0x0100000",
|
||||||
"frontierCompatibilityModeLimit": "0x789b0",
|
|
||||||
"maximumExtraDataSize": "0x20",
|
"maximumExtraDataSize": "0x20",
|
||||||
"minGasLimit": "0x1388",
|
"minGasLimit": "0x1388",
|
||||||
"networkID" : "0x2"
|
"networkID" : "0x2"
|
||||||
|
@ -3,19 +3,18 @@
|
|||||||
"engine": {
|
"engine": {
|
||||||
"Ethash": {
|
"Ethash": {
|
||||||
"params": {
|
"params": {
|
||||||
"tieBreakingGas": false,
|
|
||||||
"gasLimitBoundDivisor": "0x0400",
|
"gasLimitBoundDivisor": "0x0400",
|
||||||
"minimumDifficulty": "0x020000",
|
"minimumDifficulty": "0x020000",
|
||||||
"difficultyBoundDivisor": "0x0800",
|
"difficultyBoundDivisor": "0x0800",
|
||||||
"durationLimit": "0x08",
|
"durationLimit": "0x08",
|
||||||
"blockReward": "0x14D1120D7B160000",
|
"blockReward": "0x14D1120D7B160000",
|
||||||
"registrar": "5e70c0bbcd5636e0f9f9316e9f8633feb64d4050"
|
"registrar": "5e70c0bbcd5636e0f9f9316e9f8633feb64d4050",
|
||||||
|
"frontierCompatibilityModeLimit": "0xffffffffffffffff"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"params": {
|
"params": {
|
||||||
"accountStartNonce": "0x00",
|
"accountStartNonce": "0x00",
|
||||||
"frontierCompatibilityModeLimit": "0xffffffffffffffff",
|
|
||||||
"maximumExtraDataSize": "0x0400",
|
"maximumExtraDataSize": "0x0400",
|
||||||
"minGasLimit": "125000",
|
"minGasLimit": "125000",
|
||||||
"networkID" : "0x0"
|
"networkID" : "0x0"
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
},
|
},
|
||||||
"params": {
|
"params": {
|
||||||
"accountStartNonce": "0x0100000",
|
"accountStartNonce": "0x0100000",
|
||||||
"frontierCompatibilityModeLimit": "0x789b0",
|
|
||||||
"maximumExtraDataSize": "0x20",
|
"maximumExtraDataSize": "0x20",
|
||||||
"minGasLimit": "0x1388",
|
"minGasLimit": "0x1388",
|
||||||
"networkID" : "0x2"
|
"networkID" : "0x2"
|
||||||
|
@ -1,20 +1,25 @@
|
|||||||
{
|
{
|
||||||
"name": "Morden",
|
"name": "TestAuthority",
|
||||||
"engine": {
|
"engine": {
|
||||||
"Null": null
|
"BasicAuthority": {
|
||||||
|
"params": {
|
||||||
|
"gasLimitBoundDivisor": "0x0400",
|
||||||
|
"durationLimit": "0x0d",
|
||||||
|
"authorities" : ["0x9cce34f7ab185c7aba1b7c8140d620b4bda941d6"]
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"params": {
|
"params": {
|
||||||
"accountStartNonce": "0x0100000",
|
"accountStartNonce": "0x0100000",
|
||||||
"frontierCompatibilityModeLimit": "0x0",
|
|
||||||
"maximumExtraDataSize": "0x20",
|
"maximumExtraDataSize": "0x20",
|
||||||
"minGasLimit": "0x1388",
|
"minGasLimit": "0x1388",
|
||||||
"networkID" : "0x2"
|
"networkID" : "0x69"
|
||||||
},
|
},
|
||||||
"genesis": {
|
"genesis": {
|
||||||
"seal": {
|
"seal": {
|
||||||
"ethereum": {
|
"generic": {
|
||||||
"nonce": "0x00006d6f7264656e",
|
"fields": 1,
|
||||||
"mixHash": "0x00000000000000000000000000000000000000647572616c65787365646c6578"
|
"rlp": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"difficulty": "0x20000",
|
"difficulty": "0x20000",
|
||||||
@ -29,6 +34,6 @@
|
|||||||
"0000000000000000000000000000000000000002": { "balance": "1", "nonce": "1048576", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } },
|
"0000000000000000000000000000000000000002": { "balance": "1", "nonce": "1048576", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } },
|
||||||
"0000000000000000000000000000000000000003": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } },
|
"0000000000000000000000000000000000000003": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } },
|
||||||
"0000000000000000000000000000000000000004": { "balance": "1", "nonce": "1048576", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } },
|
"0000000000000000000000000000000000000004": { "balance": "1", "nonce": "1048576", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } },
|
||||||
"102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c": { "balance": "1606938044258990275541962092341162602522202993782792835301376", "nonce": "1048576" }
|
"9cce34f7ab185c7aba1b7c8140d620b4bda941d6": { "balance": "1606938044258990275541962092341162602522202993782792835301376", "nonce": "1048576" }
|
||||||
}
|
}
|
||||||
}
|
}
|
297
ethcore/src/basic_authority.rs
Normal file
297
ethcore/src/basic_authority.rs
Normal file
@ -0,0 +1,297 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (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/>.
|
||||||
|
|
||||||
|
//! A blockchain engine that supports a basic, non-BFT proof-of-authority.
|
||||||
|
|
||||||
|
use common::*;
|
||||||
|
use util::keys::store::AccountProvider;
|
||||||
|
use block::*;
|
||||||
|
use spec::{CommonParams, Spec};
|
||||||
|
use engine::*;
|
||||||
|
use evm::{Schedule, Factory};
|
||||||
|
use ethjson;
|
||||||
|
|
||||||
|
/// BasicAuthority params.
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct BasicAuthorityParams {
|
||||||
|
/// Gas limit divisor.
|
||||||
|
pub gas_limit_bound_divisor: U256,
|
||||||
|
/// Block duration.
|
||||||
|
pub duration_limit: u64,
|
||||||
|
/// Valid signatories.
|
||||||
|
pub authorities: HashSet<Address>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ethjson::spec::BasicAuthorityParams> for BasicAuthorityParams {
|
||||||
|
fn from(p: ethjson::spec::BasicAuthorityParams) -> Self {
|
||||||
|
BasicAuthorityParams {
|
||||||
|
gas_limit_bound_divisor: p.gas_limit_bound_divisor.into(),
|
||||||
|
duration_limit: p.duration_limit.into(),
|
||||||
|
authorities: p.authorities.into_iter().map(Into::into).collect::<HashSet<_>>(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Engine using BasicAuthority proof-of-work consensus algorithm, suitable for Ethereum
|
||||||
|
/// mainnet chains in the Olympic, Frontier and Homestead eras.
|
||||||
|
pub struct BasicAuthority {
|
||||||
|
params: CommonParams,
|
||||||
|
our_params: BasicAuthorityParams,
|
||||||
|
builtins: BTreeMap<Address, Builtin>,
|
||||||
|
factory: Factory,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BasicAuthority {
|
||||||
|
/// Create a new instance of BasicAuthority engine
|
||||||
|
pub fn new(params: CommonParams, our_params: BasicAuthorityParams, builtins: BTreeMap<Address, Builtin>) -> Self {
|
||||||
|
BasicAuthority {
|
||||||
|
params: params,
|
||||||
|
our_params: our_params,
|
||||||
|
builtins: builtins,
|
||||||
|
factory: Factory::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Engine for BasicAuthority {
|
||||||
|
fn name(&self) -> &str { "BasicAuthority" }
|
||||||
|
fn version(&self) -> SemanticVersion { SemanticVersion::new(1, 0, 0) }
|
||||||
|
// One field - the signature
|
||||||
|
fn seal_fields(&self) -> usize { 1 }
|
||||||
|
|
||||||
|
fn params(&self) -> &CommonParams { &self.params }
|
||||||
|
fn builtins(&self) -> &BTreeMap<Address, Builtin> { &self.builtins }
|
||||||
|
|
||||||
|
/// Additional engine-specific information for the user/developer concerning `header`.
|
||||||
|
fn extra_info(&self, _header: &Header) -> HashMap<String, String> { hash_map!["signature".to_owned() => "TODO".to_owned()] }
|
||||||
|
|
||||||
|
fn vm_factory(&self) -> &Factory { &self.factory }
|
||||||
|
|
||||||
|
fn schedule(&self, _env_info: &EnvInfo) -> Schedule {
|
||||||
|
Schedule::new_homestead()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256) {
|
||||||
|
header.difficulty = parent.difficulty;
|
||||||
|
header.gas_limit = {
|
||||||
|
let gas_limit = parent.gas_limit;
|
||||||
|
let bound_divisor = self.our_params.gas_limit_bound_divisor;
|
||||||
|
if gas_limit < gas_floor_target {
|
||||||
|
min(gas_floor_target, gas_limit + gas_limit / bound_divisor - x!(1))
|
||||||
|
} else {
|
||||||
|
max(gas_floor_target, gas_limit - gas_limit / bound_divisor + x!(1))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
header.note_dirty();
|
||||||
|
// info!("ethash: populate_from_parent #{}: difficulty={} and gas_limit={}", header.number, header.difficulty, header.gas_limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Apply the block reward on finalisation of the block.
|
||||||
|
/// This assumes that all uncles are valid uncles (i.e. of at least one generation before the current).
|
||||||
|
fn on_close_block(&self, _block: &mut ExecutedBlock) {}
|
||||||
|
|
||||||
|
/// Attempt to seal the block internally.
|
||||||
|
///
|
||||||
|
/// This operation is synchronous and may (quite reasonably) not be available, in which `false` will
|
||||||
|
/// be returned.
|
||||||
|
fn generate_seal(&self, block: &ExecutedBlock, accounts: Option<&AccountProvider>) -> Option<Vec<Bytes>> {
|
||||||
|
if let Some(ap) = accounts {
|
||||||
|
// check to see if author is contained in self.our_params.authorities
|
||||||
|
if self.our_params.authorities.contains(block.header().author()) {
|
||||||
|
if let Ok(secret) = ap.account_secret(block.header().author()) {
|
||||||
|
return Some(block.header().author_seal(&secret));
|
||||||
|
} else {
|
||||||
|
trace!(target: "basicauthority", "generate_seal: FAIL: accounts secret key unavailable");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
trace!(target: "basicauthority", "generate_seal: FAIL: block author {} isn't one of the authorized accounts {:?}", block.header().author(), self.our_params.authorities);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
trace!(target: "basicauthority", "generate_seal: FAIL: accounts not provided");
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
|
||||||
|
// check the seal fields.
|
||||||
|
// TODO: pull this out into common code.
|
||||||
|
if header.seal.len() != self.seal_fields() {
|
||||||
|
return Err(From::from(BlockError::InvalidSealArity(
|
||||||
|
Mismatch { expected: self.seal_fields(), found: header.seal.len() }
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_block_unordered(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
|
||||||
|
// check the signature is legit.
|
||||||
|
let sig = try!(UntrustedRlp::new(&header.seal[0]).as_val::<H520>());
|
||||||
|
let signer = Address::from(try!(ec::recover(&sig, &header.bare_hash())).sha3());
|
||||||
|
if !self.our_params.authorities.contains(&signer) {
|
||||||
|
return try!(Err(BlockError::InvalidSeal));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
|
||||||
|
// we should not calculate difficulty for genesis blocks
|
||||||
|
if header.number() == 0 {
|
||||||
|
return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() })));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check difficulty is correct given the two timestamps.
|
||||||
|
if header.difficulty() != parent.difficulty() {
|
||||||
|
return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: *parent.difficulty(), found: *header.difficulty() })))
|
||||||
|
}
|
||||||
|
let gas_limit_divisor = self.our_params.gas_limit_bound_divisor;
|
||||||
|
let min_gas = parent.gas_limit - parent.gas_limit / gas_limit_divisor;
|
||||||
|
let max_gas = parent.gas_limit + parent.gas_limit / gas_limit_divisor;
|
||||||
|
if header.gas_limit <= min_gas || header.gas_limit >= max_gas {
|
||||||
|
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas), max: Some(max_gas), found: header.gas_limit })));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_transaction_basic(&self, t: &SignedTransaction, _header: &Header) -> result::Result<(), Error> {
|
||||||
|
try!(t.check_low_s());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_transaction(&self, t: &SignedTransaction, _header: &Header) -> Result<(), Error> {
|
||||||
|
t.sender().map(|_|()) // Perform EC recovery and cache sender
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Header {
|
||||||
|
/// Get the none field of the header.
|
||||||
|
pub fn signature(&self) -> H520 {
|
||||||
|
decode(&self.seal()[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate a seal for the block with the given `secret`.
|
||||||
|
pub fn author_seal(&self, secret: &Secret) -> Vec<Bytes> {
|
||||||
|
vec![encode(&ec::sign(secret, &self.bare_hash()).unwrap_or(Signature::new())).to_vec()]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the nonce and mix hash fields of the header.
|
||||||
|
pub fn sign(&mut self, secret: &Secret) {
|
||||||
|
self.seal = self.author_seal(secret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new test chain spec with BasicAuthority consensus engine.
|
||||||
|
pub fn new_test_authority() -> Spec { Spec::load(include_bytes!("../res/test_authority.json")) }
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use common::*;
|
||||||
|
use block::*;
|
||||||
|
use engine::*;
|
||||||
|
use tests::helpers::*;
|
||||||
|
use util::keys::{TestAccountProvider, TestAccount};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn has_valid_metadata() {
|
||||||
|
let engine = new_test_authority().engine;
|
||||||
|
assert!(!engine.name().is_empty());
|
||||||
|
assert!(engine.version().major >= 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_return_factory() {
|
||||||
|
let engine = new_test_authority().engine;
|
||||||
|
engine.vm_factory();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_return_schedule() {
|
||||||
|
let engine = new_test_authority().engine;
|
||||||
|
let schedule = engine.schedule(&EnvInfo {
|
||||||
|
number: 10000000,
|
||||||
|
author: x!(0),
|
||||||
|
timestamp: 0,
|
||||||
|
difficulty: x!(0),
|
||||||
|
last_hashes: vec![],
|
||||||
|
gas_used: x!(0),
|
||||||
|
gas_limit: x!(0)
|
||||||
|
});
|
||||||
|
|
||||||
|
assert!(schedule.stack_limit > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_do_seal_verification_fail() {
|
||||||
|
let engine = new_test_authority().engine;
|
||||||
|
let header: Header = Header::default();
|
||||||
|
|
||||||
|
let verify_result = engine.verify_block_basic(&header, None);
|
||||||
|
|
||||||
|
match verify_result {
|
||||||
|
Err(Error::Block(BlockError::InvalidSealArity(_))) => {},
|
||||||
|
Err(_) => { panic!("should be block seal-arity mismatch error (got {:?})", verify_result); },
|
||||||
|
_ => { panic!("Should be error, got Ok"); },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_do_signature_verification_fail() {
|
||||||
|
let engine = new_test_authority().engine;
|
||||||
|
let mut header: Header = Header::default();
|
||||||
|
header.set_seal(vec![rlp::encode(&Signature::zero()).to_vec()]);
|
||||||
|
|
||||||
|
let verify_result = engine.verify_block_unordered(&header, None);
|
||||||
|
|
||||||
|
match verify_result {
|
||||||
|
Err(Error::Util(UtilError::Crypto(CryptoError::InvalidSignature))) => {},
|
||||||
|
Err(_) => { panic!("should be block difficulty error (got {:?})", verify_result); },
|
||||||
|
_ => { panic!("Should be error, got Ok"); },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_do_signature_verification() {
|
||||||
|
let secret = "".sha3();
|
||||||
|
let addr = KeyPair::from_secret("".sha3()).unwrap().address();
|
||||||
|
|
||||||
|
let engine = new_test_authority().engine;
|
||||||
|
let mut header: Header = Header::default();
|
||||||
|
header.set_author(addr);
|
||||||
|
header.sign(&secret);
|
||||||
|
|
||||||
|
assert!(engine.verify_block_unordered(&header, None).is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_generate_seal() {
|
||||||
|
let addr = KeyPair::from_secret("".sha3()).unwrap().address();
|
||||||
|
let accounts = hash_map![addr => TestAccount{unlocked: true, password: Default::default(), secret: "".sha3()}];
|
||||||
|
let tap = TestAccountProvider::new(accounts);
|
||||||
|
|
||||||
|
let spec = new_test_authority();
|
||||||
|
let engine = &spec.engine;
|
||||||
|
let genesis_header = spec.genesis_header();
|
||||||
|
let mut db_result = get_temp_journal_db();
|
||||||
|
let mut db = db_result.take();
|
||||||
|
spec.ensure_db_good(db.as_hashdb_mut());
|
||||||
|
let last_hashes = vec![genesis_header.hash()];
|
||||||
|
let b = OpenBlock::new(engine.deref(), false, db, &genesis_header, last_hashes, addr.clone(), x!(3141562), vec![]);
|
||||||
|
let b = b.close_and_lock();
|
||||||
|
let seal = engine.generate_seal(b.block(), Some(&tap)).unwrap();
|
||||||
|
|
||||||
|
assert!(b.try_seal(engine.deref(), seal).is_ok());
|
||||||
|
}
|
||||||
|
}
|
@ -89,7 +89,7 @@ pub struct ExecutedBlock {
|
|||||||
/// A set of references to `ExecutedBlock` fields that are publicly accessible.
|
/// A set of references to `ExecutedBlock` fields that are publicly accessible.
|
||||||
pub struct BlockRefMut<'a> {
|
pub struct BlockRefMut<'a> {
|
||||||
/// Block header.
|
/// Block header.
|
||||||
pub header: &'a Header,
|
pub header: &'a mut Header,
|
||||||
/// Block transactions.
|
/// Block transactions.
|
||||||
pub transactions: &'a Vec<SignedTransaction>,
|
pub transactions: &'a Vec<SignedTransaction>,
|
||||||
/// Block uncles.
|
/// Block uncles.
|
||||||
@ -133,7 +133,7 @@ impl ExecutedBlock {
|
|||||||
/// Get a structure containing individual references to all public fields.
|
/// Get a structure containing individual references to all public fields.
|
||||||
pub fn fields_mut(&mut self) -> BlockRefMut {
|
pub fn fields_mut(&mut self) -> BlockRefMut {
|
||||||
BlockRefMut {
|
BlockRefMut {
|
||||||
header: &self.base.header,
|
header: &mut self.base.header,
|
||||||
transactions: &self.base.transactions,
|
transactions: &self.base.transactions,
|
||||||
uncles: &self.base.uncles,
|
uncles: &self.base.uncles,
|
||||||
state: &mut self.state,
|
state: &mut self.state,
|
||||||
|
@ -42,8 +42,7 @@ use env_info::EnvInfo;
|
|||||||
use executive::{Executive, Executed, TransactOptions, contract_address};
|
use executive::{Executive, Executed, TransactOptions, contract_address};
|
||||||
use receipt::LocalizedReceipt;
|
use receipt::LocalizedReceipt;
|
||||||
pub use blockchain::CacheSize as BlockChainCacheSize;
|
pub use blockchain::CacheSize as BlockChainCacheSize;
|
||||||
use trace::{TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase, Filter as
|
use trace::{TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase, Filter as TracedbFilter};
|
||||||
TracedbFilter};
|
|
||||||
use trace;
|
use trace;
|
||||||
|
|
||||||
/// General block status
|
/// General block status
|
||||||
|
@ -34,9 +34,10 @@ use std::collections::HashSet;
|
|||||||
use util::bytes::Bytes;
|
use util::bytes::Bytes;
|
||||||
use util::hash::{Address, H256, H2048};
|
use util::hash::{Address, H256, H2048};
|
||||||
use util::numbers::U256;
|
use util::numbers::U256;
|
||||||
|
use util::keys::store::AccountProvider;
|
||||||
use blockchain::TreeRoute;
|
use blockchain::TreeRoute;
|
||||||
use block_queue::BlockQueueInfo;
|
use block_queue::BlockQueueInfo;
|
||||||
use block::{ClosedBlock, LockedBlock, SealedBlock};
|
use block::{ExecutedBlock, ClosedBlock, LockedBlock, SealedBlock};
|
||||||
use header::{BlockNumber, Header};
|
use header::{BlockNumber, Header};
|
||||||
use transaction::{LocalizedTransaction, SignedTransaction};
|
use transaction::{LocalizedTransaction, SignedTransaction};
|
||||||
use log_entry::LocalizedLogEntry;
|
use log_entry::LocalizedLogEntry;
|
||||||
@ -134,6 +135,9 @@ pub trait BlockChainClient : Sync + Send {
|
|||||||
/// Makes a non-persistent transaction call.
|
/// Makes a non-persistent transaction call.
|
||||||
fn call(&self, t: &SignedTransaction) -> Result<Executed, Error>;
|
fn call(&self, t: &SignedTransaction) -> Result<Executed, Error>;
|
||||||
|
|
||||||
|
/// Attempt to seal the block internally. See `Engine`.
|
||||||
|
fn generate_seal(&self, block: &ExecutedBlock, accounts: Option<&AccountProvider>) -> Option<Vec<Bytes>> { self.engine().generate_seal(block, accounts) }
|
||||||
|
|
||||||
/// Executes a function providing it with a reference to an engine.
|
/// Executes a function providing it with a reference to an engine.
|
||||||
fn engine(&self) -> &Engine;
|
fn engine(&self) -> &Engine;
|
||||||
|
|
||||||
|
@ -15,10 +15,10 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use common::*;
|
use common::*;
|
||||||
|
use util::keys::store::AccountProvider;
|
||||||
use block::ExecutedBlock;
|
use block::ExecutedBlock;
|
||||||
use spec::CommonParams;
|
use spec::CommonParams;
|
||||||
use evm::Schedule;
|
use evm::{Schedule, Factory};
|
||||||
use evm::Factory;
|
|
||||||
|
|
||||||
/// A consensus mechanism for the chain. Generally either proof-of-work or proof-of-stake-based.
|
/// A consensus mechanism for the chain. Generally either proof-of-work or proof-of-stake-based.
|
||||||
/// Provides hooks into each of the major parts of block import.
|
/// Provides hooks into each of the major parts of block import.
|
||||||
@ -61,6 +61,14 @@ pub trait Engine : Sync + Send {
|
|||||||
/// Block transformation functions, after the transactions.
|
/// Block transformation functions, after the transactions.
|
||||||
fn on_close_block(&self, _block: &mut ExecutedBlock) {}
|
fn on_close_block(&self, _block: &mut ExecutedBlock) {}
|
||||||
|
|
||||||
|
/// Attempt to seal the block internally.
|
||||||
|
///
|
||||||
|
/// If `Some` is returned, then you get a valid seal.
|
||||||
|
///
|
||||||
|
/// This operation is synchronous and may (quite reasonably) not be available, in which None will
|
||||||
|
/// be returned.
|
||||||
|
fn generate_seal(&self, _block: &ExecutedBlock, _accounts: Option<&AccountProvider>) -> Option<Vec<Bytes>> { None }
|
||||||
|
|
||||||
/// Phase 1 quick block verification. Only does checks that are cheap. `block` (the header's full block)
|
/// Phase 1 quick block verification. Only does checks that are cheap. `block` (the header's full block)
|
||||||
/// may be provided for additional checks. Returns either a null `Ok` or a general error detailing the problem with import.
|
/// may be provided for additional checks. Returns either a null `Ok` or a general error detailing the problem with import.
|
||||||
fn verify_block_basic(&self, _header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { Ok(()) }
|
fn verify_block_basic(&self, _header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { Ok(()) }
|
||||||
|
@ -137,6 +137,8 @@ pub enum BlockError {
|
|||||||
MismatchedH256SealElement(Mismatch<H256>),
|
MismatchedH256SealElement(Mismatch<H256>),
|
||||||
/// Proof-of-work aspect of seal, which we assume is a 256-bit value, is invalid.
|
/// Proof-of-work aspect of seal, which we assume is a 256-bit value, is invalid.
|
||||||
InvalidProofOfWork(OutOfBounds<U256>),
|
InvalidProofOfWork(OutOfBounds<U256>),
|
||||||
|
/// Some low-level aspect of the seal in incorrect.
|
||||||
|
InvalidSeal,
|
||||||
/// Gas limit header field is invalid.
|
/// Gas limit header field is invalid.
|
||||||
InvalidGasLimit(OutOfBounds<U256>),
|
InvalidGasLimit(OutOfBounds<U256>),
|
||||||
/// Receipts trie root header field is invalid.
|
/// Receipts trie root header field is invalid.
|
||||||
|
@ -27,8 +27,6 @@ use ethjson;
|
|||||||
/// Ethash params.
|
/// Ethash params.
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct EthashParams {
|
pub struct EthashParams {
|
||||||
/// Tie breaking gas.
|
|
||||||
pub tie_breaking_gas: bool,
|
|
||||||
/// Gas limit divisor.
|
/// Gas limit divisor.
|
||||||
pub gas_limit_bound_divisor: U256,
|
pub gas_limit_bound_divisor: U256,
|
||||||
/// Minimum difficulty.
|
/// Minimum difficulty.
|
||||||
@ -41,18 +39,20 @@ pub struct EthashParams {
|
|||||||
pub block_reward: U256,
|
pub block_reward: U256,
|
||||||
/// Namereg contract address.
|
/// Namereg contract address.
|
||||||
pub registrar: Address,
|
pub registrar: Address,
|
||||||
|
/// Homestead transition block number.
|
||||||
|
pub frontier_compatibility_mode_limit: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ethjson::spec::EthashParams> for EthashParams {
|
impl From<ethjson::spec::EthashParams> for EthashParams {
|
||||||
fn from(p: ethjson::spec::EthashParams) -> Self {
|
fn from(p: ethjson::spec::EthashParams) -> Self {
|
||||||
EthashParams {
|
EthashParams {
|
||||||
tie_breaking_gas: p.tie_breaking_gas,
|
|
||||||
gas_limit_bound_divisor: p.gas_limit_bound_divisor.into(),
|
gas_limit_bound_divisor: p.gas_limit_bound_divisor.into(),
|
||||||
minimum_difficulty: p.minimum_difficulty.into(),
|
minimum_difficulty: p.minimum_difficulty.into(),
|
||||||
difficulty_bound_divisor: p.difficulty_bound_divisor.into(),
|
difficulty_bound_divisor: p.difficulty_bound_divisor.into(),
|
||||||
duration_limit: p.duration_limit.into(),
|
duration_limit: p.duration_limit.into(),
|
||||||
block_reward: p.block_reward.into(),
|
block_reward: p.block_reward.into(),
|
||||||
registrar: p.registrar.into(),
|
registrar: p.registrar.into(),
|
||||||
|
frontier_compatibility_mode_limit: p.frontier_compatibility_mode_limit.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -100,9 +100,9 @@ impl Engine for Ethash {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn schedule(&self, env_info: &EnvInfo) -> Schedule {
|
fn schedule(&self, env_info: &EnvInfo) -> Schedule {
|
||||||
trace!(target: "client", "Creating schedule. fCML={}", self.params.frontier_compatibility_mode_limit);
|
trace!(target: "client", "Creating schedule. fCML={}", self.ethash_params.frontier_compatibility_mode_limit);
|
||||||
|
|
||||||
if env_info.number < self.params.frontier_compatibility_mode_limit {
|
if env_info.number < self.ethash_params.frontier_compatibility_mode_limit {
|
||||||
Schedule::new_frontier()
|
Schedule::new_frontier()
|
||||||
} else {
|
} else {
|
||||||
Schedule::new_homestead()
|
Schedule::new_homestead()
|
||||||
@ -207,7 +207,7 @@ impl Engine for Ethash {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn verify_transaction_basic(&self, t: &SignedTransaction, header: &Header) -> result::Result<(), Error> {
|
fn verify_transaction_basic(&self, t: &SignedTransaction, header: &Header) -> result::Result<(), Error> {
|
||||||
if header.number() >= self.params.frontier_compatibility_mode_limit {
|
if header.number() >= self.ethash_params.frontier_compatibility_mode_limit {
|
||||||
try!(t.check_low_s());
|
try!(t.check_low_s());
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -229,7 +229,7 @@ impl Ethash {
|
|||||||
let min_difficulty = self.ethash_params.minimum_difficulty;
|
let min_difficulty = self.ethash_params.minimum_difficulty;
|
||||||
let difficulty_bound_divisor = self.ethash_params.difficulty_bound_divisor;
|
let difficulty_bound_divisor = self.ethash_params.difficulty_bound_divisor;
|
||||||
let duration_limit = self.ethash_params.duration_limit;
|
let duration_limit = self.ethash_params.duration_limit;
|
||||||
let frontier_limit = self.params.frontier_compatibility_mode_limit;
|
let frontier_limit = self.ethash_params.frontier_compatibility_mode_limit;
|
||||||
|
|
||||||
let mut target = if header.number < frontier_limit {
|
let mut target = if header.number < frontier_limit {
|
||||||
if header.timestamp >= parent.timestamp + duration_limit {
|
if header.timestamp >= parent.timestamp + duration_limit {
|
||||||
|
@ -89,6 +89,7 @@ extern crate bloomchain;
|
|||||||
#[cfg(test)] extern crate ethcore_devtools as devtools;
|
#[cfg(test)] extern crate ethcore_devtools as devtools;
|
||||||
#[cfg(feature = "jit" )] extern crate evmjit;
|
#[cfg(feature = "jit" )] extern crate evmjit;
|
||||||
|
|
||||||
|
pub mod basic_authority;
|
||||||
pub mod block;
|
pub mod block;
|
||||||
pub mod block_queue;
|
pub mod block_queue;
|
||||||
pub mod client;
|
pub mod client;
|
||||||
|
@ -57,11 +57,7 @@ impl Engine for NullEngine {
|
|||||||
&self.builtins
|
&self.builtins
|
||||||
}
|
}
|
||||||
|
|
||||||
fn schedule(&self, env_info: &EnvInfo) -> Schedule {
|
fn schedule(&self, _env_info: &EnvInfo) -> Schedule {
|
||||||
if env_info.number < self.params.frontier_compatibility_mode_limit {
|
Schedule::new_homestead()
|
||||||
Schedule::new_frontier()
|
|
||||||
} else {
|
|
||||||
Schedule::new_homestead()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ use account_db::*;
|
|||||||
use super::genesis::Genesis;
|
use super::genesis::Genesis;
|
||||||
use super::seal::Generic as GenericSeal;
|
use super::seal::Generic as GenericSeal;
|
||||||
use ethereum;
|
use ethereum;
|
||||||
|
use basic_authority::BasicAuthority;
|
||||||
use ethjson;
|
use ethjson;
|
||||||
|
|
||||||
/// Parameters common to all engines.
|
/// Parameters common to all engines.
|
||||||
@ -31,8 +32,6 @@ use ethjson;
|
|||||||
pub struct CommonParams {
|
pub struct CommonParams {
|
||||||
/// Account start nonce.
|
/// Account start nonce.
|
||||||
pub account_start_nonce: U256,
|
pub account_start_nonce: U256,
|
||||||
/// Frontier compatibility mode limit.
|
|
||||||
pub frontier_compatibility_mode_limit: u64,
|
|
||||||
/// Maximum size of extra data.
|
/// Maximum size of extra data.
|
||||||
pub maximum_extra_data_size: usize,
|
pub maximum_extra_data_size: usize,
|
||||||
/// Network id.
|
/// Network id.
|
||||||
@ -45,7 +44,6 @@ impl From<ethjson::spec::Params> for CommonParams {
|
|||||||
fn from(p: ethjson::spec::Params) -> Self {
|
fn from(p: ethjson::spec::Params) -> Self {
|
||||||
CommonParams {
|
CommonParams {
|
||||||
account_start_nonce: p.account_start_nonce.into(),
|
account_start_nonce: p.account_start_nonce.into(),
|
||||||
frontier_compatibility_mode_limit: p.frontier_compatibility_mode_limit.into(),
|
|
||||||
maximum_extra_data_size: p.maximum_extra_data_size.into(),
|
maximum_extra_data_size: p.maximum_extra_data_size.into(),
|
||||||
network_id: p.network_id.into(),
|
network_id: p.network_id.into(),
|
||||||
min_gas_limit: p.min_gas_limit.into(),
|
min_gas_limit: p.min_gas_limit.into(),
|
||||||
@ -131,7 +129,8 @@ impl Spec {
|
|||||||
fn engine(engine_spec: ethjson::spec::Engine, params: CommonParams, builtins: BTreeMap<Address, Builtin>) -> Box<Engine> {
|
fn engine(engine_spec: ethjson::spec::Engine, params: CommonParams, builtins: BTreeMap<Address, Builtin>) -> Box<Engine> {
|
||||||
match engine_spec {
|
match engine_spec {
|
||||||
ethjson::spec::Engine::Null => Box::new(NullEngine::new(params, builtins)),
|
ethjson::spec::Engine::Null => Box::new(NullEngine::new(params, builtins)),
|
||||||
ethjson::spec::Engine::Ethash(ethash) => Box::new(ethereum::Ethash::new(params, From::from(ethash.params), builtins))
|
ethjson::spec::Engine::Ethash(ethash) => Box::new(ethereum::Ethash::new(params, From::from(ethash.params), builtins)),
|
||||||
|
ethjson::spec::Engine::BasicAuthority(basic_authority) => Box::new(BasicAuthority::new(params, From::from(basic_authority.params), builtins)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,15 +240,10 @@ impl Spec {
|
|||||||
From::from(ethjson::spec::Spec::load(reader).expect("invalid json file"))
|
From::from(ethjson::spec::Spec::load(reader).expect("invalid json file"))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new Spec which conforms to the Morden chain except that it's a NullEngine consensus.
|
/// Create a new Spec which conforms to the Frontier-era Morden chain except that it's a NullEngine consensus.
|
||||||
pub fn new_test() -> Spec {
|
pub fn new_test() -> Spec {
|
||||||
Spec::load(include_bytes!("../../res/null_morden.json"))
|
Spec::load(include_bytes!("../../res/null_morden.json"))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new Spec which conforms to the Morden chain except that it's a NullEngine consensus.
|
|
||||||
pub fn new_homestead_test() -> Spec {
|
|
||||||
Spec::load(include_bytes!("../../res/null_homestead_morden.json"))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
59
json/src/spec/basic_authority.rs
Normal file
59
json/src/spec/basic_authority.rs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (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/>.
|
||||||
|
|
||||||
|
//! Ethash params deserialization.
|
||||||
|
|
||||||
|
use uint::Uint;
|
||||||
|
use hash::Address;
|
||||||
|
|
||||||
|
/// Ethash params deserialization.
|
||||||
|
#[derive(Debug, PartialEq, Deserialize)]
|
||||||
|
pub struct BasicAuthorityParams {
|
||||||
|
/// Gas limit divisor.
|
||||||
|
#[serde(rename="gasLimitBoundDivisor")]
|
||||||
|
pub gas_limit_bound_divisor: Uint,
|
||||||
|
/// Block duration.
|
||||||
|
#[serde(rename="durationLimit")]
|
||||||
|
pub duration_limit: Uint,
|
||||||
|
/// Valid authorities
|
||||||
|
pub authorities: Vec<Address>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ethash engine deserialization.
|
||||||
|
#[derive(Debug, PartialEq, Deserialize)]
|
||||||
|
pub struct BasicAuthority {
|
||||||
|
/// Ethash params.
|
||||||
|
pub params: BasicAuthorityParams,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use serde_json;
|
||||||
|
use spec::basic_authority::BasicAuthority;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn basic_authority_deserialization() {
|
||||||
|
let s = r#"{
|
||||||
|
"params": {
|
||||||
|
"gasLimitBoundDivisor": "0x0400",
|
||||||
|
"durationLimit": "0x0d",
|
||||||
|
"authorities" : ["0xc6d9d2cd449a754c494264e1809c50e34d64562b"]
|
||||||
|
}
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
let _deserialized: BasicAuthority = serde_json::from_str(s).unwrap();
|
||||||
|
}
|
||||||
|
}
|
@ -19,6 +19,7 @@
|
|||||||
use serde::Deserializer;
|
use serde::Deserializer;
|
||||||
use serde::de::Visitor;
|
use serde::de::Visitor;
|
||||||
use spec::Ethash;
|
use spec::Ethash;
|
||||||
|
use spec::BasicAuthority;
|
||||||
|
|
||||||
/// Engine deserialization.
|
/// Engine deserialization.
|
||||||
#[derive(Debug, PartialEq, Deserialize)]
|
#[derive(Debug, PartialEq, Deserialize)]
|
||||||
@ -27,6 +28,8 @@ pub enum Engine {
|
|||||||
Null,
|
Null,
|
||||||
/// Ethash engine.
|
/// Ethash engine.
|
||||||
Ethash(Ethash),
|
Ethash(Ethash),
|
||||||
|
/// BasicAuthority engine.
|
||||||
|
BasicAuthority(BasicAuthority),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -46,13 +49,13 @@ mod tests {
|
|||||||
let s = r#"{
|
let s = r#"{
|
||||||
"Ethash": {
|
"Ethash": {
|
||||||
"params": {
|
"params": {
|
||||||
"tieBreakingGas": false,
|
|
||||||
"gasLimitBoundDivisor": "0x0400",
|
"gasLimitBoundDivisor": "0x0400",
|
||||||
"minimumDifficulty": "0x020000",
|
"minimumDifficulty": "0x020000",
|
||||||
"difficultyBoundDivisor": "0x0800",
|
"difficultyBoundDivisor": "0x0800",
|
||||||
"durationLimit": "0x0d",
|
"durationLimit": "0x0d",
|
||||||
"blockReward": "0x4563918244F40000",
|
"blockReward": "0x4563918244F40000",
|
||||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
|
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||||
|
"frontierCompatibilityModeLimit" : "0x"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}"#;
|
}"#;
|
||||||
|
@ -22,9 +22,6 @@ use hash::Address;
|
|||||||
/// Ethash params deserialization.
|
/// Ethash params deserialization.
|
||||||
#[derive(Debug, PartialEq, Deserialize)]
|
#[derive(Debug, PartialEq, Deserialize)]
|
||||||
pub struct EthashParams {
|
pub struct EthashParams {
|
||||||
/// Tie breaking gas.
|
|
||||||
#[serde(rename="tieBreakingGas")]
|
|
||||||
pub tie_breaking_gas: bool,
|
|
||||||
/// Gas limit divisor.
|
/// Gas limit divisor.
|
||||||
#[serde(rename="gasLimitBoundDivisor")]
|
#[serde(rename="gasLimitBoundDivisor")]
|
||||||
pub gas_limit_bound_divisor: Uint,
|
pub gas_limit_bound_divisor: Uint,
|
||||||
@ -42,6 +39,9 @@ pub struct EthashParams {
|
|||||||
pub block_reward: Uint,
|
pub block_reward: Uint,
|
||||||
/// Namereg contract address.
|
/// Namereg contract address.
|
||||||
pub registrar: Address,
|
pub registrar: Address,
|
||||||
|
/// Homestead transition block number.
|
||||||
|
#[serde(rename="frontierCompatibilityModeLimit")]
|
||||||
|
pub frontier_compatibility_mode_limit: Uint,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ethash engine deserialization.
|
/// Ethash engine deserialization.
|
||||||
@ -60,13 +60,13 @@ mod tests {
|
|||||||
fn ethash_deserialization() {
|
fn ethash_deserialization() {
|
||||||
let s = r#"{
|
let s = r#"{
|
||||||
"params": {
|
"params": {
|
||||||
"tieBreakingGas": false,
|
|
||||||
"gasLimitBoundDivisor": "0x0400",
|
"gasLimitBoundDivisor": "0x0400",
|
||||||
"minimumDifficulty": "0x020000",
|
"minimumDifficulty": "0x020000",
|
||||||
"difficultyBoundDivisor": "0x0800",
|
"difficultyBoundDivisor": "0x0800",
|
||||||
"durationLimit": "0x0d",
|
"durationLimit": "0x0d",
|
||||||
"blockReward": "0x4563918244F40000",
|
"blockReward": "0x4563918244F40000",
|
||||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
|
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||||
|
"frontierCompatibilityModeLimit" : "0x42"
|
||||||
}
|
}
|
||||||
}"#;
|
}"#;
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ pub mod seal;
|
|||||||
pub mod engine;
|
pub mod engine;
|
||||||
pub mod state;
|
pub mod state;
|
||||||
pub mod ethash;
|
pub mod ethash;
|
||||||
|
pub mod basic_authority;
|
||||||
|
|
||||||
pub use self::account::Account;
|
pub use self::account::Account;
|
||||||
pub use self::builtin::{Builtin, Pricing, Linear};
|
pub use self::builtin::{Builtin, Pricing, Linear};
|
||||||
@ -35,3 +36,4 @@ pub use self::seal::{Seal, Ethereum, Generic};
|
|||||||
pub use self::engine::Engine;
|
pub use self::engine::Engine;
|
||||||
pub use self::state::State;
|
pub use self::state::State;
|
||||||
pub use self::ethash::{Ethash, EthashParams};
|
pub use self::ethash::{Ethash, EthashParams};
|
||||||
|
pub use self::basic_authority::{BasicAuthority, BasicAuthorityParams};
|
||||||
|
@ -24,9 +24,6 @@ pub struct Params {
|
|||||||
/// Account start nonce.
|
/// Account start nonce.
|
||||||
#[serde(rename="accountStartNonce")]
|
#[serde(rename="accountStartNonce")]
|
||||||
pub account_start_nonce: Uint,
|
pub account_start_nonce: Uint,
|
||||||
/// Homestead transition block number.
|
|
||||||
#[serde(rename="frontierCompatibilityModeLimit")]
|
|
||||||
pub frontier_compatibility_mode_limit: Uint,
|
|
||||||
/// Maximum size of extra data.
|
/// Maximum size of extra data.
|
||||||
#[serde(rename="maximumExtraDataSize")]
|
#[serde(rename="maximumExtraDataSize")]
|
||||||
pub maximum_extra_data_size: Uint,
|
pub maximum_extra_data_size: Uint,
|
||||||
|
@ -57,13 +57,13 @@ mod tests {
|
|||||||
"engine": {
|
"engine": {
|
||||||
"Ethash": {
|
"Ethash": {
|
||||||
"params": {
|
"params": {
|
||||||
"tieBreakingGas": false,
|
|
||||||
"gasLimitBoundDivisor": "0x0400",
|
"gasLimitBoundDivisor": "0x0400",
|
||||||
"minimumDifficulty": "0x020000",
|
"minimumDifficulty": "0x020000",
|
||||||
"difficultyBoundDivisor": "0x0800",
|
"difficultyBoundDivisor": "0x0800",
|
||||||
"durationLimit": "0x0d",
|
"durationLimit": "0x0d",
|
||||||
"blockReward": "0x4563918244F40000",
|
"blockReward": "0x4563918244F40000",
|
||||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
|
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||||
|
"frontierCompatibilityModeLimit" : "0x"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -18,6 +18,7 @@ use rayon::prelude::*;
|
|||||||
use std::sync::atomic::AtomicBool;
|
use std::sync::atomic::AtomicBool;
|
||||||
|
|
||||||
use util::*;
|
use util::*;
|
||||||
|
use util::keys::store::{AccountService, AccountProvider};
|
||||||
use ethcore::views::{BlockView, HeaderView};
|
use ethcore::views::{BlockView, HeaderView};
|
||||||
use ethcore::client::{BlockChainClient, BlockId};
|
use ethcore::client::{BlockChainClient, BlockId};
|
||||||
use ethcore::block::{ClosedBlock, IsBlock};
|
use ethcore::block::{ClosedBlock, IsBlock};
|
||||||
@ -38,6 +39,8 @@ pub struct Miner {
|
|||||||
gas_floor_target: RwLock<U256>,
|
gas_floor_target: RwLock<U256>,
|
||||||
author: RwLock<Address>,
|
author: RwLock<Address>,
|
||||||
extra_data: RwLock<Bytes>,
|
extra_data: RwLock<Bytes>,
|
||||||
|
|
||||||
|
accounts: RwLock<Option<Arc<AccountService>>>, // TODO: this is horrible since AccountService already contains a single RwLock field. refactor.
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Miner {
|
impl Default for Miner {
|
||||||
@ -51,6 +54,7 @@ impl Default for Miner {
|
|||||||
gas_floor_target: RwLock::new(U256::zero()),
|
gas_floor_target: RwLock::new(U256::zero()),
|
||||||
author: RwLock::new(Address::default()),
|
author: RwLock::new(Address::default()),
|
||||||
extra_data: RwLock::new(Vec::new()),
|
extra_data: RwLock::new(Vec::new()),
|
||||||
|
accounts: RwLock::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -67,6 +71,22 @@ impl Miner {
|
|||||||
gas_floor_target: RwLock::new(U256::zero()),
|
gas_floor_target: RwLock::new(U256::zero()),
|
||||||
author: RwLock::new(Address::default()),
|
author: RwLock::new(Address::default()),
|
||||||
extra_data: RwLock::new(Vec::new()),
|
extra_data: RwLock::new(Vec::new()),
|
||||||
|
accounts: RwLock::new(None),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates new instance of miner
|
||||||
|
pub fn with_accounts(force_sealing: bool, accounts: Arc<AccountService>) -> Arc<Miner> {
|
||||||
|
Arc::new(Miner {
|
||||||
|
transaction_queue: Mutex::new(TransactionQueue::new()),
|
||||||
|
force_sealing: force_sealing,
|
||||||
|
sealing_enabled: AtomicBool::new(force_sealing),
|
||||||
|
sealing_block_last_request: Mutex::new(0),
|
||||||
|
sealing_work: Mutex::new(UsingQueue::new(5)),
|
||||||
|
gas_floor_target: RwLock::new(U256::zero()),
|
||||||
|
author: RwLock::new(Address::default()),
|
||||||
|
extra_data: RwLock::new(Vec::new()),
|
||||||
|
accounts: RwLock::new(Some(accounts)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,6 +162,30 @@ impl Miner {
|
|||||||
queue.remove_invalid(&hash, &fetch_account);
|
queue.remove_invalid(&hash, &fetch_account);
|
||||||
}
|
}
|
||||||
if let Some(block) = b {
|
if let Some(block) = b {
|
||||||
|
if !block.transactions().is_empty() {
|
||||||
|
trace!(target: "miner", "prepare_sealing: block has transaction - attempting internal seal.");
|
||||||
|
// block with transactions - see if we can seal immediately.
|
||||||
|
let a = self.accounts.read().unwrap();
|
||||||
|
let s = chain.generate_seal(block.block(), match a.deref() {
|
||||||
|
&Some(ref x) => Some(x.deref() as &AccountProvider),
|
||||||
|
&None => None,
|
||||||
|
});
|
||||||
|
if let Some(seal) = s {
|
||||||
|
trace!(target: "miner", "prepare_sealing: managed internal seal. importing...");
|
||||||
|
if let Ok(sealed) = chain.try_seal(block.lock(), seal) {
|
||||||
|
if let Ok(_) = chain.import_block(sealed.rlp_bytes()) {
|
||||||
|
trace!(target: "miner", "prepare_sealing: sealed internally and imported. leaving.");
|
||||||
|
} else {
|
||||||
|
warn!("prepare_sealing: ERROR: could not import internally sealed block. WTF?");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
warn!("prepare_sealing: ERROR: try_seal failed when given internally generated seal. WTF?");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
trace!(target: "miner", "prepare_sealing: unable to generate seal internally");
|
||||||
|
}
|
||||||
|
}
|
||||||
if sealing_work.peek_last_ref().map_or(true, |pb| pb.block().fields().header.hash() != block.block().fields().header.hash()) {
|
if sealing_work.peek_last_ref().map_or(true, |pb| pb.block().fields().header.hash() != block.block().fields().header.hash()) {
|
||||||
trace!(target: "miner", "Pushing a new, refreshed or borrowed pending {}...", block.block().fields().header.hash());
|
trace!(target: "miner", "Pushing a new, refreshed or borrowed pending {}...", block.block().fields().header.hash());
|
||||||
sealing_work.push(block);
|
sealing_work.push(block);
|
||||||
|
@ -146,7 +146,7 @@ fn execute_client(conf: Configuration) {
|
|||||||
let client = service.client();
|
let client = service.client();
|
||||||
|
|
||||||
// Miner
|
// Miner
|
||||||
let miner = Miner::new(conf.args.flag_force_sealing);
|
let miner = Miner::with_accounts(conf.args.flag_force_sealing, account_service.clone());
|
||||||
miner.set_author(conf.author());
|
miner.set_author(conf.author());
|
||||||
miner.set_gas_floor_target(conf.gas_floor_target());
|
miner.set_gas_floor_target(conf.gas_floor_target());
|
||||||
miner.set_extra_data(conf.extra_data());
|
miner.set_extra_data(conf.extra_data());
|
||||||
|
@ -20,13 +20,14 @@ use std::sync::{Arc, RwLock};
|
|||||||
use jsonrpc_core::IoHandler;
|
use jsonrpc_core::IoHandler;
|
||||||
use util::hash::{Address, H256, FixedHash};
|
use util::hash::{Address, H256, FixedHash};
|
||||||
use util::numbers::{Uint, U256};
|
use util::numbers::{Uint, U256};
|
||||||
|
use util::keys::{TestAccount, TestAccountProvider};
|
||||||
use ethcore::client::{TestBlockChainClient, EachBlockWith, Executed, TransactionId};
|
use ethcore::client::{TestBlockChainClient, EachBlockWith, Executed, TransactionId};
|
||||||
use ethcore::log_entry::{LocalizedLogEntry, LogEntry};
|
use ethcore::log_entry::{LocalizedLogEntry, LogEntry};
|
||||||
use ethcore::receipt::LocalizedReceipt;
|
use ethcore::receipt::LocalizedReceipt;
|
||||||
use ethcore::transaction::{Transaction, Action};
|
use ethcore::transaction::{Transaction, Action};
|
||||||
use ethminer::ExternalMiner;
|
use ethminer::ExternalMiner;
|
||||||
use v1::{Eth, EthClient};
|
use v1::{Eth, EthClient};
|
||||||
use v1::tests::helpers::{TestAccount, TestAccountProvider, TestSyncProvider, Config, TestMinerService};
|
use v1::tests::helpers::{TestSyncProvider, Config, TestMinerService};
|
||||||
|
|
||||||
fn blockchain_client() -> Arc<TestBlockChainClient> {
|
fn blockchain_client() -> Arc<TestBlockChainClient> {
|
||||||
let client = TestBlockChainClient::new();
|
let client = TestBlockChainClient::new();
|
||||||
|
@ -16,10 +16,8 @@
|
|||||||
|
|
||||||
//! Test rpc services.
|
//! Test rpc services.
|
||||||
|
|
||||||
mod account_provider;
|
|
||||||
mod sync_provider;
|
mod sync_provider;
|
||||||
mod miner_service;
|
mod miner_service;
|
||||||
|
|
||||||
pub use self::account_provider::{TestAccount, TestAccountProvider};
|
|
||||||
pub use self::sync_provider::{Config, TestSyncProvider};
|
pub use self::sync_provider::{Config, TestSyncProvider};
|
||||||
pub use self::miner_service::{TestMinerService};
|
pub use self::miner_service::{TestMinerService};
|
||||||
|
@ -16,9 +16,9 @@
|
|||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use jsonrpc_core::IoHandler;
|
use jsonrpc_core::IoHandler;
|
||||||
use v1::tests::helpers::{TestAccount, TestAccountProvider};
|
|
||||||
use v1::{PersonalClient, Personal};
|
|
||||||
use util::numbers::*;
|
use util::numbers::*;
|
||||||
|
use util::keys::{TestAccount, TestAccountProvider};
|
||||||
|
use v1::{PersonalClient, Personal};
|
||||||
use std::collections::*;
|
use std::collections::*;
|
||||||
|
|
||||||
fn accounts_provider() -> Arc<TestAccountProvider> {
|
fn accounts_provider() -> Arc<TestAccountProvider> {
|
||||||
@ -49,7 +49,6 @@ fn accounts() {
|
|||||||
assert_eq!(io.handle_request(request), Some(response.to_owned()));
|
assert_eq!(io.handle_request(request), Some(response.to_owned()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn new_account() {
|
fn new_account() {
|
||||||
let (test_provider, io) = setup();
|
let (test_provider, io) = setup();
|
||||||
|
@ -1484,7 +1484,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn dummy_sync_with_peer(peer_latest_hash: H256) -> ChainSync {
|
fn dummy_sync_with_peer(peer_latest_hash: H256) -> ChainSync {
|
||||||
let mut sync = ChainSync::new(SyncConfig::default(), Miner::new(false));
|
let mut sync = ChainSync::new(SyncConfig::default(), Arc::new(Miner::default()));
|
||||||
sync.peers.insert(0,
|
sync.peers.insert(0,
|
||||||
PeerInfo {
|
PeerInfo {
|
||||||
protocol_version: 0,
|
protocol_version: 0,
|
||||||
|
@ -24,6 +24,20 @@ pub use vector::*;
|
|||||||
pub use numbers::*;
|
pub use numbers::*;
|
||||||
pub use sha3::*;
|
pub use sha3::*;
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! hash_map {
|
||||||
|
( $( $x:expr => $y:expr ),* ) => {
|
||||||
|
vec![ $( ($x, $y) ),* ].into_iter().collect::<HashMap<_, _>>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! hash_mapx {
|
||||||
|
( $( $x:expr => $y:expr ),* ) => {
|
||||||
|
vec![ $( ( From::from($x), From::from($y) ) ),* ].into_iter().collect::<HashMap<_, _>>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! map {
|
macro_rules! map {
|
||||||
( $( $x:expr => $y:expr ),* ) => {
|
( $( $x:expr => $y:expr ),* ) => {
|
||||||
|
@ -19,3 +19,6 @@
|
|||||||
pub mod directory;
|
pub mod directory;
|
||||||
pub mod store;
|
pub mod store;
|
||||||
mod geth_import;
|
mod geth_import;
|
||||||
|
mod test_account_provider;
|
||||||
|
|
||||||
|
pub use self::test_account_provider::{TestAccount, TestAccountProvider};
|
||||||
|
@ -31,6 +31,8 @@ const KEY_LENGTH_AES: u32 = KEY_LENGTH/2;
|
|||||||
const KEY_LENGTH_USIZE: usize = KEY_LENGTH as usize;
|
const KEY_LENGTH_USIZE: usize = KEY_LENGTH as usize;
|
||||||
const KEY_LENGTH_AES_USIZE: usize = KEY_LENGTH_AES as usize;
|
const KEY_LENGTH_AES_USIZE: usize = KEY_LENGTH_AES as usize;
|
||||||
|
|
||||||
|
// TODO: this file needs repotting into several separate files.
|
||||||
|
|
||||||
/// Encrypted hash-map, each request should contain password
|
/// Encrypted hash-map, each request should contain password
|
||||||
pub trait EncryptedHashMap<Key: Hash + Eq> {
|
pub trait EncryptedHashMap<Key: Hash + Eq> {
|
||||||
/// Returns existing value for the key, if any
|
/// Returns existing value for the key, if any
|
||||||
@ -87,10 +89,12 @@ pub trait AccountProvider : Send + Sync {
|
|||||||
fn unlock_account(&self, account: &Address, pass: &str) -> Result<(), EncryptedHashMapError>;
|
fn unlock_account(&self, account: &Address, pass: &str) -> Result<(), EncryptedHashMapError>;
|
||||||
/// Creates account
|
/// Creates account
|
||||||
fn new_account(&self, pass: &str) -> Result<Address, ::std::io::Error>;
|
fn new_account(&self, pass: &str) -> Result<Address, ::std::io::Error>;
|
||||||
/// Returns secret for unlocked account
|
/// Returns secret for unlocked `account`.
|
||||||
fn account_secret(&self, account: &Address) -> Result<crypto::Secret, SigningError>;
|
fn account_secret(&self, account: &Address) -> Result<crypto::Secret, SigningError>;
|
||||||
/// Returns secret for unlocked account
|
/// Returns signature when unlocked `account` signs `message`.
|
||||||
fn sign(&self, account: &Address, message: &H256) -> Result<crypto::Signature, SigningError>;
|
fn sign(&self, account: &Address, message: &H256) -> Result<crypto::Signature, SigningError> {
|
||||||
|
self.account_secret(account).and_then(|s| crypto::ec::sign(&s, message).map_err(|_| SigningError::InvalidSecret))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Thread-safe accounts management
|
/// Thread-safe accounts management
|
||||||
|
@ -19,9 +19,9 @@
|
|||||||
use std::sync::RwLock;
|
use std::sync::RwLock;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::io;
|
use std::io;
|
||||||
use util::hash::{Address, H256, FixedHash};
|
use hash::{Address, FixedHash};
|
||||||
use util::crypto::{Secret, Signature, KeyPair};
|
use crypto::{Secret, KeyPair};
|
||||||
use util::keys::store::{AccountProvider, SigningError, EncryptedHashMapError};
|
use super::store::{AccountProvider, SigningError, EncryptedHashMapError};
|
||||||
|
|
||||||
/// Account mock.
|
/// Account mock.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -98,10 +98,5 @@ impl AccountProvider for TestAccountProvider {
|
|||||||
.ok_or(SigningError::NoAccount)
|
.ok_or(SigningError::NoAccount)
|
||||||
.map(|acc| acc.secret.clone())
|
.map(|acc| acc.secret.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sign(&self, _account: &Address, _message: &H256) -> Result<Signature, SigningError> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user