diff --git a/linux/libdriver/Makefile b/linux/libdriver/Makefile index 20995e79..3bc649a8 100644 --- a/linux/libdriver/Makefile +++ b/linux/libdriver/Makefile @@ -1,17 +1,63 @@ -all: libdriver.a +# make ioctl CROSS_COMPILE=riscv64-linux-gnu- libioctl.a +# make mock libmock.a CC := $(CROSS_COMPILE)gcc AR := $(CROSS_COMPILE)ar +CFLAGS := -Wall -pedantic -libdriver.a: ioctl/rollup.o ioctl/yield.o +all: libioctl.a libmock.a + +ioctl_OBJ := \ + ioctl/rollup.o \ + ioctl/yield.o \ + +base_OBJ := \ + base/buf.o \ + base/abi.o \ + base/keccak.o \ + base/merkle.o \ + +mock_OBJ := \ + mock/rollup.o \ + mock/yield.o \ + +OBJ := \ + $(ioctl_OBJ) \ + $(base_OBJ) \ + $(mock_OBJ) + +%.o: %.c + $(CC) $(CFLAGS) -MT $@ -MMD -MP -MF $(@:.o=.d) -c -o $@ $< + +libioctl.a: $(ioctl_OBJ) $(base_OBJ) $(AR) rcs $@ $^ -ioctl/rollup.o: ioctl/rollup.c - $(CC) -Wall -pedantic -march=rv64gc -mabi=lp64d -c -o $@ $< -ioctl/yield.o: ioctl/yield.c - $(CC) -Wall -pedantic -march=rv64gc -mabi=lp64d -c -o $@ $< -ioctl/rollup.o: ioctl/rollup.h base/rollup-driver-api.h -ioctl/yield.o: ioctl/yield.h base/yield-driver-api.h +libmock.a: $(mock_OBJ) $(base_OBJ) + $(AR) rcs $@ $^ + +ioctl/yield: examples/yield.c libioctl.a + $(CC) $(CFLAGS) -Iioctl -o $@ $^ +ioctl/merkle: examples/merkle.c libmock.a + $(CC) $(CFLAGS) -Iioctl -o $@ $^ +ioctl/rollup: examples/rollup.c libioctl.a + $(CC) $(CFLAGS) -Iioctl -o $@ $^ + +mock/yield: examples/yield.c libmock.a + $(CC) $(CFLAGS) -Imock -o $@ $^ +mock/merkle: examples/merkle.c libmock.a + $(CC) $(CFLAGS) -Imock -o $@ $^ +mock/rollup: examples/rollup.c libmock.a + $(CC) $(CFLAGS) -Imock -o $@ $^ + +tests/abi: tests/abi.c libmock.a + $(CC) $(CFLAGS) -Imock -o $@ $^ +tests/keccak: tests/keccak.c libmock.a + $(CC) $(CFLAGS) -Imock -o $@ $^ +tests/merkle: tests/merkle.c libmock.a + $(CC) $(CFLAGS) -Imock -o $@ $^ + +tools/merkle-zero-table: tools/merkle-zero-table.c libmock.a + $(CC) $(CFLAGS) -Imock -o $@ $^ doc: doc/theme doxygen doc/Doxyfile @@ -19,8 +65,10 @@ doc/theme: git clone --depth=1 --branch=v2.2.1 \ git@github.com:jothepro/doxygen-awesome-css.git $@ clean: - rm libdriver.a ioctl/rollup.o ioctl/yield.o + rm -f $(OBJ) rm -rf doc/html distclean: rm -rf doc/theme .PHONY: doc + +-include $(OBJ:%.o=%.d) diff --git a/linux/libdriver/README.md b/linux/libdriver/README.md index a9a48de1..751e1df3 100644 --- a/linux/libdriver/README.md +++ b/linux/libdriver/README.md @@ -1,7 +1,15 @@ +Utility functions for rollup interaction. + +- @ref libevm\_rollup +- @ref libevm\_abi +- @ref libevm\_buf +- @ref libevm\_keccak +- @ref libevm\_merkle + Userspace library to interact with cartesi kernel drivers -- @ref rollup -- @ref yield +- @ref rollup\_driver +- @ref yield\_driver # Getting Started diff --git a/linux/libdriver/base/abi.c b/linux/libdriver/base/abi.c new file mode 100644 index 00000000..78bd1d20 --- /dev/null +++ b/linux/libdriver/base/abi.c @@ -0,0 +1,217 @@ +#include +#include +#include +#include "abi.h" + +uintptr_t align(uintptr_t p, size_t a) +{ + return (p + (a-1)) & ~(a-1); +} + +uint32_t evm_abi_funsel(uint8_t a, uint8_t b, uint8_t c, uint8_t d) +{ + return EVM_ABI_FUNSEL(a, b, c, d); +} + +int evm_abi_put_funsel(evm_buf_t *me, uint32_t funsel) +{ + int rc; + evm_buf_t x[1]; + if ((rc = evm_buf_split(me, sizeof(funsel), x, me))) + return rc; + memcpy(x->p, &funsel, sizeof(funsel)); + return 0; +} + +int evm_abi_enc_uint_nr(size_t n, const uint8_t data[n], uint8_t out[EVM_WORD_LEN]) +{ + if (n > EVM_WORD_LEN) return EDOM; + + for (int i=0; i EVM_WORD_LEN) return EDOM; + + for (int i=0; i EVM_WORD_LEN) + return EDOM; + if (evm_buf_split(me, EVM_WORD_LEN, &x, me)) + return ENOBUFS; + + evm_abi_enc_uint(n, data, x.p); + return 0; +} + +int evm_abi_put_address(evm_buf_t *me, const uint8_t data[20]) +{ + evm_buf_t x; + + if (evm_buf_split(me, EVM_WORD_LEN, &x, me)) + return ENOBUFS; + + evm_abi_enc_uint_nn(EVM_ADDRESS_LEN, data, x.p); + return 0; +} + +int evm_abi_put_bytes_s(evm_buf_t *me, evm_buf_t *offset) +{ + return evm_buf_split(me, EVM_WORD_LEN, offset, me); +} + +int evm_abi_res_bytes_d(evm_buf_t *me, evm_buf_t *of, size_t n, evm_buf_t *out) +{ + int rc; + evm_buf_t sz[1]; + size_t n32 = align(n, EVM_WORD_LEN); + + if ((rc = evm_buf_split(me, EVM_WORD_LEN, sz, me))) + return rc; + if ((rc = evm_buf_split(me, n32, out, me))) + return rc; + + size_t offset = sz->p - of->p; + evm_abi_enc_uint(sizeof(offset), &offset, of->p); + evm_abi_enc_uint(sizeof(n), &n, sz->p); + memset(out->p + n, 0, n32 - n); + return 0; +} + +int evm_abi_put_bytes_d(evm_buf_t *me, evm_buf_t *offset, size_t n, const void *data) +{ + int rc; + evm_buf_t res[1]; + + if ((rc = evm_abi_res_bytes_d(me, offset, n, res))) + return rc; + + memcpy(res->p, data, n); + return 0; +} + +uint32_t evm_abi_peek_funsel(evm_buf_t *me) +{ + assert(evm_buf_length(me) >= 4); + return EVM_ABI_FUNSEL(me->p[0], me->p[1], me->p[2], me->p[3]); +} + +int evm_abi_check_funsel(evm_buf_t *me, uint32_t expected) +{ + if (evm_buf_length(me) < 4) + return ENOBUFS; + + if (evm_abi_peek_funsel(me) != expected) + return EBADMSG; + + me->p += 4; + return 0; +} + +int evm_abi_get_uint(evm_buf_t *me, size_t n, void *data) +{ + int rc; + evm_buf_t x[1]; + + if (n > EVM_WORD_LEN) return EDOM; + if ((rc = evm_buf_split(me, EVM_WORD_LEN, x, me))) + return rc; + + return evm_abi_dec_uint(x->p, n, data); +} + +int evm_abi_get_address(evm_buf_t *me, uint8_t address[EVM_ADDRESS_LEN]) +{ + int rc; + evm_buf_t x[1]; + + if ((rc = evm_buf_split(me, EVM_WORD_LEN, x, me))) + return rc; + + return evm_abi_dec_uint_nn(x->p, EVM_ADDRESS_LEN, address); +} + +int evm_abi_get_bytes_s(evm_buf_t *me, evm_buf_t of[1]) +{ + return evm_buf_split(me, EVM_WORD_LEN, of, me); +} + +/* 1. find the offset to (size, data) block, the dynamic section for this bytes. + * 2. read size part + * 3. read data part */ +int evm_abi_peek_bytes_d(evm_buf_t *me, evm_buf_t of[1], evm_buf_t *bytes) +{ + int rc; + uint64_t offset, size; + if ((rc = evm_abi_get_uint(of, sizeof(offset), &offset))) + return rc; + + evm_buf_t it[1] = {{of->p + offset - EVM_WORD_LEN, me->q}}; + if ((rc = evm_abi_get_uint(it, sizeof(size), &size))) + return rc; + + return evm_buf_split(it, size, bytes, it); +} + +int evm_abi_get_bytes_d(evm_buf_t *me, evm_buf_t of[1], size_t *n, void **data) +{ + int rc; + evm_buf_t bytes[1]; + if ((rc = evm_abi_peek_bytes_d(me, of, bytes))) + return rc; + *n = evm_buf_length(bytes); + *data = bytes->p; + return 0; +} diff --git a/linux/libdriver/base/abi.h b/linux/libdriver/base/abi.h new file mode 100644 index 00000000..e7b7525d --- /dev/null +++ b/linux/libdriver/base/abi.h @@ -0,0 +1,346 @@ +/** @file + * @defgroup libevm_abi abi + * + * This is a C library to encode and decode Ethereum Virtual Machine (EVM) + * calldata. This format is used to interacts with contracts in the Ethereum + * ecosystem. + * + * We will cover the basic concepts required to use the API we provide, but for + * a complete reference consult the solidity specification, it can be found + * here: https://docs.soliditylang.org/en/latest/abi-spec.html + * + * ## Function Selector {#funsel} + * + * 4 bytes that identify the function name and parameter types. This is used to + * distinguish between different formats. To compute it, take the four first + * bytes of the `keccak` digest of the solidity function declaration. It should + * respect a canonical format and such as having no the variable names, check + * docs for details. For reference, for a hypotetical "FunctionName" function, + * it should look something like this: + * `keccak("FunctionName(type1,type2,...,typeN)");` + * + * ## Data Sections {#sections} + * + * After the function selector, we can start encoding the function parameters. + * One by one, left to right in two sections. `Static` for types with fixed + * size. Think ints, bools, addresses, etc. And then there is the `Dynamic` + * section. In this case, the encoded size depend on the data they contain, and + * how much of it, such as of strings and bytes. So: + * + * `Static` for ints, bools, adresses. + * + * `Dynamic` for bytes. + * + * ### Static Section {#static-section} + * + * Static types are encoded directly in the buffer when the API call is made. + * Put the data in and be done with it. + * + * ### Dynamic Section {#dynamic-section} + * + * Dynamic types require two calls. The first goes into the Static section to + * reserve space for a data offset. @ref evm_abi_put_bytes_s. + * + * After we are done with with all data that goes into the static section. We + * start to actually encode the dynamic data. And here is where the second call + * comes in. We patch the offset we reserved earlier with the actual data + * position and copy its contents over. @ref evm_abi_put_bytes_d. + * + * ## Encoder + * + * Lets look at some code: + * + * @includelineno "examples/abi_put.c" + * + * ## Decoder + * + * Lets look at some code: + * + * @includelineno "examples/abi_get.c" + * + * ## Complete + * + * Lets look at some code: + * + * @includelineno "examples/abi.c" + * + * @ingroup libevm + * @{ */ +#ifndef EVM_ABI_H +#define EVM_ABI_H +#include"buf.h" + +/** length of a evm word in bytes */ +#define EVM_WORD_LEN 32 + +/** length of a evm address in bytes */ +#define EVM_ADDRESS_LEN 20 + +/** Compile time equivalent to @ref evm_abi_funsel + * @note noport. use @ref evm_abi_mk_funsel instead */ +#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__ +#define EVM_ABI_FUNSEL(A, B, C, D) \ + ( ((D) << 000) \ + | ((C) << 010) \ + | ((B) << 020) \ + | ((A) << 030)) +#else +#define EVM_ABI_FUNSEL(A, B, C, D) \ + ( ((A) << 000) \ + | ((B) << 010) \ + | ((C) << 020) \ + | ((D) << 030)) +#endif + +// put section --------------------------------------------------------------- + +/** Create a function selector from an array of bytes + * @param [in] funsel function selector bytes + * @return + * - function selector converted to big endian (as expected by EVM) */ +uint32_t +evm_abi_funsel(uint8_t a, uint8_t b, uint8_t c, uint8_t d); + +/** Encode a function selector into the buffer @p me + * + * @param [in,out] me a initialized buffer working as iterator + * @param [in] funsel function selector + * + * @return + * - 0 success + * - ENOBUFS no space left in @p me + * + * @note A function selector can be compute it with: @ref evm_keccak_funsel. + * It is always represented in big endian. */ +int +evm_abi_put_funsel(evm_buf_t *me, uint32_t funsel); + +/** Encode a unsigned integer of up to 32bytes of data into the buffer + * + * @param [in,out] me a initialized buffer working as iterator + * @param [in] n size of @p data in bytes + * @param [in] data poiter to a integer + * + * @return + * - 0 success + * - ENOBUFS no space left in @p me + * - EDOM requested @p n is too large + * + * @code + * ... + * evm_buf_t it = ...; + * uint64_t x = UINT64_C(0xdeadbeef); + * evm_abi_put_uint(&it, sizeof x, &x); + * ... + * @endcode + * @note This function takes care of endianess conversions */ +int +evm_abi_put_uint(evm_buf_t *me, size_t n, const void *data); + +/** Encode @p address (exacly @ref EVM_ADDRESS_LEN bytes) into the buffer + * + * @param [in,out] me initialized buffer + * @param [in] address exactly @ref EVM_ADDRESS_LEN bytes + * + * @return + * - 0 success + * - ENOBUFS no space left in @p me */ +int +evm_abi_put_address(evm_buf_t *me, const uint8_t address[EVM_ADDRESS_LEN]); + +/** Encode the static part of @b bytes into the message, + * used in conjunction with @ref evm_abi_put_bytes_d + * + * @param [in,out] me initialized buffer + * @param [out] offset initialize for @ref evm_abi_put_bytes_d + * @return + * - 0 success + * - ENOBUFS no space left in @p me */ +int +evm_abi_put_bytes_s(evm_buf_t *me, evm_buf_t *offset); + +/** Encode the dynamic part of @b bytes into the message, + * used in conjunction with @ref evm_abi_put_bytes_d + * + * @param [in,out] me initialized buffer + * @param [in] offset initialized from @ref evm_abi_put_bytes_h + * @param [in] n size of @b data + * @param [in] data array of bytes + * @return + * - 0 success + * - ENOBUFS no space left in @p me */ +int +evm_abi_put_bytes_d(evm_buf_t *me, evm_buf_t *offset + ,size_t n, const void *data); + +/** Reserve @b n bytes of data from the buffer into @b res to be filled by the + * caller + * + * @param [in,out] me initialized buffer + * @param [in] n amount of bytes to reserve + * @param [out] res slice of bytes extracted from @p me + * @return + * - 0 success + * - ENOBUFS no space left in @p me + * + * @note @p me must outlive @p res. + * Create a duplicate otherwise */ +int +evm_abi_res_bytes_d(evm_buf_t *me, evm_buf_t *of, size_t n, evm_buf_t *out); + +// get section --------------------------------------------------------------- + +/** Read the funsel without consuming it from the buffer @p me + * + * @param [in] me initialized buffer + * @return + * - The function selector + * + * @code + * ... + * if (evm_buf_length(it) < 4) + * return EXIT_FAILURE; + * switch (evm_abi_peek_funsel(it) { + * case EVM_ABI_FUNSEL(...): // known type, try to parse it + * case EVM_ABI_FUNSEL(...): // known type, try to parse it + * default: + * return EXIT_FAILURE; + * } + * @endcode + * + * @note user must ensure there are at least 4 bytes in the buffer. + * This function will fail and return 0 if that is not the case. */ +uint32_t +evm_abi_peek_funsel(evm_buf_t *me); + +/** Consume funsel from the buffer @p me and ensure it matches @p expected_funsel + * + * @param [in,out] me initialized buffer + * @param [in] expected expected function selector + * + * @return + * - 0 success + * - ENOBUFS no space left in @p me + * - EBADMSG in case of a missmatch */ +int +evm_abi_check_funsel(evm_buf_t *me, uint32_t expected); + +/** Decode a unsigned integer of up to 32bytes from the buffer + * + * @param [in,out] me initialized buffer + * @param [in] n size of @p data in bytes + * @param [out] data pointer to a integer + * + * @return + * - 0 success + * - ENOBUFS no space left in @p me + * - EDOM value won't fit into @p n bytes. */ +int +evm_abi_get_uint(evm_buf_t *me, size_t n, void *data); + +/** Consume and decode @b address from the buffer + * + * @param [in,out] me initialized buffer + * @param [out] address exactly 20 bytes + * + * @return + * - 0 success + * - ENOBUFS requested size @b n is not available */ +int +evm_abi_get_address(evm_buf_t *me, uint8_t address[EVM_ADDRESS_LEN]); + +/** Consume and decode the offset @p of + * + * @param [in,out] me initialized buffer + * @param [out] of offset to @p bytes data, for use in conjunction with @ref evm_abi_get_bytes_d + * @return + * - 0 success + * - ENOBUFS no space left in @p me */ +int +evm_abi_get_bytes_s(evm_buf_t *me, evm_buf_t of[1]); + +/** Decode @b bytes from the buffer by taking a pointer to its contents. + * + * @param [in,out] me initialized buffer + * @param [out] of offset to @p bytes data + * @param [out] n amount of data available in @b bytes + * @param [out] bytes memory range with contents + * @return + * - 0 success + * - ENOBUFS no space left in @p me + * @note @p of can be initialized by calling @ref evm_abi_get_bytes_s */ +int +evm_abi_get_bytes_d(evm_buf_t *me, evm_buf_t of[1], size_t *n, void **bytes); + +/** Decode @b bytes from the buffer by taking a pointer to its contents. + * + * @param [in,out] me initialized buffer + * @param [out] of offset to @p bytes data + * @param [out] n amount of data available in @b bytes + * @param [out] bytes memory range with contents + * @return + * - 0 success + * - ENOBUFS no space left in @p me + * @note @p of can be initialized by calling @ref evm_abi_get_bytes_s */ +int +evm_abi_peek_bytes_d(evm_buf_t *me, evm_buf_t of[1], evm_buf_t *bytes); + +// raw enc/dec section -------------------------------------------------------- + +/** Encode @p n bytes of @p data into @p out (up to 32). + * + * @param [in] n size of @p data in bytes + * @param [in] data integer value to encode into @p out + * @param [out] out encoded result + * @return + * - 0 success + * - EDOM @p n is too large. */ +int evm_abi_enc_uint(size_t n, const void *data, uint8_t out[EVM_WORD_LEN]); + +/** Encode @p n bytes of @p data into @p out (up to 32) in reverse order. + * + * @param [in] n size of @p data in bytes + * @param [in] data integer value to encode into @p out + * @param [out] out encoded result + * @return + * - 0 success + * - EDOM @p n is too large. + * @note use @ref evm_abi_enc_uint instead */ +int evm_abi_enc_uint_nr(size_t n, const uint8_t data[n], uint8_t out[EVM_WORD_LEN]); + +/** Encode @p n bytes of @p data into @p out (up to 32) in normal order. + * + * @param [in] n size of @p data in bytes + * @param [in] data integer value to encode into @p out + * @param [out] out encoded result + * @return + * - 0 success + * - EDOM @p n is too large. + * @note use @ref evm_abi_enc_uint instead */ +int evm_abi_enc_uint_nn(size_t n, const uint8_t data[n], uint8_t out[EVM_WORD_LEN]); + +/** Decode @p n bytes of @p data into @p out (up to 32). + * + * @param [in] data integer value to decode into @p out + * @param [in] n size of @p data in bytes + * @param [out] out decoded output */ +int evm_abi_dec_uint(const uint8_t data[EVM_WORD_LEN], size_t n, uint8_t out[n]); + +/** Decode @p n bytes of @p data into @p out (up to 32) in reverse order. + * + * @param [in] data integer value to decode into @p out + * @param [in] n size of @p data in bytes + * @param [out] out decoded output + * @note if in doubt, use @ref evm_abi_dec_uint */ +int evm_abi_dec_uint_nr(const uint8_t data[EVM_WORD_LEN], size_t n, uint8_t out[n]); + +/** Decode @p n bytes of @p data into @p out (up to 32) in normal order. + * + * @param [in] data integer value to decode into @p out + * @param [in] n size of @p data in bytes + * @param [out] out decoded output + * @note if in doubt, use @ref evm_abi_dec_uint */ +int evm_abi_dec_uint_nn(const uint8_t data[EVM_WORD_LEN], size_t n, uint8_t out[n]); + +#endif /* EVM_ABI_H */ +/** @} */ diff --git a/linux/libdriver/base/buf.c b/linux/libdriver/base/buf.c new file mode 100644 index 00000000..ab7f6c60 --- /dev/null +++ b/linux/libdriver/base/buf.c @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include"buf.h" + +void evm_buf_init(evm_buf_t *me, size_t n, void *data) +{ + *me = (evm_buf_t)EVM_BUF_INIT(me, n, data); +} +int evm_buf_split(const evm_buf_t *me, size_t n, evm_buf_t *lhs, evm_buf_t *rhs) +{ + lhs->p = me->p; + lhs->q = rhs->p = me->p + n; + rhs->q = me->q; + + return lhs->q > me->q? ENOBUFS : 0; +} + +size_t evm_buf_length(const evm_buf_t *me) +{ + return me->q - me->p; +} + +static void xxd(const uint8_t *p, const uint8_t *q, int mask) +{ + if (q < p) return; + + for (size_t i=0u, n = q - p; i < n; ++i) { + bool is_line_start = (i & mask) == 0; + bool is_line_end = (i & mask) == mask || (i+1 == n); + char separator = is_line_end? '\n' : ' '; + + if (is_line_start) printf("%p %4zu: ", (void *)(p + i), i); + printf("%02x%c", p[i], separator); + } +} +void evm_buf_xxd(void *p, void *q, int l) +{ + assert(p); + assert(q); + assert(p <= q); + assert((l & (l-1)) == 0 && "l is required to be a power of 2"); + + xxd(p, q, l-1); +} + +static void diff(size_t n, uint8_t p0[n], uint8_t p1[n]) +{ + size_t i=0; + for (; i n) low = i; + if (hi > n) hi = i; + int at = 21 + 3 * (i & 0x0f); // <- NOTE: keep in sync with `xxd` line_start format + + xxd(p0 + low, p0 + hi, 0x0f); + printf("%*.s%s\n", at, "", "xx"); + xxd(p1 + low, p1 + hi, 0x0f); +} +void evm_buf_diff(size_t n, void *p0, void *p1) +{ + diff(n, p0, p1); +} diff --git a/linux/libdriver/base/buf.h b/linux/libdriver/base/buf.h new file mode 100644 index 00000000..da43eb3f --- /dev/null +++ b/linux/libdriver/base/buf.h @@ -0,0 +1,87 @@ +/** @file + * @defgroup libevm_buf buf + * slice of contiguous memory + * + * @ingroup libevm + * @{ */ +#ifndef EVM_BUF_H +#define EVM_BUF_H +#include +#include + +/** A slice of contiguous memory from @b p to @b q. + * Size can be taken with: `q - p`. */ +typedef struct { + uint8_t *p; /**< start of memory region */ + uint8_t *q; /**< end of memory region */ +} evm_buf_t; + +/** Initialize a @b evm_buf_t + * @note noport */ +#define EVM_BUF_INIT(ME, N, XS) \ +{ .p = (uint8_t *)(XS), \ + .q = (ME)->p + (N), \ +} + +/** Declare a evm_buf_t with stack backed memory. + * @param [in] N - size in bytes + * @note noport */ +#define EVM_BUF_DECL(S, N) evm_buf_t S[1] = {{ \ + .p = (uint8_t [N]){0}, \ + .q = (S)->p + N, \ + }} + +/** Declare a evm_buf_t with parameters backed memory. + * @param [in] N - size in bytes + * @note noport */ +#define EVM_BUF_DECL3(S, N, P) evm_buf_t S[1] = {{ \ + .p = P, \ + .q = (S)->p + N, \ + }} + +/** Initialize @p me buffer backed by @p data, @p n bytes in size + * + * @param [out] me a uninitialized instance + * @param [in] n size in bytes of @b data + * @param [in] data the backing memory to be used. + * + * @note @p data memory must outlive @p me. + * user must copy the contents otherwise */ +void evm_buf_init(evm_buf_t *me, size_t n, void *data); + +/** Split a buffer in two, @b lhs with @b n bytes and @b rhs with the rest + * + * @param [in,out] me initialized buffer + * @param [in] n bytes in @b lhs + * @param [out] lhs left hand side + * @param [out] rhs right hand side + * + * @return + * - 0 success + * - ENOBUFS requested size @b n is not available */ +int evm_buf_split(const evm_buf_t *me, size_t n, evm_buf_t *lhs, evm_buf_t *rhs); + +/** Length in bytes of @p me + * + * @param [in] me initialized buffer + * @return + * - size in bytes */ +size_t evm_buf_length(const evm_buf_t *me); + +/** Print the contents of @b me buffer to stdout + * + * @param [in] p start of memory region + * @param [in] q end of memory region + * @param [in] l bytes per line (must be a power of 2). */ +void evm_buf_xxd(void *p, void *q, int l); + +/** Print the first difference found between @p p0 and @p p1 + * + * @param [in] n size in bytes of both regions + * @param [in] p0 start of memory region A + * @param [in] p1 start of memory region B + */ +void evm_buf_diff(size_t n, void *p0, void *p1); + +#endif /* EVM_BUF_H */ +/** @} */ diff --git a/linux/libdriver/base/keccak.c b/linux/libdriver/base/keccak.c new file mode 100644 index 00000000..acc82830 --- /dev/null +++ b/linux/libdriver/base/keccak.c @@ -0,0 +1,121 @@ +#include +#include"abi.h" +#include"keccak.h" + +#define ROTL64(x, y) \ + (((x) << (y)) | ((x) >> (64 - (y)))) + +static void +keccakf(uint64_t st[25]) +{ + const uint64_t keccakf_rndc[24] = { + 0x0000000000000001, 0x0000000000008082, 0x800000000000808a, + 0x8000000080008000, 0x000000000000808b, 0x0000000080000001, + 0x8000000080008081, 0x8000000000008009, 0x000000000000008a, + 0x0000000000000088, 0x0000000080008009, 0x000000008000000a, + 0x000000008000808b, 0x800000000000008b, 0x8000000000008089, + 0x8000000000008003, 0x8000000000008002, 0x8000000000000080, + 0x000000000000800a, 0x800000008000000a, 0x8000000080008081, + 0x8000000000008080, 0x0000000080000001, 0x8000000080008008 + }; + const int keccakf_rotc[24] = { + 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, + 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44, + }; + const int keccakf_piln[24] = { + 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, + 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1, + }; + + +#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__ + for (int i = 0; i < 25; i++) { + st[i] = __builtin_bswap64((uint64_t *)(st[i])); + } +#endif + + for (int r = 0; r < 24; r++) { + uint64_t t, bc[5]; + + // Theta + for (int i = 0; i < 5; i++) + bc[i] = st[i] ^ st[i + 5] ^ st[i + 10] ^ st[i + 15] ^ st[i + 20]; + + for (int i = 0; i < 5; i++) { + t = bc[(i + 4) % 5] ^ ROTL64(bc[(i + 1) % 5], 1); + for (int j = 0; j < 25; j += 5) + st[j + i] ^= t; + } + + // Rho Pi + t = st[1]; + for (int i = 0; i < 24; i++) { + int j = keccakf_piln[i]; + bc[0] = st[j]; + st[j] = ROTL64(t, keccakf_rotc[i]); + t = bc[0]; + } + + // Chi + for (int j = 0; j < 25; j += 5) { + for (int i = 0; i < 5; i++) + bc[i] = st[j + i]; + for (int i = 0; i < 5; i++) + st[j + i] ^= (~bc[(i + 1) % 5]) & bc[(i + 2) % 5]; + } + + // Iota + st[0] ^= keccakf_rndc[r]; + } + +#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__ + for (int i = 0; i < 25; i++) { + st[i] = __builtin_bswap64(*(uint64_t *)(&st[i])); + } +#endif +} + +void evm_keccak_init(evm_keccak_t *c) +{ + *c = (evm_keccak_t)EVM_KECCAK_INIT(c); +} + +void evm_keccak_update(evm_keccak_t *c, size_t n, const void *data) +{ + int j = c->pt; + for (size_t i = 0; i < n; i++) { + c->st.b[j++] ^= ((const uint8_t *) data)[i]; + if (j >= c->rsiz) { + keccakf(c->st.q); + j = 0; + } + } + c->pt = j; +} + +void evm_keccak_final(evm_keccak_t *c, void *md) +{ + c->st.b[c->pt] ^= 0x01; + c->st.b[c->rsiz - 1] ^= 0x80; + keccakf(c->st.q); + + for (int i = 0; i < EVM_KECCAK_MDLEN; i++) { + ((uint8_t *) md)[i] = c->st.b[i]; + } +} + +uint8_t *evm_keccak_data(size_t n, const void *data, uint8_t md[EVM_KECCAK_MDLEN]) +{ + EVM_KECCAK_DECL(c); + evm_keccak_update(c, n, data); + evm_keccak_final(c, md); + return md; +} + +uint32_t evm_keccak_funsel(const char *decl) +{ + uint8_t md[32]; + evm_keccak_data(strlen(decl), decl, md); + return EVM_ABI_FUNSEL(md[0], md[1], md[2], md[3]); +} + diff --git a/linux/libdriver/base/keccak.h b/linux/libdriver/base/keccak.h new file mode 100644 index 00000000..756dfd64 --- /dev/null +++ b/linux/libdriver/base/keccak.h @@ -0,0 +1,126 @@ +/** @file + * @defgroup libevm_keccak keccak + * Keccak 256 digest + * + * Data can be inserted in pieces: + * @code + * ... + * uint8_t h[EVM_KECCAK_MDLEN]; + * evm_keccak_t st[1]; + * + * evm_keccak_init(st); + * evm_keccak_update(st, 1, "h"); + * evm_keccak_update(st, 1, "e"); + * evm_keccak_update(st, 1, "l"); + * evm_keccak_update(st, 1, "l"); + * evm_keccak_update(st, 1, "o"); + * evm_keccak_final(st, h); + * ... + * @endcode + * + * all at once: + * @code + * ... + * const char data[] = "hello"; + * uint8_t h[EVM_KECCAK_MDLEN]; + * evm_keccak_data(h, sizeof(data)-1, data); + * ... + * @endcode + * + * or with a specialized call to generate @ref funsel data: + * @code + * ... + * uint32_t funsel = evm_keccak_funsel("FunctionCall(address,bytes)"); + * ... + * @endcode + * @ingroup libevm + * @{ */ +#ifndef EVM_KECCAK_H +#define EVM_KECCAK_H +#include +#include + +/** Opaque Keccak state, used to do hash computations, initialize with: + * - @ref evm_keccak_init + * - @ref EVM_KECCAK_INIT + * - @ref EVM_KECCAK_DECL */ +typedef struct { + union { + uint8_t b[200]; + uint64_t q[25]; + } st; + int pt, rsiz; +} evm_keccak_t; + +/** Bytes in the hash digest */ +#define EVM_KECCAK_MDLEN 32 + +/** Initialize a keccak state + * @note noport. use @ref evm_keccak_init */ +#define EVM_KECCAK_INIT(STATE) { \ + .st.q = { \ + 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, \ + }, \ + .pt = 0, \ + .rsiz = 200 - 2 * EVM_KECCAK_MDLEN, \ +} + +/** Declare and initialize a keccak state + * @note noport. use @ref evm_keccak_init */ +#define EVM_KECCAK_DECL(NAME) evm_keccak_t NAME[1] = {EVM_KECCAK_INIT(NAME)} + +/** Initialize a @ref evm_keccak_t hasher state. + * + * @param [out] state uninitialized @ref evm_keccak_t */ +void evm_keccak_init(evm_keccak_t *state); + +/** Hash @b n bytes of @b data + * + * @param [in,out] state initialize the hasher state + * @param [in] n bytes in @b data to process + * @param [in] data data to hash */ +void evm_keccak_update(evm_keccak_t *state, size_t n, const void *data); + +/** Finalize the hash calculation from @b state and store it in @b md + * + * @param [in] state initialize the hasher state (with all data already added to it) + * @param [out] md 32bytes to store the computed hash */ +void evm_keccak_final(evm_keccak_t *state, void *md); + +/** Hash all @b n bytes of @b data at once + * + * @param [in] n - bytes in @b data to process + * @param [in] data - data to hash + * @param [out] md - 32bytes to store the computed hash + * @return pointer to @b md + * + * Equivalent to: + * @code + * evm_keccak_t st = EVM_KECCAK_INIT(&st); + * evm_keccak_update(&st, n, data); + * evm_keccak_final(&st, md); + * return md; + * @endcode */ +uint8_t *evm_keccak_data(size_t n, const void *data + ,uint8_t md[EVM_KECCAK_MDLEN]); + +/** Compute the function selector from the solidity declaration @p decl + * + * @param [in] decl solidity call declaration, without variable names + * @param [out] funsel function selector as described by @ref funsel + * @return A @p funsel value as if defined by @ref EVM_ABI_FUNSEL + * + * Example usage: + * @code + * ... + * uint32_t funsel = evm_keccak_funsel("FunctionCall(address,bytes)"); + * ... + * @endcode + */ +uint32_t evm_keccak_funsel(const char *decl); +#endif /* EVM_KECCAK_H */ +/** $@} */ diff --git a/linux/libdriver/base/merkle-zero-table.h b/linux/libdriver/base/merkle-zero-table.h new file mode 100644 index 00000000..598f976a --- /dev/null +++ b/linux/libdriver/base/merkle-zero-table.h @@ -0,0 +1,71 @@ +#ifndef EVM_ZERO_DATA_H +#define EVM_ZERO_DATA_H + +static const uint8_t merkle_zero_table[][EVM_KECCAK_MDLEN] = { + {0x29,0x0d,0xec,0xd9,0x54,0x8b,0x62,0xa8,0xd6,0x03,0x45,0xa9,0x88,0x38,0x6f,0xc8,0x4b,0xa6,0xbc,0x95,0x48,0x40,0x08,0xf6,0x36,0x2f,0x93,0x16,0x0e,0xf3,0xe5,0x63}, + {0x63,0x3d,0xc4,0xd7,0xda,0x72,0x56,0x66,0x0a,0x89,0x2f,0x8f,0x16,0x04,0xa4,0x4b,0x54,0x32,0x64,0x9c,0xc8,0xec,0x5c,0xb3,0xce,0xd4,0xc4,0xe6,0xac,0x94,0xdd,0x1d}, + {0x89,0x07,0x40,0xa8,0xeb,0x06,0xce,0x9b,0xe4,0x22,0xcb,0x8d,0xa5,0xcd,0xaf,0xc2,0xb5,0x8c,0x0a,0x5e,0x24,0x03,0x6c,0x57,0x8d,0xe2,0xa4,0x33,0xc8,0x28,0xff,0x7d}, + {0x3b,0x8e,0xc0,0x9e,0x02,0x6f,0xdc,0x30,0x53,0x65,0xdf,0xc9,0x4e,0x18,0x9a,0x81,0xb3,0x8c,0x75,0x97,0xb3,0xd9,0x41,0xc2,0x79,0xf0,0x42,0xe8,0x20,0x6e,0x0b,0xd8}, + {0xec,0xd5,0x0e,0xee,0x38,0xe3,0x86,0xbd,0x62,0xbe,0x9b,0xed,0xb9,0x90,0x70,0x69,0x51,0xb6,0x5f,0xe0,0x53,0xbd,0x9d,0x8a,0x52,0x1a,0xf7,0x53,0xd1,0x39,0xe2,0xda}, + {0xde,0xff,0xf6,0xd3,0x30,0xbb,0x54,0x03,0xf6,0x3b,0x14,0xf3,0x3b,0x57,0x82,0x74,0x16,0x0d,0xe3,0xa5,0x0d,0xf4,0xef,0xec,0xf0,0xe0,0xdb,0x73,0xbc,0xdd,0x3d,0xa5}, + {0x61,0x7b,0xdd,0x11,0xf7,0xc0,0xa1,0x1f,0x49,0xdb,0x22,0xf6,0x29,0x38,0x7a,0x12,0xda,0x75,0x96,0xf9,0xd1,0x70,0x4d,0x74,0x65,0x17,0x7c,0x63,0xd8,0x8e,0xc7,0xd7}, + {0x29,0x2c,0x23,0xa9,0xaa,0x1d,0x8b,0xea,0x7e,0x24,0x35,0xe5,0x55,0xa4,0xa6,0x0e,0x37,0x9a,0x5a,0x35,0xf3,0xf4,0x52,0xba,0xe6,0x01,0x21,0x07,0x3f,0xb6,0xee,0xad}, + {0xe1,0xce,0xa9,0x2e,0xd9,0x9a,0xcd,0xcb,0x04,0x5a,0x67,0x26,0xb2,0xf8,0x71,0x07,0xe8,0xa6,0x16,0x20,0xa2,0x32,0xcf,0x4d,0x7d,0x5b,0x57,0x66,0xb3,0x95,0x2e,0x10}, + {0x7a,0xd6,0x6c,0x0a,0x68,0xc7,0x2c,0xb8,0x9e,0x4f,0xb4,0x30,0x38,0x41,0x96,0x6e,0x40,0x62,0xa7,0x6a,0xb9,0x74,0x51,0xe3,0xb9,0xfb,0x52,0x6a,0x5c,0xeb,0x7f,0x82}, + {0xe0,0x26,0xcc,0x5a,0x4a,0xed,0x3c,0x22,0xa5,0x8c,0xbd,0x3d,0x2a,0xc7,0x54,0xc9,0x35,0x2c,0x54,0x36,0xf6,0x38,0x04,0x2d,0xca,0x99,0x03,0x4e,0x83,0x63,0x65,0x16}, + {0x3d,0x04,0xcf,0xfd,0x8b,0x46,0xa8,0x74,0xed,0xf5,0xcf,0xae,0x63,0x07,0x7d,0xe8,0x5f,0x84,0x9a,0x66,0x04,0x26,0x69,0x7b,0x06,0xa8,0x29,0xc7,0x0d,0xd1,0x40,0x9c}, + {0xad,0x67,0x6a,0xa3,0x37,0xa4,0x85,0xe4,0x72,0x8a,0x0b,0x24,0x0d,0x92,0xb3,0xef,0x7b,0x3c,0x37,0x2d,0x06,0xd1,0x89,0x32,0x2b,0xfd,0x5f,0x61,0xf1,0xe7,0x20,0x3e}, + {0xa2,0xfc,0xa4,0xa4,0x96,0x58,0xf9,0xfa,0xb7,0xaa,0x63,0x28,0x9c,0x91,0xb7,0xc7,0xb6,0xc8,0x32,0xa6,0xd0,0xe6,0x93,0x34,0xff,0x5b,0x0a,0x34,0x83,0xd0,0x9d,0xab}, + {0x4e,0xbf,0xd9,0xcd,0x7b,0xca,0x25,0x05,0xf7,0xbe,0xf5,0x9c,0xc1,0xc1,0x2e,0xcc,0x70,0x8f,0xff,0x26,0xae,0x4a,0xf1,0x9a,0xbe,0x85,0x2a,0xfe,0x9e,0x20,0xc8,0x62}, + {0x2d,0xef,0x10,0xd1,0x3d,0xd1,0x69,0xf5,0x50,0xf5,0x78,0xbd,0xa3,0x43,0xd9,0x71,0x7a,0x13,0x85,0x62,0xe0,0x09,0x3b,0x38,0x0a,0x11,0x20,0x78,0x9d,0x53,0xcf,0x10}, + {0x77,0x6a,0x31,0xdb,0x34,0xa1,0xa0,0xa7,0xca,0xaf,0x86,0x2c,0xff,0xdf,0xff,0x17,0x89,0x29,0x7f,0xfa,0xdc,0x38,0x0b,0xd3,0xd3,0x92,0x81,0xd3,0x40,0xab,0xd3,0xad}, + {0xe2,0xe7,0x61,0x0b,0x87,0xa5,0xfd,0xf3,0xa7,0x2e,0xbe,0x27,0x12,0x87,0xd9,0x23,0xab,0x99,0x0e,0xef,0xac,0x64,0xb6,0xe5,0x9d,0x79,0xf8,0xb7,0xe0,0x8c,0x46,0xe3}, + {0x50,0x43,0x64,0xa5,0xc6,0x85,0x8b,0xf9,0x8f,0xff,0x71,0x4a,0xb5,0xbe,0x9d,0xe1,0x9e,0xd3,0x1a,0x97,0x68,0x60,0xef,0xbd,0x0e,0x77,0x2a,0x2e,0xfe,0x23,0xe2,0xe0}, + {0x4f,0x05,0xf4,0xac,0xb8,0x3f,0x5b,0x65,0x16,0x8d,0x9f,0xef,0x89,0xd5,0x6d,0x4d,0x77,0xb8,0x94,0x40,0x15,0xe6,0xb1,0xee,0xd8,0x1b,0x02,0x38,0xe2,0xd0,0xdb,0xa3}, + {0x44,0xa6,0xd9,0x74,0xc7,0x5b,0x07,0x42,0x3e,0x1d,0x6d,0x33,0xf4,0x81,0x91,0x6f,0xdd,0x45,0x83,0x0a,0xea,0x11,0xb6,0x34,0x7e,0x70,0x0c,0xd8,0xb9,0xf0,0x76,0x7c}, + {0xed,0xf2,0x60,0x29,0x1f,0x73,0x4d,0xda,0xc3,0x96,0xa9,0x56,0x12,0x7d,0xde,0x4c,0x34,0xc0,0xcf,0xb8,0xd8,0x05,0x2f,0x88,0xac,0x13,0x96,0x58,0xcc,0xf2,0xd5,0x07}, + {0x60,0x75,0xc6,0x57,0xa1,0x05,0x35,0x1e,0x7f,0x0f,0xce,0x53,0xbc,0x32,0x01,0x13,0x32,0x4a,0x52,0x2e,0x8f,0xd5,0x2d,0xc8,0x78,0xc7,0x62,0x55,0x1e,0x01,0xa4,0x6e}, + {0x6c,0xa6,0xa3,0xf7,0x63,0xa9,0x39,0x5f,0x7d,0xa1,0x60,0x14,0x72,0x5c,0xa7,0xee,0x17,0xe4,0x81,0x5c,0x0f,0xf8,0x11,0x9b,0xf3,0x3f,0x27,0x3d,0xee,0x11,0x83,0x3b}, + {0x1c,0x25,0xef,0x10,0xff,0xeb,0x3c,0x7d,0x08,0xaa,0x70,0x7d,0x17,0x28,0x6e,0x0b,0x0d,0x3c,0xbc,0xb5,0x0f,0x1b,0xd3,0xb6,0x52,0x3b,0x63,0xba,0x3b,0x52,0xdd,0x0f}, + {0xff,0xfc,0x43,0xbd,0x08,0x27,0x3c,0xcf,0x13,0x5f,0xd3,0xca,0xcb,0xee,0xf0,0x55,0x41,0x8e,0x09,0xeb,0x72,0x8d,0x72,0x7c,0x4d,0x5d,0x5c,0x55,0x6c,0xde,0xa7,0xe3}, + {0xc5,0xab,0x81,0x11,0x45,0x6b,0x1f,0x28,0xf3,0xc7,0xa0,0xa6,0x04,0xb4,0x55,0x3c,0xe9,0x05,0xcb,0x01,0x9c,0x46,0x3e,0xe1,0x59,0x13,0x7a,0xf8,0x3c,0x35,0x0b,0x22}, + {0x0f,0xf2,0x73,0xfc,0xbf,0x4a,0xe0,0xf2,0xbd,0x88,0xd6,0xcf,0x31,0x9f,0xf4,0x00,0x4f,0x8d,0x7d,0xca,0x70,0xd4,0xce,0xd4,0xe7,0x4d,0x2c,0x74,0x13,0x97,0x39,0xe6}, + {0x7f,0xa0,0x6b,0xa1,0x12,0x41,0xdd,0xd5,0xef,0xdc,0x65,0xd4,0xe3,0x9c,0x9f,0x69,0x91,0xb7,0x4f,0xd4,0xb8,0x1b,0x62,0x23,0x08,0x08,0x21,0x6c,0x87,0x6f,0x82,0x7c}, + {0x7e,0x27,0x5a,0xdf,0x31,0x3a,0x99,0x6c,0x7e,0x29,0x50,0xca,0xc6,0x7c,0xab,0xa0,0x2a,0x5f,0xf9,0x25,0xeb,0xf9,0x90,0x6b,0x58,0x94,0x9f,0x3e,0x77,0xae,0xc5,0xb9}, + {0x8f,0x61,0x62,0xfa,0x30,0x8d,0x2b,0x3a,0x15,0xdc,0x33,0xcf,0xfa,0xc8,0x5f,0x13,0xab,0x34,0x91,0x73,0x12,0x16,0x45,0xae,0xdf,0x00,0xf4,0x71,0x66,0x31,0x08,0xbe}, + {0x78,0xcc,0xaa,0xab,0x73,0x37,0x35,0x52,0xf2,0x07,0xa6,0x35,0x99,0xde,0x54,0xd7,0xd8,0xd0,0xc1,0x80,0x5f,0x86,0xce,0x7d,0xa1,0x58,0x18,0xd0,0x9f,0x4c,0xff,0x62}, + {0xcf,0x27,0x7f,0xb8,0x0a,0x82,0x47,0x84,0x60,0xe8,0x98,0x85,0x70,0xb7,0x18,0xf1,0xe0,0x83,0xce,0xb7,0x6f,0x7e,0x27,0x1a,0x1a,0x14,0x97,0xe5,0x97,0x5f,0x53,0xae}, + {0x4b,0xf6,0xff,0xa2,0xbc,0x13,0x12,0x04,0x51,0x32,0x89,0x73,0x85,0x67,0xa6,0x8f,0xa9,0xf4,0x82,0x7d,0xac,0x7f,0xd3,0xb3,0xd1,0xf2,0xe9,0x47,0x77,0xd5,0x7f,0x36}, + {0xb4,0x9d,0x61,0xe8,0xc2,0xc8,0x94,0xe1,0x24,0x86,0x17,0x6a,0xb8,0xf4,0xd7,0x06,0x9d,0x66,0x92,0xfa,0x64,0x95,0x54,0x15,0x67,0x87,0x2e,0x7e,0xcb,0xdd,0xb7,0x26}, + {0x20,0x2b,0x10,0x14,0x73,0x9f,0x29,0xb1,0xd9,0x05,0xd6,0x30,0xdd,0xeb,0x85,0x60,0xa3,0x2b,0xf2,0x3e,0x66,0x6c,0x8a,0x15,0x23,0xa4,0xa6,0x00,0x22,0x7f,0xef,0x7c}, + {0x9e,0x1d,0x1c,0xe5,0xcd,0xca,0x9c,0xdf,0x40,0xfa,0x57,0x86,0x54,0x8b,0x58,0xeb,0x19,0xdd,0xfd,0x32,0x39,0x5b,0x45,0x82,0x98,0x39,0x19,0x09,0x9d,0xbd,0x15,0x31}, + {0x13,0x78,0x4d,0x01,0xe2,0xfa,0xe9,0x04,0xde,0x62,0xc6,0xfb,0xf9,0x77,0x69,0x79,0xca,0x7a,0x27,0x77,0xae,0x26,0x32,0xee,0x27,0x8d,0x19,0xac,0xa3,0x0f,0x89,0x0e}, + {0x68,0xc4,0x77,0xf8,0x3a,0x13,0xa0,0x00,0xad,0x2b,0x5b,0x3e,0x50,0x37,0x5b,0x7c,0x3a,0xe7,0x82,0xd9,0x87,0xba,0x4b,0x5a,0x65,0x37,0x6b,0xbb,0x97,0x46,0x9f,0xb3}, + {0x71,0xe3,0x78,0x64,0xed,0xf0,0x87,0x40,0xd5,0x92,0x36,0x2c,0xd2,0x4d,0x0d,0xb0,0x67,0xbf,0x14,0xcd,0x3b,0x97,0xbd,0x2a,0x68,0xe7,0x82,0xad,0xff,0xb4,0x36,0x55}, + {0x88,0xd0,0xcc,0x35,0xe8,0xab,0xe7,0xd6,0x52,0x45,0x69,0xbe,0x8a,0xa1,0xa4,0x8b,0xb2,0x33,0x62,0x32,0x6f,0xdf,0xef,0xe9,0x61,0x34,0x8b,0xc9,0x60,0x91,0xc9,0x4c}, + {0x85,0x45,0xf2,0xc1,0xaf,0xda,0xce,0x5d,0x87,0xf0,0x6c,0xe1,0xd4,0x4b,0xc0,0xa2,0x69,0x1a,0xea,0x44,0x14,0xd0,0xad,0x64,0x0b,0xe0,0xd9,0x87,0x9c,0x83,0x74,0xd9}, + {0x23,0x60,0xc4,0x48,0x0d,0x69,0x87,0x9e,0xad,0xd7,0xae,0x50,0x84,0x09,0xf2,0xf0,0xba,0x83,0xa2,0xb0,0xfe,0x0d,0xa5,0x7b,0x40,0x00,0x8c,0x06,0x4d,0x2c,0x39,0x7d}, + {0x31,0x76,0x3a,0x8e,0x1d,0xed,0xb1,0x5a,0xd0,0xc8,0xb8,0x8d,0x43,0x7f,0xef,0x83,0x5a,0xeb,0x95,0x82,0x92,0x64,0x48,0x10,0x66,0x3d,0xc1,0x75,0x65,0x50,0xf2,0x2b}, + {0xfa,0x4c,0x9b,0x11,0x72,0xab,0x5a,0xfe,0xbd,0x15,0x95,0x73,0xe1,0x9e,0x25,0x16,0x99,0x75,0x24,0x2e,0x10,0xa2,0xed,0x38,0xdf,0xc8,0x05,0x23,0x2f,0xb5,0x0a,0x64}, + {0x35,0x28,0x59,0x4d,0x65,0xe9,0x52,0x52,0x33,0xb3,0x95,0x87,0x17,0x4b,0xcf,0x0b,0xba,0x56,0x2d,0x95,0xb5,0x0b,0x42,0x99,0x14,0xc7,0xe0,0xe8,0xa9,0xae,0x58,0xf9}, + {0x17,0x65,0x92,0x27,0x97,0xba,0x72,0x1e,0x89,0x37,0xc7,0x47,0x0a,0x26,0xd5,0xc2,0xf1,0x41,0xf3,0xd4,0x10,0x64,0x47,0xda,0xb1,0x71,0xfc,0xed,0x7d,0x65,0x2a,0x69}, + {0xf0,0x57,0x3e,0x66,0x0b,0xa0,0x1c,0xff,0x27,0x2a,0x1c,0x6f,0x79,0x27,0xd7,0x3c,0x94,0xc8,0x5d,0x38,0x0a,0x84,0x39,0x98,0x67,0x62,0x6d,0x64,0x29,0xd4,0x8b,0xae}, + {0x31,0x68,0x2c,0x19,0xf7,0x15,0xcd,0x31,0x7d,0xa1,0xa5,0x53,0xce,0x38,0x4d,0xe6,0x90,0x2f,0xe2,0x60,0x32,0xfe,0xcc,0x7d,0xe0,0x3e,0x40,0x50,0x2a,0xf0,0x5d,0xce}, + {0xd5,0x0c,0x60,0x62,0xbf,0xcd,0x7b,0xbd,0x0d,0xbd,0x68,0xd1,0xdd,0xee,0xae,0x09,0x54,0x85,0x72,0x0a,0x1c,0xe3,0x34,0xe9,0xe5,0xe5,0x15,0x30,0x66,0xf4,0x22,0x60}, + {0xe0,0xbc,0xfb,0xd0,0x3f,0xa4,0xa6,0x19,0xa5,0xbc,0x3c,0xae,0x15,0x40,0x4f,0x39,0xb4,0xb7,0xbf,0x5d,0xdd,0x51,0x61,0xea,0x64,0x2d,0x13,0xd2,0x3b,0xae,0xf5,0x45}, + {0xac,0xb9,0x8b,0x6f,0xb1,0xa5,0xfd,0xae,0x0a,0x57,0x18,0xd0,0xb8,0x12,0xe1,0xde,0x14,0x12,0xe4,0x4d,0x87,0x51,0x46,0xac,0xc1,0xa7,0xf7,0xc3,0x91,0x6c,0x00,0x3a}, + {0xbb,0x1f,0xf2,0x91,0xeb,0x2e,0x61,0x4a,0x66,0x1d,0x67,0x30,0xb5,0x02,0xba,0xe2,0xbd,0xe1,0x12,0x95,0x8c,0xee,0x69,0x84,0x78,0x84,0x5a,0x1b,0xb3,0x78,0x8a,0x6b}, + {0x9a,0x91,0xf4,0xc1,0x95,0x45,0xd6,0xcd,0x97,0x20,0xd4,0x73,0x65,0x91,0xfe,0x70,0xed,0xcf,0x3e,0x33,0xcb,0x2e,0x56,0xf2,0x1f,0xdc,0x12,0x44,0xf3,0xd2,0xef,0x6d}, + {0xbd,0xf8,0x2c,0x7d,0x25,0x2d,0x69,0xc6,0x4b,0xd6,0xfa,0x8e,0xe6,0x6a,0xd9,0xd3,0x02,0x41,0x01,0x69,0x81,0xe4,0x58,0x0e,0x3c,0x2e,0xdc,0x48,0x04,0xb7,0x52,0x36}, + {0x63,0xbc,0x3b,0x9e,0x83,0x40,0xab,0x48,0xa6,0x68,0x28,0xd7,0xd9,0xd7,0xd5,0xbe,0x2f,0x5f,0x28,0x06,0x65,0x19,0x6d,0x25,0xa3,0x9c,0x4d,0x01,0x34,0x5c,0xb9,0x5e}, + {0xa9,0x62,0xe4,0xbd,0x7d,0x1f,0x31,0x6f,0x84,0x57,0x84,0xe1,0x15,0xff,0x10,0x35,0x30,0xd8,0x5f,0x9d,0xfa,0xcd,0x63,0x8a,0xd4,0x5f,0xbe,0x93,0x8b,0xa8,0x5c,0x40}, + {0xb3,0xf1,0x00,0xa0,0xf5,0x06,0xf6,0xa6,0xc8,0x6c,0x10,0xf1,0x07,0xab,0x48,0x11,0xdd,0xc7,0x6f,0x72,0x51,0x6f,0xcd,0xfa,0x8a,0x0c,0x9b,0xd7,0x1f,0xf9,0x12,0xab}, + {0xe7,0xe8,0xdb,0xc3,0x89,0x85,0x15,0x97,0x0c,0xa6,0xfc,0xe1,0x90,0x39,0xca,0x22,0xae,0x26,0x38,0xe4,0x4b,0x24,0x64,0x1f,0xd6,0x10,0x48,0xb9,0x4d,0xc2,0xbe,0x25}, + {0x31,0xd0,0xf6,0x6a,0xb4,0x30,0x19,0x85,0x67,0x80,0xcb,0x24,0x6c,0xfd,0x37,0xb0,0xc1,0x90,0xd1,0x71,0x11,0xd5,0xc0,0x16,0xf1,0x9b,0xa7,0x15,0xee,0x62,0x2d,0xc5}, + {0xac,0xd2,0xf3,0xdf,0xd4,0x3e,0x9a,0x01,0xbc,0x64,0xa9,0xc4,0xac,0x8a,0xc8,0xa2,0xb2,0x9f,0x32,0x74,0x13,0x1a,0x12,0x01,0x0b,0x42,0xb8,0xc8,0x30,0xe8,0x79,0xbf}, + {0xa2,0x31,0x36,0x1a,0xd3,0x38,0x9f,0xe6,0xf2,0xb6,0x2a,0x46,0xcf,0x67,0x7d,0x6c,0x5f,0x7b,0x79,0x96,0x4c,0x30,0xa2,0xc3,0x4a,0xa4,0xfc,0x33,0x92,0xf5,0x01,0xc9}, + {0x14,0x0a,0xb4,0x9c,0xc3,0xbb,0x44,0xe3,0x42,0x65,0x5a,0x52,0x11,0x85,0x93,0x3b,0x22,0x30,0xec,0x27,0x4f,0xf5,0x30,0x4b,0x2c,0xea,0xd9,0x87,0x3a,0x82,0x28,0x63}, + {0x10,0xc7,0x10,0xf9,0x20,0x5b,0x30,0xe8,0xa7,0x16,0x3e,0xdb,0xfc,0xd8,0x5e,0x8f,0xba,0xf2,0xf3,0xfe,0x44,0x2f,0x2e,0xe4,0xaf,0x58,0x0e,0x28,0xb2,0xdf,0x11,0x80}, + {0x6f,0x35,0x41,0x9d,0x1d,0xa1,0x26,0x0b,0xc0,0xf3,0x3d,0x52,0xe8,0xf6,0xd7,0x3f,0xc5,0xd6,0x72,0xc0,0xdc,0xa1,0x3b,0xb9,0x60,0xb4,0xae,0x1a,0xde,0xc1,0x79,0x37}, +}; +#endif /* EVM_ZERO_DATA_H */ diff --git a/linux/libdriver/base/merkle.c b/linux/libdriver/base/merkle.c new file mode 100644 index 00000000..2c1a63fa --- /dev/null +++ b/linux/libdriver/base/merkle.c @@ -0,0 +1,57 @@ +#include +#include +#include +#include"merkle.h" +#include"merkle-zero-table.h" + +static void hash2 + (const uint8_t lhs[EVM_KECCAK_MDLEN] + ,const uint8_t rhs[EVM_KECCAK_MDLEN] + ,uint8_t out[EVM_KECCAK_MDLEN]) +{ + EVM_KECCAK_DECL(c); + evm_keccak_update(c, EVM_KECCAK_MDLEN, lhs); + evm_keccak_update(c, EVM_KECCAK_MDLEN, rhs); + evm_keccak_final(c, out); +} + +void evm_merkle_init(evm_merkle_t *me) +{ + me->n = 0; + me->zero = merkle_zero_table; + memcpy(me->sib, me->zero, sizeof(me->sib)); +} + +int evm_merkle_put(evm_merkle_t *me, uint8_t hash[EVM_KECCAK_MDLEN]) +{ + if (me->n == UINT64_MAX) + return ENOBUFS; + + int n = ffsll(me->n+1u)-1u; + for (int i=0; isib[i], hash, hash); + memcpy(me->sib[n], hash, EVM_KECCAK_MDLEN); + me->n++; + return 0; +} + +void evm_merkle_get(evm_merkle_t *me, uint8_t root[EVM_KECCAK_MDLEN]) +{ + unsigned n = (ffsll(me->n)-1u) & (EVM_MERKLE_MAX-1u); + memcpy(root, me->sib[n], EVM_KECCAK_MDLEN); + + for (int i=n; in & (UINT64_C(1) << i); + if (set) hash2(me->sib[i], root, root); + else hash2(root, me->zero[i], root); + } +} + +int evm_merkle_put_data(evm_merkle_t *me, size_t n, void *data) +{ + uint8_t hash[EVM_KECCAK_MDLEN]; + EVM_KECCAK_DECL(c); + evm_keccak_update(c, n, data); + evm_keccak_final(c, hash); + return evm_merkle_put(me, hash); +} diff --git a/linux/libdriver/base/merkle.h b/linux/libdriver/base/merkle.h new file mode 100644 index 00000000..f658bc61 --- /dev/null +++ b/linux/libdriver/base/merkle.h @@ -0,0 +1,59 @@ +/** @file + * @defgroup libevm_merkle merkle + * merkle tree of keccak256 hashes + * + * @ingroup libevm + * @{ */ +#ifndef EVM_MERKLE_H +#define EVM_MERKLE_H +#include"keccak.h" + +#define EVM_MERKLE_MAX 64 + +/** Opaque Merkle tree state, used to do hash computations, initialize with: + * - @ref evm_merkle_init */ +typedef struct { + uint64_t n; + const uint8_t (*zero)[EVM_KECCAK_MDLEN]; + uint8_t sib[EVM_MERKLE_MAX][EVM_KECCAK_MDLEN]; +} evm_merkle_t; + +/** Initialize a @ref evm_merkle_t tree state. + * + * @param [in] me uninitialized state */ +void evm_merkle_init(evm_merkle_t *me); + +/** Append a leaf node + * + * @param [in,out] me initialized state + * @param [in] hash value of the new leaf + * @return + * - 0 success + * - ENOBUFS tree is full, no more space available + * + * @code + * ... + * evm_buf_t tx, it = ...; + * uint8_t md[EVM_KECCAL_MDLEN]; + * + * // used bytes + * size_t n = it.p - tx.p; + * void *p = tx.p; + * + * evm_merkle_t merkle = EVM_MERKLE_INIT(&merkle); + * evm_merkle_put(evm_keccak_data(md, n, p), md); + * @endcode */ +int evm_merkle_put(evm_merkle_t *me, uint8_t hash[EVM_KECCAK_MDLEN]); + +/** Compute the hash of @p data, then append it as a leaf node + * + * @param [in,out] me initialized state */ +int evm_merkle_put_data(evm_merkle_t *me, size_t n, void *data); + +/** Retrieve the root hash of the merkle tree + * + * @param [in] me initialized state + * @param [out] root root hash of the merkle tree */ +void evm_merkle_get(evm_merkle_t *me, uint8_t root[EVM_KECCAK_MDLEN]); + +#endif /* EVM_MERKLE_H */ diff --git a/linux/libdriver/base/rollup-driver-api.h b/linux/libdriver/base/rollup-driver-api.h index a30d0b92..6a272db0 100644 --- a/linux/libdriver/base/rollup-driver-api.h +++ b/linux/libdriver/base/rollup-driver-api.h @@ -74,12 +74,12 @@ int rollup_driver_write_exception(struct rollup_driver *me, uint64_t n); * - Any other value indicates an error condition */ int rollup_driver_loop(struct rollup_driver *me, uint8_t hash[32], uint64_t *n); -/** Reject this input, revert state back to the previous @ref rollup_driver_loop call. +/** Revert state back to the previous @ref rollup_driver_loop call and reject this input. * * @param [in] me A sucessfuly initialized state by @ref rollup_driver_init * @return * 0 on success */ -int rollup_driver_reject_input(struct rollup_driver *me); +int rollup_driver_revert(struct rollup_driver *me); /** Retrieve the writable memory region @p tx * @param [in] me A sucessfuly initialized state by @ref rollup_driver_init diff --git a/linux/libdriver/base/yield-driver-api.h b/linux/libdriver/base/yield-driver-api.h index 823d714a..6106ca71 100644 --- a/linux/libdriver/base/yield-driver-api.h +++ b/linux/libdriver/base/yield-driver-api.h @@ -1,5 +1,5 @@ /** @file - * @defgroup yield yield + * @defgroup yield_driver yield_driver * Abstraction of the Cartesi yield driver IOCTL API * * Lets look at some code: @@ -8,8 +8,8 @@ * * @ingroup driver * @{ */ -#ifndef YIELD_API_H -#define YIELD_API_H +#ifndef YIELD_DRIVER_API_H +#define YIELD_DRIVER_API_H /** contents of @ref rollup_driver_t are implementation specific. * Define it in @p */ @@ -30,4 +30,4 @@ int yield_driver_init(struct yield_driver *me); * @note usage of @p me after this call is a BUG and will cause undefined behaviour */ int yield_driver_progress(struct yield_driver *me, float progress); -#endif /* YIELD_API_H */ +#endif /* YIELD_DRIVER_API_H */ diff --git a/linux/libdriver/examples/abi.c b/linux/libdriver/examples/abi.c new file mode 100644 index 00000000..4ce4557d --- /dev/null +++ b/linux/libdriver/examples/abi.c @@ -0,0 +1,84 @@ +#include +#include "abi.h" + +#define VOUCHER EVM_ABI_FUNSEL(0x00, 0x00, 0x00, 0x01) +#define NOTICE EVM_ABI_FUNSEL(0x00, 0x00, 0x00, 0x02) + +int encode_voucher(evm_buf_t wr[1], uint8_t address[20], size_t n, const void *data); +int send_voucher(evm_buf_t tx[1]); + +int decode_voucher(evm_buf_t rd[1], uint8_t address[20], size_t *n, uint8_t **data); +int recv_multiple(evm_buf_t rx[1]); + +int encode_voucher(evm_buf_t wr[1], uint8_t address[20], size_t n, const void *data) +{ + evm_buf_t of[1]; + + return evm_abi_put_funsel(wr, VOUCHER) + || evm_abi_put_address(wr, address) + || evm_abi_put_bytes_s(wr, of) + || evm_abi_put_bytes_d(wr, of, n, data) + ; +} + +int send_voucher(evm_buf_t tx[1]) +{ + uint8_t address[20] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + }; + char message[] = "hello world\n"; + + evm_buf_t it[1] = { *tx }; + if (encode_voucher(it, address, sizeof(message)-1, message)) + return EXIT_FAILURE; + + //size_t len = it->p - tx->p; + //merkle_put_data(len, tx->p); + //ioctl(ROLLUP_IOCTL_EMIT_OUTPUT, len, tx->p); + return EXIT_SUCCESS; + + //return evm_rollup_emit(rollup, it->p - tx->p); +} + +int decode_voucher(evm_buf_t rd[1], uint8_t address[20], size_t *n, uint8_t **data) +{ + evm_buf_t of[1]; + + return evm_abi_check_funsel(rd, VOUCHER) + || evm_abi_get_address(rd, address) + || evm_abi_get_bytes_s(rd, of) + || evm_abi_get_bytes_d(rd, of, n, data) + ; +} + +int recv_multiple(evm_buf_t rx[1]) +{ + evm_buf_t it[1] = { *rx }; + + if (evm_buf_length(it) < 4) + return EXIT_FAILURE; + + switch (evm_abi_peek_funsel(it)) { + case VOUCHER:; + uint8_t address[20]; + size_t n; + uint8_t *data; + if (decode_voucher(it, address, &n, &data) == 0) { + /* use contents */ + } + break; + case NOTICE: + /* falltrough */ + default: return EXIT_FAILURE; + } +} + +int main() +{ + evm_buf_t tx[1], rx[1]; + send_voucher(tx); + recv_multiple(rx); + + return 0; +} diff --git a/linux/libdriver/examples/abi_get.c b/linux/libdriver/examples/abi_get.c new file mode 100644 index 00000000..ce054a51 --- /dev/null +++ b/linux/libdriver/examples/abi_get.c @@ -0,0 +1,19 @@ +#include +#include"abi.h" + +int decode_voucher(evm_buf_t rd[1], uint8_t address[20], size_t *n, uint8_t **data) +{ + evm_buf_t of[1]; + + uint32_t voucher = EVM_ABI_FUNSEL(0x00, 0x00, 0x00, 0x01); + // uint32_t voucher = evm_keccak_funsel("Voucher(address, bytes)"); + + // funsel + return evm_abi_check_funsel(rd, voucher) + // static section + || evm_abi_get_address(rd, address) + || evm_abi_get_bytes_s(rd, of) + // dynamic section + || evm_abi_get_bytes_d(rd, of, n, data) + ; +} diff --git a/linux/libdriver/examples/abi_put.c b/linux/libdriver/examples/abi_put.c new file mode 100644 index 00000000..a83c77ac --- /dev/null +++ b/linux/libdriver/examples/abi_put.c @@ -0,0 +1,19 @@ +#include +#include"abi.h" + +int encode_voucher(evm_buf_t wr[1], uint8_t address[20], size_t n, const uint8_t data[n]) +{ + evm_buf_t of[1]; + + uint32_t voucher = EVM_ABI_FUNSEL(0x01, 0x02, 0x03, 0x04); + // uint32_t voucher = evm_keccak_funsel("Voucher(address, bytes)"); + + // funsel + return evm_abi_put_funsel(wr, voucher) + // static section + || evm_abi_put_address(wr, address) + || evm_abi_put_bytes_s(wr, of) + // dynamic section + || evm_abi_put_bytes_d(wr, of, n, data) + ; +} diff --git a/linux/libdriver/examples/rollup.c b/linux/libdriver/examples/rollup.c index b133dafc..1c0254c9 100644 --- a/linux/libdriver/examples/rollup.c +++ b/linux/libdriver/examples/rollup.c @@ -28,7 +28,7 @@ int main(int argc, char *argv[]) printf("inspect\n"); break; default: - rollup_driver_reject_input(R); + rollup_driver_revert(R); break; } } diff --git a/linux/libdriver/ioctl/rollup.c b/linux/libdriver/ioctl/rollup.c index 035ac89c..18a632c3 100644 --- a/linux/libdriver/ioctl/rollup.c +++ b/linux/libdriver/ioctl/rollup.c @@ -78,7 +78,7 @@ int rollup_driver_loop(struct rollup_driver *me, uint8_t hash[32], uint64_t *n) return ioctl(me->fd, IOCTL_ROLLUP_WAIT_FOR_INPUT, (unsigned long) n); } -int rollup_driver_reject_input(struct rollup_driver *me) +int rollup_driver_revert(struct rollup_driver *me) { return ioctl(me->fd, IOCTL_ROLLUP_REJECT_INPUT); } diff --git a/linux/libdriver/mock/rollup.c b/linux/libdriver/mock/rollup.c new file mode 100644 index 00000000..289750d7 --- /dev/null +++ b/linux/libdriver/mock/rollup.c @@ -0,0 +1,104 @@ +#ifndef ROLLUP_IMPL_H +#define ROLLUP_IMPL_H +#include +#include +#include +#include +#include +#include +#include"rollup.h" + +int rollup_driver_init(struct rollup_driver *me) +{ + me->ni =-1; + me->no = 0; + me->tx->len = 4 * 1024 * 1024; // 4MB + me->rx->len = 4 * 1024 * 1024; // 4MB + me->tx->data = malloc(me->tx->len); + me->rx->data = malloc(me->tx->len); + + return 0; +} + +void rollup_driver_fini(struct rollup_driver *me) +{ + free(me->tx->data); + free(me->rx->data); +} + +int rollup_driver_write_exception(rollup_driver_t *me, uint64_t n) +{ + if (me->tx->len < n) + return ENOBUFS; + + fprintf(stderr, "Rollup exception with payload: \"%.*s\"\n", + (int)me->tx->len, (char *)me->tx->data); + // exit(1); ?? + return 0; +} + +int rollup_driver_write_output(struct rollup_driver *me, bool verifiable /* being ignored */, size_t n) +{ + if (me->tx->len < n) + return ENOBUFS; + + char b[1024]; + snprintf(b, sizeof(b), "input-%u-output-%u.bin", me->ni, me->no); + FILE *file = fopen(b, "wb"); + if (!file) return EIO; + fwrite(me->tx->data, 1, n, file); + fclose(file); + + me->no++; + return 0; +} +int rollup_driver_loop(struct rollup_driver *me, uint8_t hash[32], size_t *n) +{ + FILE *file = NULL; + if (me->ni != -1) { /* don't write the first output hash */ + char b[1024]; + snprintf(b, sizeof(b), "input-%u-outputs-root-hash.bin", me->ni); + + file = fopen(b, "wb"); + if (!file) + return ROLLUP_DRIVER_INVALID_STATE; + fwrite(me->tx->data, 1, 32, file); + fclose(file); + } + + { /* read next input */ + char b[1024]; + snprintf(b, sizeof(b), "input-%u.bin", ++me->ni); + + file = fopen(b, "rb"); + if (!file) + return ROLLUP_DRIVER_INVALID_STATE; + + fseek(file, 0, SEEK_END); + *n = ftell(file); + fseek(file, 0, SEEK_SET); + + fread(me->rx->data, 1, *n, file); + fclose(file); + } + return ROLLUP_DRIVER_ADVANCE_STATE; +} + +int rollup_driver_revert(struct rollup_driver *me) +{ + exit(1); // should revert, but can't deal with it... + return 0; +} + +void *rollup_driver_get_tx(struct rollup_driver *me, size_t *max) +{ + *max = me->tx->len; + return me->tx->data; +} +const void *rollup_driver_get_rx(struct rollup_driver *me, size_t *max) +{ + *max = me->rx->len; + return me->rx->data; +} + +#endif /* ROLLUP_IMPL_H */ diff --git a/linux/libdriver/mock/rollup.h b/linux/libdriver/mock/rollup.h new file mode 100644 index 00000000..8d38c57e --- /dev/null +++ b/linux/libdriver/mock/rollup.h @@ -0,0 +1,15 @@ +#ifndef ROLLUP_H +#define ROLLUP_H +#include +#include + +struct rollup_driver { + int ni; // input number + int no; // output number + struct { + void *data; + size_t len; + } tx[1], rx[1]; +}; +#include "../base/rollup-driver-api.h" +#endif /* ROLLUP_H */ diff --git a/linux/libdriver/mock/yield.c b/linux/libdriver/mock/yield.c new file mode 100644 index 00000000..e66e423b --- /dev/null +++ b/linux/libdriver/mock/yield.c @@ -0,0 +1,16 @@ +#include +#include +#include"yield.h" + +int yield_driver_init(struct yield_driver *me) +{ + assert(me); + return 0; +} + +int yield_driver_progress(struct yield_driver *me, float progress) +{ + assert(me); + fprintf(stderr, "Progress: %6.2f\n", progress); + return 0; +} diff --git a/linux/libdriver/mock/yield.h b/linux/libdriver/mock/yield.h new file mode 100644 index 00000000..85c15321 --- /dev/null +++ b/linux/libdriver/mock/yield.h @@ -0,0 +1,9 @@ +#ifndef YIELD_H +#define YIELD_H + +struct yield_driver { + int dummy; +}; + +#include "../base/yield-driver-api.h" +#endif /* YIELD_H */ diff --git a/linux/libdriver/tests/abi.c b/linux/libdriver/tests/abi.c new file mode 100644 index 00000000..1873c854 --- /dev/null +++ b/linux/libdriver/tests/abi.c @@ -0,0 +1,303 @@ +#include +#include +#include +#include +#include"../base/abi.h" + +static void enc_u8() +{ + uint8_t x = 0x01; + uint8_t en[EVM_WORD_LEN]; + uint8_t be[EVM_WORD_LEN] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01, + }; + evm_abi_enc_uint(sizeof(x), (void *)&x, en); + assert(memcmp(en, be, sizeof(be)) == 0); +} + +static void enc_u16() +{ + uint16_t x = UINT16_C(0x0123); + uint8_t en[EVM_WORD_LEN]; + uint8_t be[EVM_WORD_LEN] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x23, + }; + evm_abi_enc_uint(sizeof(x), (void *)&x, en); + assert(memcmp(en, be, sizeof(be)) == 0); +} + +static void enc_u32() +{ + uint32_t x = UINT32_C(0x01234567); + uint8_t en[EVM_WORD_LEN]; + uint8_t be[EVM_WORD_LEN] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x23,0x45,0x67, + }; + evm_abi_enc_uint(sizeof(x), (void *)&x, en); + assert(memcmp(en, be, sizeof(be)) == 0); +} + +static void enc_u64() +{ + uint64_t x = UINT64_C(0x0123456789abcdef); + uint8_t en[EVM_WORD_LEN]; + uint8_t be[EVM_WORD_LEN] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef, + }; + evm_abi_enc_uint(sizeof(x), (void *)&x, en); + assert(memcmp(en, be, sizeof(be)) == 0); +} + +static void enc_u256() +{ + uint8_t x[EVM_WORD_LEN] = { + 0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10, + 0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00, + }; + uint8_t en[EVM_WORD_LEN]; + uint8_t be[EVM_WORD_LEN] = { + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, + }; + evm_abi_enc_uint(sizeof(x), (void *)&x, en); + assert(memcmp(en, be, sizeof(be)) == 0); +} + +static void enc_edom() +{ + uint8_t x[EVM_WORD_LEN+1] = { + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, + 0x20, + }; + uint8_t en[EVM_WORD_LEN]; + assert(evm_abi_enc_uint_nr(sizeof(x), x, en) == EDOM); + assert(evm_abi_enc_uint_nn(sizeof(x), x, en) == EDOM); +} + +static void dec_u8() +{ + uint8_t x, ex = 0x01; + uint8_t be[EVM_WORD_LEN] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01, + }; + assert(evm_abi_dec_uint(be, sizeof(x), (void *)&x) == 0); + assert(x == ex); +} + +static void dec_u16() +{ + uint16_t x, ex = UINT16_C(0x0123); + uint8_t be[EVM_WORD_LEN] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x23, + }; + assert(evm_abi_dec_uint(be, sizeof(x), (void *)&x) == 0); + assert(x == ex); +} + +static void dec_u32() +{ + uint32_t x, ex = UINT32_C(0x01234567); + uint8_t be[EVM_WORD_LEN] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x23,0x45,0x67, + }; + assert(evm_abi_dec_uint(be, sizeof(x), (void *)&x) == 0); + assert(x == ex); +} + +static void dec_u64() +{ + uint64_t x, ex = UINT64_C(0x0123456789abcdef); + uint8_t be[EVM_WORD_LEN] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef, + }; + assert(evm_abi_dec_uint(be, sizeof(x), (void *)&x) == 0); + assert(x == ex); +} + +static void dec_u256() +{ + uint8_t x[EVM_WORD_LEN], ex[EVM_WORD_LEN] = { + 0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10, + 0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00, + }; + uint8_t be[EVM_WORD_LEN] = { + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, + }; + assert(evm_abi_dec_uint(be, sizeof(x), x) == 0); + assert(memcmp(x, ex, sizeof(ex)) == 0); +} + +/* encoded value can't be represented in a uint64_t. */ +static void dec_edom() +{ + uint64_t x; + uint8_t be[EVM_WORD_LEN] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xAA,0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef, + }; + assert(evm_abi_dec_uint(be, sizeof(x), (void *)&x) == EDOM); +} + +static void put_funsel() +{ + uint8_t data[] = {0xcd, 0xcd, 0x77, 0xc0}; + uint32_t funsel = EVM_ABI_FUNSEL(data[0], data[1], data[2], data[3]); + EVM_BUF_DECL(b, 64); + evm_buf_t it[1] = {*b}; + + assert(evm_abi_put_funsel(it, funsel) == 0); + assert(memcmp(b->p, data, 4) == 0); +} + +static void put_uint() +{ + uint64_t x = UINT64_C(0x0123456789abcdef); + uint8_t be[EVM_WORD_LEN] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef, + }; + EVM_BUF_DECL(b, 64); + evm_buf_t it[1] = {*b}; + + assert(evm_abi_put_uint(it, sizeof(x), &x) == 0); + assert(memcmp(b->p, be, sizeof(be)) == 0); +} + +static void put_address() +{ + uint8_t x[20] = { + 0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef, + 0x01,0x23,0x45,0x67 + }; + uint8_t be[EVM_WORD_LEN] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x23,0x45,0x67, + 0x89,0xab,0xcd,0xef,0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,0x01,0x23,0x45,0x67, + }; + EVM_BUF_DECL(b, 64); + evm_buf_t it[1] = {*b}; + + assert(evm_abi_put_address(it, x) == 0); + assert(memcmp(b->p, be, sizeof(be)) == 0); +} + +static void put_bytes() +{ + uint64_t x = UINT64_C(0x0123456789abcdef); + uint8_t be[] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08, + 0xef,0xcd,0xab,0x89,0x67,0x45,0x23,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + }; + EVM_BUF_DECL(b, 128); + evm_buf_t it[1] = {*b}, + of[1]; + + assert(evm_abi_put_bytes_s(it, of) == 0); + assert(evm_abi_put_bytes_d(it, of, sizeof(x), &x) == 0); + assert(memcmp(b->p, be, sizeof(be)) == 0); +} + +static void get_funsel() +{ + EVM_BUF_DECL(b, 64); + evm_buf_t wr[1] = {*b}, + rd[1] = {*b}; + uint32_t funsel = EVM_ABI_FUNSEL(1, 2, 3, 4); + + assert(evm_abi_put_funsel(wr, funsel) == 0); + assert(evm_abi_peek_funsel(rd) == funsel); + assert(evm_abi_check_funsel(rd, funsel) == 0); +} + +static void get_uint() +{ + uint64_t x, ex = UINT64_C(0x0123456789abcdef); + uint8_t be[EVM_WORD_LEN] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef, + }; + EVM_BUF_DECL3(b, sizeof(be), be); + evm_buf_t rd[1] = {*b}; + + assert(evm_abi_get_uint(rd, sizeof(x), &x) == 0); + assert(x == ex); +} + +static void get_address() +{ + uint8_t x[20], ex[20] = { + 0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef, + 0x01,0x23,0x45,0x67 + }; + uint8_t be[EVM_WORD_LEN] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x23,0x45,0x67, + 0x89,0xab,0xcd,0xef,0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,0x01,0x23,0x45,0x67, + }; + EVM_BUF_DECL3(b, sizeof(be), be); + evm_buf_t it[1] = {*b}; + + assert(evm_abi_get_address(it, x) == 0); + assert(memcmp(x, ex, sizeof(ex)) == 0); +} + +static void get_bytes() +{ + uint64_t ex = UINT64_C(0x0123456789abcdef); + uint8_t be[] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08, + 0xef,0xcd,0xab,0x89,0x67,0x45,0x23,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + }; + EVM_BUF_DECL3(b, sizeof(be), be); + evm_buf_t it[1] = {*b}, + of[1], + bytes[1]; + + assert(evm_abi_get_bytes_s(it, of) == 0); + assert(evm_abi_peek_bytes_d(it, of, bytes) == 0); + assert(memcmp(bytes->p, &ex, sizeof(ex)) == 0); +} + + +int main(int argc, char *argv[]) +{ + enc_u8(); + enc_u16(); + enc_u32(); + enc_u64(); + enc_u256(); + enc_edom(); + + dec_u8(); + dec_u16(); + dec_u32(); + dec_u64(); + dec_u256(); + dec_edom(); + + put_funsel(); + put_uint(); + put_address(); + put_bytes(); + + get_funsel(); + get_uint(); + get_address(); + get_bytes(); + + return 0; +} diff --git a/linux/libdriver/tests/keccak.c b/linux/libdriver/tests/keccak.c new file mode 100644 index 00000000..79d919cd --- /dev/null +++ b/linux/libdriver/tests/keccak.c @@ -0,0 +1,44 @@ +#include +#include +#include +#include +#include"../base/abi.h" +#include"../base/keccak.h" + +static void inits() +{ + uint8_t md[3][EVM_KECCAK_MDLEN]; + uint8_t data[] = { + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, + }; + + // from DECL + EVM_KECCAK_DECL(c); + evm_keccak_update(c, sizeof(data), data); + evm_keccak_final(c, md[0]); + + // from init + evm_keccak_t t[1]; + evm_keccak_init(t); + evm_keccak_update(t, sizeof(data), data); + evm_keccak_final(t, md[2]); + assert(memcmp(md[0], md[2], EVM_KECCAK_MDLEN) == 0); + + // from data + evm_keccak_data(sizeof(data), data, md[1]); + assert(memcmp(md[0], md[1], EVM_KECCAK_MDLEN) == 0); +} + +static void funsel() +{ + const char s[] = "baz(uint32,bool)"; + assert(evm_keccak_funsel(s) == EVM_ABI_FUNSEL(0xcd, 0xcd, 0x77, 0xc0)); +} + +int main(int argc, char *argv[]) +{ + funsel(); + inits(); + return 0; +} diff --git a/linux/libdriver/tests/merkle.c b/linux/libdriver/tests/merkle.c new file mode 100644 index 00000000..7d381945 --- /dev/null +++ b/linux/libdriver/tests/merkle.c @@ -0,0 +1,30 @@ +#include +#include +#include"../base/merkle.h" + +static void print(int m, uint8_t md[EVM_KECCAK_MDLEN]) +{ + printf("%3d: ", m); + for (int i=0, n=EVM_KECCAK_MDLEN; i +#include +#include"../base/keccak.h" + +int main(int argc, char *argv[]) +{ + size_t n = 65; + if (argc > 1) + sscanf(argv[1], "--n=%zu", &n); + + printf("#ifndef EVM_MERKLE_ZERO_TABLE_H\n"); + printf("#define EVM_MERKLE_ZERO_TABLE_H\n\n"); + printf("static const uint8_t merkle_zero_table[][EVM_KECCAK_MDLEN] = {\n"); + + uint8_t md[EVM_KECCAK_MDLEN] = {0}; + evm_keccak_data(0, NULL, md); + + for (int i=0; i