diff --git a/core/src/block.rs b/core/src/block.rs index 52cf42a454b..615f79c9236 100644 --- a/core/src/block.rs +++ b/core/src/block.rs @@ -142,6 +142,17 @@ impl BlockBuilder { .collect::>(), ); + // The witness hash of cellbase transaction is assumed to be zero 0x0000....0000 + let mut witnesses = vec![H256::zero()]; + witnesses.extend( + self.inner + .commit_transactions() + .iter() + .skip(1) + .map(|tx| tx.witness_hash()), + ); + let witnesses_root = merkle_root(&witnesses[..]); + let txs_proposal = merkle_root( &self .inner @@ -156,6 +167,7 @@ impl BlockBuilder { self.inner.header = header_builder .txs_commit(txs_commit) .txs_proposal(txs_proposal) + .witnesses_root(witnesses_root) .uncles_hash(uncles_hash) .uncles_count(self.inner.uncles.len() as u32) .build(); diff --git a/core/src/header.rs b/core/src/header.rs index 5ed65fe0468..3e9148732da 100644 --- a/core/src/header.rs +++ b/core/src/header.rs @@ -53,6 +53,8 @@ pub struct RawHeader { txs_commit: H256, /// Transactions proposal merkle root. txs_proposal: H256, + /// Witness hash commitment. + witnesses_root: H256, /// Block difficulty. difficulty: U256, /// Hash of the cellbase @@ -112,6 +114,10 @@ impl fmt::Debug for Header { "txs_proposal", &format_args!("{:#x}", self.raw.txs_proposal), ) + .field( + "witnesses_root", + &format_args!("{:#x}", self.raw.witnesses_root), + ) .field("difficulty", &format_args!("{:#x}", self.raw.difficulty)) .field("cellbase_id", &format_args!("{:#x}", self.raw.cellbase_id)) .field("uncles_hash", &format_args!("{:#x}", self.raw.uncles_hash)) @@ -174,6 +180,10 @@ impl Header { &self.raw.txs_proposal } + pub fn witnesses_root(&self) -> &H256 { + &self.raw.witnesses_root + } + pub fn cellbase_id(&self) -> &H256 { &self.raw.cellbase_id } @@ -268,6 +278,11 @@ impl HeaderBuilder { self } + pub fn witnesses_root(mut self, hash: H256) -> Self { + self.inner.raw.witnesses_root = hash; + self + } + pub fn cellbase_id(mut self, hash: H256) -> Self { self.inner.raw.cellbase_id = hash; self diff --git a/core/src/transaction.rs b/core/src/transaction.rs index 67820cf6765..bcf099b14ef 100644 --- a/core/src/transaction.rs +++ b/core/src/transaction.rs @@ -141,91 +141,47 @@ impl CellOutput { } } +#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Debug, Default, OccupiedCapacity)] +pub struct Witness(pub u32, pub Vec>); + +impl Witness { + // Index of relevant input + pub fn index(&self) -> u32 { + self.0 + } + + pub fn data(&self) -> &[Vec] { + &self.1 + } +} + #[derive(Clone, Serialize, Deserialize, Eq, Debug, Default, OccupiedCapacity)] pub struct Transaction { version: Version, deps: Vec, inputs: Vec, outputs: Vec, + //Segregated Witness to provide protection from transaction malleability. + witnesses: Vec, +} + +#[derive(Serialize)] +struct RawTransaction<'a> { + version: Version, + deps: &'a [OutPoint], + inputs: &'a [CellInput], + outputs: &'a [CellOutput], } impl Hash for Transaction { fn hash(&self, state: &mut H) { - state.write(self.hash().as_fixed_bytes()) + state.write(self.witness_hash().as_fixed_bytes()) } } impl PartialEq for Transaction { fn eq(&self, other: &Transaction) -> bool { - self.hash() == other.hash() - } -} - -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct IndexTransaction { - pub index: usize, - pub transaction: Transaction, -} - -#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, Default, Hash)] -pub struct ProposalShortId([u8; 10]); - -impl Deref for ProposalShortId { - type Target = [u8; 10]; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl fmt::Debug for ProposalShortId { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "ProposalShortId(0x{})", - hex_string(&self.0).expect("hex proposal short id") - ) - } -} - -impl DerefMut for ProposalShortId { - fn deref_mut(&mut self) -> &mut [u8; 10] { - &mut self.0 - } -} - -impl ProposalShortId { - pub fn new(inner: [u8; 10]) -> Self { - ProposalShortId(inner) - } - - pub fn from_slice(slice: &[u8]) -> Option { - if slice.len() == 10usize { - let mut id = [0u8; 10]; - id.copy_from_slice(slice); - Some(ProposalShortId(id)) - } else { - None - } - } - - pub fn from_h256(h: &H256) -> Self { - let v = h.to_vec(); - let mut inner = [0u8; 10]; - inner.copy_from_slice(&v[..10]); - ProposalShortId(inner) - } - - pub fn hash(&self) -> H256 { - blake2b_256(serialize(self).expect("ProposalShortId serialize should not fail")).into() - } - - pub fn zero() -> Self { - ProposalShortId([0; 10]) - } - - pub fn into_inner(self) -> [u8; 10] { - self.0 + self.witness_hash() == other.witness_hash() } } @@ -246,11 +202,25 @@ impl Transaction { &self.outputs } + pub fn witnesses(&self) -> &[Witness] { + &self.witnesses + } + pub fn is_cellbase(&self) -> bool { self.inputs.len() == 1 && self.inputs[0].previous_output.is_null() } pub fn hash(&self) -> H256 { + let raw = RawTransaction { + version: self.version, + deps: &self.deps, + inputs: &self.inputs, + outputs: &self.outputs, + }; + blake2b_256(serialize(&raw).expect("Transaction serialize should not fail")).into() + } + + pub fn witness_hash(&self) -> H256 { blake2b_256(serialize(&self).expect("Transaction serialize should not fail")).into() } @@ -284,8 +254,9 @@ impl Transaction { self.inputs.is_empty() || self.outputs.is_empty() } + // proposal_short_id pub fn proposal_short_id(&self) -> ProposalShortId { - ProposalShortId::from_h256(&self.hash()) + ProposalShortId::from_tx_hash(&self.hash()) } pub fn get_output(&self, i: usize) -> Option { @@ -364,7 +335,120 @@ impl TransactionBuilder { self } + pub fn witness(mut self, witness: Witness) -> Self { + self.inner.witnesses.push(witness); + self + } + + pub fn witnesses(mut self, witness: Vec) -> Self { + self.inner.witnesses.extend(witness); + self + } + + pub fn witnesses_clear(mut self) -> Self { + self.inner.witnesses.clear(); + self + } + pub fn build(self) -> Transaction { self.inner } } + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct IndexTransaction { + pub index: usize, + pub transaction: Transaction, +} + +#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, Default, Hash)] +pub struct ProposalShortId([u8; 10]); + +impl Deref for ProposalShortId { + type Target = [u8; 10]; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl fmt::Debug for ProposalShortId { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "ProposalShortId(0x{})", + hex_string(&self.0).expect("hex proposal short id") + ) + } +} + +impl DerefMut for ProposalShortId { + fn deref_mut(&mut self) -> &mut [u8; 10] { + &mut self.0 + } +} + +impl ProposalShortId { + pub fn new(inner: [u8; 10]) -> Self { + ProposalShortId(inner) + } + + pub fn from_slice(slice: &[u8]) -> Option { + if slice.len() == 10usize { + let mut id = [0u8; 10]; + id.copy_from_slice(slice); + Some(ProposalShortId(id)) + } else { + None + } + } + + pub fn from_tx_hash(h: &H256) -> Self { + let v = h.to_vec(); + let mut inner = [0u8; 10]; + inner.copy_from_slice(&v[..10]); + ProposalShortId(inner) + } + + pub fn hash(&self) -> H256 { + blake2b_256(serialize(self).expect("ProposalShortId serialize should not fail")).into() + } + + pub fn zero() -> Self { + ProposalShortId([0; 10]) + } + + pub fn into_inner(self) -> [u8; 10] { + self.0 + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_tx_hash() { + let tx = TransactionBuilder::default() + .output(CellOutput::new( + 5000, + vec![1, 2, 3], + Script::default(), + None, + )) + .input(CellInput::new(OutPoint::new(H256::zero(), 0), vec![])) + .witness(Witness(0, vec![vec![7, 8, 9]])) + .build(); + + assert_eq!( + tx.hash(), + H256::from_hex_str("7e1e256d6882809b7dfb55d002e54c5b4fbdbbbe8ce906aa6eae1b429de4d3d8") + .unwrap() + ); + assert_eq!( + tx.witness_hash(), + H256::from_hex_str("1a1d35b48950bc4c1b62865951141790d449bc9092d76d4200b13eb3d4b91048") + .unwrap() + ); + } +} diff --git a/protocol/src/builder.rs b/protocol/src/builder.rs index 64c11b93ca9..67bdc09bf09 100644 --- a/protocol/src/builder.rs +++ b/protocol/src/builder.rs @@ -10,13 +10,16 @@ use crate::protocol_generated::ckb::protocol::{ Script as FbsScript, ScriptBuilder, SyncMessage, SyncMessageBuilder, SyncPayload, Time as FbsTime, TimeBuilder, TimeMessage, TimeMessageBuilder, Transaction as FbsTransaction, TransactionBuilder, UncleBlock as FbsUncleBlock, UncleBlockBuilder, - ValidTransaction as FbsValidTransaction, ValidTransactionBuilder, H256 as FbsH256, + ValidTransaction as FbsValidTransaction, ValidTransactionBuilder, Witness as FbsWitness, + WitnessBuilder, H256 as FbsH256, }; use crate::{short_transaction_id, short_transaction_id_keys}; use ckb_core::block::Block; use ckb_core::header::{BlockNumber, Header}; use ckb_core::script::Script; -use ckb_core::transaction::{CellInput, CellOutput, OutPoint, ProposalShortId, Transaction}; +use ckb_core::transaction::{ + CellInput, CellOutput, OutPoint, ProposalShortId, Transaction, Witness, +}; use ckb_core::uncle::UncleBlock; use ckb_core::Cycle; use ckb_merkle_tree::build_merkle_proof; @@ -47,6 +50,7 @@ impl<'a> FbsHeader<'a> { let parent_hash = header.parent_hash().into(); let txs_commit = header.txs_commit().into(); let txs_proposal = header.txs_proposal().into(); + let witnesses_root = header.witnesses_root().into(); let difficulty = FbsBytes::build(fbb, &uint_to_bytes(header.difficulty())); let proof = FbsBytes::build(fbb, &header.proof()); let cellbase_id = header.cellbase_id().into(); @@ -58,6 +62,7 @@ impl<'a> FbsHeader<'a> { builder.add_number(header.number()); builder.add_txs_commit(&txs_commit); builder.add_txs_proposal(&txs_proposal); + builder.add_witnesses_root(&witnesses_root); builder.add_difficulty(difficulty); builder.add_nonce(header.nonce()); builder.add_proof(proof); @@ -94,11 +99,19 @@ impl<'a> FbsTransaction<'a> { .collect::>(); let outputs = fbb.create_vector(&vec); + let vec = transaction + .witnesses() + .iter() + .map(|witness| FbsWitness::build(fbb, witness)) + .collect::>(); + let witnesses = fbb.create_vector(&vec); + let mut builder = TransactionBuilder::new(fbb); builder.add_version(transaction.version()); builder.add_deps(deps); builder.add_inputs(inputs); builder.add_outputs(outputs); + builder.add_witnesses(witnesses); builder.finish() } } @@ -171,6 +184,25 @@ impl<'a> FbsScript<'a> { } } +impl<'a> FbsWitness<'a> { + pub fn build<'b>( + fbb: &mut FlatBufferBuilder<'b>, + witness: &Witness, + ) -> WIPOffset> { + let data = witness + .data() + .iter() + .map(|item| FbsBytes::build(fbb, item)) + .collect::>(); + + let data = fbb.create_vector(&data); + let mut builder = WitnessBuilder::new(fbb); + builder.add_data(data); + builder.add_index(witness.index()); + builder.finish() + } +} + impl<'a> FbsCellOutput<'a> { pub fn build<'b>( fbb: &mut FlatBufferBuilder<'b>, @@ -447,7 +479,7 @@ impl<'a> CompactBlock<'a> { } else { short_ids.push(FbsBytes::build( fbb, - &short_transaction_id(key0, key1, &transaction.hash()), + &short_transaction_id(key0, key1, &transaction.witness_hash()), )); } } diff --git a/protocol/src/convert.rs b/protocol/src/convert.rs index 762dc36bd0e..664e704e391 100644 --- a/protocol/src/convert.rs +++ b/protocol/src/convert.rs @@ -153,6 +153,7 @@ impl<'a> TryFrom> for ckb_core::header::Header { let parent_hash = cast!(header.parent_hash())?; let txs_commit = cast!(header.txs_commit())?; let txs_proposal = cast!(header.txs_proposal())?; + let witnesses_root = cast!(header.witnesses_root())?; let cellbase_id = cast!(header.cellbase_id())?; let uncles_hash = cast!(header.uncles_hash())?; @@ -163,6 +164,7 @@ impl<'a> TryFrom> for ckb_core::header::Header { .number(header.number()) .txs_commit(TryInto::try_into(txs_commit)?) .txs_proposal(TryInto::try_into(txs_proposal)?) + .witnesses_root(TryInto::try_into(witnesses_root)?) .difficulty(U256::from_little_endian(cast!(header .difficulty() .and_then(|d| d.seq()))?)?) @@ -197,11 +199,17 @@ impl<'a> TryFrom> for ckb_core::transaction::Trans .map(TryInto::try_into) .collect(); + let witnesses: Result, FailureError> = + FlatbuffersVectorIterator::new(cast!(transaction.witnesses())?) + .map(TryInto::try_into) + .collect(); + Ok(ckb_core::transaction::TransactionBuilder::default() .version(transaction.version()) .deps(deps?) .inputs(inputs?) .outputs(outputs?) + .witnesses(witnesses?) .build()) } } @@ -218,6 +226,18 @@ impl<'a> TryFrom> } } +impl<'a> TryFrom> for ckb_core::transaction::Witness { + type Error = FailureError; + + fn try_from(wit: ckb_protocol::Witness<'a>) -> Result { + let data: Option>> = FlatbuffersVectorIterator::new(cast!(wit.data())?) + .map(|item| item.seq().map(|s| s.to_vec())) + .collect(); + + Ok(ckb_core::transaction::Witness(wit.index(), cast!(data)?)) + } +} + impl<'a> TryFrom> for ckb_core::transaction::OutPoint { type Error = FailureError; diff --git a/protocol/src/lib.rs b/protocol/src/lib.rs index 1c4402ae514..847dada4d8a 100644 --- a/protocol/src/lib.rs +++ b/protocol/src/lib.rs @@ -57,9 +57,9 @@ pub fn short_transaction_id_keys(header_nonce: u64, random_nonce: u64) -> (u64, (key0, key1) } -pub fn short_transaction_id(key0: u64, key1: u64, transaction_hash: &H256) -> ShortTransactionID { +pub fn short_transaction_id(key0: u64, key1: u64, witness_hash: &H256) -> ShortTransactionID { let mut hasher = SipHasher::new_with_keys(key0, key1); - hasher.write(transaction_hash.as_bytes()); + hasher.write(witness_hash.as_bytes()); let siphash_transaction_hash = hasher.finish(); let siphash_transaction_hash_bytes = siphash_transaction_hash.to_le_bytes(); diff --git a/protocol/src/protocol.fbs b/protocol/src/protocol.fbs index 475b0ad1e9f..dfdbcf35eeb 100644 --- a/protocol/src/protocol.fbs +++ b/protocol/src/protocol.fbs @@ -40,6 +40,7 @@ table Header { number: uint64; txs_commit: H256; txs_proposal: H256; + witnesses_root: H256; difficulty: Bytes; nonce: uint64; proof: Bytes; @@ -66,6 +67,12 @@ table Transaction { deps: [OutPoint]; inputs: [CellInput]; outputs: [CellOutput]; + witnesses: [Witness]; +} + +table Witness { + index: uint32; + data: [Bytes]; } table OutPoint { @@ -224,4 +231,3 @@ table TimeMessage { table Time { timestamp: uint64; } - diff --git a/protocol/src/protocol_generated.rs b/protocol/src/protocol_generated.rs index 204962d4751..7a14a902dfa 100644 --- a/protocol/src/protocol_generated.rs +++ b/protocol/src/protocol_generated.rs @@ -1027,6 +1027,7 @@ impl<'a> Header<'a> { if let Some(x) = args.cellbase_id { builder.add_cellbase_id(x); } if let Some(x) = args.proof { builder.add_proof(x); } if let Some(x) = args.difficulty { builder.add_difficulty(x); } + if let Some(x) = args.witnesses_root { builder.add_witnesses_root(x); } if let Some(x) = args.txs_proposal { builder.add_txs_proposal(x); } if let Some(x) = args.txs_commit { builder.add_txs_commit(x); } if let Some(x) = args.parent_hash { builder.add_parent_hash(x); } @@ -1040,12 +1041,13 @@ impl<'a> Header<'a> { pub const VT_NUMBER: flatbuffers::VOffsetT = 10; pub const VT_TXS_COMMIT: flatbuffers::VOffsetT = 12; pub const VT_TXS_PROPOSAL: flatbuffers::VOffsetT = 14; - pub const VT_DIFFICULTY: flatbuffers::VOffsetT = 16; - pub const VT_NONCE: flatbuffers::VOffsetT = 18; - pub const VT_PROOF: flatbuffers::VOffsetT = 20; - pub const VT_CELLBASE_ID: flatbuffers::VOffsetT = 22; - pub const VT_UNCLES_HASH: flatbuffers::VOffsetT = 24; - pub const VT_UNCLES_COUNT: flatbuffers::VOffsetT = 26; + pub const VT_WITNESSES_ROOT: flatbuffers::VOffsetT = 16; + pub const VT_DIFFICULTY: flatbuffers::VOffsetT = 18; + pub const VT_NONCE: flatbuffers::VOffsetT = 20; + pub const VT_PROOF: flatbuffers::VOffsetT = 22; + pub const VT_CELLBASE_ID: flatbuffers::VOffsetT = 24; + pub const VT_UNCLES_HASH: flatbuffers::VOffsetT = 26; + pub const VT_UNCLES_COUNT: flatbuffers::VOffsetT = 28; #[inline] pub fn version(&self) -> u32 { @@ -1072,6 +1074,10 @@ impl<'a> Header<'a> { self._tab.get::(Header::VT_TXS_PROPOSAL, None) } #[inline] + pub fn witnesses_root(&self) -> Option<&'a H256> { + self._tab.get::(Header::VT_WITNESSES_ROOT, None) + } + #[inline] pub fn difficulty(&self) -> Option> { self._tab.get::>>(Header::VT_DIFFICULTY, None) } @@ -1104,6 +1110,7 @@ pub struct HeaderArgs<'a> { pub number: u64, pub txs_commit: Option<&'a H256>, pub txs_proposal: Option<&'a H256>, + pub witnesses_root: Option<&'a H256>, pub difficulty: Option>>, pub nonce: u64, pub proof: Option>>, @@ -1121,6 +1128,7 @@ impl<'a> Default for HeaderArgs<'a> { number: 0, txs_commit: None, txs_proposal: None, + witnesses_root: None, difficulty: None, nonce: 0, proof: None, @@ -1160,6 +1168,10 @@ impl<'a: 'b, 'b> HeaderBuilder<'a, 'b> { self.fbb_.push_slot_always::<&H256>(Header::VT_TXS_PROPOSAL, txs_proposal); } #[inline] + pub fn add_witnesses_root(&mut self, witnesses_root: &'b H256) { + self.fbb_.push_slot_always::<&H256>(Header::VT_WITNESSES_ROOT, witnesses_root); + } + #[inline] pub fn add_difficulty(&mut self, difficulty: flatbuffers::WIPOffset>) { self.fbb_.push_slot_always::>(Header::VT_DIFFICULTY, difficulty); } @@ -1439,6 +1451,7 @@ impl<'a> Transaction<'a> { _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>, args: &'args TransactionArgs<'args>) -> flatbuffers::WIPOffset> { let mut builder = TransactionBuilder::new(_fbb); + if let Some(x) = args.witnesses { builder.add_witnesses(x); } if let Some(x) = args.outputs { builder.add_outputs(x); } if let Some(x) = args.inputs { builder.add_inputs(x); } if let Some(x) = args.deps { builder.add_deps(x); } @@ -1450,6 +1463,7 @@ impl<'a> Transaction<'a> { pub const VT_DEPS: flatbuffers::VOffsetT = 6; pub const VT_INPUTS: flatbuffers::VOffsetT = 8; pub const VT_OUTPUTS: flatbuffers::VOffsetT = 10; + pub const VT_WITNESSES: flatbuffers::VOffsetT = 12; #[inline] pub fn version(&self) -> u32 { @@ -1467,6 +1481,10 @@ impl<'a> Transaction<'a> { pub fn outputs(&self) -> Option>>> { self._tab.get::>>>>(Transaction::VT_OUTPUTS, None) } + #[inline] + pub fn witnesses(&self) -> Option>>> { + self._tab.get::>>>>(Transaction::VT_WITNESSES, None) + } } pub struct TransactionArgs<'a> { @@ -1474,6 +1492,7 @@ pub struct TransactionArgs<'a> { pub deps: Option>>>>, pub inputs: Option>>>>, pub outputs: Option>>>>, + pub witnesses: Option>>>>, } impl<'a> Default for TransactionArgs<'a> { #[inline] @@ -1483,6 +1502,7 @@ impl<'a> Default for TransactionArgs<'a> { deps: None, inputs: None, outputs: None, + witnesses: None, } } } @@ -1508,6 +1528,10 @@ impl<'a: 'b, 'b> TransactionBuilder<'a, 'b> { self.fbb_.push_slot_always::>(Transaction::VT_OUTPUTS, outputs); } #[inline] + pub fn add_witnesses(&mut self, witnesses: flatbuffers::WIPOffset>>>) { + self.fbb_.push_slot_always::>(Transaction::VT_WITNESSES, witnesses); + } + #[inline] pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> TransactionBuilder<'a, 'b> { let start = _fbb.start_table(); TransactionBuilder { @@ -1522,6 +1546,94 @@ impl<'a: 'b, 'b> TransactionBuilder<'a, 'b> { } } +pub enum WitnessOffset {} +#[derive(Copy, Clone, Debug, PartialEq)] + +pub struct Witness<'a> { + pub _tab: flatbuffers::Table<'a>, +} + +impl<'a> flatbuffers::Follow<'a> for Witness<'a> { + type Inner = Witness<'a>; + #[inline] + fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table { buf: buf, loc: loc }, + } + } +} + +impl<'a> Witness<'a> { + #[inline] + pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + Witness { + _tab: table, + } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>, + args: &'args WitnessArgs<'args>) -> flatbuffers::WIPOffset> { + let mut builder = WitnessBuilder::new(_fbb); + if let Some(x) = args.data { builder.add_data(x); } + builder.add_index(args.index); + builder.finish() + } + + pub const VT_INDEX: flatbuffers::VOffsetT = 4; + pub const VT_DATA: flatbuffers::VOffsetT = 6; + + #[inline] + pub fn index(&self) -> u32 { + self._tab.get::(Witness::VT_INDEX, Some(0)).unwrap() + } + #[inline] + pub fn data(&self) -> Option>>> { + self._tab.get::>>>>(Witness::VT_DATA, None) + } +} + +pub struct WitnessArgs<'a> { + pub index: u32, + pub data: Option>>>>, +} +impl<'a> Default for WitnessArgs<'a> { + #[inline] + fn default() -> Self { + WitnessArgs { + index: 0, + data: None, + } + } +} +pub struct WitnessBuilder<'a: 'b, 'b> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>, + start_: flatbuffers::WIPOffset, +} +impl<'a: 'b, 'b> WitnessBuilder<'a, 'b> { + #[inline] + pub fn add_index(&mut self, index: u32) { + self.fbb_.push_slot::(Witness::VT_INDEX, index, 0); + } + #[inline] + pub fn add_data(&mut self, data: flatbuffers::WIPOffset>>>) { + self.fbb_.push_slot_always::>(Witness::VT_DATA, data); + } + #[inline] + pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> WitnessBuilder<'a, 'b> { + let start = _fbb.start_table(); + WitnessBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + flatbuffers::WIPOffset::new(o.value()) + } +} + pub enum OutPointOffset {} #[derive(Copy, Clone, Debug, PartialEq)] diff --git a/rpc/src/module/pool.rs b/rpc/src/module/pool.rs index 22996e0a565..015225667ad 100644 --- a/rpc/src/module/pool.rs +++ b/rpc/src/module/pool.rs @@ -74,7 +74,7 @@ impl PoolRpc for PoolRpcImpl { } fn get_pool_transaction(&self, hash: H256) -> Result> { - let id = ProposalShortId::from_h256(&hash); + let id = ProposalShortId::from_tx_hash(&hash); Ok(self .shared .chain_state() diff --git a/script/src/verify.rs b/script/src/verify.rs index 47e3e7a23af..0bef2ed284d 100644 --- a/script/src/verify.rs +++ b/script/src/verify.rs @@ -23,6 +23,7 @@ pub struct TransactionScriptsVerifier<'a> { tx_builder: FlatBufferBuilder<'a>, input_cells: Vec<&'a CellOutput>, dep_cells: Vec<&'a CellOutput>, + witnesses: FnvHashMap]>, hash: H256, } @@ -46,11 +47,17 @@ impl<'a> TransactionScriptsVerifier<'a> { .collect(); let inputs = rtx.transaction.inputs().iter().collect(); let outputs = rtx.transaction.outputs().iter().collect(); + let witnesses: FnvHashMap]> = rtx + .transaction + .witnesses() + .iter() + .map(|wit| (wit.index(), wit.data())) + .collect(); - let mut binary_index: FnvHashMap = FnvHashMap::default(); - for dep_cell in &dep_cells { - binary_index.insert(dep_cell.data_hash(), &dep_cell.data); - } + let binary_index: FnvHashMap = dep_cells + .iter() + .map(|dep_cell| (dep_cell.data_hash(), &dep_cell.data[..])) + .collect(); let mut tx_builder = FlatBufferBuilder::new(); let tx_offset = build_tx(&mut tx_builder, &rtx.transaction); @@ -63,6 +70,7 @@ impl<'a> TransactionScriptsVerifier<'a> { outputs, input_cells, dep_cells, + witnesses, hash: rtx.transaction.hash().clone(), } } @@ -106,6 +114,7 @@ impl<'a> TransactionScriptsVerifier<'a> { script: &Script, prefix: &str, current_cell: &'a CellOutput, + witness: Option<&&'a [Vec]>, current_input: Option<&'a CellInput>, max_cycles: Cycle, ) -> Result { @@ -114,6 +123,9 @@ impl<'a> TransactionScriptsVerifier<'a> { } let mut args = vec![b"verify".to_vec()]; self.extract_script(script).and_then(|script_binary| { + if let Some(witness) = witness { + args.extend_from_slice(&witness); + } args.extend_from_slice(&script.args.as_slice()); if let Some(ref input) = current_input { args.extend_from_slice(&input.args.as_slice()); @@ -143,9 +155,11 @@ impl<'a> TransactionScriptsVerifier<'a> { pub fn verify(&self, max_cycles: Cycle) -> Result { let mut cycles = 0; - for (i, input) in self.inputs.iter().enumerate() { + for (i, (input, input_cell)) in self.inputs.iter().zip(self.input_cells.iter()).enumerate() + { let prefix = format!("Transaction {}, input {}", self.hash, i); - let cycle = self.verify_script(&self.input_cells[i].lock, &prefix, self.input_cells[i], Some(input), max_cycles - cycles).map_err(|e| { + let witness = self.witnesses.get(&(i as u32)); + let cycle = self.verify_script(&input_cell.lock, &prefix, input_cell, witness, Some(input), max_cycles - cycles).map_err(|e| { info!(target: "script", "Error validating input {} of transaction {}: {:?}", i, self.hash, e); e })?; @@ -160,7 +174,7 @@ impl<'a> TransactionScriptsVerifier<'a> { for (i, output) in self.outputs.iter().enumerate() { if let Some(ref type_) = output.type_ { let prefix = format!("Transaction {}, output {}", self.hash, i); - let cycle = self.verify_script(type_, &prefix, output, None, max_cycles - cycles).map_err(|e| { + let cycle = self.verify_script(type_, &prefix, output, None, None, max_cycles - cycles).map_err(|e| { info!(target: "script", "Error validating output {} of transaction {}: {:?}", i, self.hash, e); e })?; @@ -182,7 +196,7 @@ mod tests { use super::*; use ckb_core::cell::CellStatus; use ckb_core::script::Script; - use ckb_core::transaction::{CellInput, CellOutput, OutPoint, TransactionBuilder}; + use ckb_core::transaction::{CellInput, CellOutput, OutPoint, TransactionBuilder, Witness}; use ckb_core::Capacity; use crypto::secp::Generator; use faster_hex::hex_encode; @@ -222,7 +236,8 @@ mod tests { let gen = Generator::new(); let privkey = gen.random_privkey(); - let mut args = vec![b"foo".to_vec(), b"bar".to_vec()]; + let args = vec![b"foo".to_vec(), b"bar".to_vec()]; + let mut witness_data = vec![]; let mut bytes = vec![]; for argument in &args { @@ -235,12 +250,12 @@ mod tests { let signature_der = signature.serialize_der(); let mut hex_signature = vec![0; signature_der.len() * 2]; hex_encode(&signature_der, &mut hex_signature).expect("hex signature"); - args.insert(0, hex_signature); + witness_data.insert(0, hex_signature); - let privkey = privkey.pubkey().unwrap().serialize(); - let mut hex_privkey = vec![0; privkey.len() * 2]; - hex_encode(&privkey, &mut hex_privkey).expect("hex privkey"); - args.insert(0, hex_privkey); + let pubkey = privkey.pubkey().unwrap().serialize(); + let mut hex_pubkey = vec![0; pubkey.len() * 2]; + hex_encode(&pubkey, &mut hex_pubkey).expect("hex pubkey"); + witness_data.insert(0, hex_pubkey); let binary_hash: H256 = (&blake2b_256(&buffer)).into(); let dep_out_point = OutPoint::new(H256::from_trimmed_hex_str("123").unwrap(), 8); @@ -252,6 +267,7 @@ mod tests { let transaction = TransactionBuilder::default() .input(input.clone()) .dep(dep_out_point) + .witness(Witness(0, witness_data)) .build(); let dummy_cell = CellOutput::new(100, vec![], script, None); @@ -275,7 +291,8 @@ mod tests { let gen = Generator::new(); let privkey = gen.random_privkey(); - let mut args = vec![b"foo".to_vec(), b"bar".to_vec()]; + let args = vec![b"foo".to_vec(), b"bar".to_vec()]; + let mut witness_data = vec![]; let mut bytes = vec![]; for argument in &args { @@ -288,12 +305,12 @@ mod tests { let signature_der = signature.serialize_der(); let mut hex_signature = vec![0; signature_der.len() * 2]; hex_encode(&signature_der, &mut hex_signature).expect("hex privkey"); - args.insert(0, hex_signature); + witness_data.insert(0, hex_signature); - let privkey = privkey.pubkey().unwrap().serialize(); - let mut hex_privkey = vec![0; privkey.len() * 2]; - hex_encode(&privkey, &mut hex_privkey).expect("hex privkey"); - args.insert(0, hex_privkey); + let pubkey = privkey.pubkey().unwrap().serialize(); + let mut hex_pubkey = vec![0; pubkey.len() * 2]; + hex_encode(&pubkey, &mut hex_pubkey).expect("hex pubkey"); + witness_data.insert(0, hex_pubkey); let binary_hash: H256 = (&blake2b_256(&buffer)).into(); let dep_out_point = OutPoint::new(H256::from_trimmed_hex_str("123").unwrap(), 8); @@ -305,6 +322,7 @@ mod tests { let transaction = TransactionBuilder::default() .input(input.clone()) .dep(dep_out_point) + .witness(Witness(0, witness_data)) .build(); let dummy_cell = CellOutput::new(100, vec![], script, None); @@ -329,6 +347,7 @@ mod tests { let gen = Generator::new(); let privkey = gen.random_privkey(); let mut args = vec![b"foo".to_vec(), b"bar".to_vec()]; + let mut witness_data = vec![]; let mut bytes = vec![]; for argument in &args { @@ -341,14 +360,14 @@ mod tests { let signature_der = signature.serialize_der(); let mut hex_signature = vec![0; signature_der.len() * 2]; hex_encode(&signature_der, &mut hex_signature).expect("hex privkey"); - args.insert(0, hex_signature); + witness_data.insert(0, hex_signature); // This line makes the verification invalid args.push(b"extrastring".to_vec()); - let privkey = privkey.pubkey().unwrap().serialize(); - let mut hex_privkey = vec![0; privkey.len() * 2]; - hex_encode(&privkey, &mut hex_privkey).expect("hex privkey"); - args.insert(0, hex_privkey); + let pubkey = privkey.pubkey().unwrap().serialize(); + let mut hex_pubkey = vec![0; pubkey.len() * 2]; + hex_encode(&pubkey, &mut hex_pubkey).expect("hex pubkey"); + witness_data.insert(0, hex_pubkey); let binary_hash: H256 = (&blake2b_256(&buffer)).into(); let dep_out_point = OutPoint::new(H256::from_trimmed_hex_str("123").unwrap(), 8); @@ -360,6 +379,7 @@ mod tests { let transaction = TransactionBuilder::default() .input(input.clone()) .dep(dep_out_point) + .witness(Witness(0, witness_data)) .build(); let dummy_cell = CellOutput::new(100, vec![], script, None); @@ -383,7 +403,8 @@ mod tests { let gen = Generator::new(); let privkey = gen.random_privkey(); - let mut args = vec![b"foo".to_vec(), b"bar".to_vec()]; + let args = vec![b"foo".to_vec(), b"bar".to_vec()]; + let mut witness_data = vec![]; let mut bytes = vec![]; for argument in &args { @@ -395,14 +416,14 @@ mod tests { let signature_der = signature.serialize_der(); let mut hex_signature = vec![0; signature_der.len() * 2]; hex_encode(&signature_der, &mut hex_signature).expect("hex privkey"); - args.insert(0, hex_signature); + witness_data.insert(0, hex_signature); let dep_out_point = OutPoint::new(H256::from_trimmed_hex_str("123").unwrap(), 8); - let privkey = privkey.pubkey().unwrap().serialize(); - let mut hex_privkey = vec![0; privkey.len() * 2]; - hex_encode(&privkey, &mut hex_privkey).expect("hex privkey"); - args.insert(0, hex_privkey); + let pubkey = privkey.pubkey().unwrap().serialize(); + let mut hex_pubkey = vec![0; pubkey.len() * 2]; + hex_encode(&pubkey, &mut hex_pubkey).expect("hex pubkey"); + witness_data.insert(0, hex_pubkey); let binary_hash: H256 = (&blake2b_256(&buffer)).into(); let script = Script::new(0, args, binary_hash); @@ -411,6 +432,7 @@ mod tests { let transaction = TransactionBuilder::default() .input(input.clone()) .dep(dep_out_point) + .witness(Witness(0, witness_data)) .build(); let dummy_cell = CellOutput::new(100, vec![], script, None); @@ -449,10 +471,10 @@ mod tests { hex_encode(&signature_der, &mut hex_signature).expect("hex privkey"); args.insert(0, hex_signature); - let privkey = privkey.pubkey().unwrap().serialize(); - let mut hex_privkey = vec![0; privkey.len() * 2]; - hex_encode(&privkey, &mut hex_privkey).expect("hex privkey"); - args.insert(0, hex_privkey); + let pubkey = privkey.pubkey().unwrap().serialize(); + let mut hex_pubkey = vec![0; pubkey.len() * 2]; + hex_encode(&pubkey, &mut hex_pubkey).expect("hex pubkey"); + args.insert(0, hex_pubkey); let input = CellInput::new(OutPoint::null(), vec![]); let dummy_cell = CellOutput::new(100, vec![], Script::always_success(), None); @@ -510,10 +532,10 @@ mod tests { // This line makes the verification invalid args.push(b"extrastring".to_vec()); - let privkey = privkey.pubkey().unwrap().serialize(); - let mut hex_privkey = vec![0; privkey.len() * 2]; - hex_encode(&privkey, &mut hex_privkey).expect("hex privkey"); - args.insert(0, hex_privkey); + let pubkey = privkey.pubkey().unwrap().serialize(); + let mut hex_pubkey = vec![0; pubkey.len() * 2]; + hex_encode(&pubkey, &mut hex_pubkey).expect("hex pubkey"); + args.insert(0, hex_pubkey); let input = CellInput::new(OutPoint::null(), vec![]); let dummy_cell = CellOutput::new(100, vec![], Script::always_success(), None); diff --git a/shared/src/tx_pool/types.rs b/shared/src/tx_pool/types.rs index b1e49ad50cb..d6ff0b313a4 100644 --- a/shared/src/tx_pool/types.rs +++ b/shared/src/tx_pool/types.rs @@ -252,7 +252,7 @@ impl StagingPool { pub fn get_output(&self, o: &OutPoint) -> Option { self.vertices - .get(&ProposalShortId::from_h256(&o.hash)) + .get(&ProposalShortId::from_tx_hash(&o.hash)) .and_then(|x| x.transaction.get_output(o.index as usize)) } diff --git a/sync/src/relayer/mod.rs b/sync/src/relayer/mod.rs index fe5d2e25eff..c3dd1a601d2 100644 --- a/sync/src/relayer/mod.rs +++ b/sync/src/relayer/mod.rs @@ -190,7 +190,7 @@ where let mut txs_map: FnvHashMap = transactions .into_iter() .map(|tx| { - let short_id = short_transaction_id(key0, key1, &tx.hash()); + let short_id = short_transaction_id(key0, key1, &tx.witness_hash()); (short_id, tx) }) .collect(); @@ -198,7 +198,7 @@ where { let tx_pool = chain_state.tx_pool(); let iter = tx_pool.staging_txs_iter().filter_map(|entry| { - let short_id = short_transaction_id(key0, key1, &entry.transaction.hash()); + let short_id = short_transaction_id(key0, key1, &entry.transaction.witness_hash()); if compact_block.short_ids.contains(&short_id) { Some((short_id, entry.transaction.clone())) } else { diff --git a/test/src/specs/mining.rs b/test/src/specs/mining.rs index 96333c6da40..26fdbf5e349 100644 --- a/test/src/specs/mining.rs +++ b/test/src/specs/mining.rs @@ -38,7 +38,7 @@ impl Spec for MiningBasic { assert!(block1 .proposal_transactions() .iter() - .any(|id| ProposalShortId::from_h256(&transaction_hash).eq(id))); + .any(|id| ProposalShortId::from_tx_hash(&transaction_hash).eq(id))); info!("Generated tx should be included in next + n block's commit txs, current n = 2"); assert!(block3 diff --git a/util/jsonrpc-types/src/blockchain.rs b/util/jsonrpc-types/src/blockchain.rs index 2900335bff9..c69b17d4be0 100644 --- a/util/jsonrpc-types/src/blockchain.rs +++ b/util/jsonrpc-types/src/blockchain.rs @@ -5,7 +5,7 @@ use ckb_core::header::{Header as CoreHeader, HeaderBuilder, Seal as CoreSeal}; use ckb_core::script::Script as CoreScript; use ckb_core::transaction::{ CellInput as CoreCellInput, CellOutput as CoreCellOutput, OutPoint as CoreOutPoint, - Transaction as CoreTransaction, TransactionBuilder, + Transaction as CoreTransaction, TransactionBuilder, Witness as CoreWitness, }; use ckb_core::uncle::UncleBlock as CoreUncleBlock; use ckb_core::{BlockNumber, Capacity}; @@ -133,12 +133,34 @@ impl From for CoreCellInput { } } +#[derive(Clone, Default, Serialize, Deserialize, PartialEq, Eq, Hash, Debug)] +pub struct Witness(u32, Vec); + +impl<'a> From<&'a CoreWitness> for Witness { + fn from(core: &CoreWitness) -> Witness { + Witness( + core.index(), + core.data().iter().cloned().map(Bytes::new).collect(), + ) + } +} + +impl From for CoreWitness { + fn from(json: Witness) -> CoreWitness { + CoreWitness( + json.0, + json.1.into_iter().map(|item| item.into_vec()).collect(), + ) + } +} + #[derive(Clone, Default, Serialize, Deserialize, PartialEq, Eq, Hash, Debug)] pub struct Transaction { pub version: u32, pub deps: Vec, pub inputs: Vec, pub outputs: Vec, + pub witnesses: Vec, #[serde(skip_deserializing)] pub hash: H256, } @@ -152,6 +174,7 @@ impl<'a> From<&'a CoreTransaction> for Transaction { deps: core.deps().iter().cloned().map(Into::into).collect(), inputs: core.inputs().iter().cloned().map(Into::into).collect(), outputs: core.outputs().iter().cloned().map(Into::into).collect(), + witnesses: core.witnesses().iter().map(Into::into).collect(), hash, } } @@ -164,6 +187,7 @@ impl From for CoreTransaction { deps, inputs, outputs, + witnesses, .. } = json; @@ -172,6 +196,7 @@ impl From for CoreTransaction { .deps(deps.into_iter().map(Into::into).collect()) .inputs(inputs.into_iter().map(Into::into).collect()) .outputs(outputs.into_iter().map(Into::into).collect()) + .witnesses(witnesses.into_iter().map(Into::into).collect()) .build() } } @@ -207,6 +232,7 @@ pub struct Header { pub number: BlockNumber, pub txs_commit: H256, pub txs_proposal: H256, + pub witnesses_root: H256, pub difficulty: U256, pub cellbase_id: H256, pub uncles_hash: H256, @@ -225,6 +251,7 @@ impl<'a> From<&'a CoreHeader> for Header { number: core.number(), txs_commit: core.txs_commit().clone(), txs_proposal: core.txs_proposal().clone(), + witnesses_root: core.witnesses_root().clone(), difficulty: core.difficulty().clone(), cellbase_id: core.cellbase_id().clone(), uncles_hash: core.uncles_hash().clone(), @@ -244,6 +271,7 @@ impl From
for CoreHeader { number, txs_commit, txs_proposal, + witnesses_root, difficulty, cellbase_id, uncles_hash, @@ -259,6 +287,7 @@ impl From
for CoreHeader { .number(number) .txs_commit(txs_commit) .txs_proposal(txs_proposal) + .witnesses_root(witnesses_root) .difficulty(difficulty) .cellbase_id(cellbase_id) .uncles_hash(uncles_hash) @@ -369,7 +398,8 @@ mod tests { TransactionBuilder::default() .deps(vec![CoreOutPoint::default()]) .inputs(vec![mock_cell_input(arg.clone())]) - .outputs(vec![mock_cell_output(data, arg)]) + .outputs(vec![mock_cell_output(data, arg.clone())]) + .witness(CoreWitness(0, vec![arg])) .build() } diff --git a/verification/src/block_verifier.rs b/verification/src/block_verifier.rs index 287254767f0..a0c62d78474 100644 --- a/verification/src/block_verifier.rs +++ b/verification/src/block_verifier.rs @@ -153,6 +153,20 @@ impl MerkleRootVerifier { return Err(Error::CommitTransactionsRoot); } + // The witness hash of cellbase transaction is assumed to be zero 0x0000....0000 + let mut witnesses = vec![H256::zero()]; + witnesses.extend( + block + .commit_transactions() + .iter() + .skip(1) + .map(|tx| tx.witness_hash()), + ); + + if block.header().witnesses_root() != &merkle_root(&witnesses[..]) { + return Err(Error::WitnessesMerkleRoot); + } + let proposals = block .proposal_transactions() .iter() diff --git a/verification/src/error.rs b/verification/src/error.rs index 5e4644f8613..9b0d7b3589c 100644 --- a/verification/src/error.rs +++ b/verification/src/error.rs @@ -35,6 +35,8 @@ pub enum Error { ProposalTransactionsRoot, /// The merkle tree hash of committed transactions does not match the one in header. CommitTransactionsRoot, + /// The merkle tree witness hash of committed transactions does not match the one in header. + WitnessesMerkleRoot, /// The parent of the block is unknown. UnknownParent(H256), /// Uncles does not meet the consensus requirements.