From e06ad7e1d03781f94a70fa6356f70da58d396c6f Mon Sep 17 00:00:00 2001 From: Xuejie Xiao Date: Thu, 29 Feb 2024 10:47:00 +0800 Subject: [PATCH] Cobuild refactor (#40) * Refactor to leverage molecule-c2 as much as possible --- Makefile | 8 +- c/blake2b.h | 542 ---------------- c/blake2b_decl_only.h | 61 -- c/ckb_identity.h | 8 + c/cobuild.c | 678 ++++++++------------ c/cobuild.h | 8 +- c/cobuild_top_level_mol2.h | 102 +++ c/mol2_utils.h | 353 ++++++++++ c/omni_lock.c | 229 +++---- c/omni_lock_supply.h | 4 +- tests/omni_lock/ckb_syscall_omni_lock_sim.h | 40 ++ tests/omni_lock/omni_lock_sim.c | 5 +- 12 files changed, 877 insertions(+), 1161 deletions(-) delete mode 100644 c/blake2b.h delete mode 100644 c/blake2b_decl_only.h create mode 100644 c/cobuild_top_level_mol2.h create mode 100644 c/mol2_utils.h diff --git a/Makefile b/Makefile index 498c82d..f1e9690 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ build/secp256k1_data_info.h: build/dump_secp256k1_data build/dump_secp256k1_data: c/dump_secp256k1_data.c $(SECP256K1_SRC) mkdir -p build - gcc -I deps/secp256k1/src -I deps/secp256k1 -o $@ $< + gcc -I deps/secp256k1/src -I deps/secp256k1 -I deps/ckb-c-stdlib -o $@ $< $(SECP256K1_SRC): @@ -76,10 +76,10 @@ omni_lock_mol: ${MOLC} --language - --schema-file c/omni_lock.mol --format json > build/omni_lock_mol2.json moleculec-c2 --input build/omni_lock_mol2.json | clang-format -style=Google > c/omni_lock_mol2.h -build/cobuild.o: c/cobuild.c c/cobuild.h +build/cobuild.o: c/cobuild.c c/cobuild.h c/mol2_utils.h $(CC) -c $(OMNI_LOCK_CFLAGS) -o $@ $< -build/omni_lock.o: c/omni_lock.c c/omni_lock_supply.h c/omni_lock_acp.h build/secp256k1_data_info.h $(SECP256K1_SRC) c/ckb_identity.h +build/omni_lock.o: c/omni_lock.c c/omni_lock_supply.h c/omni_lock_acp.h build/secp256k1_data_info.h $(SECP256K1_SRC) c/ckb_identity.h c/mol2_utils.h $(CC) -c $(OMNI_LOCK_CFLAGS) -o $@ $< build/omni_lock: build/omni_lock.o build/cobuild.o @@ -92,6 +92,8 @@ cobuild_mol: ${MOLC} --language rust --schema-file c/top_level.mol | rustfmt > tests/omni_lock_rust/src/schemas/top_level.rs ${MOLC} --language - --schema-file c/basic.mol --format json > build/cobuild_basic_mol2.json moleculec-c2 --input build/cobuild_basic_mol2.json | clang-format -style=Google > c/cobuild_basic_mol2.h + ${MOLC} --language - --schema-file c/top_level.mol --format json > build/cobuild_top_level_mol2.json + moleculec-c2 --input build/cobuild_top_level_mol2.json | clang-format -style=Google > c/cobuild_top_level_mol2.h clean: clean2 rm -rf build/secp256k1_data_info.h build/dump_secp256k1_data diff --git a/c/blake2b.h b/c/blake2b.h deleted file mode 100644 index a0f5810..0000000 --- a/c/blake2b.h +++ /dev/null @@ -1,542 +0,0 @@ -/* - BLAKE2 reference source code package - reference C implementations - - Copyright 2012, Samuel Neves . You may use this under the - terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at - your option. The terms of these licenses can be found at: - - - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 - - OpenSSL license : https://www.openssl.org/source/license.html - - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 - - More information about the BLAKE2 hash function can be found at - https://blake2.net. -*/ - -// blake2.h -#ifndef BLAKE2_H -#define BLAKE2_H - -#include -#include - -#if defined(_MSC_VER) -#define BLAKE2_PACKED(x) __pragma(pack(push, 1)) x __pragma(pack(pop)) -#else -#define BLAKE2_PACKED(x) x __attribute__((packed)) -#endif - -#if defined(__cplusplus) -extern "C" { -#endif - - enum blake2b_constant - { - BLAKE2B_BLOCKBYTES = 128, - BLAKE2B_OUTBYTES = 64, - BLAKE2B_KEYBYTES = 64, - BLAKE2B_SALTBYTES = 16, - BLAKE2B_PERSONALBYTES = 16 - }; - - typedef struct blake2b_state__ - { - uint64_t h[8]; - uint64_t t[2]; - uint64_t f[2]; - uint8_t buf[BLAKE2B_BLOCKBYTES]; - size_t buflen; - size_t outlen; - uint8_t last_node; - } blake2b_state; - - BLAKE2_PACKED(struct blake2b_param__ - { - uint8_t digest_length; /* 1 */ - uint8_t key_length; /* 2 */ - uint8_t fanout; /* 3 */ - uint8_t depth; /* 4 */ - uint32_t leaf_length; /* 8 */ - uint32_t node_offset; /* 12 */ - uint32_t xof_length; /* 16 */ - uint8_t node_depth; /* 17 */ - uint8_t inner_length; /* 18 */ - uint8_t reserved[14]; /* 32 */ - uint8_t salt[BLAKE2B_SALTBYTES]; /* 48 */ - uint8_t personal[BLAKE2B_PERSONALBYTES]; /* 64 */ - }); - - typedef struct blake2b_param__ blake2b_param; - - /* Padded structs result in a compile-time error */ - enum { - BLAKE2_DUMMY_2 = 1/(sizeof(blake2b_param) == BLAKE2B_OUTBYTES) - }; - - /* Streaming API */ - int blake2b_init( blake2b_state *S, size_t outlen ); - int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen ); - int blake2b_init_param( blake2b_state *S, const blake2b_param *P ); - int blake2b_update( blake2b_state *S, const void *in, size_t inlen ); - int blake2b_final( blake2b_state *S, void *out, size_t outlen ); - - /* Simple API */ - int blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); - - /* This is simply an alias for blake2b */ - int blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); - -#if defined(__cplusplus) -} -#endif - -#endif - -//blake2-impl.h -#ifndef BLAKE2_IMPL_H -#define BLAKE2_IMPL_H - -#include -#include - -#if !defined(__cplusplus) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L) - #if defined(_MSC_VER) - #define BLAKE2_INLINE __inline - #elif defined(__GNUC__) - #define BLAKE2_INLINE __inline__ - #else - #define BLAKE2_INLINE - #endif -#else - #define BLAKE2_INLINE inline -#endif - -static BLAKE2_INLINE uint64_t load64( const void *src ) -{ -#if defined(NATIVE_LITTLE_ENDIAN) - uint64_t w; - memcpy(&w, src, sizeof w); - return w; -#else - const uint8_t *p = ( const uint8_t * )src; - return (( uint64_t )( p[0] ) << 0) | - (( uint64_t )( p[1] ) << 8) | - (( uint64_t )( p[2] ) << 16) | - (( uint64_t )( p[3] ) << 24) | - (( uint64_t )( p[4] ) << 32) | - (( uint64_t )( p[5] ) << 40) | - (( uint64_t )( p[6] ) << 48) | - (( uint64_t )( p[7] ) << 56) ; -#endif -} - -static BLAKE2_INLINE void store32( void *dst, uint32_t w ) -{ -#if defined(NATIVE_LITTLE_ENDIAN) - memcpy(dst, &w, sizeof w); -#else - uint8_t *p = ( uint8_t * )dst; - p[0] = (uint8_t)(w >> 0); - p[1] = (uint8_t)(w >> 8); - p[2] = (uint8_t)(w >> 16); - p[3] = (uint8_t)(w >> 24); -#endif -} - -static BLAKE2_INLINE void store64( void *dst, uint64_t w ) -{ -#if defined(NATIVE_LITTLE_ENDIAN) - memcpy(dst, &w, sizeof w); -#else - uint8_t *p = ( uint8_t * )dst; - p[0] = (uint8_t)(w >> 0); - p[1] = (uint8_t)(w >> 8); - p[2] = (uint8_t)(w >> 16); - p[3] = (uint8_t)(w >> 24); - p[4] = (uint8_t)(w >> 32); - p[5] = (uint8_t)(w >> 40); - p[6] = (uint8_t)(w >> 48); - p[7] = (uint8_t)(w >> 56); -#endif -} - -static BLAKE2_INLINE uint64_t rotr64( const uint64_t w, const unsigned c ) -{ - return ( w >> c ) | ( w << ( 64 - c ) ); -} - -/* prevents compiler optimizing out memset() */ -static BLAKE2_INLINE void secure_zero_memory(void *v, size_t n) -{ - static void *(*const volatile memset_v)(void *, int, size_t) = &memset; - memset_v(v, 0, n); -} - -#endif - -// blake2b-ref.c -#include -#include -#include - -static const uint64_t blake2b_IV[8] = -{ - 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, - 0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL, - 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL, - 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL -}; - -static const uint8_t blake2b_sigma[12][16] = -{ - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , - { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } , - { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } , - { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } , - { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } , - { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } , - { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } , - { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } , - { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } , - { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } , - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , - { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } -}; - - -static void blake2b_set_lastnode( blake2b_state *S ) -{ - S->f[1] = (uint64_t)-1; -} - -/* Some helper functions, not necessarily useful */ -static int blake2b_is_lastblock( const blake2b_state *S ) -{ - return S->f[0] != 0; -} - -static void blake2b_set_lastblock( blake2b_state *S ) -{ - if( S->last_node ) blake2b_set_lastnode( S ); - - S->f[0] = (uint64_t)-1; -} - -static void blake2b_increment_counter( blake2b_state *S, const uint64_t inc ) -{ - S->t[0] += inc; - S->t[1] += ( S->t[0] < inc ); -} - -static void blake2b_init0( blake2b_state *S ) -{ - size_t i; - memset( S, 0, sizeof( blake2b_state ) ); - - for( i = 0; i < 8; ++i ) S->h[i] = blake2b_IV[i]; -} - -/* init xors IV with input parameter block */ -int blake2b_init_param( blake2b_state *S, const blake2b_param *P ) -{ - const uint8_t *p = ( const uint8_t * )( P ); - size_t i; - - blake2b_init0( S ); - - /* IV XOR ParamBlock */ - for( i = 0; i < 8; ++i ) - S->h[i] ^= load64( p + sizeof( S->h[i] ) * i ); - - S->outlen = P->digest_length; - return 0; -} - - -const char *DEFAULT_PERSONAL = "ckb-default-hash"; -int blake2b_init( blake2b_state *S, size_t outlen ) -{ - blake2b_param P[1]; - - if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1; - - P->digest_length = (uint8_t)outlen; - P->key_length = 0; - P->fanout = 1; - P->depth = 1; - store32( &P->leaf_length, 0 ); - store32( &P->node_offset, 0 ); - store32( &P->xof_length, 0 ); - P->node_depth = 0; - P->inner_length = 0; - memset( P->reserved, 0, sizeof( P->reserved ) ); - memset( P->salt, 0, sizeof( P->salt ) ); - memset( P->personal, 0, sizeof( P->personal ) ); - for (int i = 0; i < BLAKE2B_PERSONALBYTES; ++i) { - (P->personal)[i] = DEFAULT_PERSONAL[i]; - } - return blake2b_init_param( S, P ); -} - - -int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen ) -{ - blake2b_param P[1]; - - if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1; - - if ( !key || !keylen || keylen > BLAKE2B_KEYBYTES ) return -1; - - P->digest_length = (uint8_t)outlen; - P->key_length = (uint8_t)keylen; - P->fanout = 1; - P->depth = 1; - store32( &P->leaf_length, 0 ); - store32( &P->node_offset, 0 ); - store32( &P->xof_length, 0 ); - P->node_depth = 0; - P->inner_length = 0; - memset( P->reserved, 0, sizeof( P->reserved ) ); - memset( P->salt, 0, sizeof( P->salt ) ); - memset( P->personal, 0, sizeof( P->personal ) ); - - if( blake2b_init_param( S, P ) < 0 ) return -1; - - { - uint8_t block[BLAKE2B_BLOCKBYTES]; - memset( block, 0, BLAKE2B_BLOCKBYTES ); - memcpy( block, key, keylen ); - blake2b_update( S, block, BLAKE2B_BLOCKBYTES ); - secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */ - } - return 0; -} - -#define G(r,i,a,b,c,d) \ - do { \ - a = a + b + m[blake2b_sigma[r][2*i+0]]; \ - d = rotr64(d ^ a, 32); \ - c = c + d; \ - b = rotr64(b ^ c, 24); \ - a = a + b + m[blake2b_sigma[r][2*i+1]]; \ - d = rotr64(d ^ a, 16); \ - c = c + d; \ - b = rotr64(b ^ c, 63); \ - } while(0) - -#define ROUND(r) \ - do { \ - G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \ - G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \ - G(r,2,v[ 2],v[ 6],v[10],v[14]); \ - G(r,3,v[ 3],v[ 7],v[11],v[15]); \ - G(r,4,v[ 0],v[ 5],v[10],v[15]); \ - G(r,5,v[ 1],v[ 6],v[11],v[12]); \ - G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \ - G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \ - } while(0) - -static void blake2b_compress( blake2b_state *S, const uint8_t block[BLAKE2B_BLOCKBYTES] ) -{ - uint64_t m[16]; - uint64_t v[16]; - size_t i; - - for( i = 0; i < 16; ++i ) { - m[i] = load64( block + i * sizeof( m[i] ) ); - } - - for( i = 0; i < 8; ++i ) { - v[i] = S->h[i]; - } - - v[ 8] = blake2b_IV[0]; - v[ 9] = blake2b_IV[1]; - v[10] = blake2b_IV[2]; - v[11] = blake2b_IV[3]; - v[12] = blake2b_IV[4] ^ S->t[0]; - v[13] = blake2b_IV[5] ^ S->t[1]; - v[14] = blake2b_IV[6] ^ S->f[0]; - v[15] = blake2b_IV[7] ^ S->f[1]; - - ROUND( 0 ); - ROUND( 1 ); - ROUND( 2 ); - ROUND( 3 ); - ROUND( 4 ); - ROUND( 5 ); - ROUND( 6 ); - ROUND( 7 ); - ROUND( 8 ); - ROUND( 9 ); - ROUND( 10 ); - ROUND( 11 ); - - for( i = 0; i < 8; ++i ) { - S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; - } -} - -#undef G -#undef ROUND - -int blake2b_update( blake2b_state *S, const void *pin, size_t inlen ) -{ - const unsigned char * in = (const unsigned char *)pin; - if( inlen > 0 ) - { - size_t left = S->buflen; - size_t fill = BLAKE2B_BLOCKBYTES - left; - if( inlen > fill ) - { - S->buflen = 0; - memcpy( S->buf + left, in, fill ); /* Fill buffer */ - blake2b_increment_counter( S, BLAKE2B_BLOCKBYTES ); - blake2b_compress( S, S->buf ); /* Compress */ - in += fill; inlen -= fill; - while(inlen > BLAKE2B_BLOCKBYTES) { - blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES); - blake2b_compress( S, in ); - in += BLAKE2B_BLOCKBYTES; - inlen -= BLAKE2B_BLOCKBYTES; - } - } - memcpy( S->buf + S->buflen, in, inlen ); - S->buflen += inlen; - } - return 0; -} - -int blake2b_final( blake2b_state *S, void *out, size_t outlen ) -{ - uint8_t buffer[BLAKE2B_OUTBYTES] = {0}; - size_t i; - - if( out == NULL || outlen < S->outlen ) - return -1; - - if( blake2b_is_lastblock( S ) ) - return -1; - - blake2b_increment_counter( S, S->buflen ); - blake2b_set_lastblock( S ); - memset( S->buf + S->buflen, 0, BLAKE2B_BLOCKBYTES - S->buflen ); /* Padding */ - blake2b_compress( S, S->buf ); - - for( i = 0; i < 8; ++i ) /* Output full hash to temp buffer */ - store64( buffer + sizeof( S->h[i] ) * i, S->h[i] ); - - memcpy( out, buffer, S->outlen ); - secure_zero_memory(buffer, sizeof(buffer)); - return 0; -} - -/* inlen, at least, should be uint64_t. Others can be size_t. */ -int blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) -{ - blake2b_state S[1]; - - /* Verify parameters */ - if ( NULL == in && inlen > 0 ) return -1; - - if ( NULL == out ) return -1; - - if( NULL == key && keylen > 0 ) return -1; - - if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1; - - if( keylen > BLAKE2B_KEYBYTES ) return -1; - - if( keylen > 0 ) - { - if( blake2b_init_key( S, outlen, key, keylen ) < 0 ) return -1; - } - else - { - if( blake2b_init( S, outlen ) < 0 ) return -1; - } - - blake2b_update( S, ( const uint8_t * )in, inlen ); - blake2b_final( S, out, outlen ); - return 0; -} - -int blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) { - return blake2b(out, outlen, in, inlen, key, keylen); -} - -#if defined(SUPERCOP) -int crypto_hash( unsigned char *out, unsigned char *in, unsigned long long inlen ) -{ - return blake2b( out, BLAKE2B_OUTBYTES, in, inlen, NULL, 0 ); -} -#endif - -#if defined(BLAKE2B_SELFTEST) -#include -#include "blake2-kat.h" -int main( void ) -{ - uint8_t key[BLAKE2B_KEYBYTES]; - uint8_t buf[BLAKE2_KAT_LENGTH]; - size_t i, step; - - for( i = 0; i < BLAKE2B_KEYBYTES; ++i ) - key[i] = ( uint8_t )i; - - for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) - buf[i] = ( uint8_t )i; - - /* Test simple API */ - for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) - { - uint8_t hash[BLAKE2B_OUTBYTES]; - blake2b( hash, BLAKE2B_OUTBYTES, buf, i, key, BLAKE2B_KEYBYTES ); - - if( 0 != memcmp( hash, blake2b_keyed_kat[i], BLAKE2B_OUTBYTES ) ) - { - goto fail; - } - } - - /* Test streaming API */ - for(step = 1; step < BLAKE2B_BLOCKBYTES; ++step) { - for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) { - uint8_t hash[BLAKE2B_OUTBYTES]; - blake2b_state S; - uint8_t * p = buf; - size_t mlen = i; - int err = 0; - - if( (err = blake2b_init_key(&S, BLAKE2B_OUTBYTES, key, BLAKE2B_KEYBYTES)) < 0 ) { - goto fail; - } - - while (mlen >= step) { - if ( (err = blake2b_update(&S, p, step)) < 0 ) { - goto fail; - } - mlen -= step; - p += step; - } - if ( (err = blake2b_update(&S, p, mlen)) < 0) { - goto fail; - } - if ( (err = blake2b_final(&S, hash, BLAKE2B_OUTBYTES)) < 0) { - goto fail; - } - - if (0 != memcmp(hash, blake2b_keyed_kat[i], BLAKE2B_OUTBYTES)) { - goto fail; - } - } - } - - puts( "ok" ); - return 0; -fail: - puts("error"); - return -1; -} -#endif - diff --git a/c/blake2b_decl_only.h b/c/blake2b_decl_only.h deleted file mode 100644 index eb069e3..0000000 --- a/c/blake2b_decl_only.h +++ /dev/null @@ -1,61 +0,0 @@ - -#ifndef __BLAKE2B_DECL_ONLY_H__ -#define __BLAKE2B_DECL_ONLY_H__ - -#include -#include - -#define BLAKE2_PACKED(x) x __attribute__((packed)) - -enum blake2b_constant { - BLAKE2B_BLOCKBYTES = 128, - BLAKE2B_OUTBYTES = 64, - BLAKE2B_KEYBYTES = 64, - BLAKE2B_SALTBYTES = 16, - BLAKE2B_PERSONALBYTES = 16 -}; -BLAKE2_PACKED(struct blake2b_param__ { - uint8_t digest_length; /* 1 */ - uint8_t key_length; /* 2 */ - uint8_t fanout; /* 3 */ - uint8_t depth; /* 4 */ - uint32_t leaf_length; /* 8 */ - uint32_t node_offset; /* 12 */ - uint32_t xof_length; /* 16 */ - uint8_t node_depth; /* 17 */ - uint8_t inner_length; /* 18 */ - uint8_t reserved[14]; /* 32 */ - uint8_t salt[BLAKE2B_SALTBYTES]; /* 48 */ - uint8_t personal[BLAKE2B_PERSONALBYTES]; /* 64 */ -}); - -typedef struct blake2b_param__ blake2b_param; - -typedef struct blake2b_state__ { - uint64_t h[8]; - uint64_t t[2]; - uint64_t f[2]; - uint8_t buf[BLAKE2B_BLOCKBYTES]; - size_t buflen; - size_t outlen; - uint8_t last_node; -} blake2b_state; - -/* Streaming API */ -int ckb_blake2b_init(blake2b_state *S, size_t outlen); -int blake2b_init(blake2b_state *S, size_t outlen); -int blake2b_init_key(blake2b_state *S, size_t outlen, const void *key, - size_t keylen); -int blake2b_update(blake2b_state *S, const void *in, size_t inlen); -int blake2b_final(blake2b_state *S, void *out, size_t outlen); -/* Simple API */ -int blake2b(void *out, size_t outlen, const void *in, size_t inlen, - const void *key, size_t keylen); - -/* This is simply an alias for blake2b */ -int blake2(void *out, size_t outlen, const void *in, size_t inlen, - const void *key, size_t keylen); - -int blake2b_init_param(blake2b_state *S, const blake2b_param *P); - -#endif diff --git a/c/ckb_identity.h b/c/ckb_identity.h index d017f81..a6aa50a 100644 --- a/c/ckb_identity.h +++ b/c/ckb_identity.h @@ -1,8 +1,15 @@ #ifndef CKB_C_STDLIB_CKB_IDENTITY_H_ #define CKB_C_STDLIB_CKB_IDENTITY_H_ + +#define BLAKE2_IMPL_H +#define BLAKE2_REF_C #include +#undef BLAKE2_REF_C +#undef BLAKE2_IMPL_H + #include +#include "blockchain.h" #include "ckb_consts.h" #include "ckb_keccak256.h" #include "ripemd160.h" @@ -383,6 +390,7 @@ int generate_sighash_all(uint8_t *msg, size_t msg_len) { return ERROR_IDENTITY_ARGUMENTS_LEN; } + // TODO: migrate this to molecule-c2 so we don't need MAX_WITNESS_SIZE /* Load witness of first input */ ret = ckb_load_witness(temp, &read_len, 0, 0, CKB_SOURCE_GROUP_INPUT); if (ret != CKB_SUCCESS) { diff --git a/c/cobuild.c b/c/cobuild.c index a497bae..32effc1 100644 --- a/c/cobuild.c +++ b/c/cobuild.c @@ -16,9 +16,15 @@ int ckb_exit(signed char); #include "molecule2_reader.h" #include "blockchain-api2.h" #include "cobuild_basic_mol2.h" +#include "cobuild_top_level_mol2.h" #include "cobuild.h" -#include "blake2b_decl_only.h" +#define BLAKE2_IMPL_H +#define BLAKE2_REF_C +#include "blake2b.h" +#undef BLAKE2_REF_C +#undef BLAKE2_IMPL_H + #include "ckb_consts.h" #include "ckb_syscall_apis.h" // clang-format on @@ -76,6 +82,8 @@ enum CobuildErrorCode { ERROR_WRONG_OTX, ERROR_NOT_COBUILD, ERROR_NO_CALLBACK, + ERROR_MOL2_UNEXPECTED, + ERROR_OVERFLOW, }; typedef enum WitnessLayoutId { @@ -107,12 +115,6 @@ const char *PERSONAL_SIGHASH_ALL = "ckb-tcob-sighash"; const char *PERSONAL_SIGHASH_ALL_ONLY = "ckb-tcob-sgohash"; const char *PERSONAL_OTX = "ckb-tcob-otxhash"; -/* - The seal cursor uses this data source. So the lifetime of data source should - be long enough. - */ -static uint8_t g_cobuild_seal_data_source[DEFAULT_DATA_SOURCE_LENGTH]; - #ifdef CKB_C_STDLIB_PRINTF static void bin_to_hex(const uint8_t *source, uint8_t *dest, size_t len) { @@ -206,6 +208,31 @@ int new_otx_blake2b(blake2b_state *S) { return ckb_blake2b_init_personal(S, 32, PERSONAL_OTX); } +static inline int get_witness_layout(BytesVecType witnesses, uint32_t index, + WitnessLayoutType *witness_layout) { + bool existing = false; + mol2_cursor_t witness = witnesses.t->get(&witnesses, index, &existing); + if (!existing) { + return ERROR_MOL2_UNEXPECTED; + } + + uint32_t id = 0; + int err = try_union_unpack_id(&witness, &id); + if (err != 0) { + return err; + } + // TODO: validate full WitnessLayout structure + if (id == WitnessLayoutSighashAll || id == WitnessLayoutSighashAllOnly || + id == WitnessLayoutOtx || id == WitnessLayoutOtxStart) { + if (witness_layout != NULL) { + *witness_layout = make_WitnessLayout(&witness); + } + return 0; + } else { + return ERROR_GENERAL; + } +} + // for lock script with message, the other witness in script group except first // one should be empty int ckb_check_others_in_group() { @@ -222,274 +249,102 @@ int ckb_check_others_in_group() { return err; } -typedef uint32_t(read_from_t)(uintptr_t arg[], uint8_t *ptr, uint32_t len, - uint32_t offset); - -static uint32_t read_from_witness(uintptr_t arg[], uint8_t *ptr, uint32_t len, - uint32_t offset) { - int err = ERROR_GENERAL; - uint64_t output_len = len; - err = ckb_load_witness(ptr, &output_len, offset, arg[0], arg[1]); - if (err != 0) { - return 0; - } - if (output_len > len) { - return len; - } else { - return (uint32_t)output_len; - } -} - -static uint32_t read_from_cell_data(uintptr_t arg[], uint8_t *ptr, uint32_t len, - uint32_t offset) { - int err; - uint64_t output_len = len; - err = ckb_load_cell_data(ptr, &output_len, offset, arg[0], arg[1]); - if (err != 0) { - return 0; - } - if (output_len > len) { - return len; - } else { - return (uint32_t)output_len; - } -} - -static uint32_t read_from_cell(uintptr_t arg[], uint8_t *ptr, uint32_t len, - uint32_t offset) { - int err; - uint64_t output_len = len; - err = ckb_load_cell(ptr, &output_len, offset, arg[0], arg[1]); - if (err != 0) { - return 0; - } - if (output_len > len) { - return len; - } else { - return (uint32_t)output_len; - } -} - -static uint32_t read_from_tx(uintptr_t arg[], uint8_t *ptr, uint32_t len, - uint32_t offset) { - int err; - uint64_t output_len = len; - err = ckb_load_transaction(ptr, &output_len, offset); - if (err != 0) { - return 0; - } - if (output_len > len) { - return len; - } else { - return (uint32_t)output_len; - } -} - -void ckb_new_cursor(mol2_cursor_t *cursor, uint32_t total_len, - read_from_t read_from, uint8_t *data_source, - uint32_t cache_len, size_t index, size_t source) { - cursor->offset = 0; - cursor->size = (uint32_t)total_len; - - mol2_data_source_t *ptr = (mol2_data_source_t *)data_source; - - ptr->read = read_from; - ptr->total_size = total_len; - ptr->args[0] = index; - ptr->args[1] = source; - - ptr->cache_size = 0; - ptr->start_point = 0; - ptr->max_cache_size = cache_len; - - cursor->data_source = ptr; -} - -int ckb_new_witness_cursor(mol2_cursor_t *cursor, uint8_t *data_source, - uint32_t cache_len, size_t index, size_t source) { - int err = ERROR_GENERAL; - uint64_t len = 0; - err = ckb_load_witness(0, &len, 0, index, source); - CHECK(err); - ckb_new_cursor(cursor, len, read_from_witness, data_source, cache_len, index, - source); - -exit: - return err; -} - -int ckb_hash_cursor(blake2b_state *ctx, mol2_cursor_t cursor) { - // one batch to drain whole cache perfectly - // tested by test_input_cell_data_size_0 - // test_input_cell_data_size_1 - // test_input_cell_data_size_2048 - // test_input_cell_data_size_2049 - // test_input_cell_data_size_500k - uint8_t batch[MAX_CACHE_SIZE]; - while (true) { - uint32_t read_len = mol2_read_at(&cursor, batch, sizeof(batch)); - BLAKE2B_UPDATE(ctx, batch, read_len); - // adjust cursor - mol2_add_offset(&cursor, read_len); - mol2_sub_size(&cursor, read_len); - mol2_validate(&cursor); - if (cursor.size == 0) { - break; - } - } - return 0; -} - -static uint32_t try_union_unpack_id(const mol2_cursor_t *cursor, uint32_t *id) { - uint32_t len = mol2_read_at(cursor, (uint8_t *)id, 4); - if (len != 4) { - // tested by: - // tested_by_no_cobuild_append_sighash_all - // tested_by_insert_witness_less_4_before_sighashall - return MOL2_ERR_DATA; - } - return CKB_SUCCESS; -} - -int ckb_fetch_message(bool *has_message, mol2_cursor_t *message_cursor, - uint8_t *data_source, size_t cache_len) { - int err = ERROR_GENERAL; - *has_message = false; - for (size_t index = 0;; index++) { - uint32_t id = 0; - uint64_t len = sizeof(id); - err = ckb_load_witness(&id, &len, 0, index, CKB_SOURCE_INPUT); - CHECK_LOOP(err); - - if (len >= sizeof(id) && id == WitnessLayoutSighashAll) { - // tested by: - // tested_by_sighashall_dup - CHECK2(!*has_message, ERROR_SIGHASHALL_DUP); - *has_message = true; - mol2_cursor_t cursor = {0}; - err = ckb_new_witness_cursor(&cursor, data_source, cache_len, index, - CKB_SOURCE_INPUT); - CHECK(err); - mol2_union_t uni = mol2_union_unpack(&cursor); - /* See molecule defintion, the index is 0: - table SighashAll { - message: Message, - seal: Bytes, +int ckb_fetch_sighash_message(BytesVecType witnesses, MessageType *message) { + int err = 0; + bool has_message = false; + uint32_t witness_len = witnesses.t->len(&witnesses); + for (uint32_t index = 0; index < witness_len; index++) { + WitnessLayoutType witness_layout = {0}; + if (get_witness_layout(witnesses, index, &witness_layout) == 0) { + uint32_t id = witness_layout.t->item_id(&witness_layout); + if (id == WitnessLayoutSighashAll) { + // tested by: + // tested_by_sighashall_dup + CHECK2(!has_message, ERROR_SIGHASHALL_DUP); + SighashAllType s = witness_layout.t->as_SighashAll(&witness_layout); + *message = s.t->message(&s); + has_message = true; } - */ - *message_cursor = mol2_table_slice_by_index(&uni.cursor, 0); - } else { - // there are some possibilities: - // 1. an invalid witness (e.g. empty) - // 2. WitnessArgs - // 3. Other cobuild WitnessLayout(e.g. SighashAllOnly) - // tested by: - // tested_by_append_witnessed_less_than_4 - // tested_by_append_witnessargs - // tested_by_append_other_witnesslayout } + // there are some possibilities: + // 1. an invalid witness (e.g. empty) + // 2. WitnessArgs + // 3. Other cobuild WitnessLayout(e.g. SighashAllOnly) + // tested by: + // tested_by_append_witnessed_less_than_4 + // tested_by_append_witnessargs + // tested_by_append_other_witnesslayout } exit: return err; } -int ckb_fetch_seal(mol2_cursor_t *seal_cursor) { - int err = ERROR_GENERAL; - mol2_cursor_t cursor; - err = ckb_new_witness_cursor(&cursor, g_cobuild_seal_data_source, - MAX_CACHE_SIZE, 0, CKB_SOURCE_GROUP_INPUT); - CHECK(err); - uint32_t id = 0; - err = try_union_unpack_id(&cursor, &id); - // when error occurs here, it might be a WitnessArgs layout. It shouldn't be - // cobuild and returns early. - CHECK(err); - if (id == WitnessLayoutSighashAll) { - mol2_union_t uni = mol2_union_unpack(&cursor); - /* See molecule defintion, the index is 1: - table SighashAll { - message: Message, - seal: Bytes, - } - */ - *seal_cursor = mol2_table_slice_by_index(&uni.cursor, 1); - } else if (id == WitnessLayoutSighashAllOnly) { - /* See molecule defintion, the index is 0: - table SighashAllOnly { - seal: Bytes, - } - tested by test_sighash_all_only - */ - mol2_union_t uni = mol2_union_unpack(&cursor); - *seal_cursor = mol2_table_slice_by_index(&uni.cursor, 0); - } else { - // the union id should be SighashAll or SighashAllOnly. otherwise, it fails - // and mark it as non cobuild. - // tested by test_wrong_union_id - printf("error in fetch_seal, id = %u", id); - CHECK2(false, ERROR_SIGHASHALL_NOSEAL); - } - *seal_cursor = convert_to_rawbytes(seal_cursor); -exit: - return err; -} - // step 2 -int ckb_fetch_otx_start(bool *has_otx, size_t *i, OtxStart *otx_start) { - uint8_t data_source[DEFAULT_DATA_SOURCE_LENGTH]; +static inline int ckb_fetch_otx_start(BytesVecType witnesses, bool *has_otx, + size_t *i, OtxStart *otx_start) { int err = ERROR_GENERAL; *has_otx = false; - for (size_t index = 0;; index++) { - uint32_t id = 0; - uint64_t len = sizeof(id); - err = ckb_load_witness(&id, &len, 0, index, CKB_SOURCE_INPUT); - CHECK_LOOP(err); - - if (len >= sizeof(id) && id == WitnessLayoutOtxStart) { - // step 4 - // test_cobuild_otx_double_otx_start - CHECK2(!*has_otx, ERROR_OTX_START_DUP); - *has_otx = true; - *i = index; - mol2_cursor_t cursor = {0}; - err = ckb_new_witness_cursor(&cursor, data_source, MAX_CACHE_SIZE, index, - CKB_SOURCE_INPUT); - CHECK(err); - mol2_union_t uni = mol2_union_unpack(&cursor); - OtxStartType start = make_OtxStart(&uni.cursor); - otx_start->start_input_cell = start.t->start_input_cell(&start); - otx_start->start_output_cell = start.t->start_output_cell(&start); - otx_start->start_cell_deps = start.t->start_cell_deps(&start); - otx_start->start_header_deps = start.t->start_header_deps(&start); + uint32_t witness_len = witnesses.t->len(&witnesses); + for (uint32_t index = 0; index < witness_len; index++) { + WitnessLayoutType witness_layout = {0}; + err = get_witness_layout(witnesses, index, &witness_layout); + if (err == 0) { + uint32_t id = witness_layout.t->item_id(&witness_layout); + if (id == WitnessLayoutOtxStart) { + // step 4 + // test_cobuild_otx_double_otx_start + CHECK2(!*has_otx, ERROR_OTX_START_DUP); + *has_otx = true; + *i = index; + + OtxStartType start = witness_layout.t->as_OtxStart(&witness_layout); + otx_start->start_input_cell = start.t->start_input_cell(&start); + otx_start->start_output_cell = start.t->start_output_cell(&start); + otx_start->start_cell_deps = start.t->start_cell_deps(&start); + otx_start->start_header_deps = start.t->start_header_deps(&start); + } } } + if (has_otx) { + err = 0; + } exit: return err; } -// hash cell, including CellOutput and cell data -static int hash_cell(blake2b_state *ctx, size_t index, size_t source, - size_t *count) { +// hash input cell, including CellOutput and cell data +static int hash_input_cell(blake2b_state *ctx, size_t index, size_t *count) { // this data source is on stack. When this function returns, all cursors bound // to this buffer become invalid. uint8_t data_source[DEFAULT_DATA_SOURCE_LENGTH]; int err = 0; // CellOutput - uint64_t cell_len = 0; - err = ckb_load_cell(0, &cell_len, 0, index, source); + uint64_t cell_len = MAX_CACHE_SIZE; + err = ckb_load_cell(MOL2_CACHE_PTR(data_source), &cell_len, 0, index, + CKB_SOURCE_INPUT); CHECK(err); mol2_cursor_t cell_cursor = {0}; - ckb_new_cursor(&cell_cursor, cell_len, read_from_cell, data_source, - MAX_CACHE_SIZE, index, source); + uint32_t cache_size = (uint32_t)cell_len; + if (cache_size > MAX_CACHE_SIZE) { + cache_size = MAX_CACHE_SIZE; + } + ckb_new_cursor_with_data(&cell_cursor, cell_len, read_from_cell, data_source, + MAX_CACHE_SIZE, index, CKB_SOURCE_INPUT, cache_size); ckb_hash_cursor(ctx, cell_cursor); (*count) += cell_len; // Cell data - uint64_t cell_data_len = 0; - err = ckb_load_cell_data(0, &cell_data_len, 0, index, source); + uint64_t cell_data_len = MAX_CACHE_SIZE; + err = ckb_load_cell_data(MOL2_CACHE_PTR(data_source), &cell_data_len, 0, + index, CKB_SOURCE_INPUT); CHECK(err); - mol2_cursor_t cell_data_cursor; - ckb_new_cursor(&cell_data_cursor, cell_data_len, read_from_cell_data, - data_source, MAX_CACHE_SIZE, index, source); + mol2_cursor_t cell_data_cursor = {0}; + cache_size = (uint32_t)cell_data_len; + if (cache_size > MAX_CACHE_SIZE) { + cache_size = MAX_CACHE_SIZE; + } + ckb_new_cursor_with_data(&cell_data_cursor, cell_data_len, + read_from_cell_data, data_source, MAX_CACHE_SIZE, + index, CKB_SOURCE_INPUT, cache_size); // only hash as uint32_t. 4 bytes is enough BLAKE2B_UPDATE(ctx, &cell_data_len, 4); (*count) += 4; @@ -501,39 +356,10 @@ static int hash_cell(blake2b_state *ctx, size_t index, size_t source, return err; } -// there is no syscall to fetch cell dep directly. Get it from scratch based on -// transaction data structure. -static int hash_cell_deps(blake2b_state *ctx, size_t *count, size_t start, - size_t size) { - int err = 0; - uint8_t data_source[DEFAULT_DATA_SOURCE_LENGTH]; - - uint64_t tx_len = 0; - err = ckb_load_transaction(0, &tx_len, 0); - CHECK(err); - - mol2_cursor_t cur = {0}; - ckb_new_cursor(&cur, tx_len, read_from_tx, data_source, MAX_CACHE_SIZE, 0, 0); - TransactionType tx = make_Transaction(&cur); - RawTransactionType raw = tx.t->raw(&tx); - CellDepVecType cell_deps = raw.t->cell_deps(&raw); - for (size_t index = start; index < (start + size); index++) { - bool existing = false; - CellDepType cell_dep = cell_deps.t->get(&cell_deps, index, &existing); - CHECK2(existing, ERROR_GENERAL); - ckb_hash_cursor(ctx, cell_dep.cur); - (*count) += cell_dep.cur.size; - } -exit: - return err; -} - -int ckb_generate_smh(bool has_message, mol2_cursor_t message_cursor, +int ckb_generate_smh(const Env *env, mol2_cursor_t message_cursor, uint8_t *smh) { + bool has_message = message_cursor.size > 0; int err = 0; - // this data source is on stack. When this function returns, all cursors bound - // to this buffer become invalid. - uint8_t data_source[DEFAULT_DATA_SOURCE_LENGTH]; blake2b_state ctx; size_t count = 0; @@ -550,29 +376,28 @@ int ckb_generate_smh(bool has_message, mol2_cursor_t message_cursor, } // hash tx hash - uint8_t tx_hash[BLAKE2B_BLOCK_SIZE]; - uint64_t tx_hash_len = sizeof(tx_hash); - err = ckb_load_tx_hash(tx_hash, &tx_hash_len, 0); - CHECK(err); - BLAKE2B_UPDATE(&ctx, tx_hash, sizeof(tx_hash)); - count += 32; + BLAKE2B_UPDATE(&ctx, env->tx_hash, sizeof(env->tx_hash)); + count += sizeof(env->tx_hash); + + TransactionType tx = env->tx; + RawTransactionType raw = tx.t->raw(&tx); + CellInputVecType inputs = raw.t->inputs(&raw); + uint32_t input_len = inputs.t->len(&inputs); + BytesVecType witnesses = tx.t->witnesses(&tx); + uint32_t witness_len = witnesses.t->len(&witnesses); // hash input cell and data - size_t index = 0; - for (;; index++) { - err = hash_cell(&ctx, index, CKB_SOURCE_INPUT, &count); - CHECK_LOOP(err); + for (uint32_t index = 0; index < input_len; index++) { + err = hash_input_cell(&ctx, index, &count); + CHECK(err); } - size_t input_len = index; // hash remaining witnesses - for (size_t index = input_len;; index++) { - uint64_t witness_len = 0; - err = ckb_load_witness(0, &witness_len, 0, index, CKB_SOURCE_INPUT); - CHECK_LOOP(err); - mol2_cursor_t witness_cursor; - ckb_new_cursor(&witness_cursor, witness_len, read_from_witness, data_source, - MAX_CACHE_SIZE, index, CKB_SOURCE_INPUT); - // only hash as uint32_t. 4 bytes is enough + for (uint32_t index = input_len; index < witness_len; index++) { + bool existing = false; + mol2_cursor_t witness_cursor = + witnesses.t->get(&witnesses, index, &existing); + CHECK2(existing, ERROR_MOL2_UNEXPECTED); + uint32_t witness_len = witness_cursor.size; BLAKE2B_UPDATE(&ctx, &witness_len, 4); count += 4; err = ckb_hash_cursor(&ctx, witness_cursor); @@ -623,22 +448,28 @@ static int collect_type_script_hash(uint8_t *type_script_hash, // (including input/output) matches the action.script_hash. Let A be the set of // action.script_hash, and B be the set of all input/output script hashes; A ∈ B // should be satisfied. -static int check_type_script_existing(mol2_cursor_t message) { +static int check_type_script_existing(MessageType msg) { int err = 0; // cache all type script hashes in input/output cells - uint8_t type_script_hash[BLAKE2B_BLOCK_SIZE * MAX_TYPESCRIPT_COUNT] = {0}; - uint32_t type_script_hash_count = 0; + static uint8_t type_script_hash[BLAKE2B_BLOCK_SIZE * MAX_TYPESCRIPT_COUNT] = { + 0}; + static uint32_t type_script_hash_count = 0; + static int type_script_hash_initialized = 0; - err = collect_type_script_hash(type_script_hash, &type_script_hash_count, - CKB_SOURCE_INPUT); - CHECK(err); - err = collect_type_script_hash(type_script_hash, &type_script_hash_count, - CKB_SOURCE_OUTPUT); - CHECK(err); - // sort for fast searching - qsort(type_script_hash, type_script_hash_count, BLAKE2B_BLOCK_SIZE, hash_cmp); + if (type_script_hash_initialized == 0) { + err = collect_type_script_hash(type_script_hash, &type_script_hash_count, + CKB_SOURCE_INPUT); + CHECK(err); + err = collect_type_script_hash(type_script_hash, &type_script_hash_count, + CKB_SOURCE_OUTPUT); + CHECK(err); + // sort for fast searching + qsort(type_script_hash, type_script_hash_count, BLAKE2B_BLOCK_SIZE, + hash_cmp); + + type_script_hash_initialized = 1; + } - MessageType msg = make_Message(&message); ActionVecType actions = msg.t->actions(&msg); uint32_t len = actions.t->len(&actions); for (uint32_t i = 0; i < len; i++) { @@ -670,39 +501,65 @@ static int parse_seal(const mol2_cursor_t original_seal, mol2_cursor_t *seal, uint32_t len = mol2_read_at(&original_seal, prefix, prefix_length); CHECK2(len == prefix_length, ERROR_SEAL); *message_calculation_flow = prefix[0]; - *seal = original_seal; - mol2_add_offset(seal, prefix_length); - mol2_sub_size(seal, prefix_length); - mol2_validate(seal); + *seal = mol2_cursor_slice_start(&original_seal, prefix_length); exit: return err; } -int ckb_cobuild_normal_entry(ScriptEntryType callback) { +int ckb_cobuild_normal_entry(const Env *env, ScriptEntryType callback) { + TransactionType tx = env->tx; + BytesVecType witnesses = tx.t->witnesses(&tx); + int err = ERROR_GENERAL; uint8_t smh[BLAKE2B_BLOCK_SIZE]; mol2_cursor_t seal = {0}; - bool has_message = false; - mol2_cursor_t message; - // the message cursor requires longer lifetime of data_source - uint8_t data_source[DEFAULT_DATA_SOURCE_LENGTH]; + MessageType message = {0}; // step 8.a, 8.b - err = ckb_fetch_message(&has_message, &message, data_source, MAX_CACHE_SIZE); + err = ckb_fetch_sighash_message(witnesses, &message); CHECK(err); + bool has_message = message.cur.size > 0; if (has_message) { - print_cursor("message", message); + print_cursor("message", message.cur); // step 8.c err = check_type_script_existing(message); CHECK(err); } + uint8_t seal_source[DEFAULT_DATA_SOURCE_LENGTH]; mol2_cursor_t original_seal = {0}; - // step 8.d - // step 8.f - err = ckb_fetch_seal(&original_seal); - CHECK(err); + { + // step 8.d + // step 8.f + mol2_cursor_t witness = {0}; + err = ckb_new_witness_cursor(&witness, seal_source, MAX_CACHE_SIZE, 0, + CKB_SOURCE_GROUP_INPUT); + CHECK(err); + uint32_t id = 0; + err = try_union_unpack_id(&witness, &id); + CHECK(err); + switch (id) { + case WitnessLayoutSighashAll: { + // TODO: validate full WitnessLayout structure + WitnessLayoutType layout = make_WitnessLayout(&witness); + SighashAllType s = layout.t->as_SighashAll(&layout); + original_seal = s.t->seal(&s); + } break; + case WitnessLayoutSighashAllOnly: { + // TODO: validate full WitnessLayout structure + WitnessLayoutType layout = make_WitnessLayout(&witness); + SighashAllOnlyType o = layout.t->as_SighashAllOnly(&layout); + original_seal = o.t->seal(&o); + } break; + default: { + // the union id should be SighashAll or SighashAllOnly. otherwise, it + // fails and mark it as non cobuild. tested by test_wrong_union_id + printf("error in fetch_seal, id = %u", id); + CHECK2(false, ERROR_SIGHASHALL_NOSEAL); + } break; + } + } print_cursor("seal", original_seal); // step 8.e @@ -717,7 +574,7 @@ int ckb_cobuild_normal_entry(ScriptEntryType callback) { if (message_calculation_flow == MessageCalculationFlowBlake2b) { // step 8.g - err = ckb_generate_smh(has_message, message, smh); + err = ckb_generate_smh(env, message.cur, smh); CHECK(err); print_raw_data("smh", smh, BLAKE2B_BLOCK_SIZE); } else { @@ -725,7 +582,7 @@ int ckb_cobuild_normal_entry(ScriptEntryType callback) { // first byte of seal CHECK2(false, ERROR_FLOW); } - err = callback(smh, seal, true); + err = callback(env, smh, seal); if (err) { printf("callback failed: %d", err); // terminated immediately @@ -735,8 +592,8 @@ int ckb_cobuild_normal_entry(ScriptEntryType callback) { return err; } -int ckb_generate_otx_smh(mol2_cursor_t message_cursor, uint8_t *smh, - const OtxStart *start, const Otx *size) { +int ckb_generate_otx_smh(const Env *env, mol2_cursor_t message_cursor, + uint8_t *smh, const OtxStart *start, const Otx *size) { int err = 0; blake2b_state ctx; size_t count = 0; @@ -758,47 +615,85 @@ int ckb_generate_otx_smh(mol2_cursor_t message_cursor, uint8_t *smh, BLAKE2B_UPDATE(&ctx, &size->input_cells, 4); count += 4; + TransactionType tx = env->tx; + RawTransactionType raw = tx.t->raw(&tx); + CellInputVecType inputs = raw.t->inputs(&raw); + // hash input cell and data + CHECK2(start->start_input_cell + size->input_cells >= start->start_input_cell, + ERROR_OVERFLOW); for (size_t index = start->start_input_cell; index < (start->start_input_cell + size->input_cells); index++) { // CellInput - uint8_t input[128]; - uint64_t input_len = sizeof(input); - err = ckb_load_input(input, &input_len, 0, index, CKB_SOURCE_INPUT); + bool existing = false; + CellInputType input = inputs.t->get(&inputs, index, &existing); + CHECK2(existing, ERROR_MOL2_UNEXPECTED); + err = ckb_hash_cursor(&ctx, input.cur); CHECK(err); - BLAKE2B_UPDATE(&ctx, input, input_len); - count += input_len; + count += input.cur.size; - err = hash_cell(&ctx, index, CKB_SOURCE_INPUT, &count); + err = hash_input_cell(&ctx, index, &count); CHECK(err); } // hash output cell and data + CHECK2( + start->start_output_cell + size->output_cells >= start->start_output_cell, + ERROR_OVERFLOW); BLAKE2B_UPDATE(&ctx, &size->output_cells, 4); count += 4; + CellOutputVecType outputs = raw.t->outputs(&raw); + BytesVecType outputs_data = raw.t->outputs_data(&raw); for (size_t index = start->start_output_cell; index < (start->start_output_cell + size->output_cells); index++) { - err = hash_cell(&ctx, index, CKB_SOURCE_OUTPUT, &count); + bool existing = false; + CellOutputType output = outputs.t->get(&outputs, index, &existing); + CHECK2(existing, ERROR_MOL2_UNEXPECTED); + err = ckb_hash_cursor(&ctx, output.cur); CHECK(err); + count += output.cur.size; + + existing = false; + mol2_cursor_t output_data_cursor = + outputs_data.t->get(&outputs_data, index, &existing); + CHECK2(existing, ERROR_MOL2_UNEXPECTED); + uint32_t data_len = output_data_cursor.size; + BLAKE2B_UPDATE(&ctx, &data_len, 4); + count += 4; + err = ckb_hash_cursor(&ctx, output_data_cursor); + CHECK(err); + count += output_data_cursor.size; } // hash cell deps + CHECK2(start->start_cell_deps + size->cell_deps >= start->start_cell_deps, + ERROR_OVERFLOW); BLAKE2B_UPDATE(&ctx, &size->cell_deps, 4); count += 4; - err = hash_cell_deps(&ctx, &count, start->start_cell_deps, size->cell_deps); - CHECK(err); + CellDepVecType cell_deps = raw.t->cell_deps(&raw); + for (size_t index = start->start_cell_deps; + index < (start->start_cell_deps + size->cell_deps); index++) { + bool existing = false; + CellDepType cell_dep = cell_deps.t->get(&cell_deps, index, &existing); + CHECK2(existing, ERROR_MOL2_UNEXPECTED); + err = ckb_hash_cursor(&ctx, cell_dep.cur); + count += cell_dep.cur.size; + } // hash header deps + CHECK2( + start->start_header_deps + size->header_deps >= start->start_header_deps, + ERROR_OVERFLOW); BLAKE2B_UPDATE(&ctx, &size->header_deps, 4); count += 4; + Byte32VecType header_deps = raw.t->header_deps(&raw); for (size_t index = start->start_header_deps; index < (start->start_header_deps + size->header_deps); index++) { - uint8_t header_dep[32]; - uint64_t header_dep_len = sizeof(header_dep); - err = ckb_load_header(header_dep, &header_dep_len, 0, index, - CKB_SOURCE_HEADER_DEP); - CHECK(err); - BLAKE2B_UPDATE(&ctx, header_dep, header_dep_len); - count += header_dep_len; + bool existing = false; + mol2_cursor_t header_dep_cursor = + header_deps.t->get(&header_deps, index, &existing); + CHECK2(existing, ERROR_MOL2_UNEXPECTED); + err = ckb_hash_cursor(&ctx, header_dep_cursor); + count += header_dep_cursor.size; } printf("ckb_generate_otx_smh totally hashed %d bytes", count); blake2b_final(&ctx, smh, BLAKE2B_BLOCK_SIZE); @@ -806,41 +701,22 @@ int ckb_generate_otx_smh(mol2_cursor_t message_cursor, uint8_t *smh, return err; } -static int get_witness_layout(size_t index, - WitnessLayoutId *witness_layout_id) { - uint32_t id = 0; - uint64_t id_len = sizeof(id); - int err = ckb_load_witness(&id, &id_len, 0, index, CKB_SOURCE_INPUT); - if (err) return err; - if (id_len < sizeof(id)) { - return ERROR_GENERAL; - } - if (id == WitnessLayoutSighashAll || id == WitnessLayoutSighashAllOnly || - id == WitnessLayoutOtx || id == WitnessLayoutOtxStart) { - *witness_layout_id = id; - } else { - return ERROR_GENERAL; - } - return 0; -} - -int ckb_cobuild_entry(ScriptEntryType callback, bool *cobuild_enabled) { +int ckb_cobuild_entry(const Env *env, ScriptEntryType callback, + bool *cobuild_enabled) { int err = 0; size_t execution_count = 0; uint8_t smh[BLAKE2B_BLOCK_SIZE] = {0}; mol2_cursor_t original_seal = {0}; mol2_cursor_t seal = {0}; + TransactionType tx = env->tx; + BytesVecType witnesses = tx.t->witnesses(&tx); + uint32_t witness_len = witnesses.t->len(&witnesses); + // Legacy Flow Handling *cobuild_enabled = false; - for (size_t index = 0;; index++) { - WitnessLayoutId id; - err = get_witness_layout(index, &id); - if (err == CKB_INDEX_OUT_OF_BOUND) { - err = 0; - break; - } - if (err == 0) { + for (uint32_t i = 0; i < witness_len; i++) { + if (get_witness_layout(witnesses, i, NULL) == 0) { *cobuild_enabled = true; break; } @@ -849,12 +725,6 @@ int ckb_cobuild_entry(ScriptEntryType callback, bool *cobuild_enabled) { goto exit; } - uint8_t current_script_hash[BLAKE2B_BLOCK_SIZE] = {0}; - uint64_t script_hash_len = BLAKE2B_BLOCK_SIZE; - err = ckb_load_script_hash(current_script_hash, &script_hash_len, 0); - CHECK(err); - CHECK2(script_hash_len == BLAKE2B_BLOCK_SIZE, ERROR_GENERAL); - // step 1 uint32_t is = 0, ie = 0, os = 0, oe = 0, cs = 0, ce = 0, hs = 0, he = 0; size_t i = 0; @@ -862,12 +732,12 @@ int ckb_cobuild_entry(ScriptEntryType callback, bool *cobuild_enabled) { OtxStart otx_start = {0}; // step 2 // step 4 - err = ckb_fetch_otx_start(&has_otx, &i, &otx_start); + err = ckb_fetch_otx_start(witnesses, &has_otx, &i, &otx_start); CHECK(err); if (!has_otx) { // step 3 printf("No otx detected"); - return ckb_cobuild_normal_entry(callback); + return ckb_cobuild_normal_entry(env, callback); } // step 5 is = otx_start.start_input_cell; @@ -879,23 +749,22 @@ int ckb_cobuild_entry(ScriptEntryType callback, bool *cobuild_enabled) { hs = otx_start.start_header_deps; he = hs; printf("ie = %d, oe = %d, ce = %d, he = %d", ie, oe, ce, he); - size_t index = i + 1; + uint32_t index = i + 1; printf("Otx starts at index %d(inclusive)", index); - for (;; index++) { - mol2_cursor_t cursor; - err = ckb_new_witness_cursor(&cursor, g_cobuild_seal_data_source, - MAX_CACHE_SIZE, index, CKB_SOURCE_INPUT); - // step 6, not WitnessLayoutOtx - CHECK_LOOP(err); - uint32_t id = 0; - err = try_union_unpack_id(&cursor, &id); - if (err || id != WitnessLayoutOtx) { + for (; index < witness_len; index++) { + WitnessLayoutType witness_layout = {0}; + err = get_witness_layout(witnesses, index, &witness_layout); + if (err != 0) { + // step 6, not WitnessLayoutOtx + break; + } + uint32_t id = witness_layout.t->item_id(&witness_layout); + if (id != WitnessLayoutOtx) { // step 6 // test_cobuild_otx_noexistent_otx_id && err == 0 break; } - mol2_union_t uni = mol2_union_unpack(&cursor); - OtxType otx = make_Otx(&uni.cursor); + OtxType otx = witness_layout.t->as_Otx(&witness_layout); MessageType message = otx.t->message(&otx); Otx size = { .input_cells = otx.t->input_cells(&otx), @@ -910,7 +779,7 @@ int ckb_cobuild_entry(ScriptEntryType callback, bool *cobuild_enabled) { CHECK2(false, ERROR_WRONG_OTX); } // step 6.c - err = check_type_script_existing(message.cur); + err = check_type_script_existing(message); CHECK(err); // step 6.d bool found = false; @@ -921,7 +790,7 @@ int ckb_cobuild_entry(ScriptEntryType callback, bool *cobuild_enabled) { err = ckb_load_cell_by_field(hash, &len, 0, index2, CKB_SOURCE_INPUT, CKB_CELL_FIELD_LOCK_HASH); CHECK(err); - if (memcmp(hash, current_script_hash, sizeof(hash)) == 0) { + if (memcmp(hash, env->current_script_hash, sizeof(hash)) == 0) { found = true; break; } @@ -940,7 +809,7 @@ int ckb_cobuild_entry(ScriptEntryType callback, bool *cobuild_enabled) { .start_cell_deps = ce, .start_header_deps = he, }; - err = ckb_generate_otx_smh(message.cur, smh, &start, &size); + err = ckb_generate_otx_smh(env, message.cur, smh, &start, &size); CHECK(err); print_raw_data("smh", smh, BLAKE2B_BLOCK_SIZE); // step 6.f @@ -955,7 +824,7 @@ int ckb_cobuild_entry(ScriptEntryType callback, bool *cobuild_enabled) { mol2_cursor_t script_hash = loop_seal.t->script_hash(&loop_seal); size_t len = mol2_read_at(&script_hash, hash, sizeof(hash)); CHECK2(len == sizeof(hash), ERROR_GENERAL); - if (memcmp(hash, current_script_hash, sizeof(hash)) == 0) { + if (memcmp(hash, env->current_script_hash, sizeof(hash)) == 0) { // step 6.g original_seal = loop_seal.t->seal(&loop_seal); print_cursor("seal", original_seal); @@ -972,7 +841,7 @@ int ckb_cobuild_entry(ScriptEntryType callback, bool *cobuild_enabled) { CHECK(err); if (message_calculation_flow == MessageCalculationFlowBlake2b) { execution_count++; - err = callback(smh, seal, true); + err = callback(env, smh, seal); if (err) { printf("callback failed: %d", err); // terminated immediately @@ -992,17 +861,14 @@ int ckb_cobuild_entry(ScriptEntryType callback, bool *cobuild_enabled) { // step 7 size_t j = index; - for (size_t index = 0;; index++) { + for (uint32_t index = 0; index < witness_len; index++) { // [0, i) [j, +infinity) if (index < i || index >= j) { - WitnessLayoutId id; - err = get_witness_layout(index, &id); - if (err == CKB_INDEX_OUT_OF_BOUND) { - err = 0; - break; - } + WitnessLayoutType witness_layout = {0}; + err = get_witness_layout(witnesses, index, &witness_layout); if (err == 0) { // test_cobuild_otx_noexistent_otx_id + uint32_t id = witness_layout.t->item_id(&witness_layout); CHECK2(id != WitnessLayoutOtx, ERROR_WRONG_OTX); } } @@ -1019,7 +885,7 @@ int ckb_cobuild_entry(ScriptEntryType callback, bool *cobuild_enabled) { err = ckb_load_cell_by_field(hash, &len, 0, index, CKB_SOURCE_INPUT, CKB_CELL_FIELD_LOCK_HASH); CHECK_LOOP(err); - if (memcmp(hash, current_script_hash, sizeof(hash)) == 0) { + if (memcmp(hash, env->current_script_hash, sizeof(hash)) == 0) { printf( "Same lock script found beyond otx, at index %d. " "ckb_cobuild_normal_entry called.", @@ -1033,7 +899,7 @@ int ckb_cobuild_entry(ScriptEntryType callback, bool *cobuild_enabled) { // TODO printf("extra callback is invoked"); execution_count++; - err = ckb_cobuild_normal_entry(callback); + err = ckb_cobuild_normal_entry(env, callback); CHECK(err); } CHECK2(execution_count > 0, ERROR_NO_CALLBACK); diff --git a/c/cobuild.h b/c/cobuild.h index 1299cc5..3a87ef5 100644 --- a/c/cobuild.h +++ b/c/cobuild.h @@ -6,10 +6,10 @@ #include #include "molecule2_reader.h" +#include "mol2_utils.h" -typedef int (*ScriptEntryType)(const uint8_t* signing_message_hash, - mol2_cursor_t seal, bool witness_existing); -int ckb_cobuild_entry(ScriptEntryType entry, bool* cobuild_enabled); -int ckb_cobuild_normal_entry(ScriptEntryType entry); +typedef int (*ScriptEntryType)(const Env* env, const uint8_t* signing_message_hash, mol2_cursor_t seal); +int ckb_cobuild_entry(const Env* env, ScriptEntryType entry, bool* cobuild_enabled); +int ckb_cobuild_normal_entry(const Env* env, ScriptEntryType entry); #endif diff --git a/c/cobuild_top_level_mol2.h b/c/cobuild_top_level_mol2.h new file mode 100644 index 0000000..41b3a9d --- /dev/null +++ b/c/cobuild_top_level_mol2.h @@ -0,0 +1,102 @@ + +#ifndef _COBUILD_TOP_LEVEL_MOL2_API2_H_ +#define _COBUILD_TOP_LEVEL_MOL2_API2_H_ + +#ifndef MOLECULEC2_VERSION +#define MOLECULEC2_VERSION 7002 +#endif +#ifndef MOLECULE2_API_VERSION_MIN +#define MOLECULE2_API_VERSION_MIN 5000 +#endif + +#include "molecule2_reader.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +// ----forward declaration-------- +struct WitnessLayoutType; +struct WitnessLayoutVTable; +struct WitnessLayoutVTable *GetWitnessLayoutVTable(void); +struct WitnessLayoutType make_WitnessLayout(mol2_cursor_t *cur); +uint32_t WitnessLayout_item_id_impl(struct WitnessLayoutType *); +struct SighashAllType WitnessLayout_as_SighashAll_impl( + struct WitnessLayoutType *); +struct SighashAllOnlyType WitnessLayout_as_SighashAllOnly_impl( + struct WitnessLayoutType *); +struct OtxType WitnessLayout_as_Otx_impl(struct WitnessLayoutType *); +struct OtxStartType WitnessLayout_as_OtxStart_impl(struct WitnessLayoutType *); + +// ----definition----------------- +typedef struct WitnessLayoutVTable { + uint32_t (*item_id)(struct WitnessLayoutType *); + struct SighashAllType (*as_SighashAll)(struct WitnessLayoutType *); + struct SighashAllOnlyType (*as_SighashAllOnly)(struct WitnessLayoutType *); + struct OtxType (*as_Otx)(struct WitnessLayoutType *); + struct OtxStartType (*as_OtxStart)(struct WitnessLayoutType *); +} WitnessLayoutVTable; +typedef struct WitnessLayoutType { + mol2_cursor_t cur; + WitnessLayoutVTable *t; +} WitnessLayoutType; + +#ifndef MOLECULEC_C2_DECLARATION_ONLY + +// ----implementation------------- +struct WitnessLayoutType make_WitnessLayout(mol2_cursor_t *cur) { + WitnessLayoutType ret; + ret.cur = *cur; + ret.t = GetWitnessLayoutVTable(); + return ret; +} +struct WitnessLayoutVTable *GetWitnessLayoutVTable(void) { + static WitnessLayoutVTable s_vtable; + static int inited = 0; + if (inited) return &s_vtable; + s_vtable.item_id = WitnessLayout_item_id_impl; + s_vtable.as_SighashAll = WitnessLayout_as_SighashAll_impl; + s_vtable.as_SighashAllOnly = WitnessLayout_as_SighashAllOnly_impl; + s_vtable.as_Otx = WitnessLayout_as_Otx_impl; + s_vtable.as_OtxStart = WitnessLayout_as_OtxStart_impl; + return &s_vtable; +} +uint32_t WitnessLayout_item_id_impl(WitnessLayoutType *this) { + return mol2_unpack_number(&this->cur); +} +SighashAllType WitnessLayout_as_SighashAll_impl(WitnessLayoutType *this) { + SighashAllType ret; + mol2_union_t u = mol2_union_unpack(&this->cur); + ret.cur = u.cursor; + ret.t = GetSighashAllVTable(); + return ret; +} +SighashAllOnlyType WitnessLayout_as_SighashAllOnly_impl( + WitnessLayoutType *this) { + SighashAllOnlyType ret; + mol2_union_t u = mol2_union_unpack(&this->cur); + ret.cur = u.cursor; + ret.t = GetSighashAllOnlyVTable(); + return ret; +} +OtxType WitnessLayout_as_Otx_impl(WitnessLayoutType *this) { + OtxType ret; + mol2_union_t u = mol2_union_unpack(&this->cur); + ret.cur = u.cursor; + ret.t = GetOtxVTable(); + return ret; +} +OtxStartType WitnessLayout_as_OtxStart_impl(WitnessLayoutType *this) { + OtxStartType ret; + mol2_union_t u = mol2_union_unpack(&this->cur); + ret.cur = u.cursor; + ret.t = GetOtxStartVTable(); + return ret; +} +#endif // MOLECULEC_C2_DECLARATION_ONLY + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif // _COBUILD_TOP_LEVEL_MOL2_API2_H_ diff --git a/c/mol2_utils.h b/c/mol2_utils.h new file mode 100644 index 0000000..e822dac --- /dev/null +++ b/c/mol2_utils.h @@ -0,0 +1,353 @@ +/* + * This file contains handy utilities for molecule-c2. Ideally most of + * this file should be part of molecule-c2 itself. + */ + +#ifndef MOL2_UTILS +#define MOL2_UTILS + +#define BLAKE2_IMPL_H +#define BLAKE2_REF_C +#include "blake2b.h" +#undef BLAKE2_REF_C +#undef BLAKE2_IMPL_H + +#include "ckb_consts.h" +#include "ckb_syscall_apis.h" +#include "molecule2_reader.h" + +// Given a data source, this macro extracts the start of cache buffer +#define MOL2_CACHE_PTR(data_source) \ + (((mol2_data_source_t *)(data_source))->cache) + +typedef uint32_t(read_from_t)(uintptr_t arg[], uint8_t *ptr, uint32_t len, + uint32_t offset); + +static uint32_t read_from_witness(uintptr_t arg[], uint8_t *ptr, uint32_t len, + uint32_t offset) { + int err; + uint64_t output_len = len; + err = ckb_load_witness(ptr, &output_len, offset, arg[0], arg[1]); + if (err != 0) { + return 0; + } + if (output_len > len) { + return len; + } else { + return (uint32_t)output_len; + } +} + +static uint32_t read_from_cell_data(uintptr_t arg[], uint8_t *ptr, uint32_t len, + uint32_t offset) { + int err; + uint64_t output_len = len; + err = ckb_load_cell_data(ptr, &output_len, offset, arg[0], arg[1]); + if (err != 0) { + return 0; + } + if (output_len > len) { + return len; + } else { + return (uint32_t)output_len; + } +} + +static uint32_t read_from_cell(uintptr_t arg[], uint8_t *ptr, uint32_t len, + uint32_t offset) { + int err; + uint64_t output_len = len; + err = ckb_load_cell(ptr, &output_len, offset, arg[0], arg[1]); + if (err != 0) { + return 0; + } + if (output_len > len) { + return len; + } else { + return (uint32_t)output_len; + } +} + +static uint32_t read_from_tx(uintptr_t arg[], uint8_t *ptr, uint32_t len, + uint32_t offset) { + int err; + uint64_t output_len = len; + err = ckb_load_transaction(ptr, &output_len, offset); + if (err != 0) { + return 0; + } + if (output_len > len) { + return len; + } else { + return (uint32_t)output_len; + } +} + +static uint32_t read_from_script(uintptr_t arg[], uint8_t *ptr, uint32_t len, + uint32_t offset) { + int err; + uint64_t output_len = len; + err = ckb_load_script(ptr, &output_len, offset); + if (err != 0) { + return 0; + } + if (output_len > len) { + return len; + } else { + return (uint32_t)output_len; + } +} + +static void ckb_new_cursor_with_data(mol2_cursor_t *cursor, uint32_t total_len, + read_from_t read_from, + uint8_t *data_source, uint32_t cache_len, + size_t index, size_t source, + uint32_t cached_size) { + cursor->offset = 0; + cursor->size = (uint32_t)total_len; + + mol2_data_source_t *ptr = (mol2_data_source_t *)data_source; + + ptr->read = read_from; + ptr->total_size = total_len; + ptr->args[0] = index; + ptr->args[1] = source; + + ptr->cache_size = cached_size; + ptr->start_point = 0; + ptr->max_cache_size = cache_len; + + cursor->data_source = ptr; +} + +static int ckb_new_witness_cursor(mol2_cursor_t *cursor, uint8_t *data_source, + uint32_t cache_len, size_t index, + size_t source) { + int err; + + // Use a single syscall to fetch cached data and total length + uint64_t len = cache_len; + err = ckb_load_witness(MOL2_CACHE_PTR(data_source), &len, 0, index, source); + if (err != 0) { + return err; + } + uint32_t cache_size = (uint32_t)len; + if (cache_size > cache_len) { + cache_size = cache_len; + } + ckb_new_cursor_with_data(cursor, len, read_from_witness, data_source, + cache_len, index, source, cache_size); + + return 0; +} + +typedef int(cursor_accessor_t)(const uint8_t *data, size_t len, void *context); +static int ckb_access_cursor(mol2_cursor_t cursor, cursor_accessor_t accessor, + void *context) { + int err = 0; + uint8_t batch[MAX_CACHE_SIZE]; + while (true) { + uint32_t read_len = mol2_read_at(&cursor, batch, sizeof(batch)); + err = accessor(batch, read_len, context); + if (err != 0) { + return err; + } + // adjust cursor + mol2_add_offset(&cursor, read_len); + mol2_sub_size(&cursor, read_len); + mol2_validate(&cursor); + if (cursor.size == 0) { + break; + } + } + return 0; +} + +#ifndef BLAKE2B_UPDATE +#define BLAKE2B_UPDATE blake2b_update +#endif + +static int _ckb_cursor_blake2b_hasher(const uint8_t *data, size_t len, + void *context) { + blake2b_state *state = (blake2b_state *)context; + BLAKE2B_UPDATE(state, data, len); + return 0; +} + +static int ckb_hash_cursor(blake2b_state *ctx, mol2_cursor_t cursor) { + // one batch to drain whole cache perfectly + // tested by test_input_cell_data_size_0 + // test_input_cell_data_size_1 + // test_input_cell_data_size_2048 + // test_input_cell_data_size_2049 + // test_input_cell_data_size_500k + return ckb_access_cursor(cursor, _ckb_cursor_blake2b_hasher, ctx); +} + +static int ckb_compare_cursor(mol2_cursor_t a, mol2_cursor_t b, int *result) { + if (a.size < b.size) { + *result = -1; + return 0; + } else if (a.size > b.size) { + *result = 1; + return 0; + } + + uint8_t batch_a[MAX_CACHE_SIZE]; + uint8_t batch_b[MAX_CACHE_SIZE]; + while (true) { + uint32_t read_len_a = mol2_read_at(&a, batch_a, sizeof(batch_a)); + uint32_t read_len_b = mol2_read_at(&b, batch_b, sizeof(batch_b)); + if (read_len_a != read_len_b) { + return MOL2_ERR_DATA; + } + int ret = memcmp(batch_a, batch_b, read_len_a); + if (ret != 0) { + *result = ret; + return 0; + } + + // adjust cursors + mol2_add_offset(&a, read_len_a); + mol2_sub_size(&a, read_len_a); + mol2_validate(&a); + mol2_add_offset(&b, read_len_b); + mol2_sub_size(&b, read_len_b); + mol2_validate(&b); + if (a.size == 0) { + break; + } + } + + *result = 0; + return 0; +} + +static int try_union_unpack_id(const mol2_cursor_t *cursor, uint32_t *id) { + uint32_t len = mol2_read_at(cursor, (uint8_t *)id, 4); + if (len != 4) { + // tested by: + // tested_by_no_cobuild_append_sighash_all + // tested_by_insert_witness_less_4_before_sighashall + return MOL2_ERR_DATA; + } + return CKB_SUCCESS; +} + +#ifndef MOL2_UTILS_CACHE_TX_SIZE +#define MOL2_UTILS_CACHE_TX_SIZE 65536 +#endif + +#ifndef MOL2_UTILS_CACHE_SCRIPT_SIZE +#define MOL2_UTILS_CACHE_SCRIPT_SIZE 1024 +#endif + +typedef struct { + uint8_t tx_source[MOL2_DATA_SOURCE_LEN(MOL2_UTILS_CACHE_TX_SIZE)]; + mol2_cursor_t tx_cursor; + TransactionType tx; + + uint8_t + current_script_source[MOL2_DATA_SOURCE_LEN(MOL2_UTILS_CACHE_SCRIPT_SIZE)]; + mol2_cursor_t current_script_cursor; + ScriptType current_script; + + uint8_t tx_hash[32]; + uint8_t current_script_hash[32]; + + void *script_specific_data; +} Env; + +static int ckb_env_initialize(Env *env) { + int err; + { + uint64_t tx_len = MOL2_UTILS_CACHE_TX_SIZE; + err = ckb_load_transaction(MOL2_CACHE_PTR(env->tx_source), &tx_len, 0); + if (err != 0) { + return err; + } + uint32_t cache_size = (uint32_t)tx_len; + if (cache_size > MOL2_UTILS_CACHE_TX_SIZE) { + cache_size = MOL2_UTILS_CACHE_TX_SIZE; + } + + ckb_new_cursor_with_data(&env->tx_cursor, tx_len, read_from_tx, + env->tx_source, MOL2_UTILS_CACHE_TX_SIZE, 0, 0, + cache_size); + + env->tx = make_Transaction(&env->tx_cursor); + } + + { + uint64_t script_len = MOL2_UTILS_CACHE_SCRIPT_SIZE; + err = ckb_load_script(MOL2_CACHE_PTR(env->current_script_source), + &script_len, 0); + if (err != 0) { + return err; + } + uint32_t cache_size = (uint32_t)script_len; + if (cache_size > MOL2_UTILS_CACHE_SCRIPT_SIZE) { + cache_size = MOL2_UTILS_CACHE_SCRIPT_SIZE; + } + + ckb_new_cursor_with_data(&env->current_script_cursor, script_len, + read_from_script, env->current_script_source, + MOL2_UTILS_CACHE_SCRIPT_SIZE, 0, 0, + MOL2_UTILS_CACHE_SCRIPT_SIZE); + + env->current_script = make_Script(&env->current_script_cursor); + } + + { + uint64_t tx_hash_len = 32; + err = ckb_load_tx_hash(env->tx_hash, &tx_hash_len, 0); + if (err != 0) { + return err; + } + if (tx_hash_len != 32) { + return MOL2_ERR_DATA; + } + } + + { + uint64_t script_hash_len = 32; + err = ckb_load_script_hash(env->current_script_hash, &script_hash_len, 0); + if (err != 0) { + return err; + } + if (script_hash_len != 32) { + return MOL2_ERR_DATA; + } + } + + env->script_specific_data = NULL; + + return 0; +} + +static inline mol2_cursor_t mol2_cursor_slice(const mol2_cursor_t *cur, + uint32_t offset, + uint32_t new_size) { + uint32_t shrinked_size; + // This way we can ensure that the new size will be no larger than original + // cursor + if (__builtin_sub_overflow(cur->size, new_size, &shrinked_size)) { + MOL2_PANIC(MOL2_ERR_OVERFLOW); + } + mol2_cursor_t res = *cur; + mol2_add_offset(&res, offset); + mol2_sub_size(&res, shrinked_size); + mol2_validate(&res); + return res; +} + +static inline mol2_cursor_t mol2_cursor_slice_start(const mol2_cursor_t *cur, + uint32_t offset) { + mol2_cursor_t res = *cur; + mol2_add_offset(&res, offset); + mol2_sub_size(&res, offset); + mol2_validate(&res); + return res; +} + +#endif /* MOL2_UTILS */ diff --git a/c/omni_lock.c b/c/omni_lock.c index c3d6156..46f80b0 100644 --- a/c/omni_lock.c +++ b/c/omni_lock.c @@ -7,8 +7,10 @@ int ckb_exit(signed char); #define MOLECULEC_VERSION 7000 +// Includes the actual implementation here +#include + #include "blockchain-api2.h" -#include "blockchain.h" #include "ckb_consts.h" #if defined(CKB_USE_SIM) @@ -31,6 +33,7 @@ int ckb_exit(signed char); #include "rce.h" #include "omni_lock_mol2.h" #include "cobuild_basic_mol2.h" +#include "cobuild_top_level_mol2.h" #include "omni_lock_acp.h" #include "omni_lock_time_lock.h" @@ -74,8 +77,8 @@ typedef struct ArgsType { uint64_t since; bool has_acp; - int ckb_minimum; // Used for ACP - int udt_minimum; // used for ACP + uint8_t ckb_minimum; // Used for ACP + uint8_t udt_minimum; // used for ACP bool has_supply; uint8_t info_cell[32]; // type script hash @@ -137,41 +140,27 @@ bool is_memory_enough(mol_seg_t seg, const uint8_t *cur, uint32_t len) { // // -int parse_args(ArgsType *args) { +int parse_args(ScriptType script, ArgsType *args) { int err = 0; - uint8_t script[SCRIPT_SIZE]; - uint64_t len = SCRIPT_SIZE; - err = ckb_checked_load_script(script, &len, 0); - CHECK(err); - - mol_seg_t script_seg; - script_seg.ptr = script; - script_seg.size = (mol_num_t)len; - mol_errno mol_err = MolReader_Script_verify(&script_seg, false); - CHECK2(mol_err == MOL_OK, ERROR_ENCODING); - - mol_seg_t args_seg = MolReader_Script_get_args(&script_seg); - mol_seg_t seg = MolReader_Bytes_raw_bytes(&args_seg); - - uint8_t *cur = seg.ptr; + // TODO: do we need to validate Script structure here? + mol2_cursor_t script_args = script.t->args(&script); // parse flags - CHECK2(is_memory_enough(seg, cur, 1), ERROR_ARGS_FORMAT); - uint8_t flags = *cur; - args->id.flags = flags; - cur = safe_move_to(seg, cur, 1); - CHECK2(cur != NULL, ERROR_ARGS_FORMAT); + CHECK2(script_args.size >= 1, ERROR_ARGS_FORMAT); + CHECK2(mol2_read_at(&script_args, &args->id.flags, 1) == 1, + ERROR_ARGS_FORMAT); + script_args = mol2_cursor_slice_start(&script_args, 1); // parse blake160 - CHECK2(is_memory_enough(seg, cur, 20), ERROR_ARGS_FORMAT); - memcpy(args->id.id, cur, BLAKE160_SIZE); - cur = safe_move_to(seg, cur, 20); - CHECK2(cur != NULL, ERROR_ARGS_FORMAT); + CHECK2(script_args.size >= 20, ERROR_ARGS_FORMAT); + CHECK2(mol2_read_at(&script_args, args->id.id, 20) == 20, ERROR_ARGS_FORMAT); + script_args = mol2_cursor_slice_start(&script_args, 20); - CHECK2(is_memory_enough(seg, cur, 1), ERROR_ARGS_FORMAT); - args->omni_lock_flags = *cur; - cur = safe_move_to(seg, cur, 1); + CHECK2(script_args.size >= 1, ERROR_ARGS_FORMAT); + CHECK2(mol2_read_at(&script_args, &args->omni_lock_flags, 1) == 1, + ERROR_ARGS_FORMAT); + script_args = mol2_cursor_slice_start(&script_args, 1); args->has_omni_root = args->omni_lock_flags & OMNI_ROOT_MASK; args->has_acp = args->omni_lock_flags & ACP_MASK; @@ -191,86 +180,38 @@ int parse_args(ArgsType *args) { expected_size += 32; } - if (expected_size == 0) { - CHECK2(cur == NULL, ERROR_ARGS_FORMAT); - } else { - CHECK2(cur != NULL, ERROR_ARGS_FORMAT); - CHECK2(is_memory_enough(seg, cur, expected_size), ERROR_ARGS_FORMAT); + CHECK2(script_args.size == expected_size, ERROR_ARGS_FORMAT); + if (expected_size > 0) { if (args->has_omni_root) { - memcpy(args->omni_root, cur, 32); - cur += 32; // it's safe to move, already checked + CHECK2(mol2_read_at(&script_args, args->omni_root, 32) == 32, + ERROR_ARGS_FORMAT); + script_args = mol2_cursor_slice_start(&script_args, 32); } if (args->has_acp) { - args->ckb_minimum = cur[0]; - args->udt_minimum = cur[1]; - cur += 2; + CHECK2(mol2_read_at(&script_args, &args->ckb_minimum, 1) == 1, + ERROR_ARGS_FORMAT); + script_args = mol2_cursor_slice_start(&script_args, 1); + CHECK2(mol2_read_at(&script_args, &args->udt_minimum, 1) == 1, + ERROR_ARGS_FORMAT); + script_args = mol2_cursor_slice_start(&script_args, 1); } if (args->has_since) { - args->since = *(uint64_t *)cur; - cur += 8; + CHECK2(mol2_read_at(&script_args, (uint8_t *)(&args->since), 8) == 8, + ERROR_ARGS_FORMAT); + script_args = mol2_cursor_slice_start(&script_args, 8); } if (args->has_supply) { - memcpy(args->info_cell, cur, 32); - cur += 32; + CHECK2(mol2_read_at(&script_args, args->info_cell, 32) == 32, + ERROR_ARGS_FORMAT); + script_args = mol2_cursor_slice_start(&script_args, 32); } - CHECK2(cur == (seg.ptr + seg.size), ERROR_INVALID_MOL_FORMAT); + CHECK2(script_args.size == 0, ERROR_INVALID_MOL_FORMAT); } exit: return err; } -static uint32_t read_from_witness(uintptr_t arg[], uint8_t *ptr, uint32_t len, - uint32_t offset) { - int err; - uint64_t output_len = len; - err = ckb_load_witness(ptr, &output_len, offset, arg[0], arg[1]); - if (err != 0) { - return 0; - } - if (output_len > len) { - return len; - } else { - return (uint32_t)output_len; - } -} - -uint8_t g_witness_data_source[DEFAULT_DATA_SOURCE_LENGTH]; -int make_witness(WitnessArgsType *witness) { - int err = 0; - uint64_t witness_len = 0; - size_t source = CKB_SOURCE_GROUP_INPUT; - err = ckb_load_witness(NULL, &witness_len, 0, 0, source); - // when witness is missing, empty or not accessible, make it zero length. - // don't fail, because owner lock without omni doesn't require witness. - // when it's zero length, any further actions on witness will fail. - if (err != 0) { - witness_len = 0; - } - - mol2_cursor_t cur; - - cur.offset = 0; - cur.size = (mol_num_t)witness_len; - - mol2_data_source_t *ptr = (mol2_data_source_t *)g_witness_data_source; - - ptr->read = read_from_witness; - ptr->total_size = (uint32_t)witness_len; - // pass index and source as args - ptr->args[0] = 0; - ptr->args[1] = source; - - ptr->cache_size = 0; - ptr->start_point = 0; - ptr->max_cache_size = MAX_CACHE_SIZE; - cur.data_source = ptr; - - *witness = make_WitnessArgs(&cur); - - return 0; -} - int smt_verify_identity(CkbIdentityType *id, SmtProofEntryVecType *proofs, RceState *rce_state) { int err = 0; @@ -361,61 +302,41 @@ static int parse_witness_lock(WitnessLockType *witness_lock, return err; } -static int get_witness_args_lock(mol2_cursor_t *lock, bool *witness_existing) { +// smh is short for signing message hash +int omnilock_entry(const Env *env, const uint8_t *smh, mol2_cursor_t seal) { int err = 0; - WitnessArgsType witness_args; - err = make_witness(&witness_args); - CHECK(err); - *witness_existing = witness_args.cur.size > 0; - - // witness or witness lock can be empty if owner lock without omni is used - if (!*witness_existing) return 0; - - BytesOptType mol_lock = witness_args.t->lock(&witness_args); - if (mol_lock.t->is_some(&mol_lock)) { - *lock = mol_lock.t->unwrap(&mol_lock); - } -exit: - return err; -} - -int omnilock_entry(const uint8_t *smh, mol2_cursor_t seal, - bool witness_existing) { - int err = 0; - ArgsType args = {0}; WitnessLockType witness_lock = {0}; // this identity can be either from witness lock (witness_lock.id) or script // args (args.id) CkbIdentityType identity = {0}; // In some scenarios(e.g. owner lock), corresponding witness doesn't exist - if (witness_existing) { + if (seal.size > 0) { err = parse_witness_lock(&witness_lock, &seal); CHECK(err); } - err = parse_args(&args); - CHECK(err); + const ArgsType *args = (const ArgsType *)env->script_specific_data; - if (args.has_omni_root) { + if (args->has_omni_root) { if (witness_lock.has_identity) { identity = witness_lock.id; } else { - identity = args.id; + identity = args->id; } } else { - identity = args.id; + identity = args->id; } // regulation compliance, also as administrators if (witness_lock.has_identity) { - CHECK2(args.has_omni_root, ERROR_INVALID_MOL_FORMAT); + CHECK2(args->has_omni_root, ERROR_INVALID_MOL_FORMAT); CHECK2(witness_lock.has_proofs, ERROR_INVALID_MOL_FORMAT); RceState rce_state; rce_init_state(&rce_state); rce_state.rcrules_in_input_cell = true; - err = rce_gather_rcrules_recursively(&rce_state, args.omni_root, 0); + err = rce_gather_rcrules_recursively(&rce_state, args->omni_root, 0); CHECK(err); CHECK2(rce_state.rcrules_count > 0, ERROR_NO_OMNIRULE); CHECK2(rce_state.has_wl, ERROR_NO_WHITE_LIST); @@ -425,19 +346,19 @@ int omnilock_entry(const uint8_t *smh, mol2_cursor_t seal, CHECK(err); } else { // time lock is not used for administrators - if (args.has_since) { - err = check_since(args.since); + if (args->has_since) { + err = check_since(args->since); CHECK(err); } - if (args.has_supply) { - err = check_supply(args.info_cell); + if (args->has_supply) { + err = check_supply(args->info_cell); CHECK(err); } // ACP without signature is not used for administrators - if (args.has_acp && !witness_lock.has_signature) { + if (args->has_acp && !witness_lock.has_signature) { uint64_t min_ckb_amount = 0; uint128_t min_udt_amount = 0; - process_amount(args.ckb_minimum, args.udt_minimum, &min_ckb_amount, + process_amount(args->ckb_minimum, args->udt_minimum, &min_ckb_amount, &min_udt_amount); // skip checking identity to follow ACP return check_payment_unlock(min_ckb_amount, min_udt_amount); @@ -458,21 +379,45 @@ int simulator_main() { int main() { #endif int err = 0; - bool cobuild_enabled = false; - err = ckb_cobuild_entry(omnilock_entry, &cobuild_enabled); + Env env; + err = ckb_env_initialize(&env); CHECK(err); - printf("cobuild_enabled = %d", cobuild_enabled); - if (!cobuild_enabled) { - bool witness_existing = true; - uint8_t smh[BLAKE2B_BLOCK_SIZE] = {0}; + ArgsType args = {0}; + err = parse_args(env.current_script, &args); + CHECK(err); + env.script_specific_data = &args; + + bool cobuild_activated = false; + err = ckb_cobuild_entry(&env, omnilock_entry, &cobuild_activated); + CHECK(err); + printf("cobuild_activated = %d", cobuild_activated); + if (!cobuild_activated) { + uint8_t witness_source[DEFAULT_DATA_SOURCE_LENGTH]; mol2_cursor_t lock = {0}; - err = get_witness_args_lock(&lock, &witness_existing); - CHECK(err); - if (witness_existing) { + { + mol2_cursor_t witness_cursor; + err = ckb_new_witness_cursor(&witness_cursor, witness_source, + MAX_CACHE_SIZE, 0, CKB_SOURCE_GROUP_INPUT); + // when witness is missing, empty or not accessible, make it zero length. + // don't fail, because owner lock without omni doesn't require witness. + // when it's zero length, any further actions on witness will fail. + if (err == 0) { + if (witness_cursor.size > 0) { + WitnessArgsType witness_args = make_WitnessArgs(&witness_cursor); + BytesOptType lock_opt = witness_args.t->lock(&witness_args); + if (lock_opt.t->is_some(&lock_opt)) { + lock = lock_opt.t->unwrap(&lock_opt); + } + } + } + } + + uint8_t smh[BLAKE2B_BLOCK_SIZE] = {0}; + if (lock.size > 0) { err = generate_sighash_all(smh, BLAKE2B_BLOCK_SIZE); CHECK(err); } - err = omnilock_entry(smh, lock, witness_existing); + err = omnilock_entry(&env, smh, lock); CHECK(err); } exit: diff --git a/c/omni_lock_supply.h b/c/omni_lock_supply.h index 0fc61bc..ccdc1b4 100644 --- a/c/omni_lock_supply.h +++ b/c/omni_lock_supply.h @@ -124,7 +124,7 @@ int compare_cells_data(size_t input_index, size_t output_index) { } -int iterate_by_type_script_hash(uint8_t* hash, size_t source, iterate_func_t func, SupplyContextType* ctx) { +int iterate_by_type_script_hash(const uint8_t* hash, size_t source, iterate_func_t func, SupplyContextType* ctx) { int err = 0; size_t i = 0; uint8_t hash2[32] = {0}; @@ -152,7 +152,7 @@ int iterate_by_type_script_hash(uint8_t* hash, size_t source, iterate_func_t fun return err; } -int check_supply(uint8_t* cell_id) { +int check_supply(const uint8_t* cell_id) { int err = 0; SupplyContextType ctx = {0}; // locate the input info cell diff --git a/tests/omni_lock/ckb_syscall_omni_lock_sim.h b/tests/omni_lock/ckb_syscall_omni_lock_sim.h index c635151..d77d2a6 100644 --- a/tests/omni_lock/ckb_syscall_omni_lock_sim.h +++ b/tests/omni_lock/ckb_syscall_omni_lock_sim.h @@ -22,6 +22,7 @@ mol_seg_t build_bytes(const uint8_t* data, uint32_t len); mol_seg_t build_script(const uint8_t* code_hash, uint8_t hash_type, const uint8_t* args, uint32_t args_len); +int ckb_load_transaction(void* addr, uint64_t* len, size_t offset); int ckb_load_tx_hash(void* addr, uint64_t* len, size_t offset); int ckb_load_witness(void* addr, uint64_t* len, size_t offset, size_t index, size_t source); @@ -107,6 +108,7 @@ typedef struct RcLockStates { uint32_t witness_count; slice_t script; + slice_t transaction; slice_t cell_data[64]; uint32_t cell_data_count; @@ -364,6 +366,22 @@ void convert_setting_to_states(void) { // make witness again, with correct signature convert_witness(); + // Build a dummy transaction that is just enough for omnilock + { + mol_builder_t witness_builder; + MolBuilder_BytesVec_init(&witness_builder); + MolBuilder_BytesVec_push(&witness_builder, g_states.witness[0].ptr, g_states.witness[0].size); + mol_seg_res_t witness_res = MolBuilder_BytesVec_build(witness_builder); + + mol_builder_t tx_builder; + MolBuilder_Transaction_init(&tx_builder); + MolBuilder_Transaction_set_witnesses(&tx_builder, witness_res.seg.ptr, witness_res.seg.size); + mol_seg_res_t tx_res = MolBuilder_Transaction_build(tx_builder); + + g_states.transaction.ptr = tx_res.seg.ptr; + g_states.transaction.size = tx_res.seg.size; + } + // Script uint8_t script_args[1 + 20 + 1 + 32 + 2 + 8] = {0}; uint32_t script_args_len = 22; @@ -568,6 +586,28 @@ int ckb_load_witness(void* addr, uint64_t* len, size_t offset, size_t index, return 0; } +int ckb_load_transaction(void* addr, uint64_t* len, size_t offset) { + slice_t seg = g_states.transaction; + + if (addr == NULL) { + *len = seg.size; + return 0; + } + if (seg.size <= offset) { + *len = 0; + return 0; + } + uint32_t remaining = seg.size - offset; + if (remaining > *len) { + memcpy(addr, seg.ptr + offset, *len); + } else { + memcpy(addr, seg.ptr + offset, remaining); + } + *len = remaining; + + return 0; +} + int ckb_checked_load_witness(void* addr, uint64_t* len, size_t offset, size_t index, size_t source) { uint64_t old_len = *len; diff --git a/tests/omni_lock/omni_lock_sim.c b/tests/omni_lock/omni_lock_sim.c index c042c6f..bbea603 100644 --- a/tests/omni_lock/omni_lock_sim.c +++ b/tests/omni_lock/omni_lock_sim.c @@ -16,7 +16,10 @@ void debug_print_hex(const char* prefix, const uint8_t* buf, size_t length) { printf("\n"); } -int ckb_cobuild_entry(ScriptEntryType, bool* cobuild_enabled) { +int ckb_cobuild_entry(const Env* env, ScriptEntryType entry, bool* cobuild_enabled) { + (void) env; + (void) entry; + *cobuild_enabled = false; return 0; }