Skip to content

Commit

Permalink
add host functions for eth methods (closes #106)
Browse files Browse the repository at this point in the history
  • Loading branch information
kp2pml30 committed Dec 11, 2024
1 parent 3be9ad6 commit cdf53af
Show file tree
Hide file tree
Showing 24 changed files with 341 additions and 30 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,6 @@ __pycache__
.DS_Store
.AppleDouble
.LSOverride

# perf
perf.data*
27 changes: 27 additions & 0 deletions executor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,33 @@ rustflags = [
#"--remap-path-prefix=src/=genvm/executor/src/"
]

[profile.dev.package.cranelift-codegen]
opt-level = 2
[profile.dev.package.cranelift-entity]
opt-level = 2
[profile.dev.package.cranelift-frontend]
opt-level = 2
[profile.dev.package.cranelift-codegen-shared]
opt-level = 2
[profile.dev.package.cranelift-bforest]
opt-level = 2
[profile.dev.package.cranelift-bitset]
opt-level = 2
[profile.dev.package.cranelift-control]
opt-level = 2
[profile.dev.package.cranelift-wasm]
opt-level = 2
[profile.dev.package.cranelift-native]
opt-level = 2
[profile.dev.package.wasmtime]
opt-level = 2
[profile.dev.package.wasmtime-cranelift]
opt-level = 2
[profile.dev.package.wasmparser]
opt-level = 2
[profile.dev.package.regalloc2]
opt-level = 2

[profile.release]
debug = true

Expand Down
4 changes: 3 additions & 1 deletion executor/codegen/data/host-fns.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
"post_nondet_result": 6,
"post_message": 7,
"consume_fuel": 8,
"deploy_contract": 9
"deploy_contract": 9,
"eth_call": 10,
"eth_send": 11
}
}
]
4 changes: 4 additions & 0 deletions executor/src/host/host_fns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ pub enum Methods {
PostMessage = 7,
ConsumeFuel = 8,
DeployContract = 9,
EthCall = 10,
EthSend = 11,
}

impl TryFrom<u8> for Methods {
Expand All @@ -28,6 +30,8 @@ impl TryFrom<u8> for Methods {
7 => Ok(Methods::PostMessage),
8 => Ok(Methods::ConsumeFuel),
9 => Ok(Methods::DeployContract),
10 => Ok(Methods::EthCall),
11 => Ok(Methods::EthSend),
_ => Err(()),
}
}
Expand Down
55 changes: 50 additions & 5 deletions executor/src/host/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,15 @@ fn read_u32(sock: &mut dyn Sock) -> Result<u32> {
Ok(u32::from_le_bytes(int_buf))
}

fn read_bytes(sock: &mut dyn Sock) -> Result<Arc<[u8]>> {
let len = read_u32(sock)?;

let res = Arc::new_uninit_slice(len as usize);
let mut res = unsafe { res.assume_init() };
sock.read_exact(Arc::get_mut(&mut res).unwrap())?;
Ok(res)
}

