Skip to content

Commit

Permalink
refactor: use serialized flatbuffer format in referenced cell
Browse files Browse the repository at this point in the history
This enables embedding signed args in common cells
  • Loading branch information
xxuejie committed Dec 18, 2018
1 parent 2d0a378 commit 49fc513
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 31 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions script/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub use crate::verify::TransactionScriptsVerifier;
pub enum ScriptError {
NoScript,
InvalidReferenceIndex,
ArgumentError,
ValidationFailure(u8),
VMError(VMInternalError),
}
89 changes: 59 additions & 30 deletions script/src/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ use crate::ScriptError;
use ckb_core::cell::ResolvedTransaction;
use ckb_core::script::Script;
use ckb_core::transaction::{CellInput, CellOutput};
use ckb_protocol::{FlatbuffersVectorIterator, Script as FbsScript};
use ckb_vm::{DefaultMachine, SparseMemory};
use flatbuffers::FlatBufferBuilder;
use flatbuffers::{get_root, FlatBufferBuilder};
use fnv::FnvHashMap;
use log::info;
use numext_fixed_hash::H256;
Expand Down Expand Up @@ -94,14 +95,40 @@ impl<'a> TransactionScriptsVerifier<'a> {
}

// Script struct might contain references to external cells, this
// method exacts the real script from Stript struct.
fn extract_script(&self, script: &'a Script) -> Result<&'a [u8], ScriptError> {
// method exacts the referenced script if any. It also fills signed args
// so we don't need to do a second time of memory copy
fn extract_script(
&self,
script: &'a Script,
signed_args: &mut Vec<Vec<u8>>,
) -> Result<&'a [u8], ScriptError> {
if let Some(ref data) = script.binary {
signed_args.extend_from_slice(&script.signed_args);
return Ok(data);
}
if let Some(ref hash) = script.reference {
return match self.dep_cell_index.get(hash) {
Some(ref cell_output) => Ok(&cell_output.data),
Some(ref cell_output) => {
let fbs_script = get_root::<FbsScript>(&cell_output.data);
// This way we can avoid copying the actual script binary one more
// time, which could be a lot of data.
let binary = fbs_script
.binary()
.and_then(|s| s.seq())
.ok_or(ScriptError::NoScript)?;
// When the reference script has signed arguments, we will concat
// signed arguments from the reference script with the signed
// arguments from the main script together.
if let Some(args) = fbs_script.signed_args() {
let args: Option<Vec<Vec<u8>>> = FlatbuffersVectorIterator::new(args)
.map(|arg| arg.seq().map(|s| s.to_vec()))
.collect();
let args = args.ok_or(ScriptError::ArgumentError)?;
signed_args.extend_from_slice(&args);
signed_args.extend_from_slice(&script.signed_args);
}
Ok(binary)
}
None => Err(ScriptError::InvalidReferenceIndex),
};
}
Expand All @@ -115,28 +142,28 @@ impl<'a> TransactionScriptsVerifier<'a> {
current_cell: &'a CellOutput,
current_input: Option<&'a CellInput>,
) -> Result<(), ScriptError> {
self.extract_script(script).and_then(|script_binary| {
let mut args = vec![b"verify".to_vec()];
args.extend_from_slice(&script.signed_args.as_slice());
args.extend_from_slice(&script.args.as_slice());

let mut machine = DefaultMachine::<u64, SparseMemory>::default();
machine.add_syscall_module(Box::new(self.build_load_tx()));
machine.add_syscall_module(Box::new(self.build_load_cell(current_cell)));
machine.add_syscall_module(Box::new(self.build_load_cell_by_field(current_cell)));
machine.add_syscall_module(Box::new(self.build_load_input_by_field(current_input)));
machine.add_syscall_module(Box::new(Debugger::new(prefix)));
machine
.run(script_binary, &args)
.map_err(ScriptError::VMError)
.and_then(|code| {
if code == 0 {
Ok(())
} else {
Err(ScriptError::ValidationFailure(code))
}
})
})
let mut args = vec![b"verify".to_vec()];
self.extract_script(script, &mut args)
.and_then(|script_binary| {
args.extend_from_slice(&script.args.as_slice());

let mut machine = DefaultMachine::<u64, SparseMemory>::default();
machine.add_syscall_module(Box::new(self.build_load_tx()));
machine.add_syscall_module(Box::new(self.build_load_cell(current_cell)));
machine.add_syscall_module(Box::new(self.build_load_cell_by_field(current_cell)));
machine.add_syscall_module(Box::new(self.build_load_input_by_field(current_input)));
machine.add_syscall_module(Box::new(Debugger::new(prefix)));
machine
.run(script_binary, &args)
.map_err(ScriptError::VMError)
.and_then(|code| {
if code == 0 {
Ok(())
} else {
Err(ScriptError::ValidationFailure(code))
}
})
})
}

pub fn verify(&self) -> Result<(), ScriptError> {
Expand Down Expand Up @@ -287,6 +314,12 @@ mod tests {
let mut buffer = Vec::new();
file.read_to_end(&mut buffer).unwrap();

let script = Script::new(0, vec![], None, Some(buffer), vec![]);
let mut builder = FlatBufferBuilder::new();
let offset = FbsScript::build(&mut builder, &script);
builder.finish(offset, None);
let buffer = builder.finished_data().to_vec();

let gen = Generator::new();
let privkey = gen.random_privkey();
let mut args = vec![b"foo".to_vec(), b"bar".to_vec()];
Expand Down Expand Up @@ -332,10 +365,6 @@ mod tests {

#[test]
fn check_invalid_dep_reference() {
let mut file = open_cell_verify();
let mut buffer = Vec::new();
file.read_to_end(&mut buffer).unwrap();

let gen = Generator::new();
let privkey = gen.random_privkey();
let mut args = vec![b"foo".to_vec(), b"bar".to_vec()];
Expand Down
2 changes: 2 additions & 0 deletions spec/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ ckb-core = { path = "../core" }
serde_derive = "1.0"
serde = "1.0"
ckb-pow = { path = "../pow" }
ckb-protocol = { path = "../protocol" }
flatbuffers = "0.5.0"
16 changes: 15 additions & 1 deletion spec/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@
use crate::consensus::Consensus;
use ckb_core::block::BlockBuilder;
use ckb_core::header::HeaderBuilder;
use ckb_core::script::Script;
use ckb_core::transaction::{CellOutput, Transaction, TransactionBuilder};
use ckb_core::Capacity;
use ckb_pow::{Pow, PowEngine};
use ckb_protocol::Script as FbsScript;
use flatbuffers::FlatBufferBuilder;
use numext_fixed_hash::H256;
use numext_fixed_uint::U256;
use serde_derive::Deserialize;
Expand Down Expand Up @@ -68,9 +71,20 @@ fn build_system_cell_transaction(cells: &[SystemCell]) -> Result<Transaction, Bo
let mut data = Vec::new();
file.read_to_end(&mut data)?;

let script = Script::new(0, vec![], None, Some(data), vec![]);
let mut builder = FlatBufferBuilder::new();
let offset = FbsScript::build(&mut builder, &script);
builder.finish(offset, None);
let script_data = builder.finished_data().to_vec();

// TODO: we should either provide a valid type hash so we can
// update system cell, or we can update this when P2SH is moved into VM.
let output = CellOutput::new(data.len() as Capacity, data, H256::default(), None);
let output = CellOutput::new(
script_data.len() as Capacity,
script_data,
H256::default(),
None,
);
outputs.push(output);
}

Expand Down

0 comments on commit 49fc513

Please sign in to comment.