mirror of
https://github.com/XRPLF/rippled.git
synced 2026-08-21 06:10:58 +00:00
feat: Hook up set_data host function
This commit is contained in:
@@ -420,4 +420,11 @@ host_functions! {
|
||||
#[gas = 500]
|
||||
#[wasm_name = "trace_num"]
|
||||
fn trace_num(&self, msg: &str, number: i64) -> HostResult<()>;
|
||||
|
||||
/// Stores `data` as the current object's data field, replacing whatever was there,
|
||||
/// and returns the number of bytes stored. Reads the data region; `DataFieldTooLarge`
|
||||
/// if it exceeds the host's limit.
|
||||
#[gas = 1000]
|
||||
#[wasm_name = "set_data"]
|
||||
fn update_data(&self, data: &[u8]) -> HostResult<i32>;
|
||||
}
|
||||
|
||||
@@ -401,6 +401,11 @@ impl HostFunctions for FakeHost {
|
||||
self.traced.borrow_mut().push(format!("{msg}={number}"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Reads a data blob and returns the count of bytes stored.
|
||||
fn update_data(&self, data: &[u8]) -> HostResult<i32> {
|
||||
Ok(data.len() as i32)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -615,6 +620,7 @@ fn the_trait_is_implementable() {
|
||||
assert_eq!(out[0], 3);
|
||||
assert_eq!(host.trace("hello", b"xy", true), Ok(()));
|
||||
assert_eq!(host.trace_num("count", -1), Ok(()));
|
||||
assert_eq!(host.update_data(b"abcd"), Ok(4));
|
||||
|
||||
assert_eq!(*host.traced.borrow(), ["hello/2/true", "count=-1"]);
|
||||
}
|
||||
@@ -719,6 +725,7 @@ fn the_spec_table_matches_the_declarations() {
|
||||
("sha512_half", 2000),
|
||||
("trace", 500),
|
||||
("trace_num", 500),
|
||||
("set_data", 1000),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -391,6 +391,10 @@ mod ffi {
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "traceNum"]
|
||||
fn trace_num(self: &HostContext, msg: &str, number: i64) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "updateData"]
|
||||
fn update_data(self: &HostContext, data: &[u8]) -> i32;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -657,6 +661,10 @@ impl HostFunctions for CxxHost<'_> {
|
||||
fn trace_num(&self, msg: &str, number: i64) -> HostResult<()> {
|
||||
reported(self.ctx.trace_num(msg, number))
|
||||
}
|
||||
|
||||
fn update_data(&self, data: &[u8]) -> HostResult<i32> {
|
||||
scalar(self.ctx.update_data(data))
|
||||
}
|
||||
}
|
||||
|
||||
fn run_escrow(
|
||||
|
||||
@@ -384,6 +384,9 @@ mod tests {
|
||||
fn trace_num(&self, _msg: &str, _number: i64) -> HostResult<()> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn update_data(&self, _data: &[u8]) -> HostResult<i32> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
}
|
||||
|
||||
fn state(budget: u64) -> VmState<'static> {
|
||||
|
||||
@@ -781,6 +781,20 @@ pub(crate) fn register_host_functions(
|
||||
})
|
||||
},
|
||||
),
|
||||
HostFunctionSpec::UpdateData => linker.func_wrap(
|
||||
HOST_MODULE,
|
||||
op.wasm_name(),
|
||||
|mut caller: Caller<'_, VmState<'_>>,
|
||||
ptr: i32,
|
||||
len: i32|
|
||||
-> Result<i32, wasmi::Error> {
|
||||
charged(&mut caller, HostFunctionSpec::UpdateData, |c| {
|
||||
let host = c.data().host;
|
||||
let data = read_borrowed(c, Region::new(ptr, len))?;
|
||||
host.update_data(data)
|
||||
})
|
||||
},
|
||||
),
|
||||
}?;
|
||||
}
|
||||
Ok(())
|
||||
|
||||
@@ -254,6 +254,11 @@ fn call_for(op: HostFunctionSpec) -> Call {
|
||||
"(call $trace_num (i32.const 0) (i32.const 0) (i64.const 0))",
|
||||
3,
|
||||
),
|
||||
HostFunctionSpec::UpdateData => (
|
||||
import::SET_DATA,
|
||||
"(call $set_data (i32.const 0) (i32.const 8))",
|
||||
2,
|
||||
),
|
||||
};
|
||||
Call {
|
||||
import,
|
||||
|
||||
@@ -748,6 +748,25 @@ fn vault_id_reads_the_account_and_seq() {
|
||||
assert_eq!(*host.vault_keylets_asked.borrow(), vec![(account, 5)]);
|
||||
}
|
||||
|
||||
/// A call that reads an input region and returns a scalar rather than writing bytes:
|
||||
/// the data blob reaches the host, and the byte count it reports comes back as the
|
||||
/// call's status.
|
||||
#[test]
|
||||
fn set_data_passes_the_data_through_and_returns_the_count() {
|
||||
let host = FakeHost::new().answering_update_data(Ok(8));
|
||||
|
||||
let wat = module(
|
||||
&[import::SET_DATA, ONE_PAGE],
|
||||
"(call $set_data (i32.const 64) (i32.const 8))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 8, "the byte count the host reported");
|
||||
assert_eq!(
|
||||
*host.update_data_asked.borrow(),
|
||||
[vec![0u8; 8]],
|
||||
"the 8-byte region reached the host"
|
||||
);
|
||||
}
|
||||
|
||||
/// A leading scalar parameter reaches the host as declared.
|
||||
#[test]
|
||||
fn home_le_field_passes_the_field_selector_through() {
|
||||
|
||||
@@ -98,7 +98,7 @@ fn a_disabled_feature_does_not_pass() {
|
||||
|
||||
/// Every host function the ABI declares, spelled as a guest imports it. The count
|
||||
/// is asserted against the ABI so a function added to it cannot be left out here.
|
||||
const ALL_IMPORTS: [&str; 41] = [
|
||||
const ALL_IMPORTS: [&str; 42] = [
|
||||
import::LDGR_INDEX,
|
||||
import::PARENT_LDGR_TIME,
|
||||
import::PARENT_LDGR_HASH,
|
||||
@@ -140,6 +140,7 @@ const ALL_IMPORTS: [&str; 41] = [
|
||||
import::SHA512_HALF,
|
||||
import::TRACE,
|
||||
import::TRACE_NUM,
|
||||
import::SET_DATA,
|
||||
];
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -282,6 +282,10 @@ pub struct FakeHost {
|
||||
pub digested: RefCell<Vec<Vec<u8>>>,
|
||||
/// Every `trace`/`trace_num` call, in order.
|
||||
pub traces: RefCell<Vec<Trace>>,
|
||||
/// What `update_data` answers, whatever data it is given.
|
||||
pub update_data_answer: HostResult<i32>,
|
||||
/// Every data blob `update_data` was given.
|
||||
pub update_data_asked: RefCell<Vec<Vec<u8>>>,
|
||||
}
|
||||
|
||||
impl Default for FakeHost {
|
||||
@@ -370,6 +374,8 @@ impl Default for FakeHost {
|
||||
fields_asked: RefCell::new(Vec::new()),
|
||||
digested: RefCell::new(Vec::new()),
|
||||
traces: RefCell::new(Vec::new()),
|
||||
update_data_answer: Ok(0),
|
||||
update_data_asked: RefCell::new(Vec::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -671,6 +677,11 @@ impl FakeHost {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn answering_update_data(mut self, answer: HostResult<i32>) -> FakeHost {
|
||||
self.update_data_answer = answer;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn traces(&self) -> Vec<Trace> {
|
||||
self.traces.borrow().clone()
|
||||
}
|
||||
@@ -1069,6 +1080,11 @@ impl HostFunctions for FakeHost {
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_data(&self, data: &[u8]) -> HostResult<i32> {
|
||||
self.update_data_asked.borrow_mut().push(data.to_vec());
|
||||
self.update_data_answer
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -1132,6 +1148,8 @@ pub mod import {
|
||||
r#"(import "host_lib" "trace" (func $trace (param i32 i32 i32 i32 i32) (result i32)))"#;
|
||||
pub const TRACE_NUM: &str =
|
||||
r#"(import "host_lib" "trace_num" (func $trace_num (param i32 i32 i64) (result i32)))"#;
|
||||
pub const SET_DATA: &str =
|
||||
r#"(import "host_lib" "set_data" (func $set_data (param i32 i32) (result i32)))"#;
|
||||
}
|
||||
|
||||
/// One page of linear memory, exported under the name the engine looks for.
|
||||
|
||||
@@ -273,6 +273,11 @@ public:
|
||||
|
||||
[[nodiscard]] std::int32_t
|
||||
traceNum(rust::Str msg, std::int64_t number) const noexcept;
|
||||
|
||||
// Stores `data` as the current object's data field and returns the number of bytes
|
||||
// stored, or a negative `HostFunctionError` code.
|
||||
[[nodiscard]] std::int32_t
|
||||
updateData(rust::Slice<std::uint8_t const> data) const noexcept;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -862,4 +862,16 @@ HostContext::traceNum(rust::Str msg, std::int64_t number) const noexcept
|
||||
});
|
||||
}
|
||||
|
||||
std::int32_t
|
||||
HostContext::updateData(rust::Slice<std::uint8_t const> data) const noexcept
|
||||
{
|
||||
return guarded(hostFunctions_.getJournal(), kHostInternal, [&] {
|
||||
auto const stored = hostFunctions_.updateData(Slice{data.data(), data.size()});
|
||||
if (!stored)
|
||||
return hfErrorToInt(stored.error());
|
||||
|
||||
return *stored;
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
Reference in New Issue
Block a user