btcs

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

commit d28de7fcf808c69c93528472cd341812eecdede6
parent 25ccf3328c9ba80c12a981ecabda89afbd7a29d2
Author: William Casarin <jb55@jb55.com>
Date:   Sun,  3 Dec 2017 21:14:33 -0800

data: implement PUSHDATA

* Generate proper pushdata
* Parse pushdata properly

I'm making a bunch of assumptions here, need to compare
against core

Closes #7

Diffstat:
Malloc.c | 14+++++++-------
Malloc.h | 4++--
Mlexer.l | 4++++
Mmain.c | 5+++++
Mop.c | 4++--
Mparser.y | 3+++
Mscript.c | 23++++++++++++++---------
Mscript_num.c | 3++-
Mval.c | 33+++++++++++++++++++++++++++------
Mval.h | 2+-
10 files changed, 67 insertions(+), 28 deletions(-)

diff --git a/alloc.c b/alloc.c @@ -50,10 +50,10 @@ num_pool_new(int *ind) { } u8 * -byte_pool_new(u16 len, u16 *ind) { +byte_pool_new(u32 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; + u32 *c = (u32*)g_arenas.bytes_top; *c++ = len; u8 *p = (u8*)c; p += len; @@ -70,23 +70,23 @@ byte_pool_new(u16 len, u16 *ind) { u8 * byte_pool_pop() { u8 *p = (u8*)stack_pop(&g_arenas.bytes_map); - u16 *c = (u16*)p; - memset(p, 0, *c + sizeof(u16)); + u32 *c = (u32*)p; + memset(p, 0, *c + sizeof(u32)); return p; } u8 * -byte_pool_get(int ind, u16 *len) { +byte_pool_get(int ind, u32 *len) { assert(ind <= stack_size(&g_arenas.bytes_map) - 1); void **vp; u8 *p; - u16 *up; + u32 *up; 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; + up = (u32*)p; *len = *up++; p = (u8*)up; assert((g_arenas.bytes_top - g_arenas.bytes + *len) <= g_arenas.nbytes); diff --git a/alloc.h b/alloc.h @@ -14,8 +14,8 @@ struct num * num_pool_new(int *ind); struct num * num_pool_pop(); struct num * num_pool_get(const int ind); -u8 *byte_pool_new(u16 len, u16 *ind); -u8 *byte_pool_get(int ind, u16 *len); +u8 *byte_pool_new(u32 len, u16 *ind); +u8 *byte_pool_get(int ind, u32 *len); u8 *byte_pool_pop(); struct arenas { diff --git a/lexer.l b/lexer.l @@ -24,6 +24,10 @@ [oO][pP]_ {} @[a-fA-F0-9]+ { + if (strlen(yytext + 1) % 2 != 0) { + yylval.str = "Invalid data string, byte string must have even length"; + return T_ERR; + } yylval.str = yytext + 1; return T_DATA; } diff --git a/main.c b/main.c @@ -64,3 +64,8 @@ void yyerror(const char* s) { exit(1); } +void +parse_error(char* err) { + fprintf(stderr, "[btcs] parse error: %s\n", err); + exit(1); +} diff --git a/op.c b/op.c @@ -370,13 +370,13 @@ val_print(struct val val) { printf("%d", val.ind); break; case VT_DATA: { - u16 len; + u32 len; u8 *data = byte_pool_get(val.ind, &len); if (len == 0) printf("0", len); else - printf("0x", len); + printf("@", len); print_bytes(data, len, 0); break; diff --git a/parser.y b/parser.y @@ -22,6 +22,7 @@ void yyerror(const char* s); %token T_INT %token T_VAL %token T_DATA +%token T_ERR %token T_NEWLINE T_QUIT T_EXAMPLE %type<opcode> T_OP @@ -29,6 +30,7 @@ void yyerror(const char* s); %type<str> T_DATA %type<val> T_VAL %type<str> T_EXAMPLE +%type<str> T_ERR %start script @@ -43,6 +45,7 @@ line: T_NEWLINE | T_VAL { stack_push_val(&g_reader_stack, $1); } | T_OP { stack_push_op(&g_reader_stack, $1); } | T_DATA { script_push_datastr(&g_reader_stack, $1); } + | T_ERR { parse_error($1); } | T_EXAMPLE { ; } diff --git a/script.c b/script.c @@ -35,7 +35,7 @@ cast_to_bool(struct val val) { return sn->val != 0; } case VT_DATA: { - u16 len; + u32 len; const u8 * bytes = byte_pool_get(val.ind, &len); return *bytes != 0; } @@ -47,6 +47,8 @@ int script_getop(u8 **p, u8 *end, enum opcode *popcode, u8 *buf, int bufsize, u32 *outlen) { *popcode = OP_INVALIDOPCODE; u32 nsize = 0; + // TODO: HACK: I don't know why I have to do this... 🤔 + end++; int opcode; @@ -54,7 +56,7 @@ script_getop(u8 **p, u8 *end, enum opcode *popcode, u8 *buf, int bufsize, u32 *o memset(buf, 0, bufsize); opcode = **p; - *p = *p + 1; + (*p)++; if (opcode <= OP_PUSHDATA4) { if (opcode < OP_PUSHDATA1) { @@ -72,27 +74,30 @@ script_getop(u8 **p, u8 *end, enum opcode *popcode, u8 *buf, int bufsize, u32 *o if ((end - *p) < 2) { return 0; } - nsize = readle16(*p); - *p += 2; + nsize = **(u16**)p; + printf("nsize %d %02x %02x\n", nsize, **p, *(*p + 1)); + *p = *p + 2; break; case OP_PUSHDATA4: if ((end - *p) < 4) { return 0; } - nsize = readle32(*p); - *p += 4; + nsize = **(u32**)p; + *p = *p + 4; break; default: break; } } - if ((end - *p) < 0 || (u32)(end - *p) < nsize) { + if ((end - *p) < 0 || (end - *p) < nsize) { + printf("early exit %d %d\n", (u32)(end - *p), nsize); return 0; } if (buf) { *outlen = nsize; + printf("memcpy outlen %d %02x %02x\n", nsize, **p, *(*p + 1)); memcpy(buf, *p, nsize); } *p += nsize; @@ -156,7 +161,7 @@ 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 (if_exec && opcode <= OP_PUSHDATA4) { /* if (fRequireMinimal && !CheckMinimalPush(vchPushValue, opcode)) { */ /* return set_error(serror, SCRIPT_ERR_MINIMALDATA); */ /* } */ @@ -718,7 +723,7 @@ script_serialize(struct stack *stack, u8 *buf, int buflen, int* len) { struct val *valp; void **sp; u8 *p = buf; - u16 valsize; + u32 valsize; *len = 0; sp = stack->bottom; diff --git a/script_num.c b/script_num.c @@ -139,7 +139,7 @@ sn_from_val(struct val val, struct num ** sn, int require_minimal) { return SN_SUCCESS; case VT_DATA: { u8 *data; - u16 size; + u32 size; enum sn_result res; assert(val.ind != -1); data = byte_pool_get(val.ind, &size); @@ -178,6 +178,7 @@ sn_to_val(struct num *sn) { snref->val = sn->val; val.ind = ind; } + } return val; } diff --git a/val.c b/val.c @@ -28,7 +28,7 @@ val_from_int(s64 intval) { } void -val_serialize(struct val val, u16 *len, u8 *buf, int bufsize) { +val_serialize(struct val val, u32 *len, u8 *buf, int bufsize) { struct num *sn; int n; u16 valsize; @@ -59,13 +59,34 @@ val_serialize(struct val val, u16 *len, u8 *buf, int bufsize) { case VT_DATA: { u8 *p; p = byte_pool_get(val.ind, len); - if (*len <= 0xFF) { - *buf++ = *len & 0xFF; + if (*len < OP_PUSHDATA1) { + *buf++ = *len; memcpy(buf, p, *len); - *len = *len + 1; + *len += 1; + } + else if (*len <= 0xFF) { + *buf++ = OP_PUSHDATA1; + *buf++ = *len; + memcpy(buf, p, *len); + *len += 2; + } + else if (*len <= 0xFFFF) { + *buf++ = OP_PUSHDATA2; + u16 *sp = (u16*)buf; + // TODO: writele16 + *sp = *len; + buf += 2; + memcpy(buf, p, *len); + *len += 3; } else { - assert(!"serialize bigger pushdata"); + *buf++ = OP_PUSHDATA4; + u32 *ip = (u32*)buf; + // TODO: writele32 + *ip = *len; + buf += 4; + memcpy(buf, p, *len); + *len += 5; } return; } @@ -88,7 +109,7 @@ val_serialize(struct val val, u16 *len, u8 *buf, int bufsize) { int val_eq(struct val a, struct val b, int require_minimal) { - u16 alen, blen; + u32 alen, blen; int eq = 0; static const int tmpsize = 4096; diff --git a/val.h b/val.h @@ -34,7 +34,7 @@ int val_eq(struct val a, struct val b, int require_minimal); void -val_serialize(struct val val, u16 *len, u8 *buf, int bufsize); +val_serialize(struct val val, u32 *len, u8 *buf, int bufsize); struct val val_from_int(s64);