btcs

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

commit 743e16de217c5a621c6c76e89175edeb09797da1
parent d5f06434148bdd7f2d230626ab96c5947a79c41a
Author: William Casarin <jb55@jb55.com>
Date:   Wed, 25 Oct 2017 19:15:00 -0700

progress: lots of it

Diffstat:
MMakefile | 2+-
Mmain.c | 4++--
Mmisc.h | 15+++++++++++++--
Mop.c | 13+++++++++++++
Mop.h | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Mparser.y | 2+-
Mscript.c | 246++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Mscript.h | 4+++-
Mstack.c | 31++++++++++++++++++++++++++++---
Mstack.h | 43++++++++++++++++++++++++++++++++-----------
10 files changed, 370 insertions(+), 41 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,5 +1,5 @@ -CFLAGS=-O2 -Wall -Wno-unused-variable +CFLAGS=-O0 -g -Wall -Wno-unused-variable FORBIN=script.o \ parser.tab.o \ diff --git a/main.c b/main.c @@ -24,9 +24,9 @@ int main() { script_eval(&reader_stack, &tmp_stack); printf("script: "); - script_print(&reader_stack); + script_print_ops(&reader_stack); printf("stack: "); - script_print(&tmp_stack); + script_print_vals(&tmp_stack); stack_free(&reader_stack); stack_free(&tmp_stack); diff --git a/misc.h b/misc.h @@ -4,7 +4,18 @@ #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) -typedef unsigned char u8; -typedef unsigned int u32; +#include <stdint.h> + +#define DEBUG 0 + +/* #define debug(fmt, ...) \ */ +/* do { if (DEBUG) fprintf(stderr, fmt, __VA_ARGS__); } while (0) */ + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef int64_t s64; + +#define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[(!!(COND))*2-1] #endif /* BCS_MISC_H */ diff --git a/op.c b/op.c @@ -349,3 +349,16 @@ op_tokenize(char *str) { } +void +val_print(struct val val) { + printf("%d", val.val); +} + +const char * +val_name(struct val val) { + switch(val.type) { + case VT_INT: return val.is_big? "bi" : "ci"; break; + case VT_DATA: return val.is_big? "bd" : "cd"; break; + } + return "UNK_VAL"; +} diff --git a/op.h b/op.h @@ -2,6 +2,10 @@ #ifndef BCS_OP_H #define BCS_OP_H +#include "misc.h" +#include "stack.h" +#include <stdio.h> +#include <math.h> enum opcode { @@ -296,11 +300,58 @@ enum opcode_token _OP_INVALIDOPCODE, }; +enum valtype { + VT_INT=0, + VT_DATA, // this could be an overflowed int + VT_N +}; +// UPDATE VAL_TYPE_BITS if you need more valtypes + + +static const int VAL_TYPE_BITS = 1; +static const int COMPACT_VAL_BITS = (32-VAL_TYPE_BITS-1); + +struct val { + u8 is_big : 1; + u8 type : VAL_TYPE_BITS; + u32 val : COMPACT_VAL_BITS; +}; + +STATIC_ASSERT(sizeof(struct val) <= 4, stack_op_doesnt_fit_in_stack); + // Maximum value that an opcode can be static const unsigned int MAX_OPCODE = OP_NOP10; void op_add(enum opcode); const char * op_name(enum opcode); 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 DEBUG + printf("pushing val "); + val_print(val); + printf("\n"); +#endif + void *tmp = 0; + stack_push_small(stack, &val, sizeof(struct val)); +} + +static inline struct val +stack_top_val(struct stack *stack, int ind) { + struct val val; + void *p = stack_top(stack, ind); + memcpy(&val, &p, sizeof(struct val)); + 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; +} + #endif /* BCS_OP_H */ diff --git a/parser.y b/parser.y @@ -30,7 +30,7 @@ script: ; line: T_NEWLINE - | T_OP { stack_push(&reader_stack, $1); } + | T_OP { stack_push_small(&reader_stack, &$1, 4); } | T_EXAMPLE { ; } diff --git a/script.c b/script.c @@ -4,27 +4,64 @@ #include "stack.h" #include <stdio.h> +static int stack_end(struct stack *stack) { + return stack_size(stack); +} + void script_add_error(const char *serror) { // TODO: set_error printf("error: %s\n", serror); } -u8 +struct val script_num(int n) { - // TODO: scriptnum - /* assert(!"Implement script_num"); */ - return (u8)n; + // TODO: scriptnum arithmetic + struct val val; + val.type = VT_INT; + val.is_big = n > (2^COMPACT_VAL_BITS)-1; + + if (!val.is_big) { + val.val = n; + return val; + } + else { + // looks like we have an int bigger than COMPACT_VAL_BITS in size, + // we'll need to store it in a buffer somewhere + assert(!"Implement script_num"); + } } +int +cast_to_bool(struct val val) { + // TODO: implement cast_to_bool + assert(!"Implement cast_to_bool"); + /* for (unsigned int i = 0; i < vch.size(); i++) */ + /* { */ + /* if (vch[i] != 0) */ + /* { */ + /* // Can be negative zero */ + /* if (i == vch.size()-1 && vch[i] == 0x80) */ + /* return false; */ + /* return true; */ + /* } */ + /* } */ + /* return false; */ +} + void script_eval(struct stack *script, struct stack *stack) { int op_count = 0; - u8 *p = script->bottom; - struct stack tmpstack; + void **p = script->bottom; + struct val val; + struct stack _altstack; + struct stack *altstack = &_altstack; + u8 tmpbuf[32]; + stack_init(altstack); while (p < script->top) { - u8 opcode = *p++; + enum opcode opcode = *(enum opcode*)p; + p++; // TODO: pushdata ops assert(!(opcode >= OP_PUSHDATA1 && opcode <= OP_PUSHDATA4)); @@ -70,41 +107,210 @@ script_eval(struct stack *script, struct stack *stack) { case OP_15: case OP_16: { - u8 sn = script_num((int)opcode - (int)(OP_1 - 1)); - stack_push(stack, sn); + struct val sn = script_num((int)opcode - (int)(OP_1 - 1)); + stack_push_val(stack, sn); } break; case OP_NOP: break; + case OP_INVALIDOPCODE: + { + return script_add_error("SCRIPT_ERR_INVALID_OPCODE"); + } + break; + + case OP_FROMALTSTACK: + { + if (stack_size(altstack) < 1) + return script_add_error("SCRIPT_ERR_INVALID_ALTSTACK_OPERATION"); + stack_push(stack, stack_top(altstack, -1)); + stack_pop(altstack); + } + break; + + case OP_2DROP: + { + // (x1 x2 -- ) + if (stack_size(stack) < 2) + return script_add_error("SCRIPT_ERR_INVALID_STACK_OPERATION"); + stack_pop(stack); + stack_pop(stack); + } + break; case OP_2DUP: { - // (x1 x2 -- x1 x2 x1 x2) - if (stack_size(stack) < 2) - return script_add_error("SCRIPT_ERR_INVALID_STACK_OPERATION"); - u8 v1 = stack_top(stack, -2); - u8 v2 = stack_top(stack, -1); - stack_push(stack, v1); - stack_push(stack, v2); + // (x1 x2 -- x1 x2 x1 x2) + if (stack_size(stack) < 2) + return script_add_error("SCRIPT_ERR_INVALID_STACK_OPERATION"); + struct val v1 = stack_top_val(stack, -2); + struct val v2 = stack_top_val(stack, -1); + stack_push_val(stack, v1); + stack_push_val(stack, v2); + } + break; + + case OP_3DUP: + { + // (x1 x2 x3 -- x1 x2 x3 x1 x2 x3) + if (stack_size(stack) < 3) + return script_add_error("SCRIPT_ERR_INVALID_STACK_OPERATION"); + struct val v1 = stack_top_val(stack, -3); + struct val v2 = stack_top_val(stack, -2); + struct val v3 = stack_top_val(stack, -1); + stack_push_val(stack, v1); + stack_push_val(stack, v2); + stack_push_val(stack, v3); + } + break; + + case OP_2OVER: + { + // (x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2) + if (stack_size(stack) < 4) + return script_add_error("SCRIPT_ERR_INVALID_STACK_OPERATION"); + struct val v1 = stack_top_val(stack, -4); + struct val v2 = stack_top_val(stack, -3); + stack_push_val(stack, v1); + stack_push_val(stack, v2); + } + break; + + case OP_2ROT: + { + // (x1 x2 x3 x4 x5 x6 -- x3 x4 x5 x6 x1 x2) + if (stack_size(stack) < 6) + return script_add_error("SCRIPT_ERR_INVALID_STACK_OPERATION"); + struct val t6 = stack_top_val(stack, -6); + struct val t5 = stack_top_val(stack, -5); + *(stack->top - 6) = *(stack->top - 4); + *(stack->top - 5) = *(stack->top - 3); + *(stack->top - 4) = *(stack->top - 2); + *(stack->top - 3) = *(stack->top - 1); + stack_set_val(stack, -2, t6); + stack_set_val(stack, -1, t5); + } + break; + + case OP_2SWAP: + { + // (x1 x2 x3 x4 -- x3 x4 x1 x2) + if (stack_size(stack) < 4) + return script_add_error("SCRIPT_ERR_INVALID_STACK_OPERATION"); + + struct val t4 = stack_top_val(stack, -4); + struct val t3 = stack_top_val(stack, -3); + + *(stack->top - 4) = *(stack->top - 2); + *(stack->top - 3) = *(stack->top - 1); + stack_set_val(stack, -2, t4); + stack_set_val(stack, -1, t3); + } + break; + + case OP_IFDUP: + { + // (x - 0 | x x) + if (stack_size(stack) < 1) + return script_add_error("SCRIPT_ERR_INVALID_STACK_OPERATION"); + struct val val = stack_top_val(stack, -1); + if (cast_to_bool(val)) + stack_push_val(stack, val); + } + break; + + case OP_DEPTH: + { + // -- stacksize + struct val sn = script_num(stack_size(stack)); + stack_push_val(stack, sn); } break; + + case OP_DROP: + { + // (x -- ) + if (stack_size(stack) < 1) + return script_add_error("SCRIPT_ERR_INVALID_STACK_OPERATION"); + stack_pop(stack); + } + break; + + case OP_DUP: + { + // (x -- x x) + if (stack_size(stack) < 1) + return script_add_error("SCRIPT_ERR_INVALID_STACK_OPERATION"); + struct val val = stack_top_val(stack, (-1)); + stack_push_val(stack, val); + } + break; + + case OP_NIP: + { + // (x1 x2 -- x2) + if (stack_size(stack) < 2) + return script_add_error("SCRIPT_ERR_INVALID_STACK_OPERATION"); + // TODO: sanity check - stack_size() == stack_end(stack) + stack_erase(stack, stack_size(stack) - 2); + } + break; + + case OP_OVER: + { + // (x1 x2 -- x1 x2 x1) + if (stack_size(stack) < 2) + return script_add_error("SCRIPT_ERR_INVALID_STACK_OPERATION"); + struct val val = stack_top_val(stack, (-2)); + stack_push_val(stack, val); + } + break; + + default: { + sprintf((char*)tmpbuf, "unhandled opcode %s", op_name(opcode)); + return script_add_error((char*)tmpbuf); + } + } } } -void script_print(struct stack *stack) { - u8 *p = stack->bottom; - while (p < stack->top) - printf("%s ", op_name(*p++)); +void script_print_ops(struct stack *stack) { + void **p = stack->bottom; + while (p < stack->top) { + enum opcode opcode = (enum opcode)*p++; + printf("%s ", op_name(opcode)); + } printf("\n"); } +void script_print_vals(struct stack *stack) { + void **p = stack->bottom; + while (p < stack->top) { + struct val val; + memcpy(&val, &*p++, sizeof(struct val)); + val_print(val); + putchar(' '); + } + putchar('\n'); +} + int script_init(struct script *script) { + stack_init(&script->pushdata); return stack_init(&script->data); } void script_free(struct script *script) { stack_free(&script->data); + void **p = script->pushdata.bottom; + assert(p); + while (p < script->pushdata.top) { + assert(*p); + p++; + } +} + +int script_new(struct script *script) { } diff --git a/script.h b/script.h @@ -22,12 +22,14 @@ static const unsigned int LOCKTIME_THRESHOLD = 500000000; // Tue Nov 5 00:53:20 struct script { struct stack data; + struct stack pushdata; // a stack of pushdata stacks }; int script_new(struct script *); void script_free(struct script *); void script_eval(struct stack *, struct stack*); -void script_print(struct stack *); +void script_print_ops(struct stack *); +void script_print_vals(struct stack *); #endif /* BTCS_SCRIPT_H */ diff --git a/stack.c b/stack.c @@ -1,6 +1,7 @@ #include "stack.h" #include <stdlib.h> +#include <stdio.h> #include <string.h> void @@ -11,7 +12,7 @@ stack_clear(struct stack *stack) { int stack_init_size(struct stack *stack, int capacity) { if (capacity > DEFAULT_STACK_SIZE) { - u8 *bottom = (u8*)malloc(capacity); + void *bottom = malloc(capacity * sizeof(void*)); if (!bottom) return 0; stack->bottom = bottom; stack->top = bottom; @@ -45,11 +46,35 @@ stack_expand(struct stack *stack) { } void -stack_push(struct stack *stack, u8 byte) { +stack_push(struct stack *stack, void *val) { + #if DEBUG + printf("pushing: %d %p\n", stack_size(stack), val); + #endif + if ((stack_size(stack) + 1) > stack->capacity) { stack_expand(stack); } - *stack->top++ = byte; + *stack->top++ = val; +} + +// TODO: UNTESTED! +void * +stack_erase(struct stack *stack, int ind) { + int size = stack_size(stack); + assert(ind >= 0); + assert(ind < size); + void * erased = *(stack->bottom + ind); + memcpy(stack->bottom + ind, stack->bottom + ind + size, size); + stack->top--; + return erased; +} + +void * +stack_pop(struct stack *stack) { + assert(stack_size(stack) != 0); + void * v = stack_top(stack, -1); + stack->top--; + return v; } diff --git a/stack.h b/stack.h @@ -3,31 +3,36 @@ #define BTCS_STACK_H #include <assert.h> +#include <string.h> +#include <stdio.h> #include "misc.h" // Maximum number of values on script interpreter stack static const int MAX_STACK_SIZE = 1000; // Maximum number of values on script interpreter stack -static const int DEFAULT_STACK_SIZE = 64; +static const int DEFAULT_STACK_SIZE = 100; struct stack { - u8 _data[DEFAULT_STACK_SIZE]; - u8 * bottom; - u8 * top; + void *_data[DEFAULT_STACK_SIZE]; + void **bottom; + void **top; int capacity; }; -int stack_init(struct stack *); -int stack_init_size(struct stack *, int size); -void stack_free(struct stack *); -void stack_clear(struct stack *); -void stack_push(struct stack *, u8 val); +int stack_init(struct stack *); +int stack_init_size(struct stack *, int size); +void stack_free(struct stack *); +void stack_clear(struct stack *); +void* stack_pop(struct stack *); +void* stack_erase(struct stack *, int ind); +void stack_push(struct stack *, void *val); -static inline u8 +static inline void * stack_top(struct stack *stack, int ind) { - u8 *p = stack->top + ind; + void **p = stack->top + ind; assert(p >= stack->bottom); + assert(p); return *p; } @@ -36,4 +41,20 @@ stack_size(struct stack *stack) { return stack->top - stack->bottom; } +static inline void +stack_push_small(struct stack *stack, void *p, size_t size) { +#if DEBUG + u8 *b = (u8*)p; + printf("pushing small", ""); + for (size_t i = 0; i < size; ++i) { + printf(" %02x", b[i]); + } + printf("\n"); +#endif + assert(size <= sizeof(void*)); + void *tmp = 0; + memcpy(&tmp, p, size); + stack_push(stack, tmp); +} + #endif /* BTCS_STACK_H */