From 23f385938f5dec61da6707c432c482cebc977bd7 Mon Sep 17 00:00:00 2001 From: Xuejie Xiao Date: Wed, 20 Mar 2019 06:51:19 +0000 Subject: [PATCH 001/139] refactor: Revise script structure --- Cargo.lock | 4 +- benches/Cargo.toml | 1 + benches/benches/process_block.rs | 24 +- chain/src/tests/basic.rs | 6 +- chain/src/tests/util.rs | 3 +- core/src/cell.rs | 3 +- core/src/script.rs | 163 ++---------- core/src/transaction.rs | 48 ++-- miner/src/block_assembler.rs | 36 ++- miner/src/config.rs | 3 +- nodes_template/default.toml | 5 +- protocol/src/builder.rs | 33 +-- protocol/src/convert.rs | 16 +- protocol/src/protocol.fbs | 7 +- protocol/src/protocol_generated.rs | 64 ++--- rpc/src/module/chain.rs | 2 +- script/src/syscalls/builder.rs | 2 - script/src/syscalls/load_cell_by_field.rs | 13 +- script/src/syscalls/load_input_by_field.rs | 16 +- script/src/syscalls/mod.rs | 128 +++++---- script/src/verify.rs | 243 +++++++++--------- shared/src/tx_pool/types.rs | 3 +- spec/Cargo.toml | 2 - spec/src/lib.rs | 29 +-- src/cli/mod.rs | 2 +- src/cli/run_impl.rs | 13 +- src/main.rs | 1 - sync/Cargo.toml | 1 + sync/src/synchronizer/mod.rs | 3 +- sync/src/tests/relayer.rs | 19 +- sync/src/types.rs | 10 +- test/src/node.rs | 12 +- util/jsonrpc-types/src/blockchain.rs | 120 +++------ util/jsonrpc-types/src/cell.rs | 4 +- verification/src/tests/block_verifier.rs | 18 +- verification/src/tests/commit_verifier.rs | 25 +- .../src/tests/transaction_verifier.rs | 13 +- verification/src/tests/uncle_verifier.rs | 3 +- verification/src/transaction_verifier.rs | 14 +- 39 files changed, 489 insertions(+), 623 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1bb8b25fef..efc1b770cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -325,6 +325,7 @@ dependencies = [ "ckb-shared 0.7.0-pre", "ckb-traits 0.7.0-pre", "criterion 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "hash 0.7.0-pre", "numext-fixed-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "numext-fixed-uint 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -363,8 +364,6 @@ version = "0.7.0-pre" dependencies = [ "ckb-core 0.7.0-pre", "ckb-pow 0.7.0-pre", - "ckb-protocol 0.7.0-pre", - "flatbuffers 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "numext-fixed-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "numext-fixed-uint 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", @@ -638,6 +637,7 @@ dependencies = [ "faketime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "flatbuffers 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "hash 0.7.0-pre", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "numext-fixed-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "numext-fixed-uint 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/benches/Cargo.toml b/benches/Cargo.toml index c50db6d7bb..3ac9e62b56 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -22,6 +22,7 @@ numext-fixed-uint = { version = "0.1", features = ["support_rand", "support_heap rand = "0.6" tempfile = "3.0" ckb-traits = { path = "../traits" } +hash = {path = "../util/hash"} [[bench]] name = "cuckoo" diff --git a/benches/benches/process_block.rs b/benches/benches/process_block.rs index 6dc843dadb..e9d98920b5 100644 --- a/benches/benches/process_block.rs +++ b/benches/benches/process_block.rs @@ -13,6 +13,7 @@ use ckb_shared::shared::{Shared, SharedBuilder}; use ckb_shared::store::ChainKVStore; use ckb_traits::ChainProvider; use criterion::{criterion_group, criterion_main, Criterion}; +use hash::blake2b_256; use numext_fixed_hash::H256; use numext_fixed_uint::U256; use rand::random; @@ -139,17 +140,18 @@ fn new_chain() -> ( ) { let cellbase = TransactionBuilder::default() .input(CellInput::new_cellbase_input(0)) - .output(CellOutput::new(0, vec![], H256::zero(), None)) + .output(CellOutput::new(0, vec![], Script::default(), None)) .build(); - let script = create_script(); + let (script, binary) = create_script(); // create genesis block with 100 tx let commit_transactions: Vec = (0..100) .map(|i| { TransactionBuilder::default() - .input(CellInput::new(OutPoint::null(), script.clone())) - .output(CellOutput::new(50000, vec![i], script.type_hash(), None)) + .input(CellInput::new(OutPoint::null(), vec![])) + .output(CellOutput::new(50000, vec![i], script.clone(), None)) + .embed(binary.clone()) .build() }) .collect(); @@ -185,7 +187,7 @@ fn gen_block(blocks: &mut Vec, parent_index: usize) { let cellbase = TransactionBuilder::default() .input(CellInput::new_cellbase_input(number)) - .output(CellOutput::new(0, vec![], H256::zero(), None)) + .output(CellOutput::new(0, vec![], Script::default(), None)) .build(); // spent n-2 block's tx and proposal n-1 block's tx @@ -225,14 +227,15 @@ fn gen_block(blocks: &mut Vec, parent_index: usize) { } fn create_transaction(hash: H256) -> Transaction { - let script = create_script(); + let (script, binary) = create_script(); TransactionBuilder::default() - .output(CellOutput::new(50000, vec![], script.type_hash(), None)) - .input(CellInput::new(OutPoint::new(hash, 0), script)) + .output(CellOutput::new(50000, vec![], script, None)) + .input(CellInput::new(OutPoint::new(hash, 0), vec![])) + .embed(binary) .build() } -fn create_script() -> Script { +fn create_script() -> (Script, Vec) { let mut file = File::open( Path::new(env!("CARGO_MANIFEST_DIR")).join("../nodes_template/spec/cells/always_success"), ) @@ -240,5 +243,6 @@ fn create_script() -> Script { let mut buffer = Vec::new(); file.read_to_end(&mut buffer).unwrap(); - Script::new(0, Vec::new(), None, Some(buffer), Vec::new()) + let script = Script::new(0, vec![], (&blake2b_256(&buffer)).into()); + (script, buffer) } diff --git a/chain/src/tests/basic.rs b/chain/src/tests/basic.rs index 4f59999211..03f1ddc1ab 100644 --- a/chain/src/tests/basic.rs +++ b/chain/src/tests/basic.rs @@ -4,9 +4,9 @@ use ckb_core::block::Block; use ckb_core::block::BlockBuilder; use ckb_core::cell::CellProvider; use ckb_core::header::HeaderBuilder; +use ckb_core::script::Script; use ckb_core::transaction::{CellInput, CellOutput, OutPoint, TransactionBuilder}; use ckb_traits::ChainProvider; -use numext_fixed_hash::H256; use numext_fixed_uint::U256; use std::sync::Arc; @@ -18,7 +18,7 @@ fn test_genesis_transaction_spend() { CellOutput::new( 100_000_000, vec![], - H256::default(), + Script::default(), None ); 100 @@ -62,7 +62,7 @@ fn test_genesis_transaction_fetch() { CellOutput::new( 100_000_000, vec![], - H256::default(), + Script::default(), None ); 100 diff --git a/chain/src/tests/util.rs b/chain/src/tests/util.rs index c14c93d8d8..e5909b953d 100644 --- a/chain/src/tests/util.rs +++ b/chain/src/tests/util.rs @@ -3,6 +3,7 @@ use ckb_chain_spec::consensus::Consensus; use ckb_core::block::Block; use ckb_core::block::BlockBuilder; use ckb_core::header::{Header, HeaderBuilder}; +use ckb_core::script::Script; use ckb_core::transaction::{ CellInput, CellOutput, OutPoint, ProposalShortId, Transaction, TransactionBuilder, }; @@ -36,7 +37,7 @@ pub(crate) fn start_chain( fn create_cellbase(number: BlockNumber) -> Transaction { TransactionBuilder::default() .input(CellInput::new_cellbase_input(number)) - .output(CellOutput::new(0, vec![], H256::zero(), None)) + .output(CellOutput::new(0, vec![], Script::default(), None)) .build() } diff --git a/core/src/cell.rs b/core/src/cell.rs index ba18000dad..63b923d6d4 100644 --- a/core/src/cell.rs +++ b/core/src/cell.rs @@ -131,6 +131,7 @@ impl ResolvedTransaction { #[cfg(test)] mod tests { + use super::super::script::Script; use super::*; use numext_fixed_hash::H256; use std::collections::HashMap; @@ -177,7 +178,7 @@ mod tests { let o = CellOutput { capacity: 2, data: vec![], - lock: H256::default(), + lock: Script::default(), type_: None, }; diff --git a/core/src/script.rs b/core/src/script.rs index 83de334772..6b159fcc4c 100644 --- a/core/src/script.rs +++ b/core/src/script.rs @@ -13,54 +13,11 @@ use std::mem; pub struct Script { pub version: u8, pub args: Vec>, - - // There're 2 ways of specifying script: one way is directly embed - // the script to run in binary part; however, a common use case is - // that CKB would provide common system cells containing common verification - // algorithm, such as P2SH-SHA3-SECP256K1, in the meantime, crypto experts might - // also put alternative advanced verfication algorithms on the chain. So another - // way of loading a script, is that reference can be used to specify - // an existing cell, when CKB runs the script, CKB will load the script from the - // existing cell. This has the benefit of promoting code reuse, and reducing - // transaction size: a typical secp256k1 verfication algorithm can take 1.2 MB - // in space, which is not ideal to put in every tx input. - // Note that the referenced cell here might also be included in transaction's - // deps part, otherwise CKB will fail to verify the script. - // CKB only enforces that reference and binary cannot both be - // None, when they both contains actual value(though this is not recommended), - // binary will be used. - // When calculating script type hash, reference, binary, - // and signed_args will all be included. - pub reference: Option, - pub binary: Option>, - // Pre-defined arguments that are considered part of the script. - // When signed_args contains , , and args contains , - // , , binary will then be executed with arguments , , - // , , . - // This can be useful when binary is fixed, but depending on different - // use case, we might have different initial parameters. For example, in - // secp256k1 verification, we need to provide pubkey first, this cannot be - // part of arguments, otherwise users can provide signatures signed by - // arbitrary private keys. On the other hand, include pubkey inside - // binary is not good for distribution, since the script here can - // be over 1 megabytes. So signed_args helps here to preserve one common - // binary, while enable us to provide different pubkeys for different - // transactions. - // For most verification algorithms, args will contain the signature - // and any additional parameters needed by cell validator, while - // signed_args will contain pubkey used in the signing part. - pub signed_args: Vec>, -} - -struct OptionDisplay(Option); - -impl fmt::Display for OptionDisplay { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self.0 { - Some(ref v) => write!(f, "Some({})", v), - None => write!(f, "None"), - } - } + // The reference hash here can be used to refer to binary in any of the + // following locations: + // 1. Data part of a dep cell in current transaction + // 2. An embed item in current transaction + pub reference: H256, } fn prefix_hex(bytes: &[u8]) -> String { @@ -78,61 +35,23 @@ impl fmt::Debug for Script { .entries(self.args.iter().map(|arg| prefix_hex(arg))) .finish()?; - write!( - f, - ", reference: {}", - OptionDisplay( - self.reference - .as_ref() - .map(|reference| format!("{:#x}", reference)) - ) - )?; - - write!( - f, - ", binary: {}", - OptionDisplay(self.binary.as_ref().map(|binary| prefix_hex(binary))) - )?; - - write!(f, " , signed_args: ")?; - - f.debug_list() - .entries( - self.signed_args - .iter() - .map(|signed_arg| prefix_hex(signed_arg)), - ) - .finish()?; + write!(f, ", reference: {:#x}", self.reference,)?; write!(f, " }}") } } -type ScriptTuple = ( - u8, - Vec>, - Option, - Option>, - Vec>, -); +type ScriptTuple = (u8, Vec>, H256); const VEC_WRITE_ALL_EXPECT: &str = "Essentially, Vec::write_all invoke extend_from_slice, should not fail"; impl Script { - pub fn new( - version: u8, - args: Vec>, - reference: Option, - binary: Option>, - signed_args: Vec>, - ) -> Self { + pub fn new(version: u8, args: Vec>, reference: H256) -> Self { Script { version, args, reference, - binary, - signed_args, } } @@ -141,35 +60,16 @@ impl Script { version, args, reference, - binary, - signed_args, } = self; - (version, args, reference, binary, signed_args) + (version, args, reference) } - pub fn type_hash(&self) -> H256 { + pub fn hash(&self) -> H256 { match self.version { 0 => { let mut bytes = vec![]; - // TODO: switch to flatbuffer serialization once we - // can do stable serialization using flatbuffer. - if let Some(ref data) = self.reference { - bytes - .write_all(data.as_bytes()) - .expect(VEC_WRITE_ALL_EXPECT); - } - // A separator is used here to prevent the rare case - // that some binary might contain the exactly - // same data as reference. In this case we might - // still want to distinguish between the 2 script in - // the hash. Note this might not solve every problem, - // when flatbuffer change is done, we can leverage flatbuffer - // serialization directly, which will be more reliable. - bytes.write_all(b"|").expect(VEC_WRITE_ALL_EXPECT); - if let Some(ref data) = self.binary { - bytes.write_all(&data).expect(VEC_WRITE_ALL_EXPECT) - } - for argument in &self.signed_args { + bytes.write_all(self.reference.as_bytes()).expect(VEC_WRITE_ALL_EXPECT); + for argument in &self.args { bytes.write_all(argument).expect(VEC_WRITE_ALL_EXPECT); } blake2b_256(bytes).into() @@ -181,50 +81,43 @@ impl Script { impl OccupiedCapacity for Script { fn occupied_capacity(&self) -> usize { - mem::size_of::() - + self.args.occupied_capacity() - + self.reference.occupied_capacity() - + self.binary.occupied_capacity() - + self.signed_args.occupied_capacity() + mem::size_of::() + self.args.occupied_capacity() + self.reference.occupied_capacity() } } #[cfg(test)] mod tests { use super::{Script, H256}; + use hash::blake2b_256; #[test] - fn empty_script_type_hash() { - let script = Script::new(0, vec![], None, None, vec![]); + fn empty_script_hash() { + let script = Script::new(0, vec![], H256::zero()); let expect = - H256::from_hex_str("4b29eb5168ba6f74bff824b15146246109c732626abd3c0578cbf147d8e28479") + H256::from_hex_str("266cec97cbede2cfbce73666f08deed9560bdf7841a7a5a51b3a3f09da249e21") .unwrap(); - assert_eq!(script.type_hash(), expect); + assert_eq!(script.hash(), expect); } #[test] - fn always_success_script_type_hash() { + fn always_success_script_hash() { let always_success = include_bytes!("../../nodes_template/spec/cells/always_success"); - let script = Script::new(0, vec![], None, Some(always_success.to_vec()), vec![]); + let always_success_hash: H256 = (&blake2b_256(&always_success[..])).into(); + + let script = Script::new(0, vec![], always_success_hash); let expect = - H256::from_hex_str("9f94d2511b787387638faa4a5bfd448baf21aa5fde3afaa54bb791188b5cf002") + H256::from_hex_str("9a9a6bdbc38d4905eace1822f85237e3a1e238bb3f277aa7b7c8903441123510") .unwrap(); - assert_eq!(script.type_hash(), expect); + assert_eq!(script.hash(), expect); } #[test] - fn one_script_type_hash() { - let one = Script::new( - 0, - vec![vec![1]], - Some(H256::zero()), - Some(vec![1]), - vec![vec![1]], - ); + fn one_script_hash() { + let one = Script::new(0, vec![vec![1]], H256::zero()); let expect = - H256::from_hex_str("afb140d0673571ed5710d220d6146d41bd8bc18a3a4ff723dad4331da5af5bb6") + H256::from_hex_str("dade0e507e27e2a5995cf39c8cf454b6e70fa80d03c1187db7a4cb2c9eab79da") .unwrap(); - assert_eq!(one.type_hash(), expect); + assert_eq!(one.hash(), expect); } } diff --git a/core/src/transaction.rs b/core/src/transaction.rs index d35d1d861c..15cd8fd20c 100644 --- a/core/src/transaction.rs +++ b/core/src/transaction.rs @@ -65,36 +65,30 @@ pub struct CellInput { pub previous_output: OutPoint, // Depends on whether the operation is Transform or Destroy, this is the proof to transform // lock or destroy lock. - pub unlock: Script, + pub args: Vec>, } impl CellInput { - pub fn new(previous_output: OutPoint, unlock: Script) -> Self { + pub fn new(previous_output: OutPoint, args: Vec>) -> Self { CellInput { previous_output, - unlock, + args, } } pub fn new_cellbase_input(block_number: BlockNumber) -> Self { CellInput { previous_output: OutPoint::null(), - unlock: Script::new( - 0, - Vec::new(), - None, - Some(block_number.to_le_bytes().to_vec()), - Vec::new(), - ), + args: vec![block_number.to_le_bytes().to_vec()], } } - pub fn destruct(self) -> (OutPoint, Script) { + pub fn destruct(self) -> (OutPoint, Vec>) { let CellInput { previous_output, - unlock, + args, } = self; - (previous_output, unlock) + (previous_output, args) } } @@ -103,7 +97,7 @@ pub struct CellOutput { pub capacity: Capacity, #[serde(with = "serde_bytes")] pub data: Vec, - pub lock: H256, + pub lock: Script, #[serde(rename = "type")] pub type_: Option