From a77de235e2145b25839d40b0df66adcd20d79ead Mon Sep 17 00:00:00 2001 From: Grant Wuerker Date: Sat, 4 Mar 2023 13:55:57 -0700 Subject: [PATCH 1/4] std abi --- crates/library/std/src/abi.fe | 121 ++++++++++++++++++++ crates/library/std/src/buf.fe | 45 ++++++++ crates/library/std/src/context.fe | 47 ++++++++ crates/tests/fixtures/files/abi.fe | 104 +++++++++++++++++ crates/tests/fixtures/files/contract_abi.fe | 50 ++++++++ notes | 2 + 6 files changed, 369 insertions(+) create mode 100644 crates/library/std/src/abi.fe create mode 100644 crates/tests/fixtures/files/abi.fe create mode 100644 crates/tests/fixtures/files/contract_abi.fe create mode 100644 notes diff --git a/crates/library/std/src/abi.fe b/crates/library/std/src/abi.fe new file mode 100644 index 0000000000..d792145c4e --- /dev/null +++ b/crates/library/std/src/abi.fe @@ -0,0 +1,121 @@ +use ingot::evm +use ingot::context::CalldataReader +use ingot::traits::Max +use ingot::buf::{MemoryBuffer, MemoryBufferReader, MemoryBufferWriter} + +pub trait AbiDecode { + fn decode(mut reader: CalldataReader) -> Self; +} + +pub trait AbiEncode { + fn encode(self, mut writer: MemoryBufferWriter); +} + +impl AbiDecode for u256 { + fn decode(mut reader: CalldataReader) -> Self { + return reader.read_u256() + } +} + +impl AbiEncode for u256 { + fn encode(self, mut writer: MemoryBufferWriter) { + return writer.write(value: self) + } +} + +impl AbiDecode for u128 { + fn decode(mut reader: CalldataReader) -> Self { + let value: u256 = reader.read_u256() + if value > u128::max() { + revert + } else { + return u128(value) + } + } +} + +impl AbiEncode for u128 { + fn encode(self, mut writer: MemoryBufferWriter) { + return writer.write(value: u256(self)) + } +} + +impl AbiDecode for u64 { + fn decode(mut reader: CalldataReader) -> Self { + let value: u256 = reader.read_u256() + if value > u64::max() { + revert + } else { + return u64(value) + } + } +} + +impl AbiEncode for u64 { + fn encode(self, mut writer: MemoryBufferWriter) { + return writer.write(value: u256(self)) + } +} + +// // generic impl of array encoding +// impl AbiEncode for Array +// where +// T: AbiEncode +// { +// fn encode(self, mut writer: MemoryBufferWriter) { +// let i: usize = 0 + +// while i < N { +// self[i].encode(buf) +// i += 1 +// } +// } +// } + +// // generic impl of array decoding +// impl AbiDecode for Array +// where +// T: AbiDecode +// { +// fn decode(mut reader: CalldataReader) -> Self { +// let decoded_array: Self +// let i: usize = 0 + +// while i < N { +// decoded_array[i] = T::decode(buf) +// i += 1 +// } + +// return decoded_array +// } +// } + +// // tuple encode derivation +// impl AbiEncode for (T1, T2, ...) +// where +// T1: AbiEncode +// T2: AbiEncode +// ... +// { +// fn encode(self, mut writer: MemoryBufferWriter) { +// self.item0.encode(writer) +// self.item1.encode(writer) +// ... +// } +// } + +// // tuple decode derivation +// impl AbiDecode for (T1, T2, ...) +// where +// T1: AbiDecode +// T1: AbiDecode +// ... +// { +// fn decode(mut reader: CalldataReader) -> Self { +// return ( +// u256::decode(reader), +// u256::decode(reader) +// ... +// ) +// } +// } \ No newline at end of file diff --git a/crates/library/std/src/buf.fe b/crates/library/std/src/buf.fe index a1d97af4e6..3363a6c8fb 100644 --- a/crates/library/std/src/buf.fe +++ b/crates/library/std/src/buf.fe @@ -297,3 +297,48 @@ pub struct RawCallBuffer { return self.buf.writer() } } + +pub struct CalldataReader { + cur: Cursor + + fn read_offset(mut self, len: u256) -> u256 { + return self.cur.advance(len) + } + + fn read_n(mut self, len: u256) -> u256 { + let offset: u256 = self.read_offset(len) + unsafe { + let value: u256 = evm::call_data_load(offset) + return evm::shr(bits: 256 - len * 8, value) + } + } + + pub fn read_u8(mut self) -> u8 { + return u8(self.read_n(len: 1)) + } + + pub fn read_u16(mut self) -> u16 { + return u16(self.read_n(len: 2)) + } + + pub fn read_u32(mut self) -> u32 { + return u32(self.read_n(len: 4)) + } + + pub fn read_u64(mut self) -> u64 { + return u64(self.read_n(len: 8)) + } + + pub fn read_u128(mut self) -> u128 { + return u128(self.read_n(len: 16)) + } + + pub fn read_u256(mut self) -> u256 { + let offset: u256 = self.read_offset(len: 32) + unsafe { + let value: u256 = evm::mload(offset) + return value + } + } + +} \ No newline at end of file diff --git a/crates/library/std/src/context.fe b/crates/library/std/src/context.fe index 8a7dd10026..8030c2d878 100644 --- a/crates/library/std/src/context.fe +++ b/crates/library/std/src/context.fe @@ -19,6 +19,46 @@ pub trait Emittable { fn emit(self, _ val: OutOfReachMarker); } +pub struct CalldataReader { + cur_offset: u256 + len: u256 + + pub unsafe fn new(len: u256) -> CalldataReader { + return CalldataReader(cur_offset: 0, len) + } + + pub fn remainder(self) -> u256 { + return self.len - self.cur_offset + } + + pub fn read_u256(mut self) -> u256 { + unsafe { + let value: u256 = evm::call_data_load(offset: self.cur_offset) + self.cur_offset += 32 + assert self.cur_offset <= self.len + return value + } + } + + pub fn read_u16(mut self) -> u16 { + unsafe { + let value: u256 = evm::call_data_load(offset: self.cur_offset) + self.cur_offset += 2 + assert self.cur_offset <= self.len + return u16(evm::shr(bits: 240, value)) + } + } + + pub fn read_u32(mut self) -> u32 { + unsafe { + let value: u256 = evm::call_data_load(offset: self.cur_offset) + self.cur_offset += 4 + assert self.cur_offset <= self.len + return u32(evm::shr(bits: 224, value)) + } + } +} + pub struct Context { pub fn base_fee(self) -> u256 { unsafe { return evm::base_fee() } @@ -107,6 +147,13 @@ pub struct Context { } } + pub fn calldata_reader(self) -> CalldataReader { + unsafe { + let len: u256 = evm::call_data_size() + return CalldataReader::new(len) + } + } + pub fn emit(mut self, _ val: T) { val.emit(OutOfReachMarker()) } diff --git a/crates/tests/fixtures/files/abi.fe b/crates/tests/fixtures/files/abi.fe new file mode 100644 index 0000000000..b9a67eac96 --- /dev/null +++ b/crates/tests/fixtures/files/abi.fe @@ -0,0 +1,104 @@ +// error: cannot glob import from ingot +// use std::{abi::Abi, evm} +use std::abi::{AbiDecode, AbiEncode} +use std::context::{Context, CalldataReader} +use std::buf::{ + MemoryBuffer, + MemoryBufferWriter, + MemoryBufferReader, + RawCallBuffer +} +use std::evm + +contract AbiDecodeU256 { + pub unsafe fn __call__(ctx: Context) { + let mut reader: CalldataReader = ctx.calldata_reader() + let value: u256 = u256::decode(reader) + + let mut buf: MemoryBuffer = MemoryBuffer::new(len: 32) + let mut writer: MemoryBufferWriter = buf.writer() + writer.write(value) + + evm::return_mem(buf) + } +} + +// #test +// unsafe fn u256_decode() { +// let mut buf: MemoryBuffer = MemoryBuffer::new(len: 64) + +// let mut writer: MemoryBufferWriter = buf.writer() +// writer.write(value: 26) +// writer.write(value: 42) + +// let mut reader: MemoryBufferReader = buf.reader() +// assert u256::decode(reader) == 26 +// assert u256::decode(reader) == 42 +// } + +#test +fn u256_encode() { + let mut buf: MemoryBuffer = MemoryBuffer::new(len: 64) + + let mut writer: MemoryBufferWriter = buf.writer() + 26.encode(writer) + 42.encode(writer) + + let mut reader: MemoryBufferReader = buf.reader() + assert reader.read_u256() == 26 + assert reader.read_u256() == 42 +} + +#test +fn u256_decode(mut ctx: Context) { + let decoder: address = address(AbiDecodeU256.create(ctx, 0)) + let mut buf: RawCallBuffer = RawCallBuffer::new( + input_len: 32, + output_len: 32 + ) + + let mut writer: MemoryBufferWriter = buf.writer() + writer.write(value: 42) + + ctx.raw_call(addr: decoder, value: 0, buf) + + let mut reader: MemoryBufferReader = buf.reader() + assert reader.read_u256() == 42 +} + +// type MyArray = Array +// type MyTuple = (u256, u256) + +// #test +// unsafe fn array_decode() { +// evm::mstore(offset: HASH_SCRATCH_SPACE_START, value: 26) +// evm::mstore(offset: HASH_SCRATCH_SPACE_START + 32, value: 42) + +// let mut buf: MemoryBuffer = MemoryBuffer::new( +// start: HASH_SCRATCH_SPACE_START, +// end: HASH_SCRATCH_SPACE_START + HASH_SCRATCH_SPACE_SIZE +// ) + +// // parser panics +// // let my_array: = Array::decode(buf) +// let my_array: MyArray = MyArray::decode(buf) +// assert my_array[0] == 26 +// assert my_array[1] == 42 +// } + +// #test +// unsafe fn tuple_decode() { +// evm::mstore(offset: HASH_SCRATCH_SPACE_START, value: 26) +// evm::mstore(offset: HASH_SCRATCH_SPACE_START + 32, value: 42) + +// let mut buf: MemoryBuffer = MemoryBuffer::new( +// start: HASH_SCRATCH_SPACE_START, +// end: HASH_SCRATCH_SPACE_START + HASH_SCRATCH_SPACE_SIZE +// ) + +// // parser panics +// // let my_tuple: (u256, u256) = (u256, u256)::decode(buf) +// let my_tuple: MyTuple = MyTuple::decode(buf) +// assert my_tuple.item0 == 26 +// assert my_tuple.item1 == 42 +// } diff --git a/crates/tests/fixtures/files/contract_abi.fe b/crates/tests/fixtures/files/contract_abi.fe new file mode 100644 index 0000000000..77d1535284 --- /dev/null +++ b/crates/tests/fixtures/files/contract_abi.fe @@ -0,0 +1,50 @@ +use std::buf::{MemoryBuffer, MemoryBufferReader, MemoryBufferWriter} +use std::abi::{AbiEncode, AbiDecode} +use std::context::CalldataReader +use std::evm + +struct UnknownSelector { + pub selector: u256 +} + +contract MyContractInterface { + pub fn set_foo(mut self, foo: u256) { + revert UnknownSelector(selector: 0) + } + + pub fn get_foo(self) -> u256 { + revert UnknownSelector(selector: 0) + } +} + +contract MyContract { + foo: u256 + + pub unsafe fn __call__(mut self, ctx: Context) { + let mut reader: CalldataReader = ctx.calldata_reader() + + let selector: u32 = reader.read_u32() + + if selector == 0xe5d5dfbc { + self.foo = u256::decode(reader) + } else if selector == 0x62e2d0de { + let mut buf: MemoryBuffer = MemoryBuffer::new(len: 32) + let mut writer: MemoryBufferWriter = buf.writer() + // self.foo.encode(writer) + let value: u256 = self.foo + value.encode(writer) + evm::return_mem(offset: buf.offset(), len: buf.len()) + } else { + revert UnknownSelector(selector) + } + } +} + +#test +unsafe fn contract_call() { + let mut ctx: Context = Context() + let mut my_contract: MyContractInterface = MyContractInterface(address(MyContract.create(ctx, 0))) + assert my_contract.get_foo() == 0 + my_contract.set_foo(foo: 42) + assert my_contract.get_foo() == 42 +} \ No newline at end of file diff --git a/notes b/notes new file mode 100644 index 0000000000..bda5674dfc --- /dev/null +++ b/notes @@ -0,0 +1,2 @@ +- update vs code syntax (comments issue) +- ctx gas remaining From f4b3ca018585d5345868547e17269aec080b8eca Mon Sep 17 00:00:00 2001 From: Grant Wuerker Date: Mon, 3 Jul 2023 14:59:18 -0600 Subject: [PATCH 2/4] hacking --- crates/tests/fixtures/files/contract_abi.fe | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/tests/fixtures/files/contract_abi.fe b/crates/tests/fixtures/files/contract_abi.fe index 77d1535284..b1d7e1cab1 100644 --- a/crates/tests/fixtures/files/contract_abi.fe +++ b/crates/tests/fixtures/files/contract_abi.fe @@ -33,7 +33,7 @@ contract MyContract { // self.foo.encode(writer) let value: u256 = self.foo value.encode(writer) - evm::return_mem(offset: buf.offset(), len: buf.len()) + evm::return_mem(buf) } else { revert UnknownSelector(selector) } From a6148af64ed739ddc3e14b2c2133bec682f2b788 Mon Sep 17 00:00:00 2001 From: Grant Wuerker Date: Mon, 3 Jul 2023 15:02:42 -0600 Subject: [PATCH 3/4] hacking --- crates/tests/fixtures/files/abi.fe | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/crates/tests/fixtures/files/abi.fe b/crates/tests/fixtures/files/abi.fe index b9a67eac96..b0bdc87b97 100644 --- a/crates/tests/fixtures/files/abi.fe +++ b/crates/tests/fixtures/files/abi.fe @@ -9,6 +9,7 @@ use std::buf::{ RawCallBuffer } use std::evm +use std::traits::Max contract AbiDecodeU256 { pub unsafe fn __call__(ctx: Context) { @@ -23,6 +24,19 @@ contract AbiDecodeU256 { } } +contract AbiDecodeU128 { + pub unsafe fn __call__(ctx: Context) { + let mut reader: CalldataReader = ctx.calldata_reader() + let value: u128 = u128::decode(reader) + + let mut buf: MemoryBuffer = MemoryBuffer::new(len: 32) + let mut writer: MemoryBufferWriter = buf.writer() + writer.write(value) + + evm::return_mem(buf) + } +} + // #test // unsafe fn u256_decode() { // let mut buf: MemoryBuffer = MemoryBuffer::new(len: 64) @@ -66,6 +80,21 @@ fn u256_decode(mut ctx: Context) { assert reader.read_u256() == 42 } +#test +fn u128_decode_invalid(mut ctx: Context) { + let decoder: address = address(AbiDecodeU128.create(ctx, 0)) + let mut buf: RawCallBuffer = RawCallBuffer::new( + input_len: 32, + output_len: 32 + ) + + let mut writer: MemoryBufferWriter = buf.writer() + writer.write(value: u256::max()) + + assert not ctx.raw_call(addr: decoder, value: 0, buf) +} + + // type MyArray = Array // type MyTuple = (u256, u256) From b5ac590e877b5c5a867c0f9fadbc47a8c345a4e5 Mon Sep 17 00:00:00 2001 From: Grant Wuerker Date: Wed, 5 Jul 2023 14:09:29 -0600 Subject: [PATCH 4/4] hacking --- crates/library/std/src/abi.fe | 102 ++++++++++++++++++++++++ crates/tests/fixtures/files/abi.fe | 122 +++++++++++++++++++++++------ 2 files changed, 201 insertions(+), 23 deletions(-) diff --git a/crates/library/std/src/abi.fe b/crates/library/std/src/abi.fe index d792145c4e..084a55cd9f 100644 --- a/crates/library/std/src/abi.fe +++ b/crates/library/std/src/abi.fe @@ -11,6 +11,9 @@ pub trait AbiEncode { fn encode(self, mut writer: MemoryBufferWriter); } + +// unsigned integers + impl AbiDecode for u256 { fn decode(mut reader: CalldataReader) -> Self { return reader.read_u256() @@ -57,6 +60,105 @@ impl AbiEncode for u64 { } } +impl AbiDecode for u32 { + fn decode(mut reader: CalldataReader) -> Self { + let value: u256 = reader.read_u256() + if value > u32::max() { + revert + } else { + return u32(value) + } + } +} + +impl AbiEncode for u32 { + fn encode(self, mut writer: MemoryBufferWriter) { + return writer.write(value: u256(self)) + } +} + +impl AbiDecode for u16 { + fn decode(mut reader: CalldataReader) -> Self { + let value: u256 = reader.read_u256() + if value > u16::max() { + revert + } else { + return u16(value) + } + } +} + +impl AbiEncode for u16 { + fn encode(self, mut writer: MemoryBufferWriter) { + return writer.write(value: u256(self)) + } +} + +impl AbiDecode for u8 { + fn decode(mut reader: CalldataReader) -> Self { + let value: u256 = reader.read_u256() + if value > u8::max() { + revert + } else { + return u8(value) + } + } +} + +impl AbiEncode for u8 { + fn encode(self, mut writer: MemoryBufferWriter) { + return writer.write(value: u256(self)) + } +} + +// signed integers + +// todo + +// misc types + +impl AbiDecode for bool { + fn decode(mut reader: CalldataReader) -> Self { + let value: u256 = reader.read_u256() + + if value == 0 { + return false + } else if value == 1 { + return true + } else { + revert + } + } +} + +impl AbiEncode for bool { + fn encode(self, mut writer: MemoryBufferWriter) { + if self { + writer.write(value: 1) + } else { + writer.write(value: 0) + } + } +} + +impl AbiDecode for address { + fn decode(mut reader: CalldataReader) -> Self { + let value: u256 = reader.read_u256() + + if value > 2 ^ 160 { + revert + } else { + return address(value) + } + } +} + +impl AbiEncode for address { + fn encode(self, mut writer: MemoryBufferWriter) { + writer.write(value: u256(self)) + } +} + // // generic impl of array encoding // impl AbiEncode for Array // where diff --git a/crates/tests/fixtures/files/abi.fe b/crates/tests/fixtures/files/abi.fe index b0bdc87b97..5d3d3ec019 100644 --- a/crates/tests/fixtures/files/abi.fe +++ b/crates/tests/fixtures/files/abi.fe @@ -1,5 +1,3 @@ -// error: cannot glob import from ingot -// use std::{abi::Abi, evm} use std::abi::{AbiDecode, AbiEncode} use std::context::{Context, CalldataReader} use std::buf::{ @@ -18,7 +16,7 @@ contract AbiDecodeU256 { let mut buf: MemoryBuffer = MemoryBuffer::new(len: 32) let mut writer: MemoryBufferWriter = buf.writer() - writer.write(value) + value.encode(writer) evm::return_mem(buf) } @@ -31,41 +29,58 @@ contract AbiDecodeU128 { let mut buf: MemoryBuffer = MemoryBuffer::new(len: 32) let mut writer: MemoryBufferWriter = buf.writer() - writer.write(value) + value.encode(writer) evm::return_mem(buf) } } -// #test -// unsafe fn u256_decode() { -// let mut buf: MemoryBuffer = MemoryBuffer::new(len: 64) +contract AbiDecodeAddress { + pub unsafe fn __call__(ctx: Context) { + let mut reader: CalldataReader = ctx.calldata_reader() + let value: address = address::decode(reader) + + let mut buf: MemoryBuffer = MemoryBuffer::new(len: 32) + let mut writer: MemoryBufferWriter = buf.writer() + value.encode(writer) -// let mut writer: MemoryBufferWriter = buf.writer() -// writer.write(value: 26) -// writer.write(value: 42) + evm::return_mem(buf) + } +} -// let mut reader: MemoryBufferReader = buf.reader() -// assert u256::decode(reader) == 26 -// assert u256::decode(reader) == 42 -// } +contract AbiDecodeBool { + pub unsafe fn __call__(ctx: Context) { + let mut reader: CalldataReader = ctx.calldata_reader() + let value: bool = bool::decode(reader) + + let mut buf: MemoryBuffer = MemoryBuffer::new(len: 32) + let mut writer: MemoryBufferWriter = buf.writer() + value.encode(writer) + + evm::return_mem(buf) + } +} #test -fn u256_encode() { - let mut buf: MemoryBuffer = MemoryBuffer::new(len: 64) +fn u256_abi(mut ctx: Context) { + let decoder: address = address(AbiDecodeU256.create(ctx, 0)) + let mut buf: RawCallBuffer = RawCallBuffer::new( + input_len: 32, + output_len: 32 + ) let mut writer: MemoryBufferWriter = buf.writer() - 26.encode(writer) - 42.encode(writer) + writer.write(value: 42) + + ctx.raw_call(addr: decoder, value: 0, buf) let mut reader: MemoryBufferReader = buf.reader() - assert reader.read_u256() == 26 assert reader.read_u256() == 42 } #test -fn u256_decode(mut ctx: Context) { - let decoder: address = address(AbiDecodeU256.create(ctx, 0)) +fn u128_abi(mut ctx: Context) { + let decoder: address = address(AbiDecodeU128.create(ctx, 0)) let mut buf: RawCallBuffer = RawCallBuffer::new( input_len: 32, output_len: 32 @@ -74,26 +89,87 @@ fn u256_decode(mut ctx: Context) { let mut writer: MemoryBufferWriter = buf.writer() writer.write(value: 42) - ctx.raw_call(addr: decoder, value: 0, buf) + assert ctx.raw_call(addr: decoder, value: 0, buf) let mut reader: MemoryBufferReader = buf.reader() assert reader.read_u256() == 42 } #test -fn u128_decode_invalid(mut ctx: Context) { +fn u128_abi_invalid(mut ctx: Context) { let decoder: address = address(AbiDecodeU128.create(ctx, 0)) let mut buf: RawCallBuffer = RawCallBuffer::new( input_len: 32, output_len: 32 ) + let mut writer: MemoryBufferWriter = buf.writer() + writer.write(value: u256(u128::max()) + 1) + + assert not ctx.raw_call(addr: decoder, value: 0, buf) +} + +#test +fn address_abi(mut ctx: Context) { + let decoder: address = address(AbiDecodeAddress.create(ctx, 0)) + let mut buf: RawCallBuffer = RawCallBuffer::new( + input_len: 32, + output_len: 32 + ) + + let mut writer: MemoryBufferWriter = buf.writer() + writer.write(value: 42) + + assert ctx.raw_call(addr: decoder, value: 0, buf) + + let mut reader: MemoryBufferReader = buf.reader() + assert reader.read_u256() == 42 +} + +#test +fn address_abi_invalid(mut ctx: Context) { + let decoder: address = address(AbiDecodeAddress.create(ctx, 0)) + let mut buf: RawCallBuffer = RawCallBuffer::new( + input_len: 32, + output_len: 32 + ) + let mut writer: MemoryBufferWriter = buf.writer() writer.write(value: u256::max()) assert not ctx.raw_call(addr: decoder, value: 0, buf) } +#test +fn bool_abi(mut ctx: Context) { + let decoder: address = address(AbiDecodeBool.create(ctx, 0)) + let mut buf: RawCallBuffer = RawCallBuffer::new( + input_len: 32, + output_len: 32 + ) + + let mut writer: MemoryBufferWriter = buf.writer() + writer.write(value: 1) + + assert ctx.raw_call(addr: decoder, value: 0, buf) + + let mut reader: MemoryBufferReader = buf.reader() + assert reader.read_u256() == 1 +} + +#test +fn bool_abi_invalid(mut ctx: Context) { + let decoder: address = address(AbiDecodeBool.create(ctx, 0)) + let mut buf: RawCallBuffer = RawCallBuffer::new( + input_len: 32, + output_len: 32 + ) + + let mut writer: MemoryBufferWriter = buf.writer() + writer.write(value: 3) + + assert not ctx.raw_call(addr: decoder, value: 0, buf) +} // type MyArray = Array // type MyTuple = (u256, u256)