spec loading cleanup (#858)
* spec loading cleanup in progress * changed engine field in json spec * refactored engine params * polishing spec loading refactor * fixed compiling json tests * fixed compiling parity * removed warnings * removed commented out code * fixed failing test * bringing back removed TODO in spec.
This commit is contained in:
parent
d823fd7685
commit
373284ca0a
@ -1,24 +1,33 @@
|
||||
{
|
||||
"name": "Frontier/Homestead",
|
||||
"engineName": "Ethash",
|
||||
"engine": {
|
||||
"Ethash": {
|
||||
"params": {
|
||||
"accountStartNonce": "0x00",
|
||||
"frontierCompatibilityModeLimit": "0x118c30",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"tieBreakingGas": false,
|
||||
"minGasLimit": "0x1388",
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
|
||||
}
|
||||
}
|
||||
},
|
||||
"params": {
|
||||
"accountStartNonce": "0x00",
|
||||
"frontierCompatibilityModeLimit": "0x118c30",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x1"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
"ethereum": {
|
||||
"nonce": "0x0000000000000042",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
},
|
||||
"difficulty": "0x400000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"author": "0x0000000000000000000000000000000000000000",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
|
@ -1,24 +1,33 @@
|
||||
{
|
||||
"name": "Frontier (Test)",
|
||||
"engineName": "Ethash",
|
||||
"engine": {
|
||||
"Ethash": {
|
||||
"params": {
|
||||
"accountStartNonce": "0x00",
|
||||
"frontierCompatibilityModeLimit": "0x118c30",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"tieBreakingGas": false,
|
||||
"minGasLimit": "0x1388",
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
|
||||
}
|
||||
}
|
||||
},
|
||||
"params": {
|
||||
"accountStartNonce": "0x00",
|
||||
"frontierCompatibilityModeLimit": "0x118c30",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x1"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
"ethereum": {
|
||||
"nonce": "0x0000000000000042",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
},
|
||||
"difficulty": "0x400000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"author": "0x0000000000000000000000000000000000000000",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
|
@ -1,24 +1,33 @@
|
||||
{
|
||||
"name": "Frontier (Test)",
|
||||
"engineName": "Ethash",
|
||||
"engine": {
|
||||
"Ethash": {
|
||||
"params": {
|
||||
"accountStartNonce": "0x00",
|
||||
"frontierCompatibilityModeLimit": "0xffffffffffffffff",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"tieBreakingGas": false,
|
||||
"minGasLimit": "0x1388",
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
|
||||
}
|
||||
}
|
||||
},
|
||||
"params": {
|
||||
"accountStartNonce": "0x00",
|
||||
"frontierCompatibilityModeLimit": "0xffffffffffffffff",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x1"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
"ethereum": {
|
||||
"nonce": "0x0000000000000042",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
},
|
||||
"difficulty": "0x400000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"author": "0x0000000000000000000000000000000000000000",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
|
@ -1,24 +1,33 @@
|
||||
{
|
||||
"name": "Homestead (Test)",
|
||||
"engineName": "Ethash",
|
||||
"engine": {
|
||||
"Ethash": {
|
||||
"params": {
|
||||
"accountStartNonce": "0x00",
|
||||
"frontierCompatibilityModeLimit": 0,
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"tieBreakingGas": false,
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
|
||||
}
|
||||
}
|
||||
},
|
||||
"params": {
|
||||
"accountStartNonce": "0x00",
|
||||
"frontierCompatibilityModeLimit": 0,
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x1"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
"ethereum": {
|
||||
"nonce": "0x0000000000000042",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
},
|
||||
"difficulty": "0x400000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"author": "0x0000000000000000000000000000000000000000",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
|
@ -1,24 +1,33 @@
|
||||
{
|
||||
"name": "Morden",
|
||||
"engineName": "Ethash",
|
||||
"engine": {
|
||||
"Ethash": {
|
||||
"params": {
|
||||
"accountStartNonce": "0x0100000",
|
||||
"frontierCompatibilityModeLimit": "0x789b0",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"tieBreakingGas": false,
|
||||
"minGasLimit": "0x1388",
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar": "",
|
||||
"registrar": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"params": {
|
||||
"accountStartNonce": "0x0100000",
|
||||
"frontierCompatibilityModeLimit": "0x789b0",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x2"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
"ethereum": {
|
||||
"nonce": "0x00006d6f7264656e",
|
||||
"mixHash": "0x00000000000000000000000000000000000000647572616c65787365646c6578"
|
||||
}
|
||||
},
|
||||
"difficulty": "0x20000",
|
||||
"mixHash": "0x00000000000000000000000000000000000000647572616c65787365646c6578",
|
||||
"author": "0x0000000000000000000000000000000000000000",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
|
@ -1,24 +1,33 @@
|
||||
{
|
||||
"name": "Olympic",
|
||||
"engineName": "Ethash",
|
||||
"engine": {
|
||||
"Ethash": {
|
||||
"params": {
|
||||
"accountStartNonce": "0x00",
|
||||
"frontierCompatibilityModeLimit": "0xffffffffffffffff",
|
||||
"maximumExtraDataSize": "0x0400",
|
||||
"tieBreakingGas": false,
|
||||
"minGasLimit": "125000",
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x08",
|
||||
"blockReward": "0x14D1120D7B160000",
|
||||
"registrar": "5e70c0bbcd5636e0f9f9316e9f8633feb64d4050",
|
||||
"registrar": "5e70c0bbcd5636e0f9f9316e9f8633feb64d4050"
|
||||
}
|
||||
}
|
||||
},
|
||||
"params": {
|
||||
"accountStartNonce": "0x00",
|
||||
"frontierCompatibilityModeLimit": "0xffffffffffffffff",
|
||||
"maximumExtraDataSize": "0x0400",
|
||||
"minGasLimit": "125000",
|
||||
"networkID" : "0x0"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
"ethereum": {
|
||||
"nonce": "0x000000000000002a",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
},
|
||||
"difficulty": "0x20000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"author": "0x0000000000000000000000000000000000000000",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
|
@ -1,24 +1,23 @@
|
||||
{
|
||||
"name": "Morden",
|
||||
"engineName": "NullEngine",
|
||||
"engine": {
|
||||
"Null": null
|
||||
},
|
||||
"params": {
|
||||
"accountStartNonce": "0x0100000",
|
||||
"frontierCompatibilityModeLimit": "0x0",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"tieBreakingGas": false,
|
||||
"minGasLimit": "0x1388",
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar": "",
|
||||
"networkID" : "0x2"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
"ethereum": {
|
||||
"nonce": "0x00006d6f7264656e",
|
||||
"mixHash": "0x00000000000000000000000000000000000000647572616c65787365646c6578"
|
||||
}
|
||||
},
|
||||
"difficulty": "0x20000",
|
||||
"mixHash": "0x00000000000000000000000000000000000000647572616c65787365646c6578",
|
||||
"author": "0x0000000000000000000000000000000000000000",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
|
@ -1,24 +1,23 @@
|
||||
{
|
||||
"name": "Morden",
|
||||
"engineName": "NullEngine",
|
||||
"engine": {
|
||||
"Null": null
|
||||
},
|
||||
"params": {
|
||||
"accountStartNonce": "0x0100000",
|
||||
"frontierCompatibilityModeLimit": "0x789b0",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"tieBreakingGas": false,
|
||||
"minGasLimit": "0x1388",
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar": "",
|
||||
"networkID" : "0x2"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
"ethereum": {
|
||||
"nonce": "0x00006d6f7264656e",
|
||||
"mixHash": "0x00000000000000000000000000000000000000647572616c65787365646c6578"
|
||||
}
|
||||
},
|
||||
"difficulty": "0x20000",
|
||||
"mixHash": "0x00000000000000000000000000000000000000647572616c65787365646c6578",
|
||||
"author": "0x0000000000000000000000000000000000000000",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
|
@ -497,16 +497,16 @@ mod tests {
|
||||
use tests::helpers::*;
|
||||
use super::*;
|
||||
use common::*;
|
||||
use engine::*;
|
||||
|
||||
#[test]
|
||||
fn open_block() {
|
||||
use spec::*;
|
||||
let engine = Spec::new_test().to_engine().unwrap();
|
||||
let genesis_header = engine.spec().genesis_header();
|
||||
let spec = Spec::new_test();
|
||||
let engine = &spec.engine;
|
||||
let genesis_header = spec.genesis_header();
|
||||
let mut db_result = get_temp_journal_db();
|
||||
let mut db = db_result.take();
|
||||
engine.spec().ensure_db_good(db.as_hashdb_mut());
|
||||
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, Address::zero(), x!(3141562), vec![]);
|
||||
let b = b.close_and_lock();
|
||||
@ -516,19 +516,20 @@ mod tests {
|
||||
#[test]
|
||||
fn enact_block() {
|
||||
use spec::*;
|
||||
let engine = Spec::new_test().to_engine().unwrap();
|
||||
let genesis_header = engine.spec().genesis_header();
|
||||
let spec = Spec::new_test();
|
||||
let engine = &spec.engine;
|
||||
let genesis_header = spec.genesis_header();
|
||||
|
||||
let mut db_result = get_temp_journal_db();
|
||||
let mut db = db_result.take();
|
||||
engine.spec().ensure_db_good(db.as_hashdb_mut());
|
||||
spec.ensure_db_good(db.as_hashdb_mut());
|
||||
let b = OpenBlock::new(engine.deref(), false, db, &genesis_header, vec![genesis_header.hash()], Address::zero(), x!(3141562), vec![]).close_and_lock().seal(engine.deref(), vec![]).unwrap();
|
||||
let orig_bytes = b.rlp_bytes();
|
||||
let orig_db = b.drain();
|
||||
|
||||
let mut db_result = get_temp_journal_db();
|
||||
let mut db = db_result.take();
|
||||
engine.spec().ensure_db_good(db.as_hashdb_mut());
|
||||
spec.ensure_db_good(db.as_hashdb_mut());
|
||||
let e = enact_and_seal(&orig_bytes, engine.deref(), false, db, &genesis_header, vec![genesis_header.hash()]).unwrap();
|
||||
|
||||
assert_eq!(e.rlp_bytes(), orig_bytes);
|
||||
@ -541,12 +542,13 @@ mod tests {
|
||||
#[test]
|
||||
fn enact_block_with_uncle() {
|
||||
use spec::*;
|
||||
let engine = Spec::new_test().to_engine().unwrap();
|
||||
let genesis_header = engine.spec().genesis_header();
|
||||
let spec = Spec::new_test();
|
||||
let engine = &spec.engine;
|
||||
let genesis_header = spec.genesis_header();
|
||||
|
||||
let mut db_result = get_temp_journal_db();
|
||||
let mut db = db_result.take();
|
||||
engine.spec().ensure_db_good(db.as_hashdb_mut());
|
||||
spec.ensure_db_good(db.as_hashdb_mut());
|
||||
let mut open_block = OpenBlock::new(engine.deref(), false, db, &genesis_header, vec![genesis_header.hash()], Address::zero(), x!(3141562), vec![]);
|
||||
let mut uncle1_header = Header::new();
|
||||
uncle1_header.extra_data = b"uncle1".to_vec();
|
||||
@ -561,7 +563,7 @@ mod tests {
|
||||
|
||||
let mut db_result = get_temp_journal_db();
|
||||
let mut db = db_result.take();
|
||||
engine.spec().ensure_db_good(db.as_hashdb_mut());
|
||||
spec.ensure_db_good(db.as_hashdb_mut());
|
||||
let e = enact_and_seal(&orig_bytes, engine.deref(), false, db, &genesis_header, vec![genesis_header.hash()]).unwrap();
|
||||
|
||||
let bytes = e.rlp_bytes();
|
||||
|
@ -464,7 +464,7 @@ mod tests {
|
||||
|
||||
fn get_test_queue() -> BlockQueue {
|
||||
let spec = get_test_spec();
|
||||
let engine = spec.to_engine().unwrap();
|
||||
let engine = spec.engine;
|
||||
BlockQueue::new(BlockQueueConfig::default(), Arc::new(engine), IoChannel::disconnected())
|
||||
}
|
||||
|
||||
@ -472,7 +472,7 @@ mod tests {
|
||||
fn can_be_created() {
|
||||
// TODO better test
|
||||
let spec = Spec::new_test();
|
||||
let engine = spec.to_engine().unwrap();
|
||||
let engine = spec.engine;
|
||||
let _ = BlockQueue::new(BlockQueueConfig::default(), Arc::new(engine), IoChannel::disconnected());
|
||||
}
|
||||
|
||||
@ -533,7 +533,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_mem_limit() {
|
||||
let spec = get_test_spec();
|
||||
let engine = spec.to_engine().unwrap();
|
||||
let engine = spec.engine;
|
||||
let mut config = BlockQueueConfig::default();
|
||||
config.max_mem_use = super::MIN_MEM_LIMIT; // empty queue uses about 15000
|
||||
let queue = BlockQueue::new(config, Arc::new(engine), IoChannel::disconnected());
|
||||
|
@ -18,6 +18,7 @@ use util::*;
|
||||
use crypto::sha2::Sha256;
|
||||
use crypto::ripemd160::Ripemd160;
|
||||
use crypto::digest::Digest;
|
||||
use ethjson;
|
||||
|
||||
/// Definition of a contract whose implementation is built-in.
|
||||
pub struct Builtin {
|
||||
@ -46,13 +47,12 @@ impl Builtin {
|
||||
}
|
||||
|
||||
/// Create a new object from a builtin-function name with a linear cost associated with input size.
|
||||
pub fn from_named_linear(name: &str, base_cost: usize, word_cost: usize) -> Option<Builtin> {
|
||||
new_builtin_exec(name).map(|b| {
|
||||
pub fn from_named_linear(name: &str, base_cost: usize, word_cost: usize) -> Builtin {
|
||||
let cost = Box::new(move|s: usize| -> U256 {
|
||||
U256::from(base_cost) + U256::from(word_cost) * U256::from((s + 31) / 32)
|
||||
});
|
||||
Self::new(cost, b)
|
||||
})
|
||||
|
||||
Self::new(cost, new_builtin_exec(name))
|
||||
}
|
||||
|
||||
/// Simple forwarder for cost.
|
||||
@ -60,26 +60,17 @@ impl Builtin {
|
||||
|
||||
/// Simple forwarder for execute.
|
||||
pub fn execute(&self, input: &[u8], output: &mut[u8]) { (*self.execute)(input, output); }
|
||||
}
|
||||
|
||||
/// Create a builtin from JSON.
|
||||
///
|
||||
/// JSON must be of the form `{ "name": "identity", "pricing": {"base": 10, "word": 20} }`.
|
||||
pub fn from_json(json: &Json) -> Option<Builtin> {
|
||||
// NICE: figure out a more convenient means of handing errors here.
|
||||
if let Json::String(ref name) = json["name"] {
|
||||
if let Json::Object(ref o) = json["pricing"] {
|
||||
if let Json::Object(ref o) = o["linear"] {
|
||||
if let Json::U64(ref word) = o["word"] {
|
||||
if let Json::U64(ref base) = o["base"] {
|
||||
return Self::from_named_linear(&name[..], *base as usize, *word as usize);
|
||||
impl From<ethjson::spec::Builtin> for Builtin {
|
||||
fn from(b: ethjson::spec::Builtin) -> Self {
|
||||
match b.pricing {
|
||||
ethjson::spec::Pricing::Linear(linear) => {
|
||||
Self::from_named_linear(b.name.as_ref(), linear.base, linear.word)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Copy a bunch of bytes to a destination; if the `src` is too small to fill `dest`,
|
||||
/// leave the rest unchanged.
|
||||
@ -92,14 +83,14 @@ pub fn copy_to(src: &[u8], dest: &mut[u8]) {
|
||||
|
||||
/// Create a new builtin executor according to `name`.
|
||||
/// TODO: turn in to a factory with dynamic registration.
|
||||
pub fn new_builtin_exec(name: &str) -> Option<Box<Fn(&[u8], &mut [u8])>> {
|
||||
pub fn new_builtin_exec(name: &str) -> Box<Fn(&[u8], &mut [u8])> {
|
||||
match name {
|
||||
"identity" => Some(Box::new(move|input: &[u8], output: &mut[u8]| {
|
||||
"identity" => Box::new(move|input: &[u8], output: &mut[u8]| {
|
||||
for i in 0..min(input.len(), output.len()) {
|
||||
output[i] = input[i];
|
||||
}
|
||||
})),
|
||||
"ecrecover" => Some(Box::new(move|input: &[u8], output: &mut[u8]| {
|
||||
}),
|
||||
"ecrecover" => Box::new(move|input: &[u8], output: &mut[u8]| {
|
||||
#[repr(packed)]
|
||||
#[derive(Debug)]
|
||||
struct InType {
|
||||
@ -122,8 +113,8 @@ pub fn new_builtin_exec(name: &str) -> Option<Box<Fn(&[u8], &mut [u8])>> {
|
||||
}
|
||||
}
|
||||
}
|
||||
})),
|
||||
"sha256" => Some(Box::new(move|input: &[u8], output: &mut[u8]| {
|
||||
}),
|
||||
"sha256" => Box::new(move|input: &[u8], output: &mut[u8]| {
|
||||
let mut sha = Sha256::new();
|
||||
sha.input(input);
|
||||
if output.len() >= 32 {
|
||||
@ -133,21 +124,23 @@ pub fn new_builtin_exec(name: &str) -> Option<Box<Fn(&[u8], &mut [u8])>> {
|
||||
sha.result(ret.as_slice_mut());
|
||||
copy_to(&ret, output);
|
||||
}
|
||||
})),
|
||||
"ripemd160" => Some(Box::new(move|input: &[u8], output: &mut[u8]| {
|
||||
}),
|
||||
"ripemd160" => Box::new(move|input: &[u8], output: &mut[u8]| {
|
||||
let mut sha = Ripemd160::new();
|
||||
sha.input(input);
|
||||
let mut ret = H256::new();
|
||||
sha.result(&mut ret.as_slice_mut()[12..32]);
|
||||
copy_to(&ret, output);
|
||||
})),
|
||||
_ => None
|
||||
}),
|
||||
_ => {
|
||||
panic!("invalid builtin name {}", name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn identity() {
|
||||
let f = new_builtin_exec("identity").unwrap();
|
||||
let f = new_builtin_exec("identity");
|
||||
let i = [0u8, 1, 2, 3];
|
||||
|
||||
let mut o2 = [255u8; 2];
|
||||
@ -167,7 +160,7 @@ fn identity() {
|
||||
#[test]
|
||||
fn sha256() {
|
||||
use rustc_serialize::hex::FromHex;
|
||||
let f = new_builtin_exec("sha256").unwrap();
|
||||
let f = new_builtin_exec("sha256");
|
||||
let i = [0u8; 0];
|
||||
|
||||
let mut o = [255u8; 32];
|
||||
@ -186,7 +179,7 @@ fn sha256() {
|
||||
#[test]
|
||||
fn ripemd160() {
|
||||
use rustc_serialize::hex::FromHex;
|
||||
let f = new_builtin_exec("ripemd160").unwrap();
|
||||
let f = new_builtin_exec("ripemd160");
|
||||
let i = [0u8; 0];
|
||||
|
||||
let mut o = [255u8; 32];
|
||||
@ -213,7 +206,7 @@ fn ecrecover() {
|
||||
let s = k.sign(&m).unwrap();
|
||||
println!("Signed: {}", s);*/
|
||||
|
||||
let f = new_builtin_exec("ecrecover").unwrap();
|
||||
let f = new_builtin_exec("ecrecover");
|
||||
let i = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03").unwrap();
|
||||
|
||||
let mut o = [255u8; 32];
|
||||
@ -260,9 +253,15 @@ fn ecrecover() {
|
||||
assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);*/
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn from_unknown_linear() {
|
||||
let _ = Builtin::from_named_linear("dw", 10, 20);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_named_linear() {
|
||||
let b = Builtin::from_named_linear("identity", 10, 20).unwrap();
|
||||
let b = Builtin::from_named_linear("identity", 10, 20);
|
||||
assert_eq!((*b.cost)(0), U256::from(10));
|
||||
assert_eq!((*b.cost)(1), U256::from(30));
|
||||
assert_eq!((*b.cost)(32), U256::from(30));
|
||||
@ -276,9 +275,14 @@ fn from_named_linear() {
|
||||
|
||||
#[test]
|
||||
fn from_json() {
|
||||
let text = r#"{"name": "identity", "pricing": {"linear": {"base": 10, "word": 20}}}"#;
|
||||
let json = Json::from_str(text).unwrap();
|
||||
let b = Builtin::from_json(&json).unwrap();
|
||||
let b = Builtin::from(ethjson::spec::Builtin {
|
||||
name: "identity".to_owned(),
|
||||
pricing: ethjson::spec::Pricing::Linear(ethjson::spec::Linear {
|
||||
base: 10,
|
||||
word: 20,
|
||||
})
|
||||
});
|
||||
|
||||
assert_eq!((*b.cost)(0), U256::from(10));
|
||||
assert_eq!((*b.cost)(1), U256::from(30));
|
||||
assert_eq!((*b.cost)(32), U256::from(30));
|
||||
|
@ -140,14 +140,15 @@ impl<V> Client<V> where V: Verifier {
|
||||
let mut state_path = path.to_path_buf();
|
||||
state_path.push("state");
|
||||
|
||||
let engine = Arc::new(try!(spec.to_engine()));
|
||||
let state_path_str = state_path.to_str().unwrap();
|
||||
let mut state_db = journaldb::new(state_path_str, config.pruning);
|
||||
|
||||
if state_db.is_empty() && engine.spec().ensure_db_good(state_db.as_hashdb_mut()) {
|
||||
state_db.commit(0, &engine.spec().genesis_header().hash(), None).expect("Error commiting genesis state to state DB");
|
||||
if state_db.is_empty() && spec.ensure_db_good(state_db.as_hashdb_mut()) {
|
||||
state_db.commit(0, &spec.genesis_header().hash(), None).expect("Error commiting genesis state to state DB");
|
||||
}
|
||||
|
||||
let engine = Arc::new(spec.engine);
|
||||
|
||||
let block_queue = BlockQueue::new(config.queue, engine.clone(), message_channel);
|
||||
let panic_handler = PanicHandler::new_in_arc();
|
||||
panic_handler.forward_from(&block_queue);
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
use common::*;
|
||||
use block::ExecutedBlock;
|
||||
use spec::Spec;
|
||||
use spec::CommonParams;
|
||||
use evm::Schedule;
|
||||
use evm::Factory;
|
||||
|
||||
@ -35,7 +35,7 @@ pub trait Engine : Sync + Send {
|
||||
fn extra_info(&self, _header: &Header) -> HashMap<String, String> { HashMap::new() }
|
||||
|
||||
/// Get the general parameters of the chain.
|
||||
fn spec(&self) -> &Spec;
|
||||
fn params(&self) -> &CommonParams;
|
||||
|
||||
/// Get current EVM factory
|
||||
fn vm_factory(&self) -> &Factory;
|
||||
@ -43,14 +43,18 @@ pub trait Engine : Sync + Send {
|
||||
/// Get the EVM schedule for the given `env_info`.
|
||||
fn schedule(&self, env_info: &EnvInfo) -> Schedule;
|
||||
|
||||
/// Builtin-contracts we would like to see in the chain.
|
||||
/// (In principle these are just hints for the engine since that has the last word on them.)
|
||||
fn builtins(&self) -> &BTreeMap<Address, Builtin>;
|
||||
|
||||
/// Some intrinsic operation parameters; by default they take their value from the `spec()`'s `engine_params`.
|
||||
fn maximum_extra_data_size(&self) -> usize { decode(&self.spec().engine_params.get("maximumExtraDataSize").unwrap()) }
|
||||
fn maximum_extra_data_size(&self) -> usize { self.params().maximum_extra_data_size }
|
||||
/// Maximum number of uncles a block is allowed to declare.
|
||||
fn maximum_uncle_count(&self) -> usize { 2 }
|
||||
/// The number of generations back that uncles can be.
|
||||
fn maximum_uncle_age(&self) -> usize { 6 }
|
||||
/// The nonce with which accounts begin.
|
||||
fn account_start_nonce(&self) -> U256 { decode(&self.spec().engine_params.get("accountStartNonce").unwrap()) }
|
||||
fn account_start_nonce(&self) -> U256 { self.params().account_start_nonce }
|
||||
|
||||
/// Block transformation functions, before the transactions.
|
||||
fn on_new_block(&self, _block: &mut ExecutedBlock) {}
|
||||
@ -94,23 +98,13 @@ pub trait Engine : Sync + Send {
|
||||
// TODO: builtin contract routing - to do this properly, it will require removing the built-in configuration-reading logic
|
||||
// from Spec into here and removing the Spec::builtins field.
|
||||
/// Determine whether a particular address is a builtin contract.
|
||||
fn is_builtin(&self, a: &Address) -> bool { self.spec().builtins.contains_key(a) }
|
||||
fn is_builtin(&self, a: &Address) -> bool { self.builtins().contains_key(a) }
|
||||
/// Determine the code execution cost of the builtin contract with address `a`.
|
||||
/// Panics if `is_builtin(a)` is not true.
|
||||
fn cost_of_builtin(&self, a: &Address, input: &[u8]) -> U256 { self.spec().builtins.get(a).unwrap().cost(input.len()) }
|
||||
fn cost_of_builtin(&self, a: &Address, input: &[u8]) -> U256 { self.builtins().get(a).unwrap().cost(input.len()) }
|
||||
/// Execution the builtin contract `a` on `input` and return `output`.
|
||||
/// Panics if `is_builtin(a)` is not true.
|
||||
fn execute_builtin(&self, a: &Address, input: &[u8], output: &mut [u8]) { self.spec().builtins.get(a).unwrap().execute(input, output); }
|
||||
fn execute_builtin(&self, a: &Address, input: &[u8], output: &mut [u8]) { self.builtins().get(a).unwrap().execute(input, output); }
|
||||
|
||||
// TODO: sealing stuff - though might want to leave this for later.
|
||||
|
||||
/// Get a named parameter from the `spec()`'s `engine_params` item and convert to u64, or 0 if that fails.
|
||||
fn u64_param(&self, name: &str) -> u64 {
|
||||
self.spec().engine_params.get(name).map_or(0u64, |a| decode(&a))
|
||||
}
|
||||
|
||||
/// Get a named parameter from the `spec()`'s `engine_params` item and convert to U256, or 0 if that fails.
|
||||
fn u256_param(&self, name: &str) -> U256 {
|
||||
self.spec().engine_params.get(name).map_or(x!(0), |a| decode(&a))
|
||||
}
|
||||
}
|
||||
|
@ -55,21 +55,6 @@ impl Default for EnvInfo {
|
||||
}
|
||||
}
|
||||
|
||||
impl FromJson for EnvInfo {
|
||||
fn from_json(json: &Json) -> EnvInfo {
|
||||
let current_number: u64 = xjson!(&json["currentNumber"]);
|
||||
EnvInfo {
|
||||
number: current_number,
|
||||
author: xjson!(&json["currentCoinbase"]),
|
||||
difficulty: xjson!(&json["currentDifficulty"]),
|
||||
gas_limit: xjson!(&json["currentGasLimit"]),
|
||||
timestamp: xjson!(&json["currentTimestamp"]),
|
||||
last_hashes: (1..cmp::min(current_number + 1, 257)).map(|i| format!("{}", current_number - i).as_bytes().sha3()).collect(),
|
||||
gas_used: x!(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ethjson::vm::Env> for EnvInfo {
|
||||
fn from(e: ethjson::vm::Env) -> Self {
|
||||
let number = e.number.into();
|
||||
@ -90,24 +75,20 @@ mod tests {
|
||||
extern crate rustc_serialize;
|
||||
|
||||
use super::*;
|
||||
use rustc_serialize::*;
|
||||
use util::from_json::FromJson;
|
||||
use util::hash::*;
|
||||
use util::numbers::U256;
|
||||
use std::str::FromStr;
|
||||
use ethjson;
|
||||
|
||||
#[test]
|
||||
fn it_serializes_form_json() {
|
||||
let env_info = EnvInfo::from_json(&json::Json::from_str(
|
||||
r#"
|
||||
{
|
||||
"currentCoinbase": "0x000000f00000000f000000000000f00000000f00",
|
||||
"currentNumber": 1112339,
|
||||
"currentDifficulty": 50000,
|
||||
"currentGasLimit" : 40000,
|
||||
"currentTimestamp" : 1100
|
||||
}
|
||||
"#
|
||||
).unwrap());
|
||||
let env_info = EnvInfo::from(ethjson::vm::Env {
|
||||
author: ethjson::hash::Address(Address::from_str("000000f00000000f000000000000f00000000f00").unwrap()),
|
||||
number: ethjson::uint::Uint(U256::from(1_112_339)),
|
||||
difficulty: ethjson::uint::Uint(U256::from(50_000)),
|
||||
gas_limit: ethjson::uint::Uint(U256::from(40_000)),
|
||||
timestamp: ethjson::uint::Uint(U256::from(1_100))
|
||||
});
|
||||
|
||||
assert_eq!(env_info.number, 1112339);
|
||||
assert_eq!(env_info.author, Address::from_str("000000f00000000f000000000000f00000000f00").unwrap());
|
||||
|
@ -19,42 +19,63 @@ extern crate ethash;
|
||||
use self::ethash::{quick_get_difficulty, EthashManager, H256 as EH256};
|
||||
use common::*;
|
||||
use block::*;
|
||||
use spec::*;
|
||||
use spec::CommonParams;
|
||||
use engine::*;
|
||||
use evm::Schedule;
|
||||
use evm::Factory;
|
||||
use evm::{Schedule, Factory};
|
||||
use ethjson;
|
||||
|
||||
/// Ethash params.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct EthashParams {
|
||||
/// Tie breaking gas.
|
||||
pub tie_breaking_gas: bool,
|
||||
/// Gas limit divisor.
|
||||
pub gas_limit_bound_divisor: U256,
|
||||
/// Minimum difficulty.
|
||||
pub minimum_difficulty: U256,
|
||||
/// Difficulty bound divisor.
|
||||
pub difficulty_bound_divisor: U256,
|
||||
/// Block duration.
|
||||
pub duration_limit: u64,
|
||||
/// Block reward.
|
||||
pub block_reward: U256,
|
||||
/// Namereg contract address.
|
||||
pub registrar: Address,
|
||||
}
|
||||
|
||||
impl From<ethjson::spec::EthashParams> for EthashParams {
|
||||
fn from(p: ethjson::spec::EthashParams) -> Self {
|
||||
EthashParams {
|
||||
tie_breaking_gas: p.tie_breaking_gas,
|
||||
gas_limit_bound_divisor: p.gas_limit_bound_divisor.into(),
|
||||
minimum_difficulty: p.minimum_difficulty.into(),
|
||||
difficulty_bound_divisor: p.difficulty_bound_divisor.into(),
|
||||
duration_limit: p.duration_limit.into(),
|
||||
block_reward: p.block_reward.into(),
|
||||
registrar: p.registrar.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Engine using Ethash proof-of-work consensus algorithm, suitable for Ethereum
|
||||
/// mainnet chains in the Olympic, Frontier and Homestead eras.
|
||||
pub struct Ethash {
|
||||
spec: Spec,
|
||||
params: CommonParams,
|
||||
ethash_params: EthashParams,
|
||||
builtins: BTreeMap<Address, Builtin>,
|
||||
pow: EthashManager,
|
||||
factory: Factory,
|
||||
u64_params: RwLock<HashMap<String, u64>>,
|
||||
u256_params: RwLock<HashMap<String, U256>>,
|
||||
}
|
||||
|
||||
impl Ethash {
|
||||
/// Create a new boxed instance of Ethash engine
|
||||
pub fn new_boxed(spec: Spec) -> Box<Engine> {
|
||||
Box::new(Ethash {
|
||||
spec: spec,
|
||||
pow: EthashManager::new(),
|
||||
// TODO [todr] should this return any specific factory?
|
||||
factory: Factory::default(),
|
||||
u64_params: RwLock::new(HashMap::new()),
|
||||
u256_params: RwLock::new(HashMap::new())
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn new_test(spec: Spec) -> Ethash {
|
||||
/// Create a new instance of Ethash engine
|
||||
pub fn new(params: CommonParams, ethash_params: EthashParams, builtins: BTreeMap<Address, Builtin>) -> Self {
|
||||
Ethash {
|
||||
spec: spec,
|
||||
params: params,
|
||||
ethash_params: ethash_params,
|
||||
builtins: builtins,
|
||||
pow: EthashManager::new(),
|
||||
factory: Factory::default(),
|
||||
u64_params: RwLock::new(HashMap::new()),
|
||||
u256_params: RwLock::new(HashMap::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -65,17 +86,23 @@ impl Engine for Ethash {
|
||||
// Two fields - mix
|
||||
fn seal_fields(&self) -> usize { 2 }
|
||||
|
||||
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> { HashMap::new() }
|
||||
fn spec(&self) -> &Spec { &self.spec }
|
||||
|
||||
fn vm_factory(&self) -> &Factory {
|
||||
&self.factory
|
||||
}
|
||||
|
||||
fn schedule(&self, env_info: &EnvInfo) -> Schedule {
|
||||
trace!(target: "client", "Creating schedule. param={:?}, fCML={}", self.spec().engine_params.get("frontierCompatibilityModeLimit"), self.u64_param("frontierCompatibilityModeLimit"));
|
||||
if env_info.number < self.u64_param("frontierCompatibilityModeLimit") {
|
||||
trace!(target: "client", "Creating schedule. fCML={}", self.params.frontier_compatibility_mode_limit);
|
||||
|
||||
if env_info.number < self.params.frontier_compatibility_mode_limit {
|
||||
Schedule::new_frontier()
|
||||
} else {
|
||||
Schedule::new_homestead()
|
||||
@ -86,7 +113,7 @@ impl Engine for Ethash {
|
||||
header.difficulty = self.calculate_difficuty(header, parent);
|
||||
header.gas_limit = {
|
||||
let gas_limit = parent.gas_limit;
|
||||
let bound_divisor = self.u256_param("gasLimitBoundDivisor");
|
||||
let bound_divisor = self.ethash_params.gas_limit_bound_divisor;
|
||||
if gas_limit < gas_floor_target {
|
||||
min(gas_floor_target, gas_limit + gas_limit / bound_divisor - x!(1))
|
||||
} else {
|
||||
@ -100,7 +127,7 @@ impl Engine for Ethash {
|
||||
/// 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) {
|
||||
let reward = self.spec().engine_params.get("blockReward").map_or(U256::from(0u64), |a| decode(&a));
|
||||
let reward = self.ethash_params.block_reward;
|
||||
let fields = block.fields_mut();
|
||||
|
||||
// Bestow block reward
|
||||
@ -125,7 +152,7 @@ impl Engine for Ethash {
|
||||
try!(UntrustedRlp::new(&header.seal[1]).as_val::<H64>());
|
||||
|
||||
// TODO: consider removing these lines.
|
||||
let min_difficulty = decode(self.spec().engine_params.get("minimumDifficulty").unwrap());
|
||||
let min_difficulty = self.ethash_params.minimum_difficulty;
|
||||
if header.difficulty < min_difficulty {
|
||||
return Err(From::from(BlockError::DifficultyOutOfBounds(OutOfBounds { min: Some(min_difficulty), max: None, found: header.difficulty })))
|
||||
}
|
||||
@ -170,7 +197,7 @@ impl Engine for Ethash {
|
||||
if header.difficulty != expected_difficulty {
|
||||
return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: expected_difficulty, found: header.difficulty })))
|
||||
}
|
||||
let gas_limit_divisor = decode(self.spec().engine_params.get("gasLimitBoundDivisor").unwrap());
|
||||
let gas_limit_divisor = self.ethash_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 {
|
||||
@ -180,7 +207,7 @@ impl Engine for Ethash {
|
||||
}
|
||||
|
||||
fn verify_transaction_basic(&self, t: &SignedTransaction, header: &Header) -> result::Result<(), Error> {
|
||||
if header.number() >= self.u64_param("frontierCompatibilityModeLimit") {
|
||||
if header.number() >= self.params.frontier_compatibility_mode_limit {
|
||||
try!(t.check_low_s());
|
||||
}
|
||||
Ok(())
|
||||
@ -189,16 +216,6 @@ impl Engine for Ethash {
|
||||
fn verify_transaction(&self, t: &SignedTransaction, _header: &Header) -> Result<(), Error> {
|
||||
t.sender().map(|_|()) // Perform EC recovery and cache sender
|
||||
}
|
||||
|
||||
fn u64_param(&self, name: &str) -> u64 {
|
||||
*self.u64_params.write().unwrap().entry(name.to_owned()).or_insert_with(||
|
||||
self.spec().engine_params.get(name).map_or(0u64, |a| decode(&a)))
|
||||
}
|
||||
|
||||
fn u256_param(&self, name: &str) -> U256 {
|
||||
*self.u256_params.write().unwrap().entry(name.to_owned()).or_insert_with(||
|
||||
self.spec().engine_params.get(name).map_or(x!(0), |a| decode(&a)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature="dev", allow(wrong_self_convention))] // to_ethash should take self
|
||||
@ -209,10 +226,11 @@ impl Ethash {
|
||||
panic!("Can't calculate genesis block difficulty");
|
||||
}
|
||||
|
||||
let min_difficulty = self.u256_param("minimumDifficulty");
|
||||
let difficulty_bound_divisor = self.u256_param("difficultyBoundDivisor");
|
||||
let duration_limit = self.u64_param("durationLimit");
|
||||
let frontier_limit = self.u64_param("frontierCompatibilityModeLimit");
|
||||
let min_difficulty = self.ethash_params.minimum_difficulty;
|
||||
let difficulty_bound_divisor = self.ethash_params.difficulty_bound_divisor;
|
||||
let duration_limit = self.ethash_params.duration_limit;
|
||||
let frontier_limit = self.params.frontier_compatibility_mode_limit;
|
||||
|
||||
let mut target = if header.number < frontier_limit {
|
||||
if header.timestamp >= parent.timestamp + duration_limit {
|
||||
parent.difficulty - (parent.difficulty / difficulty_bound_divisor)
|
||||
@ -283,16 +301,16 @@ mod tests {
|
||||
use block::*;
|
||||
use engine::*;
|
||||
use tests::helpers::*;
|
||||
use super::{Ethash};
|
||||
use super::super::new_morden;
|
||||
|
||||
#[test]
|
||||
fn on_close_block() {
|
||||
let engine = new_morden().to_engine().unwrap();
|
||||
let genesis_header = engine.spec().genesis_header();
|
||||
let spec = new_morden();
|
||||
let engine = &spec.engine;
|
||||
let genesis_header = spec.genesis_header();
|
||||
let mut db_result = get_temp_journal_db();
|
||||
let mut db = db_result.take();
|
||||
engine.spec().ensure_db_good(db.as_hashdb_mut());
|
||||
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, Address::zero(), x!(3141562), vec![]);
|
||||
let b = b.close();
|
||||
@ -301,11 +319,12 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn on_close_block_with_uncle() {
|
||||
let engine = new_morden().to_engine().unwrap();
|
||||
let genesis_header = engine.spec().genesis_header();
|
||||
let spec = new_morden();
|
||||
let engine = &spec.engine;
|
||||
let genesis_header = spec.genesis_header();
|
||||
let mut db_result = get_temp_journal_db();
|
||||
let mut db = db_result.take();
|
||||
engine.spec().ensure_db_good(db.as_hashdb_mut());
|
||||
spec.ensure_db_good(db.as_hashdb_mut());
|
||||
let last_hashes = vec![genesis_header.hash()];
|
||||
let mut b = OpenBlock::new(engine.deref(), false, db, &genesis_header, last_hashes, Address::zero(), x!(3141562), vec![]);
|
||||
let mut uncle = Header::new();
|
||||
@ -320,27 +339,20 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn has_valid_metadata() {
|
||||
let engine = Ethash::new_boxed(new_morden());
|
||||
let engine = new_morden().engine;
|
||||
assert!(!engine.name().is_empty());
|
||||
assert!(engine.version().major >= 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_return_params() {
|
||||
let engine = Ethash::new_test(new_morden());
|
||||
assert!(engine.u64_param("durationLimit") > 0);
|
||||
assert!(engine.u256_param("minimumDifficulty") > U256::zero());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_return_factory() {
|
||||
let engine = Ethash::new_test(new_morden());
|
||||
let engine = new_morden().engine;
|
||||
engine.vm_factory();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_return_schedule() {
|
||||
let engine = Ethash::new_test(new_morden());
|
||||
let engine = new_morden().engine;
|
||||
let schedule = engine.schedule(&EnvInfo {
|
||||
number: 10000000,
|
||||
author: x!(0),
|
||||
@ -368,7 +380,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn can_do_seal_verification_fail() {
|
||||
let engine = Ethash::new_test(new_morden());
|
||||
let engine = new_morden().engine;
|
||||
//let engine = Ethash::new_test(new_morden());
|
||||
let header: Header = Header::default();
|
||||
|
||||
let verify_result = engine.verify_block_basic(&header, None);
|
||||
@ -382,7 +395,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn can_do_difficulty_verification_fail() {
|
||||
let engine = Ethash::new_test(new_morden());
|
||||
let engine = new_morden().engine;
|
||||
let mut header: Header = Header::default();
|
||||
header.set_seal(vec![rlp::encode(&H256::zero()).to_vec(), rlp::encode(&H64::zero()).to_vec()]);
|
||||
|
||||
@ -397,7 +410,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn can_do_proof_of_work_verification_fail() {
|
||||
let engine = Ethash::new_test(new_morden());
|
||||
let engine = new_morden().engine;
|
||||
let mut header: Header = Header::default();
|
||||
header.set_seal(vec![rlp::encode(&H256::zero()).to_vec(), rlp::encode(&H64::zero()).to_vec()]);
|
||||
header.set_difficulty(U256::from_str("ffffffffffffffffffffffffffffffffffffffffffffaaaaaaaaaaaaaaaaaaaa").unwrap());
|
||||
@ -413,7 +426,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn can_do_seal_unordered_verification_fail() {
|
||||
let engine = Ethash::new_test(new_morden());
|
||||
let engine = new_morden().engine;
|
||||
let header: Header = Header::default();
|
||||
|
||||
let verify_result = engine.verify_block_unordered(&header, None);
|
||||
@ -427,7 +440,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn can_do_seal256_verification_fail() {
|
||||
let engine = Ethash::new_test(new_morden());
|
||||
let engine = new_morden().engine;
|
||||
let mut header: Header = Header::default();
|
||||
header.set_seal(vec![rlp::encode(&H256::zero()).to_vec(), rlp::encode(&H64::zero()).to_vec()]);
|
||||
let verify_result = engine.verify_block_unordered(&header, None);
|
||||
@ -441,7 +454,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn can_do_proof_of_work_unordered_verification_fail() {
|
||||
let engine = Ethash::new_test(new_morden());
|
||||
let engine = new_morden().engine;
|
||||
let mut header: Header = Header::default();
|
||||
header.set_seal(vec![rlp::encode(&H256::from("b251bd2e0283d0658f2cadfdc8ca619b5de94eca5742725e2e757dd13ed7503d")).to_vec(), rlp::encode(&H64::zero()).to_vec()]);
|
||||
header.set_difficulty(U256::from_str("ffffffffffffffffffffffffffffffffffffffffffffaaaaaaaaaaaaaaaaaaaa").unwrap());
|
||||
@ -457,7 +470,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn can_verify_block_family_genesis_fail() {
|
||||
let engine = Ethash::new_test(new_morden());
|
||||
let engine = new_morden().engine;
|
||||
let header: Header = Header::default();
|
||||
let parent_header: Header = Header::default();
|
||||
|
||||
@ -472,7 +485,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn can_verify_block_family_difficulty_fail() {
|
||||
let engine = Ethash::new_test(new_morden());
|
||||
let engine = new_morden().engine;
|
||||
let mut header: Header = Header::default();
|
||||
header.set_number(2);
|
||||
let mut parent_header: Header = Header::default();
|
||||
@ -489,7 +502,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn can_verify_block_family_gas_fail() {
|
||||
let engine = Ethash::new_test(new_morden());
|
||||
let engine = new_morden().engine;
|
||||
let mut header: Header = Header::default();
|
||||
header.set_number(2);
|
||||
header.set_difficulty(U256::from_str("0000000000000000000000000000000000000000000000000000000000020000").unwrap());
|
||||
|
@ -30,22 +30,22 @@ pub use self::denominations::*;
|
||||
use super::spec::*;
|
||||
|
||||
/// Create a new Olympic chain spec.
|
||||
pub fn new_olympic() -> Spec { Spec::from_json_utf8(include_bytes!("../../res/ethereum/olympic.json")) }
|
||||
pub fn new_olympic() -> Spec { Spec::load(include_bytes!("../../res/ethereum/olympic.json")) }
|
||||
|
||||
/// Create a new Frontier mainnet chain spec.
|
||||
pub fn new_frontier() -> Spec { Spec::from_json_utf8(include_bytes!("../../res/ethereum/frontier.json")) }
|
||||
pub fn new_frontier() -> Spec { Spec::load(include_bytes!("../../res/ethereum/frontier.json")) }
|
||||
|
||||
/// Create a new Frontier chain spec as though it never changes to Homestead.
|
||||
pub fn new_frontier_test() -> Spec { Spec::from_json_utf8(include_bytes!("../../res/ethereum/frontier_test.json")) }
|
||||
pub fn new_frontier_test() -> Spec { Spec::load(include_bytes!("../../res/ethereum/frontier_test.json")) }
|
||||
|
||||
/// Create a new Homestead chain spec as though it never changed from Frontier.
|
||||
pub fn new_homestead_test() -> Spec { Spec::from_json_utf8(include_bytes!("../../res/ethereum/homestead_test.json")) }
|
||||
pub fn new_homestead_test() -> Spec { Spec::load(include_bytes!("../../res/ethereum/homestead_test.json")) }
|
||||
|
||||
/// Create a new Frontier main net chain spec without genesis accounts.
|
||||
pub fn new_mainnet_like() -> Spec { Spec::from_json_utf8(include_bytes!("../../res/ethereum/frontier_like_test.json")) }
|
||||
pub fn new_mainnet_like() -> Spec { Spec::load(include_bytes!("../../res/ethereum/frontier_like_test.json")) }
|
||||
|
||||
/// Create a new Morden chain spec.
|
||||
pub fn new_morden() -> Spec { Spec::from_json_utf8(include_bytes!("../../res/ethereum/morden.json")) }
|
||||
pub fn new_morden() -> Spec { Spec::load(include_bytes!("../../res/ethereum/morden.json")) }
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
@ -57,11 +57,12 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn ensure_db_good() {
|
||||
let engine = new_morden().to_engine().unwrap();
|
||||
let genesis_header = engine.spec().genesis_header();
|
||||
let spec = new_morden();
|
||||
let engine = &spec.engine;
|
||||
let genesis_header = spec.genesis_header();
|
||||
let mut db_result = get_temp_journal_db();
|
||||
let mut db = db_result.take();
|
||||
engine.spec().ensure_db_good(db.as_hashdb_mut());
|
||||
spec.ensure_db_good(db.as_hashdb_mut());
|
||||
let s = State::from_existing(db, genesis_header.state_root.clone(), engine.account_start_nonce());
|
||||
assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000001")), U256::from(1u64));
|
||||
assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000002")), U256::from(1u64));
|
||||
@ -79,7 +80,7 @@ mod tests {
|
||||
let genesis = morden.genesis_block();
|
||||
assert_eq!(BlockView::new(&genesis).header_view().sha3(), H256::from_str("0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303").unwrap());
|
||||
|
||||
let _ = morden.to_engine();
|
||||
let _ = morden.engine;
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -90,6 +91,6 @@ mod tests {
|
||||
let genesis = frontier.genesis_block();
|
||||
assert_eq!(BlockView::new(&genesis).header_view().sha3(), H256::from_str("d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3").unwrap());
|
||||
|
||||
let _ = frontier.to_engine();
|
||||
let _ = frontier.engine;
|
||||
}
|
||||
}
|
||||
|
@ -327,7 +327,7 @@ mod tests {
|
||||
fn new() -> Self {
|
||||
TestSetup {
|
||||
state: get_temp_state(),
|
||||
engine: get_test_spec().to_engine().unwrap(),
|
||||
engine: get_test_spec().engine,
|
||||
sub_state: Substate::new(false),
|
||||
env_info: get_test_env_info()
|
||||
}
|
||||
|
@ -27,33 +27,6 @@ use substate::*;
|
||||
use tests::helpers::*;
|
||||
use ethjson;
|
||||
|
||||
struct TestEngineFrontier {
|
||||
vm_factory: Factory,
|
||||
spec: Spec,
|
||||
max_depth: usize
|
||||
}
|
||||
|
||||
impl TestEngineFrontier {
|
||||
fn new(max_depth: usize, vm_type: VMType) -> TestEngineFrontier {
|
||||
TestEngineFrontier {
|
||||
vm_factory: Factory::new(vm_type),
|
||||
spec: ethereum::new_frontier_test(),
|
||||
max_depth: max_depth
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Engine for TestEngineFrontier {
|
||||
fn name(&self) -> &str { "TestEngine" }
|
||||
fn spec(&self) -> &Spec { &self.spec }
|
||||
fn vm_factory(&self) -> &Factory { &self.vm_factory }
|
||||
fn schedule(&self, _env_info: &EnvInfo) -> Schedule {
|
||||
let mut schedule = Schedule::new_frontier();
|
||||
schedule.max_depth = self.max_depth;
|
||||
schedule
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct CallCreate {
|
||||
data: Bytes,
|
||||
@ -206,7 +179,7 @@ fn do_json_test_for(vm_type: &VMType, json_data: &[u8]) -> Vec<String> {
|
||||
let mut state = state_result.reference_mut();
|
||||
state.populate_from(From::from(vm.pre_state.clone()));
|
||||
let info = From::from(vm.env);
|
||||
let engine = TestEngineFrontier::new(1, vm_type.clone());
|
||||
let engine = TestEngine::new(1, Factory::new(vm_type.clone()));
|
||||
let params = ActionParams::from(vm.transaction);
|
||||
|
||||
let mut substate = Substate::new(false);
|
||||
|
@ -30,9 +30,9 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> {
|
||||
let tests = ethjson::state::Test::load(json_data).unwrap();
|
||||
let mut failed = Vec::new();
|
||||
let engine = match era {
|
||||
ChainEra::Frontier => ethereum::new_mainnet_like(),
|
||||
ChainEra::Homestead => ethereum::new_homestead_test(),
|
||||
}.to_engine().unwrap();
|
||||
ChainEra::Frontier => ethereum::new_mainnet_like().engine,
|
||||
ChainEra::Homestead => ethereum::new_homestead_test().engine
|
||||
};
|
||||
|
||||
for (name, test) in tests.into_iter() {
|
||||
let mut fail = false;
|
||||
|
@ -76,18 +76,6 @@ impl From<ethjson::state::Log> for LogEntry {
|
||||
}
|
||||
}
|
||||
|
||||
impl FromJson for LogEntry {
|
||||
/// Convert given JSON object to a LogEntry.
|
||||
fn from_json(json: &Json) -> LogEntry {
|
||||
// TODO: check bloom.
|
||||
LogEntry {
|
||||
address: xjson!(&json["address"]),
|
||||
topics: xjson!(&json["topics"]),
|
||||
data: xjson!(&json["data"]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Log localized in a blockchain.
|
||||
#[derive(Default, Debug, PartialEq, Clone)]
|
||||
pub struct LocalizedLogEntry {
|
||||
|
@ -14,26 +14,29 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use util::hash::Address;
|
||||
use builtin::Builtin;
|
||||
use engine::Engine;
|
||||
use spec::Spec;
|
||||
use evm::Schedule;
|
||||
use evm::Factory;
|
||||
use spec::CommonParams;
|
||||
use evm::{Schedule, Factory};
|
||||
use env_info::EnvInfo;
|
||||
|
||||
/// An engine which does not provide any consensus mechanism.
|
||||
pub struct NullEngine {
|
||||
spec: Spec,
|
||||
factory: Factory
|
||||
params: CommonParams,
|
||||
builtins: BTreeMap<Address, Builtin>,
|
||||
factory: Factory,
|
||||
}
|
||||
|
||||
impl NullEngine {
|
||||
/// Returns new instance of NullEngine with default VM Factory
|
||||
pub fn new_boxed(spec: Spec) -> Box<Engine> {
|
||||
Box::new(NullEngine{
|
||||
spec: spec,
|
||||
// TODO [todr] should this return any specific factory?
|
||||
pub fn new(params: CommonParams, builtins: BTreeMap<Address, Builtin>) -> Self {
|
||||
NullEngine{
|
||||
params: params,
|
||||
builtins: builtins,
|
||||
factory: Factory::default()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,12 +45,20 @@ impl Engine for NullEngine {
|
||||
&self.factory
|
||||
}
|
||||
|
||||
fn name(&self) -> &str { "NullEngine" }
|
||||
fn name(&self) -> &str {
|
||||
"NullEngine"
|
||||
}
|
||||
|
||||
fn spec(&self) -> &Spec { &self.spec }
|
||||
fn params(&self) -> &CommonParams {
|
||||
&self.params
|
||||
}
|
||||
|
||||
fn builtins(&self) -> &BTreeMap<Address, Builtin> {
|
||||
&self.builtins
|
||||
}
|
||||
|
||||
fn schedule(&self, env_info: &EnvInfo) -> Schedule {
|
||||
if env_info.number < self.u64_param("frontierCompatibilityModeLimit") {
|
||||
if env_info.number < self.params.frontier_compatibility_mode_limit {
|
||||
Schedule::new_frontier()
|
||||
} else {
|
||||
Schedule::new_homestead()
|
||||
|
@ -80,12 +80,22 @@ impl From<ethjson::blockchain::Account> for PodAccount {
|
||||
balance: a.balance.into(),
|
||||
nonce: a.nonce.into(),
|
||||
code: a.code.into(),
|
||||
storage: a.storage.into_iter().fold(BTreeMap::new(), |mut acc, (key, value)| {
|
||||
storage: a.storage.into_iter().map(|(key, value)| {
|
||||
let key: U256 = key.into();
|
||||
let value: U256 = value.into();
|
||||
acc.insert(H256::from(key), H256::from(value));
|
||||
acc
|
||||
})
|
||||
(H256::from(key), H256::from(value))
|
||||
}).collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ethjson::spec::Account> for PodAccount {
|
||||
fn from(a: ethjson::spec::Account) -> Self {
|
||||
PodAccount {
|
||||
balance: a.balance.map_or_else(U256::zero, Into::into),
|
||||
nonce: a.nonce.map_or_else(U256::zero, Into::into),
|
||||
code: vec![],
|
||||
storage: BTreeMap::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -46,33 +46,20 @@ impl PodState {
|
||||
pub fn drain(self) -> BTreeMap<Address, PodAccount> { self.0 }
|
||||
}
|
||||
|
||||
impl FromJson for PodState {
|
||||
/// Translate the JSON object into a hash map of account information ready for insertion into State.
|
||||
fn from_json(json: &Json) -> PodState {
|
||||
PodState(json.as_object().unwrap().iter().fold(BTreeMap::new(), |mut state, (address, acc)| {
|
||||
let balance = acc.find("balance").map(&U256::from_json);
|
||||
let nonce = acc.find("nonce").map(&U256::from_json);
|
||||
let storage = acc.find("storage").map(&BTreeMap::from_json);
|
||||
let code = acc.find("code").map(&Bytes::from_json);
|
||||
if balance.is_some() || nonce.is_some() || storage.is_some() || code.is_some() {
|
||||
state.insert(address_from_hex(address), PodAccount{
|
||||
balance: balance.unwrap_or_else(U256::zero),
|
||||
nonce: nonce.unwrap_or_else(U256::zero),
|
||||
storage: storage.unwrap_or_else(BTreeMap::new),
|
||||
code: code.unwrap_or_else(Vec::new)
|
||||
});
|
||||
}
|
||||
state
|
||||
}))
|
||||
impl From<ethjson::blockchain::State> for PodState {
|
||||
fn from(s: ethjson::blockchain::State) -> PodState {
|
||||
let state = s.into_iter().map(|(addr, acc)| (addr.into(), PodAccount::from(acc))).collect();
|
||||
PodState(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ethjson::blockchain::State> for PodState {
|
||||
fn from(s: ethjson::blockchain::State) -> PodState {
|
||||
PodState(s.0.into_iter().fold(BTreeMap::new(), |mut acc, (key, value)| {
|
||||
acc.insert(key.into(), PodAccount::from(value));
|
||||
acc
|
||||
}))
|
||||
impl From<ethjson::spec::State> for PodState {
|
||||
fn from(s: ethjson::spec::State) -> PodState {
|
||||
let state: BTreeMap<_,_> = s.into_iter()
|
||||
.filter(|pair| !pair.1.is_empty())
|
||||
.map(|(addr, acc)| (addr.into(), PodAccount::from(acc)))
|
||||
.collect();
|
||||
PodState(state)
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,30 +72,3 @@ impl fmt::Display for PodState {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
extern crate rustc_serialize;
|
||||
|
||||
use super::*;
|
||||
use rustc_serialize::*;
|
||||
use util::from_json::FromJson;
|
||||
use util::hash::*;
|
||||
|
||||
#[test]
|
||||
fn it_serializes_form_json() {
|
||||
let pod_state = PodState::from_json(&json::Json::from_str(
|
||||
r#"
|
||||
{
|
||||
"0000000000000000000000000000000000000000": {
|
||||
"balance": "1000",
|
||||
"nonce": "100",
|
||||
"storage": {},
|
||||
"code" : []
|
||||
}
|
||||
}
|
||||
"#
|
||||
).unwrap());
|
||||
|
||||
assert!(pod_state.get().get(&ZERO_ADDRESS).is_some());
|
||||
}
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ impl ClientService {
|
||||
panic_handler.forward_from(&net_service);
|
||||
|
||||
info!("Starting {}", net_service.host_info());
|
||||
info!("Configured for {} using {} engine", spec.name, spec.engine_name);
|
||||
info!("Configured for {} using {:?} engine", spec.name, spec.engine.name());
|
||||
let client = try!(Client::new(config, spec, db_path, net_service.io().channel()));
|
||||
panic_handler.forward_from(client.deref());
|
||||
let client_io = Arc::new(ClientIoHandler {
|
||||
|
@ -16,26 +16,9 @@
|
||||
|
||||
use util::rlp::*;
|
||||
use util::numbers::{Uint, U256};
|
||||
use util::hash::{H64, Address, H256};
|
||||
use util::hash::{Address, H256};
|
||||
use ethjson;
|
||||
|
||||
/// Genesis seal type.
|
||||
pub enum Seal {
|
||||
/// Classic ethereum seal.
|
||||
Ethereum {
|
||||
/// Seal nonce.
|
||||
nonce: H64,
|
||||
/// Seal mix hash.
|
||||
mix_hash: H256,
|
||||
},
|
||||
/// Generic seal.
|
||||
Generic {
|
||||
/// Number of seal fields.
|
||||
fields: usize,
|
||||
/// Seal rlp.
|
||||
rlp: Vec<u8>,
|
||||
},
|
||||
}
|
||||
use super::seal::Seal;
|
||||
|
||||
/// Genesis components.
|
||||
pub struct Genesis {
|
||||
@ -66,16 +49,7 @@ pub struct Genesis {
|
||||
impl From<ethjson::spec::Genesis> for Genesis {
|
||||
fn from(g: ethjson::spec::Genesis) -> Self {
|
||||
Genesis {
|
||||
seal: match (g.nonce, g.mix_hash) {
|
||||
(Some(nonce), Some(mix_hash)) => Seal::Ethereum {
|
||||
nonce: nonce.into(),
|
||||
mix_hash: mix_hash.into(),
|
||||
},
|
||||
_ => Seal::Generic {
|
||||
fields: g.seal_fields.unwrap(),
|
||||
rlp: g.seal_rlp.unwrap().into(),
|
||||
}
|
||||
},
|
||||
seal: From::from(g.seal),
|
||||
difficulty: g.difficulty.into(),
|
||||
author: g.author.into(),
|
||||
timestamp: g.timestamp.into(),
|
||||
|
@ -17,6 +17,7 @@
|
||||
//! Blockchain params.
|
||||
|
||||
mod genesis;
|
||||
mod seal;
|
||||
pub mod spec;
|
||||
|
||||
pub use self::spec::*;
|
||||
|
81
ethcore/src/spec/seal.rs
Normal file
81
ethcore/src/spec/seal.rs
Normal file
@ -0,0 +1,81 @@
|
||||
// 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/>.
|
||||
|
||||
//! Spec seal.
|
||||
|
||||
use util::rlp::*;
|
||||
use util::hash::{H64, H256};
|
||||
use ethjson;
|
||||
|
||||
/// Classic ethereum seal.
|
||||
pub struct Ethereum {
|
||||
/// Seal nonce.
|
||||
pub nonce: H64,
|
||||
/// Seal mix hash.
|
||||
pub mix_hash: H256,
|
||||
}
|
||||
|
||||
impl Into<Generic> for Ethereum {
|
||||
fn into(self) -> Generic {
|
||||
let mut s = RlpStream::new();
|
||||
s.append(&self.mix_hash);
|
||||
s.append(&self.nonce);
|
||||
Generic {
|
||||
fields: 2,
|
||||
rlp: s.out()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generic seal.
|
||||
pub struct Generic {
|
||||
/// Number of seal fields.
|
||||
pub fields: usize,
|
||||
/// Seal rlp.
|
||||
pub rlp: Vec<u8>,
|
||||
}
|
||||
|
||||
/// Genesis seal type.
|
||||
pub enum Seal {
|
||||
/// Classic ethereum seal.
|
||||
Ethereum(Ethereum),
|
||||
/// Generic seal.
|
||||
Generic(Generic),
|
||||
}
|
||||
|
||||
impl From<ethjson::spec::Seal> for Seal {
|
||||
fn from(s: ethjson::spec::Seal) -> Self {
|
||||
match s {
|
||||
ethjson::spec::Seal::Ethereum(eth) => Seal::Ethereum(Ethereum {
|
||||
nonce: eth.nonce.into(),
|
||||
mix_hash: eth.mix_hash.into()
|
||||
}),
|
||||
ethjson::spec::Seal::Generic(g) => Seal::Generic(Generic {
|
||||
fields: g.fields,
|
||||
rlp: g.rlp.into()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Generic> for Seal {
|
||||
fn into(self) -> Generic {
|
||||
match self {
|
||||
Seal::Generic(generic) => generic,
|
||||
Seal::Ethereum(eth) => eth.into()
|
||||
}
|
||||
}
|
||||
}
|
@ -21,55 +21,51 @@ use engine::*;
|
||||
use pod_state::*;
|
||||
use null_engine::*;
|
||||
use account_db::*;
|
||||
use super::genesis::Genesis;
|
||||
use super::seal::Generic as GenericSeal;
|
||||
use ethereum;
|
||||
use super::genesis::{Seal as GenesisSeal, Genesis};
|
||||
use ethjson;
|
||||
|
||||
/// Convert JSON value to equivalent RLP representation.
|
||||
// TODO: handle container types.
|
||||
fn json_to_rlp(json: &Json) -> Bytes {
|
||||
match *json {
|
||||
Json::Boolean(o) => encode(&(if o {1u64} else {0})).to_vec(),
|
||||
Json::I64(o) => encode(&(o as u64)).to_vec(),
|
||||
Json::U64(o) => encode(&o).to_vec(),
|
||||
Json::String(ref s) if s.len() >= 2 && &s[0..2] == "0x" && U256::from_str(&s[2..]).is_ok() => {
|
||||
encode(&U256::from_str(&s[2..]).unwrap()).to_vec()
|
||||
},
|
||||
Json::String(ref s) => {
|
||||
encode(s).to_vec()
|
||||
},
|
||||
_ => panic!()
|
||||
}
|
||||
/// Parameters common to all engines.
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct CommonParams {
|
||||
/// Account start nonce.
|
||||
pub account_start_nonce: U256,
|
||||
/// Frontier compatibility mode limit.
|
||||
pub frontier_compatibility_mode_limit: u64,
|
||||
/// Maximum size of extra data.
|
||||
pub maximum_extra_data_size: usize,
|
||||
/// Network id.
|
||||
pub network_id: U256,
|
||||
/// Minimum gas limit.
|
||||
pub min_gas_limit: U256,
|
||||
}
|
||||
|
||||
/// Convert JSON to a string->RLP map.
|
||||
fn json_to_rlp_map(json: &Json) -> HashMap<String, Bytes> {
|
||||
json.as_object().unwrap().iter().map(|(k, v)| (k, json_to_rlp(v))).fold(HashMap::new(), |mut acc, kv| {
|
||||
acc.insert(kv.0.clone(), kv.1);
|
||||
acc
|
||||
})
|
||||
impl From<ethjson::spec::Params> for CommonParams {
|
||||
fn from(p: ethjson::spec::Params) -> Self {
|
||||
CommonParams {
|
||||
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(),
|
||||
network_id: p.network_id.into(),
|
||||
min_gas_limit: p.min_gas_limit.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Parameters for a block chain; includes both those intrinsic to the design of the
|
||||
/// chain and those to be interpreted by the active chain engine.
|
||||
#[derive(Debug)]
|
||||
pub struct Spec {
|
||||
/// User friendly spec name
|
||||
pub name: String,
|
||||
/// What engine are we using for this?
|
||||
pub engine_name: String,
|
||||
pub engine: Box<Engine>,
|
||||
|
||||
/// Known nodes on the network in enode format.
|
||||
pub nodes: Vec<String>,
|
||||
/// Network ID
|
||||
pub network_id: U256,
|
||||
|
||||
/// Parameters concerning operation of the specific engine we're using.
|
||||
/// Maps the parameter name to an RLP-encoded value.
|
||||
pub engine_params: HashMap<String, Bytes>,
|
||||
|
||||
/// Builtin-contracts we would like to see in the chain.
|
||||
/// (In principle these are just hints for the engine since that has the last word on them.)
|
||||
pub builtins: BTreeMap<Address, Builtin>,
|
||||
/// Parameters common to all engines.
|
||||
pub params: CommonParams,
|
||||
|
||||
/// The genesis block's parent hash field.
|
||||
pub parent_hash: H256,
|
||||
@ -101,15 +97,41 @@ pub struct Spec {
|
||||
genesis_state: PodState,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature="dev", allow(wrong_self_convention))] // because to_engine(self) should be to_engine(&self)
|
||||
impl From<ethjson::spec::Spec> for Spec {
|
||||
fn from(s: ethjson::spec::Spec) -> Self {
|
||||
let builtins = s.accounts.builtins().into_iter().map(|p| (p.0.into(), From::from(p.1))).collect();
|
||||
let g = Genesis::from(s.genesis);
|
||||
let seal: GenericSeal = g.seal.into();
|
||||
let params = CommonParams::from(s.params);
|
||||
Spec {
|
||||
name: s.name.into(),
|
||||
params: params.clone(),
|
||||
engine: Spec::engine(s.engine, params, builtins),
|
||||
nodes: s.nodes.unwrap_or_else(Vec::new),
|
||||
parent_hash: g.parent_hash,
|
||||
transactions_root: g.transactions_root,
|
||||
receipts_root: g.receipts_root,
|
||||
author: g.author,
|
||||
difficulty: g.difficulty,
|
||||
gas_limit: g.gas_limit,
|
||||
gas_used: g.gas_used,
|
||||
timestamp: g.timestamp,
|
||||
extra_data: g.extra_data,
|
||||
seal_fields: seal.fields,
|
||||
seal_rlp: seal.rlp,
|
||||
state_root_memo: RwLock::new(g.state_root),
|
||||
genesis_state: From::from(s.accounts)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Spec {
|
||||
/// Convert this object into a boxed Engine of the right underlying type.
|
||||
// TODO avoid this hard-coded nastiness - use dynamic-linked plugin framework instead.
|
||||
pub fn to_engine(self) -> Result<Box<Engine>, Error> {
|
||||
match self.engine_name.as_ref() {
|
||||
"NullEngine" => Ok(NullEngine::new_boxed(self)),
|
||||
"Ethash" => Ok(ethereum::Ethash::new_boxed(self)),
|
||||
_ => Err(Error::UnknownEngineName(self.engine_name.clone()))
|
||||
/// Convert engine spec into a boxed Engine of the right underlying type.
|
||||
/// TODO avoid this hard-coded nastiness - use dynamic-linked plugin framework instead.
|
||||
fn engine(engine_spec: ethjson::spec::Engine, params: CommonParams, builtins: BTreeMap<Address, Builtin>) -> Box<Engine> {
|
||||
match engine_spec {
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,7 +147,7 @@ impl Spec {
|
||||
pub fn nodes(&self) -> &Vec<String> { &self.nodes }
|
||||
|
||||
/// Get the configured Network ID.
|
||||
pub fn network_id(&self) -> U256 { self.network_id }
|
||||
pub fn network_id(&self) -> U256 { self.params.network_id }
|
||||
|
||||
/// Get the header of the genesis block.
|
||||
pub fn genesis_header(&self) -> Header {
|
||||
@ -168,49 +190,9 @@ impl Spec {
|
||||
ret.out()
|
||||
}
|
||||
|
||||
/// Overwrite the genesis components with the given JSON, assuming standard Ethereum test format.
|
||||
pub fn overwrite_genesis(&mut self, genesis: &Json) {
|
||||
let (seal_fields, seal_rlp) = {
|
||||
if genesis.find("mixHash").is_some() && genesis.find("nonce").is_some() {
|
||||
let mut s = RlpStream::new();
|
||||
s.append(&H256::from_json(&genesis["mixHash"]));
|
||||
s.append(&H64::from_json(&genesis["nonce"]));
|
||||
(2, s.out())
|
||||
} else {
|
||||
// backup algo that will work with sealFields/sealRlp (and without).
|
||||
(
|
||||
u64::from_json(&genesis["sealFields"]) as usize,
|
||||
Bytes::from_json(&genesis["sealRlp"])
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
self.parent_hash = H256::from_json(&genesis["parentHash"]);
|
||||
self.transactions_root = genesis.find("transactionsTrie").and_then(|_| Some(H256::from_json(&genesis["transactionsTrie"]))).unwrap_or(SHA3_NULL_RLP.clone());
|
||||
self.receipts_root = genesis.find("receiptTrie").and_then(|_| Some(H256::from_json(&genesis["receiptTrie"]))).unwrap_or(SHA3_NULL_RLP.clone());
|
||||
self.author = Address::from_json(&genesis["coinbase"]);
|
||||
self.difficulty = U256::from_json(&genesis["difficulty"]);
|
||||
self.gas_limit = U256::from_json(&genesis["gasLimit"]);
|
||||
self.gas_used = U256::from_json(&genesis["gasUsed"]);
|
||||
self.timestamp = u64::from_json(&genesis["timestamp"]);
|
||||
self.extra_data = Bytes::from_json(&genesis["extraData"]);
|
||||
self.seal_fields = seal_fields;
|
||||
self.seal_rlp = seal_rlp;
|
||||
self.state_root_memo = RwLock::new(genesis.find("stateRoot").and_then(|_| Some(H256::from_json(&genesis["stateRoot"]))));
|
||||
}
|
||||
|
||||
/// Overwrite the genesis components.
|
||||
pub fn overwrite_genesis_params(&mut self, g: Genesis) {
|
||||
let (seal_fields, seal_rlp) = match g.seal {
|
||||
GenesisSeal::Generic { fields, rlp } => (fields, rlp),
|
||||
GenesisSeal::Ethereum { nonce, mix_hash } => {
|
||||
let mut s = RlpStream::new();
|
||||
s.append(&mix_hash);
|
||||
s.append(&nonce);
|
||||
(2, s.out())
|
||||
}
|
||||
};
|
||||
|
||||
let seal: GenericSeal = g.seal.into();
|
||||
self.parent_hash = g.parent_hash;
|
||||
self.transactions_root = g.transactions_root;
|
||||
self.receipts_root = g.receipts_root;
|
||||
@ -220,8 +202,8 @@ impl Spec {
|
||||
self.gas_used = g.gas_used;
|
||||
self.timestamp = g.timestamp;
|
||||
self.extra_data = g.extra_data;
|
||||
self.seal_fields = seal_fields;
|
||||
self.seal_rlp = seal_rlp;
|
||||
self.seal_fields = seal.fields;
|
||||
self.seal_rlp = seal.rlp;
|
||||
self.state_root_memo = RwLock::new(g.state_root);
|
||||
}
|
||||
|
||||
@ -235,74 +217,7 @@ impl Spec {
|
||||
pub fn is_state_root_valid(&self) -> bool {
|
||||
self.state_root_memo.read().unwrap().clone().map_or(true, |sr| sr == self.genesis_state.root())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromJson for Spec {
|
||||
/// Loads a chain-specification from a json data structure
|
||||
fn from_json(json: &Json) -> Spec {
|
||||
// once we commit ourselves to some json parsing library (serde?)
|
||||
// move it to proper data structure
|
||||
let mut builtins = BTreeMap::new();
|
||||
let mut state = PodState::new();
|
||||
|
||||
if let Some(&Json::Object(ref accounts)) = json.find("accounts") {
|
||||
for (address, acc) in accounts.iter() {
|
||||
let addr = Address::from_str(address).unwrap();
|
||||
if let Some(ref builtin_json) = acc.find("builtin") {
|
||||
if let Some(builtin) = Builtin::from_json(builtin_json) {
|
||||
builtins.insert(addr.clone(), builtin);
|
||||
}
|
||||
}
|
||||
}
|
||||
state = xjson!(&json["accounts"]);
|
||||
}
|
||||
|
||||
let nodes = if let Some(&Json::Array(ref ns)) = json.find("nodes") {
|
||||
ns.iter().filter_map(|n| if let Json::String(ref s) = *n { Some(s.clone()) } else {None}).collect()
|
||||
} else { Vec::new() };
|
||||
|
||||
let genesis = &json["genesis"];//.as_object().expect("No genesis object in JSON");
|
||||
|
||||
let (seal_fields, seal_rlp) = {
|
||||
if genesis.find("mixHash").is_some() && genesis.find("nonce").is_some() {
|
||||
let mut s = RlpStream::new();
|
||||
s.append(&H256::from_str(&genesis["mixHash"].as_string().expect("mixHash not a string.")[2..]).expect("Invalid mixHash string value"));
|
||||
s.append(&H64::from_str(&genesis["nonce"].as_string().expect("nonce not a string.")[2..]).expect("Invalid nonce string value"));
|
||||
(2, s.out())
|
||||
} else {
|
||||
// backup algo that will work with sealFields/sealRlp (and without).
|
||||
(
|
||||
usize::from_str(&genesis["sealFields"].as_string().unwrap_or("0x")[2..]).expect("Invalid sealFields integer data"),
|
||||
genesis["sealRlp"].as_string().unwrap_or("0x")[2..].from_hex().expect("Invalid sealRlp hex data")
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
Spec {
|
||||
name: json.find("name").map_or("unknown", |j| j.as_string().unwrap()).to_owned(),
|
||||
engine_name: json["engineName"].as_string().unwrap().to_owned(),
|
||||
engine_params: json_to_rlp_map(&json["params"]),
|
||||
nodes: nodes,
|
||||
network_id: U256::from_str(&json["params"]["networkID"].as_string().unwrap()[2..]).unwrap(),
|
||||
builtins: builtins,
|
||||
parent_hash: H256::from_str(&genesis["parentHash"].as_string().unwrap()[2..]).unwrap(),
|
||||
author: Address::from_str(&genesis["author"].as_string().unwrap()[2..]).unwrap(),
|
||||
difficulty: U256::from_str(&genesis["difficulty"].as_string().unwrap()[2..]).unwrap(),
|
||||
gas_limit: U256::from_str(&genesis["gasLimit"].as_string().unwrap()[2..]).unwrap(),
|
||||
gas_used: U256::from(0u8),
|
||||
timestamp: u64::from_str(&genesis["timestamp"].as_string().unwrap()[2..]).unwrap(),
|
||||
transactions_root: SHA3_NULL_RLP.clone(),
|
||||
receipts_root: SHA3_NULL_RLP.clone(),
|
||||
extra_data: genesis["extraData"].as_string().unwrap()[2..].from_hex().unwrap(),
|
||||
genesis_state: state,
|
||||
seal_fields: seal_fields,
|
||||
seal_rlp: seal_rlp,
|
||||
state_root_memo: RwLock::new(genesis.find("stateRoot").and_then(|_| genesis["stateRoot"].as_string()).map(|s| H256::from_str(&s[2..]).unwrap())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Spec {
|
||||
/// Ensure that the given state DB has the trie nodes in for the genesis state.
|
||||
pub fn ensure_db_good(&self, db: &mut HashDB) -> bool {
|
||||
if !db.contains(&self.state_root()) {
|
||||
@ -321,21 +236,20 @@ impl Spec {
|
||||
} else { false }
|
||||
}
|
||||
|
||||
/// Create a new Spec from a JSON UTF-8 data resource `data`.
|
||||
pub fn from_json_utf8(data: &[u8]) -> Spec {
|
||||
Self::from_json_str(::std::str::from_utf8(data).unwrap())
|
||||
}
|
||||
|
||||
/// Create a new Spec from a JSON string.
|
||||
pub fn from_json_str(s: &str) -> Spec {
|
||||
Self::from_json(&Json::from_str(s).expect("Json is invalid"))
|
||||
/// Loads spec from json file.
|
||||
pub fn load(reader: &[u8]) -> Self {
|
||||
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.
|
||||
pub fn new_test() -> Spec { Self::from_json_utf8(include_bytes!("../../res/null_morden.json")) }
|
||||
pub fn new_test() -> Spec {
|
||||
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 { Self::from_json_utf8(include_bytes!("../../res/null_homestead_morden.json")) }
|
||||
pub fn new_homestead_test() -> Spec {
|
||||
Spec::load(include_bytes!("../../res/null_homestead_morden.json"))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -353,7 +267,5 @@ mod tests {
|
||||
assert_eq!(test_spec.state_root(), H256::from_str("f3f4696bbf3b3b07775128eb7a3763279a394e382130f27c21e70233e04946a9").unwrap());
|
||||
let genesis = test_spec.genesis_block();
|
||||
assert_eq!(BlockView::new(&genesis).header_view().sha3(), H256::from_str("0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303").unwrap());
|
||||
|
||||
let _ = test_spec.to_engine();
|
||||
}
|
||||
}
|
||||
|
@ -548,7 +548,7 @@ fn should_not_trace_call_transaction_to_builtin() {
|
||||
|
||||
let mut info = EnvInfo::default();
|
||||
info.gas_limit = x!(1_000_000);
|
||||
let engine = Spec::new_test().to_engine().unwrap();
|
||||
let engine = Spec::new_test().engine;
|
||||
|
||||
let t = Transaction {
|
||||
nonce: x!(0),
|
||||
@ -573,7 +573,7 @@ fn should_not_trace_subcall_transaction_to_builtin() {
|
||||
|
||||
let mut info = EnvInfo::default();
|
||||
info.gas_limit = x!(1_000_000);
|
||||
let engine = Spec::new_test().to_engine().unwrap();
|
||||
let engine = Spec::new_test().engine;
|
||||
|
||||
let t = Transaction {
|
||||
nonce: x!(0),
|
||||
@ -611,7 +611,7 @@ fn should_not_trace_callcode() {
|
||||
|
||||
let mut info = EnvInfo::default();
|
||||
info.gas_limit = x!(1_000_000);
|
||||
let engine = Spec::new_test().to_engine().unwrap();
|
||||
let engine = Spec::new_test().engine;
|
||||
|
||||
let t = Transaction {
|
||||
nonce: x!(0),
|
||||
@ -651,7 +651,7 @@ fn should_not_trace_delegatecall() {
|
||||
let mut info = EnvInfo::default();
|
||||
info.gas_limit = x!(1_000_000);
|
||||
info.number = 0x789b0;
|
||||
let engine = Spec::new_test().to_engine().unwrap();
|
||||
let engine = Spec::new_test().engine;
|
||||
|
||||
println!("schedule.have_delegate_call: {:?}", engine.schedule(&info).have_delegate_call);
|
||||
|
||||
|
@ -40,8 +40,7 @@ fn returns_state_root_basic() {
|
||||
let client_result = generate_dummy_client(6);
|
||||
let client = client_result.reference();
|
||||
let test_spec = get_test_spec();
|
||||
let test_engine = test_spec.to_engine().unwrap();
|
||||
let state_root = test_engine.spec().genesis_header().state_root;
|
||||
let state_root = test_spec.genesis_header().state_root;
|
||||
|
||||
assert!(client.state_data(&state_root).is_some());
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ impl<T> GuardedTempResult<T> {
|
||||
|
||||
pub struct TestEngine {
|
||||
factory: Factory,
|
||||
spec: Spec,
|
||||
engine: Box<Engine>,
|
||||
max_depth: usize
|
||||
}
|
||||
|
||||
@ -60,18 +60,29 @@ impl TestEngine {
|
||||
pub fn new(max_depth: usize, factory: Factory) -> TestEngine {
|
||||
TestEngine {
|
||||
factory: factory,
|
||||
spec: ethereum::new_frontier_test(),
|
||||
engine: ethereum::new_frontier_test().engine,
|
||||
max_depth: max_depth
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Engine for TestEngine {
|
||||
fn name(&self) -> &str { "TestEngine" }
|
||||
fn spec(&self) -> &Spec { &self.spec }
|
||||
fn name(&self) -> &str {
|
||||
"TestEngine"
|
||||
}
|
||||
|
||||
fn params(&self) -> &CommonParams {
|
||||
self.engine.params()
|
||||
}
|
||||
|
||||
fn builtins(&self) -> &BTreeMap<Address, Builtin> {
|
||||
self.engine.builtins()
|
||||
}
|
||||
|
||||
fn vm_factory(&self) -> &Factory {
|
||||
&self.factory
|
||||
}
|
||||
|
||||
fn schedule(&self, _env_info: &EnvInfo) -> Schedule {
|
||||
let mut schedule = Schedule::new_frontier();
|
||||
schedule.max_depth = self.max_depth;
|
||||
@ -136,17 +147,17 @@ pub fn generate_dummy_client(block_number: u32) -> GuardedTempResult<Arc<Client>
|
||||
|
||||
let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap();
|
||||
let test_spec = get_test_spec();
|
||||
let test_engine = test_spec.to_engine().unwrap();
|
||||
let state_root = test_engine.spec().genesis_header().state_root;
|
||||
let mut rolling_hash = test_engine.spec().genesis_header().hash();
|
||||
let test_engine = &test_spec.engine;
|
||||
let state_root = test_spec.genesis_header().state_root;
|
||||
let mut rolling_hash = test_spec.genesis_header().hash();
|
||||
let mut rolling_block_number = 1;
|
||||
let mut rolling_timestamp = 40;
|
||||
|
||||
for _ in 0..block_number {
|
||||
let mut header = Header::new();
|
||||
|
||||
header.gas_limit = decode(test_engine.spec().engine_params.get("minGasLimit").unwrap());
|
||||
header.difficulty = decode(test_engine.spec().engine_params.get("minimumDifficulty").unwrap());
|
||||
header.gas_limit = test_engine.params().min_gas_limit;
|
||||
header.difficulty = U256::from(0x20000);
|
||||
header.timestamp = rolling_timestamp;
|
||||
header.number = rolling_block_number;
|
||||
header.parent_hash = rolling_hash;
|
||||
@ -171,8 +182,9 @@ pub fn generate_dummy_client(block_number: u32) -> GuardedTempResult<Arc<Client>
|
||||
|
||||
pub fn push_blocks_to_client(client: &Arc<Client>, timestamp_salt: u64, starting_number: usize, block_number: usize) {
|
||||
let test_spec = get_test_spec();
|
||||
let test_engine = test_spec.to_engine().unwrap();
|
||||
let state_root = test_engine.spec().genesis_header().state_root;
|
||||
let test_engine = &test_spec.engine;
|
||||
//let test_engine = test_spec.to_engine().unwrap();
|
||||
let state_root = test_spec.genesis_header().state_root;
|
||||
let mut rolling_hash = client.chain_info().best_block_hash;
|
||||
let mut rolling_block_number = starting_number as u64;
|
||||
let mut rolling_timestamp = timestamp_salt + starting_number as u64 * 10;
|
||||
@ -180,8 +192,8 @@ pub fn push_blocks_to_client(client: &Arc<Client>, timestamp_salt: u64, starting
|
||||
for _ in 0..block_number {
|
||||
let mut header = Header::new();
|
||||
|
||||
header.gas_limit = decode(test_engine.spec().engine_params.get("minGasLimit").unwrap());
|
||||
header.difficulty = decode(test_engine.spec().engine_params.get("minimumDifficulty").unwrap());
|
||||
header.gas_limit = test_engine.params().min_gas_limit;
|
||||
header.difficulty = U256::from(0x20000);
|
||||
header.timestamp = rolling_timestamp;
|
||||
header.number = rolling_block_number;
|
||||
header.parent_hash = rolling_hash;
|
||||
@ -279,24 +291,23 @@ pub fn get_temp_state_in(path: &Path) -> State {
|
||||
|
||||
pub fn get_good_dummy_block_seq(count: usize) -> Vec<Bytes> {
|
||||
let test_spec = get_test_spec();
|
||||
let test_engine = test_spec.to_engine().unwrap();
|
||||
get_good_dummy_block_fork_seq(1, count, &test_engine.spec().genesis_header().hash())
|
||||
get_good_dummy_block_fork_seq(1, count, &test_spec.genesis_header().hash())
|
||||
}
|
||||
|
||||
pub fn get_good_dummy_block_fork_seq(start_number: usize, count: usize, parent_hash: &H256) -> Vec<Bytes> {
|
||||
let test_spec = get_test_spec();
|
||||
let test_engine = test_spec.to_engine().unwrap();
|
||||
let test_engine = &test_spec.engine;
|
||||
let mut rolling_timestamp = start_number as u64 * 10;
|
||||
let mut parent = *parent_hash;
|
||||
let mut r = Vec::new();
|
||||
for i in start_number .. start_number + count + 1 {
|
||||
let mut block_header = Header::new();
|
||||
block_header.gas_limit = decode(test_engine.spec().engine_params.get("minGasLimit").unwrap());
|
||||
block_header.gas_limit = test_engine.params().min_gas_limit;
|
||||
block_header.difficulty = U256::from(i).mul(U256([0, 1, 0, 0]));
|
||||
block_header.timestamp = rolling_timestamp;
|
||||
block_header.number = i as u64;
|
||||
block_header.parent_hash = parent;
|
||||
block_header.state_root = test_engine.spec().genesis_header().state_root;
|
||||
block_header.state_root = test_spec.genesis_header().state_root;
|
||||
|
||||
parent = block_header.hash();
|
||||
rolling_timestamp = rolling_timestamp + 10;
|
||||
@ -310,13 +321,13 @@ pub fn get_good_dummy_block_fork_seq(start_number: usize, count: usize, parent_h
|
||||
pub fn get_good_dummy_block() -> Bytes {
|
||||
let mut block_header = Header::new();
|
||||
let test_spec = get_test_spec();
|
||||
let test_engine = test_spec.to_engine().unwrap();
|
||||
block_header.gas_limit = decode(test_engine.spec().engine_params.get("minGasLimit").unwrap());
|
||||
block_header.difficulty = decode(test_engine.spec().engine_params.get("minimumDifficulty").unwrap());
|
||||
let test_engine = &test_spec.engine;
|
||||
block_header.gas_limit = test_engine.params().min_gas_limit;
|
||||
block_header.difficulty = U256::from(0x20000);
|
||||
block_header.timestamp = 40;
|
||||
block_header.number = 1;
|
||||
block_header.parent_hash = test_engine.spec().genesis_header().hash();
|
||||
block_header.state_root = test_engine.spec().genesis_header().state_root;
|
||||
block_header.parent_hash = test_spec.genesis_header().hash();
|
||||
block_header.state_root = test_spec.genesis_header().state_root;
|
||||
|
||||
create_test_block(&block_header)
|
||||
}
|
||||
@ -324,12 +335,12 @@ pub fn get_good_dummy_block() -> Bytes {
|
||||
pub fn get_bad_state_dummy_block() -> Bytes {
|
||||
let mut block_header = Header::new();
|
||||
let test_spec = get_test_spec();
|
||||
let test_engine = test_spec.to_engine().unwrap();
|
||||
block_header.gas_limit = decode(test_engine.spec().engine_params.get("minGasLimit").unwrap());
|
||||
block_header.difficulty = decode(test_engine.spec().engine_params.get("minimumDifficulty").unwrap());
|
||||
let test_engine = &test_spec.engine;
|
||||
block_header.gas_limit = test_engine.params().min_gas_limit;
|
||||
block_header.difficulty = U256::from(0x20000);
|
||||
block_header.timestamp = 40;
|
||||
block_header.number = 1;
|
||||
block_header.parent_hash = test_engine.spec().genesis_header().hash();
|
||||
block_header.parent_hash = test_spec.genesis_header().hash();
|
||||
block_header.state_root = x!(0xbad);
|
||||
|
||||
create_test_block(&block_header)
|
||||
|
@ -183,7 +183,7 @@ fn verify_header(header: &Header, engine: &Engine) -> Result<(), Error> {
|
||||
if header.gas_used > header.gas_limit {
|
||||
return Err(From::from(BlockError::TooMuchGasUsed(OutOfBounds { max: Some(header.gas_limit), min: None, found: header.gas_used })));
|
||||
}
|
||||
let min_gas_limit = decode(engine.spec().engine_params.get("minGasLimit").unwrap());
|
||||
let min_gas_limit = engine.params().min_gas_limit;
|
||||
if header.gas_limit < min_gas_limit {
|
||||
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas_limit), max: None, found: header.gas_limit })));
|
||||
}
|
||||
@ -336,12 +336,10 @@ mod tests {
|
||||
// Test against morden
|
||||
let mut good = Header::new();
|
||||
let spec = Spec::new_test();
|
||||
let engine = spec.to_engine().unwrap();
|
||||
let engine = &spec.engine;
|
||||
|
||||
let min_gas_limit = decode(engine.spec().engine_params.get("minGasLimit").unwrap());
|
||||
let min_difficulty = decode(engine.spec().engine_params.get("minimumDifficulty").unwrap());
|
||||
let min_gas_limit = engine.params().min_gas_limit;
|
||||
good.gas_limit = min_gas_limit;
|
||||
good.difficulty = min_difficulty;
|
||||
good.timestamp = 40;
|
||||
good.number = 10;
|
||||
|
||||
|
@ -21,7 +21,7 @@ use hash::H256;
|
||||
use blockchain::state::State;
|
||||
use blockchain::header::Header;
|
||||
use blockchain::block::Block;
|
||||
use spec::Genesis;
|
||||
use spec::{Genesis, Seal, Ethereum};
|
||||
|
||||
/// Blockchain deserialization.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
@ -54,10 +54,10 @@ impl BlockChain {
|
||||
/// Returns spec compatible genesis struct.
|
||||
pub fn genesis(&self) -> Genesis {
|
||||
Genesis {
|
||||
nonce: Some(self.genesis_block.nonce.clone()),
|
||||
mix_hash: Some(self.genesis_block.mix_hash.clone()),
|
||||
seal_fields: None,
|
||||
seal_rlp: None,
|
||||
seal: Seal::Ethereum(Ethereum {
|
||||
nonce: self.genesis_block.nonce.clone(),
|
||||
mix_hash: self.genesis_block.mix_hash.clone(),
|
||||
}),
|
||||
difficulty: self.genesis_block.difficulty,
|
||||
author: self.genesis_block.author.clone(),
|
||||
timestamp: self.genesis_block.timestamp,
|
||||
|
@ -22,7 +22,7 @@ use blockchain::account::Account;
|
||||
|
||||
/// Blockchain test state deserializer.
|
||||
#[derive(Debug, PartialEq, Deserialize, Clone)]
|
||||
pub struct State(pub BTreeMap<Address, Account>);
|
||||
pub struct State(BTreeMap<Address, Account>);
|
||||
|
||||
impl IntoIterator for State {
|
||||
type Item = <BTreeMap<Address, Account> as IntoIterator>::Item;
|
||||
|
@ -22,9 +22,19 @@ use spec::builtin::Builtin;
|
||||
/// Spec account.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
pub struct Account {
|
||||
builtin: Option<Builtin>,
|
||||
balance: Option<Uint>,
|
||||
nonce: Option<Uint>,
|
||||
/// Builtin contract.
|
||||
pub builtin: Option<Builtin>,
|
||||
/// Balance.
|
||||
pub balance: Option<Uint>,
|
||||
/// Nonce.
|
||||
pub nonce: Option<Uint>,
|
||||
}
|
||||
|
||||
impl Account {
|
||||
/// Returns true if account does not have nonce and balance.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.balance.is_none() && self.nonce.is_none()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -17,14 +17,16 @@
|
||||
//! Spec builtin deserialization.
|
||||
|
||||
/// Linear pricing.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
#[derive(Debug, PartialEq, Deserialize, Clone)]
|
||||
pub struct Linear {
|
||||
base: u64,
|
||||
word: u64,
|
||||
/// Base price.
|
||||
pub base: usize,
|
||||
/// Price for word.
|
||||
pub word: usize,
|
||||
}
|
||||
|
||||
/// Pricing variants.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
#[derive(Debug, PartialEq, Deserialize, Clone)]
|
||||
pub enum Pricing {
|
||||
/// Linear pricing.
|
||||
#[serde(rename="linear")]
|
||||
@ -32,10 +34,12 @@ pub enum Pricing {
|
||||
}
|
||||
|
||||
/// Spec builtin.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
#[derive(Debug, PartialEq, Deserialize, Clone)]
|
||||
pub struct Builtin {
|
||||
name: String,
|
||||
pricing: Pricing,
|
||||
/// Builtin name.
|
||||
pub name: String,
|
||||
/// Builtin pricing.
|
||||
pub pricing: Pricing,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
63
json/src/spec/engine.rs
Normal file
63
json/src/spec/engine.rs
Normal file
@ -0,0 +1,63 @@
|
||||
// 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/>.
|
||||
|
||||
//! Engine deserialization.
|
||||
|
||||
use serde::{Deserializer, Error};
|
||||
use serde::de::Visitor;
|
||||
use spec::Ethash;
|
||||
|
||||
/// Engine deserialization.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
pub enum Engine {
|
||||
/// Null engine.
|
||||
Null,
|
||||
/// Ethash engine.
|
||||
Ethash(Ethash),
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use serde_json;
|
||||
use spec::Engine;
|
||||
|
||||
#[test]
|
||||
fn engine_deserialization() {
|
||||
let s = r#"{
|
||||
"Null": null
|
||||
}"#;
|
||||
|
||||
let deserialized: Engine = serde_json::from_str(s).unwrap();
|
||||
assert_eq!(Engine::Null, deserialized);
|
||||
|
||||
let s = r#"{
|
||||
"Ethash": {
|
||||
"params": {
|
||||
"tieBreakingGas": false,
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
|
||||
}
|
||||
}
|
||||
}"#;
|
||||
|
||||
let _deserialized: Engine = serde_json::from_str(s).unwrap();
|
||||
}
|
||||
}
|
||||
|
75
json/src/spec/ethash.rs
Normal file
75
json/src/spec/ethash.rs
Normal file
@ -0,0 +1,75 @@
|
||||
// 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 EthashParams {
|
||||
/// Tie breaking gas.
|
||||
#[serde(rename="tieBreakingGas")]
|
||||
pub tie_breaking_gas: bool,
|
||||
/// Gas limit divisor.
|
||||
#[serde(rename="gasLimitBoundDivisor")]
|
||||
pub gas_limit_bound_divisor: Uint,
|
||||
/// Minimum difficulty.
|
||||
#[serde(rename="minimumDifficulty")]
|
||||
pub minimum_difficulty: Uint,
|
||||
/// Difficulty bound divisor.
|
||||
#[serde(rename="difficultyBoundDivisor")]
|
||||
pub difficulty_bound_divisor: Uint,
|
||||
/// Block duration.
|
||||
#[serde(rename="durationLimit")]
|
||||
pub duration_limit: Uint,
|
||||
/// Block reward.
|
||||
#[serde(rename="blockReward")]
|
||||
pub block_reward: Uint,
|
||||
/// Namereg contract address.
|
||||
pub registrar: Address,
|
||||
}
|
||||
|
||||
/// Ethash engine deserialization.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
pub struct Ethash {
|
||||
/// Ethash params.
|
||||
pub params: EthashParams,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use serde_json;
|
||||
use spec::ethash::Ethash;
|
||||
|
||||
#[test]
|
||||
fn ethash_deserialization() {
|
||||
let s = r#"{
|
||||
"params": {
|
||||
"tieBreakingGas": false,
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
|
||||
}
|
||||
}"#;
|
||||
|
||||
let _deserialized: Ethash = serde_json::from_str(s).unwrap();
|
||||
}
|
||||
}
|
@ -17,27 +17,15 @@
|
||||
//! Spec genesis deserialization.
|
||||
|
||||
use uint::Uint;
|
||||
use hash::{H64, Address, H256};
|
||||
use hash::{Address, H256};
|
||||
use bytes::Bytes;
|
||||
use spec::Seal;
|
||||
|
||||
/// Spec genesis.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
pub struct Genesis {
|
||||
// old seal
|
||||
/// Seal nonce.
|
||||
pub nonce: Option<H64>,
|
||||
#[serde(rename="mixHash")]
|
||||
/// Seal mix hash.
|
||||
pub mix_hash: Option<H256>,
|
||||
|
||||
// new seal // TODO: consider moving it to a separate seal structure
|
||||
#[serde(rename="sealFields")]
|
||||
/// Number of seal fields.
|
||||
pub seal_fields: Option<usize>,
|
||||
#[serde(rename="sealRlp")]
|
||||
/// Seal rlp.
|
||||
pub seal_rlp: Option<Bytes>,
|
||||
|
||||
/// Seal.
|
||||
pub seal: Seal,
|
||||
/// Difficulty.
|
||||
pub difficulty: Uint,
|
||||
/// Block author.
|
||||
@ -77,7 +65,12 @@ mod tests {
|
||||
let s = r#"{
|
||||
"nonce": "0x0000000000000042",
|
||||
"difficulty": "0x400000000",
|
||||
"seal": {
|
||||
"ethereum": {
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x00006d6f7264656e"
|
||||
}
|
||||
},
|
||||
"author": "0x0000000000000000000000000000000000000000",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
|
@ -21,9 +21,17 @@ pub mod builtin;
|
||||
pub mod genesis;
|
||||
pub mod params;
|
||||
pub mod spec;
|
||||
pub mod seal;
|
||||
pub mod engine;
|
||||
pub mod state;
|
||||
pub mod ethash;
|
||||
|
||||
pub use self::account::Account;
|
||||
pub use self::builtin::Builtin;
|
||||
pub use self::builtin::{Builtin, Pricing, Linear};
|
||||
pub use self::genesis::Genesis;
|
||||
pub use self::params::Params;
|
||||
pub use self::spec::Spec;
|
||||
pub use self::seal::{Seal, Ethereum, Generic};
|
||||
pub use self::engine::Engine;
|
||||
pub use self::state::State;
|
||||
pub use self::ethash::{Ethash, EthashParams};
|
||||
|
@ -17,34 +17,25 @@
|
||||
//! Spec params deserialization.
|
||||
|
||||
use uint::Uint;
|
||||
use hash::Address;
|
||||
|
||||
/// Spec params.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
pub struct Params {
|
||||
/// Account start nonce.
|
||||
#[serde(rename="accountStartNonce")]
|
||||
account_start_nonce: Uint,
|
||||
pub account_start_nonce: Uint,
|
||||
/// Homestead transition block number.
|
||||
#[serde(rename="frontierCompatibilityModeLimit")]
|
||||
frontier_compatibility_mode_limit: Uint,
|
||||
pub frontier_compatibility_mode_limit: Uint,
|
||||
/// Maximum size of extra data.
|
||||
#[serde(rename="maximumExtraDataSize")]
|
||||
maximum_extra_data_size: Uint,
|
||||
#[serde(rename="tieBreakingGas")]
|
||||
tie_breaking_gas: bool,
|
||||
#[serde(rename="minGasLimit")]
|
||||
min_gas_limit: Uint,
|
||||
#[serde(rename="gasLimitBoundDivisor")]
|
||||
gas_limit_bound_divisor: Uint,
|
||||
#[serde(rename="minimumDifficulty")]
|
||||
minimum_difficulty: Uint,
|
||||
#[serde(rename="difficultyBoundDivisor")]
|
||||
difficulty_bound_divisor: Uint,
|
||||
#[serde(rename="durationLimit")]
|
||||
duration_limit: Uint,
|
||||
#[serde(rename="blockReward")]
|
||||
block_reward: Uint,
|
||||
registrar: Address,
|
||||
pub maximum_extra_data_size: Uint,
|
||||
/// Network id.
|
||||
#[serde(rename="networkID")]
|
||||
network_id: Uint,
|
||||
pub network_id: Uint,
|
||||
/// Minimum gas limit.
|
||||
#[serde(rename="minGasLimit")]
|
||||
pub min_gas_limit: Uint,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -55,19 +46,13 @@ mod tests {
|
||||
#[test]
|
||||
fn params_deserialization() {
|
||||
let s = r#"{
|
||||
"accountStartNonce": "0x00",
|
||||
"frontierCompatibilityModeLimit": "0x118c30",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"tieBreakingGas": false,
|
||||
"networkID" : "0x1",
|
||||
"minGasLimit": "0x1388",
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"networkID" : "0x1"
|
||||
"accountStartNonce": "0x00"
|
||||
}"#;
|
||||
|
||||
let _deserialized: Params = serde_json::from_str(s).unwrap();
|
||||
// TODO: validate all fields
|
||||
}
|
||||
|
73
json/src/spec/seal.rs
Normal file
73
json/src/spec/seal.rs
Normal file
@ -0,0 +1,73 @@
|
||||
// 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/>.
|
||||
|
||||
//! Spec seal deserialization.
|
||||
|
||||
use hash::{H64, H256};
|
||||
use bytes::Bytes;
|
||||
|
||||
/// Ethereum seal.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
pub struct Ethereum {
|
||||
/// Seal nonce.
|
||||
pub nonce: H64,
|
||||
/// Seal mix hash.
|
||||
#[serde(rename="mixHash")]
|
||||
pub mix_hash: H256,
|
||||
}
|
||||
|
||||
/// Generic seal.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
pub struct Generic {
|
||||
/// Number of fields.
|
||||
pub fields: usize,
|
||||
/// Their rlp.
|
||||
pub rlp: Bytes,
|
||||
}
|
||||
|
||||
/// Seal variants.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
pub enum Seal {
|
||||
/// Ethereum seal.
|
||||
#[serde(rename="ethereum")]
|
||||
Ethereum(Ethereum),
|
||||
/// Generic seal.
|
||||
#[serde(rename="generic")]
|
||||
Generic(Generic),
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use serde_json;
|
||||
use spec::Seal;
|
||||
|
||||
#[test]
|
||||
fn builtin_deserialization() {
|
||||
let s = r#"[{
|
||||
"ethereum": {
|
||||
"nonce": "0x0000000000000042",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
},{
|
||||
"generic": {
|
||||
"fields": 1,
|
||||
"rlp": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa"
|
||||
}
|
||||
}]"#;
|
||||
let _deserialized: Vec<Seal> = serde_json::from_str(s).unwrap();
|
||||
// TODO: validate all fields
|
||||
}
|
||||
}
|
@ -16,21 +16,33 @@
|
||||
|
||||
//! Spec deserialization.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use hash::Address;
|
||||
use spec::account::Account;
|
||||
use spec::params::Params;
|
||||
use spec::genesis::Genesis;
|
||||
use std::io::Read;
|
||||
use serde_json;
|
||||
use serde_json::Error;
|
||||
use spec::{Params, Genesis, Engine, State};
|
||||
|
||||
/// Spec deserialization.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
pub struct Spec {
|
||||
name: String,
|
||||
#[serde(rename="engineName")]
|
||||
engine_name: String, // TODO: consider making it an enum
|
||||
params: Params,
|
||||
genesis: Genesis,
|
||||
accounts: BTreeMap<Address, Account>,
|
||||
/// Spec name.
|
||||
pub name: String,
|
||||
/// Engine.
|
||||
pub engine: Engine,
|
||||
/// Spec params.
|
||||
pub params: Params,
|
||||
/// Genesis header.
|
||||
pub genesis: Genesis,
|
||||
/// Genesis state.
|
||||
pub accounts: State,
|
||||
/// Boot nodes.
|
||||
pub nodes: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
impl Spec {
|
||||
/// Loads test from json.
|
||||
pub fn load<R>(reader: R) -> Result<Self, Error> where R: Read {
|
||||
serde_json::from_reader(reader)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -42,25 +54,34 @@ mod tests {
|
||||
fn spec_deserialization() {
|
||||
let s = r#"{
|
||||
"name": "Morden",
|
||||
"engineName": "Ethash",
|
||||
"engine": {
|
||||
"Ethash": {
|
||||
"params": {
|
||||
"accountStartNonce": "0x0100000",
|
||||
"frontierCompatibilityModeLimit": "0x789b0",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"tieBreakingGas": false,
|
||||
"minGasLimit": "0x1388",
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar": "",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
|
||||
}
|
||||
}
|
||||
},
|
||||
"params": {
|
||||
"accountStartNonce": "0x0100000",
|
||||
"frontierCompatibilityModeLimit": "0x789b0",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x2"
|
||||
},
|
||||
"genesis": {
|
||||
"nonce": "0x00006d6f7264656e",
|
||||
"seal": {
|
||||
"ethereum": {
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x00006d6f7264656e"
|
||||
}
|
||||
},
|
||||
"difficulty": "0x20000",
|
||||
"mixHash": "0x00000000000000000000000000000000000000647572616c65787365646c6578",
|
||||
"author": "0x0000000000000000000000000000000000000000",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
|
44
json/src/spec/state.rs
Normal file
44
json/src/spec/state.rs
Normal file
@ -0,0 +1,44 @@
|
||||
// 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/>.
|
||||
|
||||
//! Blockchain test state deserializer.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use hash::Address;
|
||||
use spec::{Account, Builtin};
|
||||
|
||||
/// Blockchain test state deserializer.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
pub struct State(BTreeMap<Address, Account>);
|
||||
|
||||
impl State {
|
||||
/// Returns all builtins.
|
||||
pub fn builtins(&self) -> BTreeMap<Address, Builtin> {
|
||||
self.0
|
||||
.iter()
|
||||
.filter_map(|ref pair| pair.1.builtin.clone().map(|b| (pair.0.clone(), b.clone())))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for State {
|
||||
type Item = <BTreeMap<Address, Account> as IntoIterator>::Item;
|
||||
type IntoIter = <BTreeMap<Address, Account> as IntoIterator>::IntoIter;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.into_iter()
|
||||
}
|
||||
}
|
@ -23,7 +23,7 @@ use util::numbers::{U256, Uint as U};
|
||||
|
||||
/// Lenient uint json deserialization for test json files.
|
||||
#[derive(Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
|
||||
pub struct Uint(U256);
|
||||
pub struct Uint(pub U256);
|
||||
|
||||
impl Into<U256> for Uint {
|
||||
fn into(self) -> U256 {
|
||||
@ -37,9 +37,15 @@ impl Into<u64> for Uint {
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<usize> for Uint {
|
||||
fn into(self) -> usize {
|
||||
// TODO: clean it after util conversions refactored.
|
||||
u64::from(self.0) as usize
|
||||
}
|
||||
}
|
||||
impl Into<u8> for Uint {
|
||||
fn into(self) -> u8 {
|
||||
<Uint as Into<u64>>::into(self) as u8
|
||||
u64::from(self.0) as u8
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,6 +61,10 @@ struct UintVisitor;
|
||||
impl Visitor for UintVisitor {
|
||||
type Value = Uint;
|
||||
|
||||
fn visit_u64<E>(&mut self, value: u64) -> Result<Self::Value, E> where E: Error {
|
||||
Ok(Uint(U256::from(value)))
|
||||
}
|
||||
|
||||
fn visit_str<E>(&mut self, value: &str) -> Result<Self::Value, E> where E: Error {
|
||||
let value = match value.len() {
|
||||
0 => U256::from(0),
|
||||
@ -83,12 +93,13 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn uint_deserialization() {
|
||||
let s = r#"["0xa", "10", "", "0x"]"#;
|
||||
let s = r#"["0xa", "10", "", "0x", 0]"#;
|
||||
let deserialized: Vec<Uint> = serde_json::from_str(s).unwrap();
|
||||
assert_eq!(deserialized, vec![
|
||||
Uint(U256::from(10)),
|
||||
Uint(U256::from(10)),
|
||||
Uint(U256::from(0)),
|
||||
Uint(U256::from(0)),
|
||||
Uint(U256::from(0))
|
||||
]);
|
||||
}
|
||||
|
@ -476,7 +476,7 @@ impl Configuration {
|
||||
"frontier" | "homestead" | "mainnet" => ethereum::new_frontier(),
|
||||
"morden" | "testnet" => ethereum::new_morden(),
|
||||
"olympic" => ethereum::new_olympic(),
|
||||
f => Spec::from_json_utf8(contents(f).unwrap_or_else(|_| {
|
||||
f => Spec::load(contents(f).unwrap_or_else(|_| {
|
||||
die!("{}: Couldn't read chain specification file. Sure it exists?", f)
|
||||
}).as_ref()),
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user