-
Notifications
You must be signed in to change notification settings - Fork 236
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
After this change, we only have the following syscalls: * `LOAD_TX` which loads transaction metadata * `LOAD_CELL` which loads a cell as a whole * `LOAD_CELL_BY_FIELD` which loads a single field in a cell * `DEBUG` which prints debug information for contract debugging `LOAD_CELL`/`LOAD_CELL_BY_FIELD` can be configured to load input cell, output cell or current cell. Note that with `LOAD_CELL_BY_FIELD`, we won't need separate fetch script hash syscalls anymore.
- Loading branch information
Showing
8 changed files
with
486 additions
and
520 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
use crate::syscalls::{Source, LOAD_CELL_SYSCALL_NUMBER, SUCCESS}; | ||
use ckb_core::transaction::CellOutput; | ||
use ckb_protocol::CellOutput as FbsCellOutput; | ||
use ckb_vm::{CoreMachine, Error as VMError, Memory, Register, Syscalls, A0, A1, A2, A3, A4, A7}; | ||
use flatbuffers::FlatBufferBuilder; | ||
use std::cmp; | ||
|
||
#[derive(Debug)] | ||
pub struct LoadCell<'a> { | ||
outputs: &'a [&'a CellOutput], | ||
input_cells: &'a [&'a CellOutput], | ||
current: &'a CellOutput, | ||
} | ||
|
||
impl<'a> LoadCell<'a> { | ||
pub fn new( | ||
outputs: &'a [&'a CellOutput], | ||
input_cells: &'a [&'a CellOutput], | ||
current: &'a CellOutput, | ||
) -> LoadCell<'a> { | ||
LoadCell { | ||
outputs, | ||
input_cells, | ||
current, | ||
} | ||
} | ||
|
||
fn fetch_cell(&self, source: Source, index: usize) -> Option<&CellOutput> { | ||
match source { | ||
Source::Input => self.input_cells.get(index).cloned(), | ||
Source::Output => self.outputs.get(index).cloned(), | ||
Source::Current => Some(self.current), | ||
} | ||
} | ||
} | ||
|
||
impl<'a, R: Register, M: Memory> Syscalls<R, M> for LoadCell<'a> { | ||
fn initialize(&mut self, _machine: &mut CoreMachine<R, M>) -> Result<(), VMError> { | ||
Ok(()) | ||
} | ||
|
||
fn ecall(&mut self, machine: &mut CoreMachine<R, M>) -> Result<bool, VMError> { | ||
if machine.registers()[A7].to_u64() != LOAD_CELL_SYSCALL_NUMBER { | ||
return Ok(false); | ||
} | ||
|
||
let addr = machine.registers()[A0].to_usize(); | ||
let size_addr = machine.registers()[A1].to_usize(); | ||
let size = machine.memory_mut().load64(size_addr)? as usize; | ||
|
||
let index = machine.registers()[A3].to_usize(); | ||
let source = Source::parse_from_u64(machine.registers()[A4].to_u64())?; | ||
|
||
let cell = self | ||
.fetch_cell(source, index) | ||
.ok_or_else(|| VMError::OutOfBound)?; | ||
|
||
// NOTE: this is a very expensive operation here since we need to copy | ||
// everything in a cell to a flatbuffer object, serialize the object | ||
// into a buffer, and then copy requested data to VM memory space. So | ||
// we should charge cycles proportional to the full Cell size no matter | ||
// how much data the actual script is requesting, the per-byte cycle charged | ||
// here, should also be significantly higher than LOAD_CELL_BY_FIELD. | ||
// Also, while this is debatable, I suggest we charge full cycles for | ||
// subsequent calls even if we have cache implemented here. | ||
// TODO: find a way to cache this without consuming too much memory | ||
let mut builder = FlatBufferBuilder::new(); | ||
let offset = FbsCellOutput::build(&mut builder, cell); | ||
builder.finish(offset, None); | ||
let data = builder.finished_data(); | ||
|
||
let offset = machine.registers()[A2].to_usize(); | ||
let real_size = cmp::min(size, data.len() - offset); | ||
machine.memory_mut().store64(size_addr, real_size as u64)?; | ||
machine | ||
.memory_mut() | ||
.store_bytes(addr, &data[offset..offset + real_size])?; | ||
machine.registers_mut()[A0] = R::from_u8(SUCCESS); | ||
Ok(true) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
use byteorder::{LittleEndian, WriteBytesExt}; | ||
use ckb_core::transaction::CellOutput; | ||
use ckb_protocol::Script as FbsScript; | ||
use crate::syscalls::{Field, Source, ITEM_MISSING, LOAD_CELL_BY_FIELD_SYSCALL_NUMBER, SUCCESS}; | ||
use ckb_vm::{ | ||
CoreMachine, Error as VMError, Memory, Register, Syscalls, A0, A1, A2, A3, A4, A5, A7, | ||
}; | ||
use flatbuffers::FlatBufferBuilder; | ||
use std::cmp; | ||
|
||
#[derive(Debug)] | ||
pub struct LoadCellByField<'a> { | ||
outputs: &'a [&'a CellOutput], | ||
input_cells: &'a [&'a CellOutput], | ||
current: &'a CellOutput, | ||
} | ||
|
||
impl<'a> LoadCellByField<'a> { | ||
pub fn new( | ||
outputs: &'a [&'a CellOutput], | ||
input_cells: &'a [&'a CellOutput], | ||
current: &'a CellOutput, | ||
) -> LoadCellByField<'a> { | ||
LoadCellByField { | ||
outputs, | ||
input_cells, | ||
current, | ||
} | ||
} | ||
|
||
fn fetch_cell(&self, source: Source, index: usize) -> Option<&CellOutput> { | ||
match source { | ||
Source::Input => self.input_cells.get(index).cloned(), | ||
Source::Output => self.outputs.get(index).cloned(), | ||
Source::Current => Some(self.current), | ||
} | ||
} | ||
} | ||
|
||
fn store_data<R: Register, M: Memory>( | ||
machine: &mut CoreMachine<R, M>, | ||
data: &[u8], | ||
) -> Result<(), VMError> { | ||
let addr = machine.registers()[A0].to_usize(); | ||
let size_addr = machine.registers()[A1].to_usize(); | ||
let offset = machine.registers()[A2].to_usize(); | ||
|
||
let size = machine.memory_mut().load64(size_addr)? as usize; | ||
let real_size = cmp::min(size, data.len() - offset); | ||
machine.memory_mut().store64(size_addr, real_size as u64)?; | ||
machine | ||
.memory_mut() | ||
.store_bytes(addr, &data[offset..offset + real_size])?; | ||
Ok(()) | ||
} | ||
|
||
impl<'a, R: Register, M: Memory> Syscalls<R, M> for LoadCellByField<'a> { | ||
fn initialize(&mut self, _machine: &mut CoreMachine<R, M>) -> Result<(), VMError> { | ||
Ok(()) | ||
} | ||
|
||
fn ecall(&mut self, machine: &mut CoreMachine<R, M>) -> Result<bool, VMError> { | ||
if machine.registers()[A7].to_u64() != LOAD_CELL_BY_FIELD_SYSCALL_NUMBER { | ||
return Ok(false); | ||
} | ||
|
||
let index = machine.registers()[A3].to_usize(); | ||
let source = Source::parse_from_u64(machine.registers()[A4].to_u64())?; | ||
let field = Field::parse_from_u64(machine.registers()[A5].to_u64())?; | ||
|
||
let cell = self | ||
.fetch_cell(source, index) | ||
.ok_or_else(|| VMError::OutOfBound)?; | ||
|
||
let return_code = match field { | ||
Field::Capacity => { | ||
let mut buffer = vec![]; | ||
buffer.write_u64::<LittleEndian>(cell.capacity)?; | ||
store_data(machine, &buffer)?; | ||
SUCCESS | ||
} | ||
Field::Data => { | ||
store_data(machine, &cell.data)?; | ||
SUCCESS | ||
} | ||
Field::LockHash => { | ||
store_data(machine, &cell.lock.as_bytes())?; | ||
SUCCESS | ||
} | ||
Field::Contract => match cell.contract { | ||
Some(ref contract) => { | ||
let mut builder = FlatBufferBuilder::new(); | ||
let offset = FbsScript::build(&mut builder, &contract); | ||
builder.finish(offset, None); | ||
let data = builder.finished_data(); | ||
store_data(machine, data)?; | ||
SUCCESS | ||
} | ||
None => ITEM_MISSING, | ||
}, | ||
Field::ContractHash => match cell.contract { | ||
Some(ref contract) => { | ||
store_data(machine, &contract.type_hash().as_bytes())?; | ||
SUCCESS | ||
} | ||
None => ITEM_MISSING, | ||
}, | ||
}; | ||
machine.registers_mut()[A0] = R::from_u8(return_code); | ||
Ok(true) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
use ckb_vm::{CoreMachine, Error as VMError, Memory, Register, Syscalls, A0, A1, A2, A7}; | ||
use std::cmp; | ||
use syscalls::{LOAD_TX_SYSCALL_NUMBER, SUCCESS}; | ||
|
||
pub struct LoadTx<'a> { | ||
tx: &'a [u8], | ||
} | ||
|
||
impl<'a> LoadTx<'a> { | ||
pub fn new(tx: &'a [u8]) -> LoadTx<'a> { | ||
LoadTx { tx } | ||
} | ||
} | ||
|
||
impl<'a, R: Register, M: Memory> Syscalls<R, M> for LoadTx<'a> { | ||
fn initialize(&mut self, _machine: &mut CoreMachine<R, M>) -> Result<(), VMError> { | ||
Ok(()) | ||
} | ||
|
||
fn ecall(&mut self, machine: &mut CoreMachine<R, M>) -> Result<bool, VMError> { | ||
if machine.registers()[A7].to_u64() != LOAD_TX_SYSCALL_NUMBER { | ||
return Ok(false); | ||
} | ||
|
||
let addr = machine.registers()[A0].to_usize(); | ||
let size_addr = machine.registers()[A1].to_usize(); | ||
let size = machine.memory_mut().load64(size_addr)? as usize; | ||
|
||
let data = self.tx; | ||
|
||
let offset = machine.registers()[A2].to_usize(); | ||
let real_size = cmp::min(size, data.len() - offset); | ||
machine.memory_mut().store64(size_addr, real_size as u64)?; | ||
machine | ||
.memory_mut() | ||
.store_bytes(addr, &data[offset..offset + real_size])?; | ||
machine.registers_mut()[A0] = R::from_u8(SUCCESS); | ||
Ok(true) | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.