fn write_result(sock: &mut dyn Sock, res: Result<&vm::RunOk, &anyhow::Error>) -> Result<()> {
let str: String;
let data = match res {
Expand Down Expand Up @@ -152,11 +161,7 @@ impl Host {
sock.write_all(&[host_fns::Methods::GetCode as u8])?;
sock.write_all(&account.raw())?;

let len = read_u32(sock)? as usize;
let mut res = Vec::with_capacity(len);
unsafe { res.set_len(len) };
sock.read_exact(&mut res)?;
Ok(Arc::from(res))
read_bytes(sock)
}

pub fn storage_read(
Expand Down Expand Up @@ -307,4 +312,44 @@ impl Host {
sock.write_all(&gas.to_le_bytes())?;
Ok(())
}

pub fn eth_call(
&mut self,
address: AccountAddress,
calldata: &[u8],
data: &str,
) -> Result<Arc<[u8]>> {
let Ok(mut sock) = (*self.sock).lock() else {
anyhow::bail!("can't take lock")
};
let sock: &mut dyn Sock = &mut *sock;
sock.write_all(&[host_fns::Methods::EthCall as u8])?;

sock.write_all(&address.raw())?;

sock.write_all(&(calldata.len() as u32).to_le_bytes())?;
sock.write_all(calldata)?;

sock.write_all(&(data.as_bytes().len() as u32).to_le_bytes())?;
sock.write_all(data.as_bytes())?;

read_bytes(sock)
}

pub fn eth_send(&mut self, address: AccountAddress, calldata: &[u8], data: &str) -> Result<()> {
let Ok(mut sock) = (*self.sock).lock() else {
anyhow::bail!("can't take lock")
};
let sock: &mut dyn Sock = &mut *sock;
sock.write_all(&[host_fns::Methods::EthSend as u8])?;

sock.write_all(&address.raw())?;

sock.write_all(&(calldata.len() as u32).to_le_bytes())?;
sock.write_all(calldata)?;

sock.write_all(&(data.as_bytes().len() as u32).to_le_bytes())?;
sock.write_all(data.as_bytes())?;
Ok(())
}
}
50 changes: 50 additions & 0 deletions executor/src/wasi/genlayer_sdk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -732,6 +732,56 @@ impl generated::genlayer_sdk::GenlayerSdk for ContextVFS<'_> {
res.map_err(|_e| generated::types::Errno::Io)?;
Ok(())
}

fn eth_send(
&mut self,
mem: &mut wiggle::GuestMemory<'_>,
account: &generated::types::Addr,
calldata: &generated::types::Bytes,
data: wiggle::GuestPtr<str>,
) -> Result<(), generated::types::Error> {
if !self.context.data.conf.is_deterministic {
return Err(generated::types::Errno::DeterministicViolation.into());
}
let supervisor = self.context.data.supervisor.clone();
let address = AccountAddress::read_from_mem(account, mem)?;
let calldata = calldata.read_owned(mem)?;
let data = super::common::read_string(mem, data)?;
let Ok(mut supervisor) = supervisor.lock() else {
return Err(generated::types::Errno::Io.into());
};
let res = supervisor
.host
.eth_send(address, &calldata, &data)
.map_err(generated::types::Error::trap)?;
Ok(())
}

fn eth_call(
&mut self,
mem: &mut wiggle::GuestMemory<'_>,
account: &generated::types::Addr,
calldata: &generated::types::Bytes,
data: wiggle::GuestPtr<str>,
) -> Result<generated::types::Fd, generated::types::Error> {
if !self.context.data.conf.is_deterministic {
return Err(generated::types::Errno::DeterministicViolation.into());
}
let supervisor = self.context.data.supervisor.clone();
let address = AccountAddress::read_from_mem(account, mem)?;
let calldata = calldata.read_owned(mem)?;
let data = super::common::read_string(mem, data)?;
let Ok(mut supervisor) = supervisor.lock() else {
return Err(generated::types::Errno::Io.into());
};
let res = supervisor
.host
.eth_call(address, &calldata, &data)
.map_err(generated::types::Error::trap)?;
Ok(generated::types::Fd::from(self.vfs.place_content(
FileContentsUnevaluated::from_contents(res, 0),
)))
}
}

impl Context {
Expand Down
14 changes: 14 additions & 0 deletions executor/src/wasi/witx/genlayer_sdk.witx
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,18 @@
(param $buf $bytes)
(result $error (expected (error $errno)))
)

(@interface func (export "eth_call")
(param $account $addr)
(param $calldata $bytes)
(param $data string)
(result $error (expected $fd (error $errno)))
)

(@interface func (export "eth_send")
(param $account $addr)
(param $calldata $bytes)
(param $data string)
(result $error (expected (error $errno)))
)
)
12 changes: 12 additions & 0 deletions executor/testdata/cases/py-core/send_message_eth.jsonnet
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
local simple = import 'templates/simple.jsonnet';
simple.run('${jsonnetDir}/send_message_eth.py') {
"calldata": |||
{
"method": "__init__",
"args": []
}
|||,
"message": super.message + {
"is_init": true,
}
}
3 changes: 3 additions & 0 deletions executor/testdata/cases/py-core/send_message_eth.msgs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
eth_send:
{}
b')\xe9\x9f\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n'
17 changes: 17 additions & 0 deletions executor/testdata/cases/py-core/send_message_eth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# { "Depends": "py-genlayer:test" }
from genlayer import *


@gl.eth_contract
class Ghost:
class View:
pass

class Write:
def test(self, x: u256, /) -> None: ...

Check notice

Code scanning / CodeQL

Statement has no effect Note test

This statement has no effect.


