btcs

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

commit f356e6217ece3187666cc957b3b7ee2a94ad27c7
parent db74143362d279e09ed7f5a4cad83bb7590f2dc6
Author: William Casarin <jb55@jb55.com>
Date:   Sun, 12 Nov 2017 19:24:48 -0800

integers: more tests pass for negative integers

Diffstat:
MMakefile | 2+-
Malloc.c | 21+++++++++++++++------
Mmisc.h | 10++++++++++
Mop.c | 7+++++++
Mscript.c | 24+++++++++++++++---------
Mscript_num.c | 66+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Mstack.c | 2+-
Mtest.c | 23+++++++++++++++++++++--
Mval.h | 1-
Mvalstack.h | 3++-
10 files changed, 131 insertions(+), 28 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,5 +1,5 @@ -CFLAGS=-O0 -Ideps -std=c99 -g -Wall -Wno-unused-variable -Wno-unused-function -Wunreachable-code +CFLAGS=-g -O0 -Ideps -std=c99 -Wall -Wno-unused-variable -Wno-unused-function -Wunreachable-code DEPS=script.c \ oplookup.c \ diff --git a/alloc.c b/alloc.c @@ -21,6 +21,7 @@ alloc_arena_sizes(struct arenas *arenas, const int nums, const int bytes) { arenas->bytes = (u8*)calloc(bytes, 1); stack_init_size(&arenas->bytes_map, bytes); arenas->bytes_top = arenas->bytes; + arenas->num_count = 0; arenas->nbytes = bytes; } @@ -44,12 +45,13 @@ num_pool_new(int *ind) { p = &g_arenas.nums[*ind]; assert(p); assert(p->val == 0 && p->ind == 0); + p->ind = *ind; return p; } u8 * -byte_pool_new(const u16 len, u16 *ind) { - assert(g_arenas.bytes_top - g_arenas.bytes + len <= g_arenas.nbytes); +byte_pool_new(u16 len, u16 *ind) { + assert((g_arenas.bytes_top - g_arenas.bytes + len) <= g_arenas.nbytes); u8 *start = g_arenas.bytes_top; u16 *c = (u16*)g_arenas.bytes_top; *c++ = len; @@ -59,7 +61,8 @@ byte_pool_new(const u16 len, u16 *ind) { stack_push(&g_arenas.bytes_map, (void*)start); g_arenas.bytes_top = p; assert(*p == 0); - return start; + assert(((g_arenas.bytes_top - g_arenas.bytes) + len) <= g_arenas.nbytes); + return p - len; } @@ -74,14 +77,19 @@ byte_pool_pop() { u8 * -byte_pool_get(const int ind, u16 *len) { +byte_pool_get(int ind, u16 *len) { + assert(ind <= stack_size(&g_arenas.bytes_map) - 1); + void **vp; u8 *p; u16 *up; - p = (u8*)(g_arenas.bytes_map.bottom + ind); + vp = g_arenas.bytes_map.bottom + ind; + p = (u8*)(*vp); + assert((g_arenas.bytes_top - g_arenas.bytes + *len) <= g_arenas.nbytes); assert(p); up = (u16*)p; - *len = *(up++); + *len = *up++; p = (u8*)up; + assert((g_arenas.bytes_top - g_arenas.bytes + *len) <= g_arenas.nbytes); return p; } @@ -93,3 +101,4 @@ free_arenas(struct arenas *arenas) { free(arenas->bytes); free(arenas->nums); } + diff --git a/misc.h b/misc.h @@ -7,6 +7,7 @@ /* #include <endian.h> */ #include <string.h> #include <stdint.h> +#include <stdio.h> #define DEBUG 0 @@ -45,5 +46,14 @@ readle32(const u8* ptr) { return le32toh(x); } +void static inline +print_bytes(u8 *bytes, size_t size) { + size_t i; + for (i = 0; i < size; i++) { + printf("%02x ", bytes[i]); + } + printf("\n"); +} + #endif /* BCS_MISC_H */ diff --git a/op.c b/op.c @@ -368,6 +368,13 @@ val_print(struct val val) { case VT_SMALLINT: printf("si:%d", val.ind); break; + case VT_DATA: { + u16 len; + u8 *data = byte_pool_get(val.ind, &len); + printf("data (%d): ", len); + print_bytes(data, len); + break; + } default: assert(!"val_print data"); } diff --git a/script.c b/script.c @@ -160,7 +160,6 @@ script_eval(u8 *script, size_t script_size, struct stack *stack) { /* 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) { @@ -546,14 +545,21 @@ script_eval(u8 *script, size_t script_size, struct stack *stack) { if (stack_size(stack) < 2) return SCRIPTERR("SCRIPT_ERR_INVALID_STACK_OPERATION"); struct num *bn1, *bn2, bn; - sn_from_val(stack_top_val(stack, -2), &bn1, require_minimal); - sn_from_val(stack_top_val(stack, -1), &bn2, require_minimal); + enum sn_result res; + bn.ind = -1; + res = sn_from_val(stack_top_val(stack, -2), &bn1, require_minimal); + if (res == SN_ERR_OVERFLOWED_INT) + return SCRIPTERR("SCRIPT_INT_OVERFLOW"); + res = sn_from_val(stack_top_val(stack, -1), &bn2, require_minimal); + if (res == SN_ERR_OVERFLOWED_INT) + return SCRIPTERR("SCRIPT_INT_OVERFLOW"); /* struct num bn(0); */ switch (opcode) { case OP_ADD: - bn.val = bn1->val + bn2->val; - break; + printf("%d + %d\n", bn1->val, bn2->val); + bn.val = bn1->val + bn2->val; + break; case OP_SUB: bn.val = bn1->val - bn2->val; @@ -580,7 +586,8 @@ script_eval(u8 *script, size_t script_size, struct stack *stack) { } stack_pop(stack); stack_pop(stack); - stack_push_val(stack, sn_to_val(&bn)); + struct val bnval = sn_to_val(&bn); + stack_push_val(stack, bnval); if (opcode == OP_NUMEQUALVERIFY) { @@ -640,11 +647,12 @@ void script_print(u8 *script, size_t script_size) { void script_print_vals(struct stack *stack) { void **p = stack->bottom; + int c = 0; while (p < stack->top) { struct val val; memcpy(&val, &*p++, sizeof(struct val)); + printf("s[%d] ", c++); val_print(val); - putchar(' '); } putchar('\n'); } @@ -695,8 +703,6 @@ script_serialize(struct stack *stack, u8 *buf, int buflen, int* len) { /* 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; diff --git a/script_num.c b/script_num.c @@ -2,6 +2,7 @@ #include "script_num.h" #include "alloc.h" #include "val.h" +#include <limits.h> /** * Numeric opcodes (OP_1ADD, etc) are restricted to operating on 4-byte @@ -57,27 +58,70 @@ sn_serialize(struct num *sn, u8 *buf, int bufsize, u16 *len) { // 0x80 to it, since it will be subtracted and interpreted as a negative when // converting to an integral. - if (*p & 0x80) + if (*(p - 1) & 0x80) *p++ = neg ? 0x80 : 0; else if (neg) - *p++ |= 0x80; + *(p - 1) |= 0x80; *len = p - buf; } +static inline int +int_overflowed(s64 val) { + return val < INT_MIN || val > INT_MAX; +} + int sn_overflowed(struct num *num) { - return (num->val & ~0xFFFFFFFF) != 0; + return int_overflowed(num->val); +} + + +s64 +int_from_data(u8 *data, u16 size) { + s64 result = 0; + + if (size == 0) + return 0; + + for (size_t i = 0; i != size; ++i) + result |= (s64)((data[i]) << 8*i); + + // If the input vector's most significant byte is 0x80, remove it from + // the result's msb and return a negative. + if (data[size-1] & 0x80) { + return -((s64)(result & ~(0x80ULL << (8 * (size - 1))))); + } + + return result; +} + + +enum sn_result +sn_from_data(u8 *data, u16 size, struct num **num) { + int ind; + s64 i; + i = int_from_data(data, size); + if (int_overflowed(i)) { + *num = 0; + return SN_ERR_OVERFLOWED_INT; + } + *num = num_pool_new(&ind); + (*num)->val = i; + (*num)->ind = ind; + return SN_SUCCESS; } // Return a script num only if it's still a 4-byte integer enum sn_result sn_from_val(struct val val, struct num ** sn, int require_minimal) { + switch (val.type) { case VT_SCRIPTNUM: *sn = num_pool_get(val.ind); + assert((*sn)->ind == val.ind); assert(*sn); // we're trying to return a scriptnum that is larger than 4 bytes. This is not @@ -88,9 +132,17 @@ sn_from_val(struct val val, struct num ** sn, int require_minimal) { } break; - case VT_DATA: - assert(!"TODO implement raw data to script num"); + case VT_DATA: { + u8 *data; + u16 size; + enum sn_result res; + assert(val.ind != -1); + data = byte_pool_get(val.ind, &size); + return sn_from_data(data, size, sn); } + } + + assert("!sn_from_val: unhandled"); return SN_SUCCESS; } @@ -101,7 +153,7 @@ sn_to_val(struct num *sn) { struct num *snref; int ind; - // TODO: implement return overflowed scriptnums + //TODO: implement return overflowed scriptnums assert(!sn_overflowed(sn)); val.type = VT_SCRIPTNUM; @@ -109,7 +161,7 @@ sn_to_val(struct num *sn) { val.ind = sn->ind; else { snref = num_pool_new(&ind); - *sn = *snref; + snref->val = sn->val; val.ind = ind; } return val; diff --git a/stack.c b/stack.c @@ -13,7 +13,7 @@ stack_clear(struct stack *stack) { int stack_init_size(struct stack *stack, int capacity) { if (capacity > DEFAULT_STACK_SIZE) { - void *bottom = malloc(capacity * sizeof(void*)); + void *bottom = calloc(capacity, sizeof(void*)); if (!bottom) return 0; stack->bottom = bottom; stack->top = bottom; diff --git a/test.c b/test.c @@ -19,8 +19,6 @@ typedef void (program)(struct stack *script, struct stack *stack,\ static void cmp_data(const u8 *a, const u8 *b, int alen, int blen, const char *msg) { int i = 0; - cmp_ok(alen, "==", blen); - if (memcmp(a, b, blen) != 0) { printf("#\n# Failed data cmp test\n# > "); for (i = 0; i < alen; ++i) @@ -89,6 +87,25 @@ TEST(test_2dup_not_enough_input) { ok(res == 0, "2dup fail on small stack"); } +TEST(negative_integer) { + static u8 buf[6]; + int len; + u8 in_script[] = { 0x01, 0x82 }; + int res = script_eval(in_script, ARRAY_SIZE(in_script), stack); + script_serialize(stack, buf, ARRAY_SIZE(buf), &len); + cmp_data(buf, in_script, len, ARRAY_SIZE(in_script), "negative 2 serializes ok"); +} + +TEST(add_negative_two) { + static u8 buf[12]; + int len; + static u8 in_script[] = { 0x01, 0x82, 0x01, 0x82, OP_ADD }; + static u8 expected_out[] = { 0x01, 0x84 }; + int res = script_eval(in_script, ARRAY_SIZE(in_script), stack); + script_serialize(stack, buf, ARRAY_SIZE(buf), &len); + cmp_data(buf, expected_out, len, ARRAY_SIZE(expected_out), "add negative two twice"); +} + TEST(big_int_serializes_ok) { int len; static u8 buf[12]; @@ -160,6 +177,8 @@ main(int argc, char *argv[]) { RUNTEST(test_simple); RUNTEST(test_nip); RUNTEST(test_2dup_not_enough_input); + RUNTEST(negative_integer); + RUNTEST(add_negative_two); RUNTEST(big_int_serializes_ok); stack_free(script); diff --git a/val.h b/val.h @@ -4,7 +4,6 @@ #include "misc.h" #include "stack.h" -#include "op.h" enum valtype { VT_SCRIPTNUM=0, diff --git a/valstack.h b/valstack.h @@ -41,7 +41,8 @@ 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); + p = byte_pool_new(len, &ind); + memcpy(p, data, len); val.ind = ind; stack_push_val(stack, val); }