btcs

bitcoin script parser/evaluator/compiler/decompiler
git clone git://jb55.com/btcs
Log | Files | Refs | README | LICENSE

commit 907597bddc77066eaf8b624fba10a309d8c34750
parent 270f7597b189064a7b298a14210e35884da8a266
Author: William Casarin <jb55@jb55.com>
Date:   Sun, 12 Nov 2017 13:11:05 -0800

IMPLEMENT ALL THE THINGS

not doing any descriptibe commit messages yet... too much churn

Diffstat:
MMakefile | 2+-
Malloc.c | 18++++++++++--------
Malloc.h | 11++++++-----
Mmain.c | 10+++++++---
Mop.c | 4++++
Mop.h | 14--------------
Mparser.y | 2+-
Mscript.c | 107++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Mtest.c | 5+++--
Mval.c | 9+++++++--
Mval.h | 27++++-----------------------
Avalstack.h | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
12 files changed, 166 insertions(+), 98 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,5 +1,5 @@ -CFLAGS=-O0 -Ideps -std=c99 -g -Wall -Wno-unused-variable -Wno-unused-function +CFLAGS=-O0 -Ideps -std=c99 -g -Wall -Wno-unused-variable -Wno-unused-function -Wunreachable-code DEPS=script.c \ oplookup.c \ diff --git a/alloc.c b/alloc.c @@ -40,14 +40,16 @@ num_pool_new(int *ind) { return p; } -char * -byte_pool_new(const u16 len) { +u8 * +byte_pool_new(const u16 len, u16 *ind) { assert(g_arenas.bytes_top - g_arenas.bytes + len <= g_arenas.nbytes); - char *start = g_arenas.bytes_top; + u8 *start = g_arenas.bytes_top; u16 *c = (u16*)g_arenas.bytes_top; *c++ = len; - char *p = (char*)c; + u8 *p = (u8*)c; p += len; + *ind = stack_size(&g_arenas.bytes_map); + stack_push(&g_arenas.bytes_map, (void*)start); g_arenas.bytes_top = p; assert(*p == 0); return start; @@ -55,7 +57,7 @@ byte_pool_new(const u16 len) { // useful for quick alloc/deallocs -char * +u8 * byte_pool_pop() { char *p = (char*)stack_pop(&g_arenas.bytes_map); u16 *c = (u16*)p; @@ -64,15 +66,15 @@ byte_pool_pop() { } -char * +u8 * byte_pool_get(const int ind, u16 *len) { char *p; u16 *up; - p = (char*)(g_arenas.bytes_map.bottom + ind); + p = (u8*)(g_arenas.bytes_map.bottom + ind); assert(p); up = (u16*)p; *len = *(up++); - p = (char*)up; + p = (u8*)up; return p; } diff --git a/alloc.h b/alloc.h @@ -4,6 +4,7 @@ #include "consts.h" #include "script_num.h" +#include "misc.h" // MAX_OPS_PER_SCRIPT * #define MAX_PUSHDATA_REFS (MAX_OPS_PER_SCRIPT * MAX_STACK_SIZE) @@ -12,14 +13,14 @@ struct num * num_pool_new(int *ind); struct num * num_pool_get(const int ind); -char * byte_pool_new(const u16 len); -char * byte_pool_get(const int ind, u16 *len); -char * byte_pool_pop(); +u8 *byte_pool_new(const u16 len, u16 *ind); +u8 *byte_pool_get(const int ind, u16 *len); +u8 *byte_pool_pop(); struct arenas { struct num *nums; - char *bytes; - char *bytes_top; + u8 *bytes; + u8 *bytes_top; struct stack bytes_map; int nbytes; int num_count; diff --git a/main.c b/main.c @@ -18,6 +18,9 @@ int main() { yyin = stdin; size_t size; + size_t bufsize = MAX_STACK_SIZE * MAX_STACK_SIZE; + int compiled_len; + u8 *buf = (u8*)malloc(bufsize); struct stack tmp_stack; alloc_arenas(0, MAX_STACK_SIZE, MAX_STACK_SIZE * MAX_STACK_SIZE); stack_init(&tmp_stack); @@ -27,13 +30,14 @@ int main() { } while(!feof(yyin)); size = g_reader_buf_top - g_reader_buf; - script_eval(g_reader_buf, size, &tmp_stack); + script_serialize(g_reader_buf, buf, bufsize, &compiled_len); + script_eval(buf, size, &tmp_stack); printf("script: "); - script_print_ops(g_reader_buf, g_reader_buf_top -); + script_print_ops(g_reader_buf, g_reader_buf_top); printf("stack: "); script_print_vals(&tmp_stack); - stack_free(&reader_stack); + stack_free(&g_reader_buf); stack_free(&tmp_stack); free_arenas(0); diff --git a/op.c b/op.c @@ -359,8 +359,12 @@ val_print(struct val val) { case VT_SCRIPTNUM: assert(val.ind != -1); n = num_pool_get(val.ind); + assert(n); printf("sn:%lu", n->val); break; + case VT_OP: + printf("op:%d", val.ind); + break; case VT_SMALLINT: printf("si:%d", val.ind); break; diff --git a/op.h b/op.h @@ -310,20 +310,6 @@ enum opcode op_tokenize(char *); void val_print(struct val); const char * val_name(struct val); -static inline void -stack_push_val(struct stack *stack, struct val val) { -#if 1 - printf("pushing val "); - val_print(val); - printf("\n"); -#endif - stack_push_small(struct val, stack, &val); -} - -static inline void -stack_push_op(struct stack *stack, enum opcode opcode) { - stack_push_small(enum opcode, stack, &opcode); -} #endif /* BCS_OP_H */ diff --git a/parser.y b/parser.y @@ -30,7 +30,7 @@ script: ; line: T_NEWLINE - | T_OP { stack_push_small(enum opcode, &reader_stack, &$1); } + | T_OP { stack_push_op(&reader_stack, &$1); } | T_EXAMPLE { ; } diff --git a/script.c b/script.c @@ -4,6 +4,7 @@ #include "script_num.h" #include "stack.h" #include "alloc.h" +#include "valstack.h" #include <stdio.h> #define SCRIPTERR(serr) script_add_error(c, opcode, serr) @@ -35,7 +36,7 @@ cast_to_bool(struct val val) { } case VT_DATA: { u16 len; - const char * bytes = byte_pool_get(val.ind, &len); + const u8 * bytes = byte_pool_get(val.ind, &len); return *bytes != 0; } } @@ -43,40 +44,62 @@ cast_to_bool(struct val val) { } int -script_getop(u8 **p, u8 *end, enum opcode *opcode, u8 *buf, int bufsize, u32 *outlen) { +script_getop(u8 **p, u8 *end, enum opcode *popcode, u8 *buf, int bufsize, u32 *outlen) { + *popcode = OP_INVALIDOPCODE; u32 nsize = 0; - *opcode = *(*p++); + int opcode; - if (*opcode > OP_PUSHDATA4) - return 1; + if (buf) + memset(buf, 0, bufsize); - if (*opcode < OP_PUSHDATA1) - nsize = *opcode; - else if (*opcode == OP_PUSHDATA1) { - if (end - *p < 1) - return 0; - nsize = *(*p++); - } - else if (*opcode == OP_PUSHDATA2) { - if (end - *p < 2) - return 0; - nsize = readle16(*p); - *p += 2; - } - else if (*opcode == OP_PUSHDATA4) { - if (end - *p < 4) + opcode = **p; + *p = *p + 1; + + if (opcode <= OP_PUSHDATA4) { + if (opcode < OP_PUSHDATA1) { + nsize = opcode; + } + else { + switch (opcode) { + case OP_PUSHDATA1: + if ((end - *p) < 1) { + return 0; + } + nsize = *(*p++); + break; + case OP_PUSHDATA2: + if ((end - *p) < 2) { + return 0; + } + nsize = readle16(*p); + *p += 2; + break; + case OP_PUSHDATA4: + if ((end - *p) < 4) { + return 0; + } + nsize = readle32(*p); + *p += 4; + break; + default: + break; + } + } + + if ((end - *p) < 0 || (u32)(end - *p) < nsize) { return 0; - nsize = readle32(*p); - *p += 4; - } + } - if (buf) { - *outlen = nsize; - memcpy(buf, *p, nsize); + if (buf) { + *outlen = nsize; + memcpy(buf, *p, nsize); + } *p += nsize; } + *popcode = opcode; + return 1; } @@ -110,10 +133,6 @@ script_eval(u8 *script, size_t script_size, struct stack *stack) { enum opcode opcode; script_getop(&p, top, &opcode, (u8*)tmpbuf, ARRAY_SIZE(tmpbuf), &tmplen); int if_exec = !stack_size(ifstack); - p++; - // TODO: pushdata ops - assert(!(opcode >= OP_PUSHDATA1 && opcode <= OP_PUSHDATA4)); - // Note OP_RESERVED does not count towards the opcode limit. if (opcode > OP_16 && ++op_count > MAX_OPS_PER_SCRIPT) SCRIPTERR("MAX_OPS_PER_SCRIPT"); @@ -137,7 +156,13 @@ script_eval(u8 *script, size_t script_size, struct stack *stack) { SCRIPTERR("SCRIPT_ERR_DISABLED_OPCODE"); // Disabled opcodes. } - + if (if_exec && 0 <= opcode && opcode <= OP_PUSHDATA4) { + /* if (fRequireMinimal && !CheckMinimalPush(vchPushValue, opcode)) { */ + /* return set_error(serror, SCRIPT_ERR_MINIMALDATA); */ + /* } */ + printf("pushing data of size %d\n", tmplen); + stack_push_data(stack, (u8*)tmpbuf, tmplen); + } else if (if_exec || (OP_IF <= opcode && opcode <= OP_ENDIF)) switch (opcode) { case OP_1NEGATE: case OP_1: @@ -659,19 +684,23 @@ script_push_int(struct stack *script, s64 intval) { void script_serialize(struct stack *stack, u8 *buf, int buflen, int* len) { - struct val val; - u8 *huh = (u8*)&val; + struct val *valp; + void **sp; u8 *p = buf; u16 valsize; *len = 0; - - while (stack_size(stack) != 0) { - val = stack_pop_val(stack); - printf("%02x %02x %02x %02x | ", huh[0], huh[1], huh[2], huh[3]); - printf("%d %d\n", val.type, val.ind); - val_serialize(val, &valsize, p, buflen-(p-buf)); + sp = stack->bottom; + + while (sp < stack->top) { + /* printf("%02x %02x %02x %02x | ", huh[0], huh[1], huh[2], huh[3]); */ + /* printf("%d %d\n", val.type, val.ind); */ + valp = (struct val*)sp; + val_print(*valp); + printf("\n"); + val_serialize(*valp, &valsize, p, buflen-(p-buf)); p += valsize; *len += valsize; assert(p-buf <= buflen); + sp++; } } diff --git a/test.c b/test.c @@ -3,6 +3,7 @@ #include "script.h" #include "op.h" #include "alloc.h" +#include "valstack.h" #include "tap.c/tap.h" typedef void (program)(struct stack *script, struct stack *stack,\ @@ -95,8 +96,8 @@ TEST(big_int_serializes_ok) { 0x04, 0xff, 0xff, 0xff, 0x7f, OP_ADD }; static u8 expected_out[] = { 0x05, 0xfe, 0xff, 0xff, 0xff, 0 }; - script_push_int(script, 2147483647); - script_push_int(script, 2147483647); + script_push_int(script, 2147483647UL); + script_push_int(script, 2147483647UL); stack_push_op(script, OP_ADD); script_serialize(script, buf, ARRAY_SIZE(buf), &len); diff --git a/val.c b/val.c @@ -41,9 +41,14 @@ val_serialize(struct val val, u16 *len, u8 *buf, int bufsize) { *buf = (u8)valsize; *len = valsize + 1; return; + case VT_OP: + *len = 1; + *buf = val.ind & 0xFF; + return; case VT_DATA: assert("!implement val_serialize VT_DATA"); byte_pool_get(val.ind, len); + return; case VT_SMALLINT: *len = 1; n = val.ind; @@ -66,8 +71,8 @@ val_eq(struct val a, struct val b, int require_minimal) { int eq = 0; static const int tmpsize = 4096; - static u8 tmpa[tmpsize]; - static u8 tmpb[tmpsize]; + static u8 tmpa[4096]; + static u8 tmpb[4096]; const u8 *abytes, *bbytes; // TODO: do I need to serialize to compare? diff --git a/val.h b/val.h @@ -4,19 +4,21 @@ #include "misc.h" #include "stack.h" +#include "op.h" enum valtype { VT_SCRIPTNUM=0, VT_SMALLINT, + VT_OP, VT_DATA, VT_N }; // UPDATE VAL_TYPE_BITS if you need more valtypes -#define VAL_TYPE_BITS 2 +#define VAL_TYPE_BITS 3 /* static const int COMPACT_VAL_BITS = (32-VAL_TYPE_BITS-1); */ -#define VAL_COMPACT_BITS 30 +#define VAL_COMPACT_BITS 29 struct val { u8 type : VAL_TYPE_BITS; @@ -29,27 +31,6 @@ struct val { STATIC_ASSERT(sizeof(struct val) <= 4, val_doesnt_fit_in_stack); -static inline struct val -stack_top_val(struct stack *stack, int ind) { - struct val val; - stack_top_small(struct val, stack, &val, ind); - return val; -} - -static inline struct val -stack_pop_val(struct stack *stack) { - struct val val; - val = stack_top_val(stack, -1); - stack_pop(stack); - return val; -} - -static inline void -stack_set_val(struct stack *stack, int ind, struct val val) { - struct val *pval = (struct val *)(stack->top + ind); - *pval = val; -} - int val_eq(struct val a, struct val b, int require_minimal); diff --git a/valstack.h b/valstack.h @@ -0,0 +1,55 @@ + +#ifndef BTCS_VALSTACK_H +#define BTCS_VALSTACK_H + +#include "op.h" +#include "val.h" + +static inline struct val +stack_top_val(struct stack *stack, int ind) { + struct val val; + stack_top_small(struct val, stack, &val, ind); + return val; +} + +static inline struct val +stack_pop_val(struct stack *stack) { + struct val val; + val = stack_top_val(stack, -1); + stack_pop(stack); + return val; +} + +static inline void +stack_set_val(struct stack *stack, int ind, struct val val) { + struct val *pval = (struct val *)(stack->top + ind); + *pval = val; +} + +static inline void +stack_push_val(struct stack *stack, struct val val) { +#if 0 + printf("pushing val "); + val_print(val); + printf("\n"); +#endif + stack_push_small(struct val, stack, &val); +} + +static inline void +stack_push_data(struct stack *stack, u8 *data, int len) { + struct val val = { .type = VT_DATA }; + u8 *p; + u16 ind; + byte_pool_new(len, &ind); + val.ind = ind; + stack_push_val(stack, val); +} + +static inline void +stack_push_op(struct stack *stack, enum opcode opcode) { + struct val val = { .type = VT_OP, .ind = opcode }; + stack_push_val(stack, val); +} + +#endif /* BTCS_VALSTACK_H */