@gl.contract
class Contract:
def __init__(self):
Ghost(Address(b'\x30' * 20)).emit().test(u256(10))
1 change: 1 addition & 0 deletions executor/testdata/cases/py-core/send_message_eth.stdout
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
executed with `Return("\x00")`
2 changes: 1 addition & 1 deletion executor/testdata/cases/runners/lock/runner.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
},
{
"With": {
"runner": "genvm-cpython:YOTOKTSLMY4ZVTNGCE2SMI3G6J3RYISK3N7SI7RMI4TAE===",
"runner": "genvm-cpython:USAGYVDRJ44TS55HEPUZFJGDRBZZB5LZKDMM4SECP6KGQ===",
"action": {
"Seq": [
{
Expand Down
27 changes: 27 additions & 0 deletions executor/testdata/runner/base_host.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ async def deploy_contract(
self, calldata: bytes, code: bytes, data: DefaultTransactionData, /
) -> None: ...
async def consume_gas(self, gas: int, /) -> None: ...
async def eth_send(
self, account: bytes, calldata: bytes, data: typing.Any, /
) -> None: ...

Check notice

Code scanning / CodeQL

Statement has no effect Note test

This statement has no effect.
async def eth_call(
self, account: bytes, calldata: bytes, data: typing.Any, /
) -> bytes: ...

Check notice

Code scanning / CodeQL

Statement has no effect Note test

This statement has no effect.


async def host_loop(handler: IHost):
Expand Down Expand Up @@ -169,6 +175,27 @@ async def read_result() -> tuple[ResultCode, bytes]:
)

await handler.deploy_contract(calldata, code, message_data)

case Methods.ETH_SEND:
account = await read_exact(ACCOUNT_ADDR_SIZE)
calldata_len = await recv_int()
calldata = await read_exact(calldata_len)
data_len = await recv_int()
data = await read_exact(data_len)
data_str = data.decode('utf-8')

await handler.eth_send(account, calldata, json.loads(data_str))
case Methods.ETH_CALL:
account = await read_exact(ACCOUNT_ADDR_SIZE)
calldata_len = await recv_int()
calldata = await read_exact(calldata_len)
data_len = await recv_int()
data = await read_exact(data_len)
data_str = data.decode('utf-8')

res = await handler.eth_call(account, calldata, json.loads(data_str))
await send_int(len(res))
await send_all(res)
case x:
raise Exception(f'unknown method {x}')

Expand Down
2 changes: 2 additions & 0 deletions executor/testdata/runner/host_fns.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ class Methods(IntEnum):
POST_MESSAGE = 7
CONSUME_FUEL = 8
DEPLOY_CONTRACT = 9
ETH_CALL = 10
ETH_SEND = 11
12 changes: 12 additions & 0 deletions executor/testdata/runner/mock_host.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,18 @@ async def deploy_contract(
self.messages_file = open(self.messages_path, 'wt')
self.messages_file.write(f'deploy:\n\t{data}\n\t{calldata}\n\t{code}\n')

async def eth_send(
self, account: bytes, calldata: bytes, data: typing.Any, /
) -> None:
if self.messages_file is None:
self.messages_file = open(self.messages_path, 'wt')

Check warning

Code scanning / CodeQL

File is not always closed Warning test

File is opened but is not closed.
self.messages_file.write(f'eth_send:\n\t{data}\n\t{calldata}\n')

async def eth_call(
self, account: bytes, calldata: bytes, data: typing.Any, /
) -> bytes:
assert False

async def consume_gas(self, gas: int):
pass

Expand Down
32 changes: 32 additions & 0 deletions runners/cpython-and-ext/extension/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,5 +209,37 @@ fn genlayer_wasi(m: &Bound<'_, PyModule>) -> PyResult<()> {
map_error(res)
}

#[pyfn(m)]
fn eth_call(addr: &[u8], calldata: &[u8], data: &str) -> PyResult<u32> {
let addr = get_addr(&addr)?;
let res = unsafe {
genvm_sdk_rust::eth_call(
addr,
genvm_sdk_rust::Bytes {
buf: calldata.as_ptr() as *const u8,
buf_len: calldata.len() as u32,
},
data,
)
};
map_error(res)
}

#[pyfn(m)]
fn eth_send(addr: &[u8], calldata: &[u8], data: &str) -> PyResult<()> {
let addr = get_addr(&addr)?;
let res = unsafe {
genvm_sdk_rust::eth_send(
addr,
genvm_sdk_rust::Bytes {
buf: calldata.as_ptr() as *const u8,
buf_len: calldata.len() as u32,
},
data,
)
};
map_error(res)
}

Ok(())
}
Loading

0 comments on commit cdf53af

Please sign in to comment.