diff options
author | aiju <aiju@phicode.de> | 2012-06-08 23:48:41 +0200 |
---|---|---|
committer | aiju <aiju@phicode.de> | 2012-06-08 23:48:41 +0200 |
commit | 42656f38efa50235c363ab1522021cf6726b8e93 (patch) | |
tree | 1eab38291d041040e94766721c3169b069cd9256 | |
parent | baea7ec1e2c4175d00b0aa9f4208d103e38b2323 (diff) |
more bitcoin
-rw-r--r-- | sys/src/cmd/auth/factotum/ecdsa.c | 42 | ||||
-rw-r--r-- | sys/src/cmd/btc/dat.h | 28 | ||||
-rw-r--r-- | sys/src/cmd/btc/fs.c | 254 | ||||
-rw-r--r-- | sys/src/cmd/btc/httpfs.c | 129 | ||||
-rw-r--r-- | sys/src/cmd/btc/json.c | 326 | ||||
-rw-r--r-- | sys/src/cmd/btc/json.h | 32 | ||||
-rw-r--r-- | sys/src/cmd/btc/mkfile | 17 | ||||
-rw-r--r-- | sys/src/cmd/btc/sign.c | 412 |
8 files changed, 1219 insertions, 21 deletions
diff --git a/sys/src/cmd/auth/factotum/ecdsa.c b/sys/src/cmd/auth/factotum/ecdsa.c index d21c97299..f31c51a17 100644 --- a/sys/src/cmd/auth/factotum/ecdsa.c +++ b/sys/src/cmd/auth/factotum/ecdsa.c @@ -15,7 +15,8 @@ static char *phasenames[] = { struct State { ECpriv p; - char *sign; + uchar buf[100]; + int n; }; static int @@ -36,11 +37,11 @@ decryptkey(Fsstate *fss, char *key, char *password) memset(buf, 0, sizeof buf); base58enc(keyenc, buf, 37); if(keyenc[0] != 0x80) - return failure(fss, "invalid key '%s'", buf); + return RpcNeedkey; sha2_256(keyenc, 33, hash, nil); sha2_256(hash, 32, hash, nil); if(memcmp(keyenc + 33, hash, 4) != 0) - return failure(fss, "checksum error"); + return RpcNeedkey; st = fss->ps; st->p.d = betomp(keyenc + 1, 32, nil); st->p.x = mpnew(0); @@ -67,6 +68,7 @@ ecdsainit(Proto *, Fsstate *fss) dom.h = uitomp(1, nil); dom.G = strtoec(&dom, "0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", nil, nil); } + fss->ps = nil; if((iscli = isclient(_strfindattr(fss->attr, "role"))) < 0) return failure(fss, nil); if(iscli==0) @@ -91,7 +93,7 @@ ecdsainit(Proto *, Fsstate *fss) } if(key == nil || password == nil) return RpcNeedkey; - fss->ps = malloc(sizeof(State)); + fss->ps = emalloc(sizeof(State)); ret = decryptkey(fss, key, password); if(ret != RpcOk) return ret; @@ -103,12 +105,11 @@ ecdsainit(Proto *, Fsstate *fss) return RpcOk; } -static char * -derencode(mpint *r, mpint *s) +static void +derencode(mpint *r, mpint *s, uchar *buf, int *n) { - uchar buf[100], rk[33], sk[33]; - char *str; - int rl, sl, i; + uchar rk[33], sk[33]; + int rl, sl; mptobe(r, rk, 32, nil); mptobe(s, sk, 32, nil); @@ -132,17 +133,13 @@ derencode(mpint *r, mpint *s) buf[4 + rl] = 0x02; buf[5 + rl] = sl; memmove(buf + 6 + rl, sk, sl); - str = malloc(1024); - for(i = 0; i < 6 + rl + sl; i++) - sprint(str + 2 * i, "%.2x", buf[i]); - return str; + *n = 6 + rl + sl; } static int ecdsawrite(Fsstate *fss, void *va, uint n) { State *st; - uchar hash[32]; mpint *r, *s; st = fss->ps; @@ -150,12 +147,10 @@ ecdsawrite(Fsstate *fss, void *va, uint n) default: return phaseerror(fss, "write"); case CHaveKey: - sha2_256(va, n, hash, nil); - sha2_256(hash, 32, hash, nil); r = mpnew(0); s = mpnew(0); - ecdsasign(&dom, &st->p, hash, 32, r, s); - st->sign = derencode(r, s); + ecdsasign(&dom, &st->p, va, n, r, s); + derencode(r, s, st->buf, &st->n); mpfree(r); mpfree(s); fss->phase = CHaveText; @@ -166,11 +161,16 @@ ecdsawrite(Fsstate *fss, void *va, uint n) static int ecdsaread(Fsstate *fss, void *va, uint *n) { + State *st; + + st = fss->ps; switch(fss->phase){ default: return phaseerror(fss, "read"); case CHaveText: - *n = snprint(va, *n, ((State *)fss->ps)->sign); + if(*n > st->n) + *n = st->n; + memcpy(va, st->buf, *n); fss->phase = Established; return RpcOk; } @@ -182,13 +182,13 @@ ecdsaclose(Fsstate *fss) State *st; st = fss->ps; + if(st == nil) + return; if(st->p.x != nil){ mpfree(st->p.x); mpfree(st->p.y); mpfree(st->p.d); } - if(st->sign != nil) - free(st->sign); free(st); fss->ps = nil; } diff --git a/sys/src/cmd/btc/dat.h b/sys/src/cmd/btc/dat.h new file mode 100644 index 000000000..4cdc1d259 --- /dev/null +++ b/sys/src/cmd/btc/dat.h @@ -0,0 +1,28 @@ +typedef struct Aux Aux; +typedef struct DirEntry DirEntry; + +enum +{ + TROOT, + TADDR, + TADDRSUB, + TADDRTX, + TADDRBALANCE, +}; + +struct DirEntry +{ + char *name; + Qid qid; + int par; + char *(*walk)(Fid *, char *, Qid *); + char *(*str)(DirEntry *, Aux *); + void (*write)(Req *); + int sub[20]; +}; + +struct Aux +{ + char *addr; + char *str; +}; diff --git a/sys/src/cmd/btc/fs.c b/sys/src/cmd/btc/fs.c new file mode 100644 index 000000000..eaf2aebd3 --- /dev/null +++ b/sys/src/cmd/btc/fs.c @@ -0,0 +1,254 @@ +#include <u.h> +#include <libc.h> +#include <fcall.h> +#include <thread.h> +#include <9p.h> +#include "dat.h" + +Reqqueue *queue; + +static char *addrwalk(Fid *, char *, Qid *); +char *balancestr(DirEntry *, Aux *); +char *txstr(DirEntry *, Aux *); + +DirEntry entr[] = { + [TROOT] = { + .name = "", + .qid = {TROOT, 0, QTDIR}, + .par = TROOT, + .sub = {TADDR}, + }, + [TADDR] = { + .name = "addr", + .qid = {TADDR, 0, QTDIR}, + .walk = addrwalk, + .par = TROOT, + }, + [TADDRSUB] = { + .qid = {TADDRSUB, 0, QTDIR}, + .par = TADDR, + .sub = {TADDRBALANCE, TADDRTX}, + }, + [TADDRBALANCE] = { + .name = "balance", + .qid = {TADDRBALANCE, 0, 0}, + .par = TADDRSUB, + .str = balancestr, + }, + [TADDRTX] = { + .name = "tx", + .qid = {TADDRTX, 0, 0}, + .par = TADDRSUB, + .str = txstr, + }, +}; + +void +pfree(void **v) +{ + if(*v != nil) + free(*v); + *v = nil; +} + +static void +btcattach(Req *req) +{ + req->ofcall.qid = (Qid){0, 0, QTDIR}; + req->fid->qid = req->ofcall.qid; + req->fid->aux = emalloc9p(sizeof(Aux)); + respond(req, nil); +} + +static char * +addrwalk(Fid *fid, char *name, Qid *qid) +{ + Aux *a; + + a = fid->aux; + pfree(&a->addr); + a->addr = strdup(name); + fid->qid = entr[TADDRSUB].qid; + *qid = fid->qid; + return nil; +} + +static char * +btcwalk1(Fid *fid, char *name, Qid *qid) +{ + DirEntry *d; + int *s; + + d = entr + (int)fid->qid.path; + if(strcmp(name, "..") == 0){ + fid->qid = entr[d->par].qid; + *qid = fid->qid; + return nil; + } + if(d->walk) + return d->walk(fid, name, qid); + for(s = d->sub; *s; s++) + if(strcmp(entr[*s].name, name) == 0){ + fid->qid = entr[*s].qid; + *qid = fid->qid; + return nil; + } + return "directory entry not found"; +} + +static char * +btcclone(Fid *oldfid, Fid *newfid) +{ + Aux *a, *b; + + a = oldfid->aux; + b = emalloc9p(sizeof(Aux)); + memcpy(b, a, sizeof(Aux)); + if(b->addr) + b->addr = strdup(b->addr); + newfid->aux = b; + return nil; +} + +static void +btcopenread(Req *req) +{ + DirEntry *d; + Aux *a; + + d = entr + (int)req->fid->qid.path; + a = req->fid->aux; + a->str = d->str(d, a); + if(a->str == nil) + responderror(req); + else + respond(req, nil); +} + +static void +btcopen(Req *req) +{ + DirEntry *d; + + d = entr + (int)req->fid->qid.path; + switch(req->ifcall.mode & 3){ + case OREAD: + if(d->str == nil && (req->fid->qid.type & QTDIR) == 0) + goto noperm; + reqqueuepush(queue, req, btcopenread); + return; + case OWRITE: + if(d->write == nil) + goto noperm; + break; + case ORDWR: + if(d->str == nil || d->write == nil) + goto noperm; + break; + case OEXEC: + goto noperm; + } + respond(req, nil); + return; +noperm: + respond(req, "permission denied"); +} + +static void +fill(Dir *de, int t, Aux *a) +{ + DirEntry *d; + + d = entr + t; + de->qid = d->qid; + if(d->qid.type & QTDIR) + de->mode = 0555; + else + de->mode = (d->str ? 0444 : 0) | (d->write ? 0222 : 0); + if(d->name != nil) + de->name = strdup(d->name); + else if(a->addr != nil) + de->name = strdup(a->addr); + else + de->name = strdup(""); + de->uid = strdup("satoshi"); + de->gid = strdup("satoshi"); + de->muid = strdup("satoshi"); + de->atime = de->mtime = time(0); +} + +static void +btcstat(Req *req) +{ + fill(&req->d, (int)req->fid->qid.path, req->fid->aux); + respond(req, nil); +} + +static int +btcdirgen(int n, Dir *dir, void *aux) +{ + DirEntry *d; + + d = entr + (int)((Req*)aux)->fid->qid.path; + if(n >= nelem(d->sub) || d->sub[n] == 0) + return -1; + fill(dir, d->sub[n], ((Req*)aux)->fid->aux); + return 0; +} + +static void +btcread(Req *req) +{ + DirEntry *d; + Aux *a; + + d = entr + (int)req->fid->qid.path; + a = req->fid->aux; + if(req->fid->qid.type & QTDIR){ + dirread9p(req, btcdirgen, req); + respond(req, nil); + }else if(d->str && a->str){ + readstr(req, a->str); + respond(req, nil); + }else + respond(req, "permission denied"); +} + +static void +btcflush(Req *req) +{ + reqqueueflush(queue, req->oldreq); + respond(req, nil); +} + +static void +btcdestroyfid(Fid *fid) +{ + Aux *a; + + a = fid->aux; + if(a != nil){ + pfree(&a->addr); + pfree(&a->str); + free(a); + } + fid->aux = nil; +} + +Srv btcsrv = { + .attach = btcattach, + .walk1 = btcwalk1, + .clone = btcclone, + .stat = btcstat, + .open = btcopen, + .read = btcread, + .flush = btcflush, + .destroyfid = btcdestroyfid, +}; + +void +gofs(void) +{ + queue = reqqueuecreate(); + threadpostmountsrv(&btcsrv, nil, "/mnt/btc", 0); +} diff --git a/sys/src/cmd/btc/httpfs.c b/sys/src/cmd/btc/httpfs.c new file mode 100644 index 000000000..0222762e5 --- /dev/null +++ b/sys/src/cmd/btc/httpfs.c @@ -0,0 +1,129 @@ +#include <u.h> +#include <libc.h> +#include <fcall.h> +#include <thread.h> +#include <9p.h> +#include <String.h> +#include "dat.h" +#include "json.h" + +void gofs(void); + +char * +graburl(char *url) +{ + int fd, fd2, n, rc, size; + char buf[2048]; + char *res; + + fd = open("/mnt/web/clone", ORDWR); + if(fd < 0) + return nil; + if(read(fd, buf, 512) < 0){ + close(fd); + return nil; + } + n = atoi(buf); + sprint(buf, "url %s", url); + if(write(fd, buf, strlen(buf)) < 0){ + close(fd); + return nil; + } + sprint(buf, "/mnt/web/%d/body", n); + fd2 = open(buf, OREAD); + if(fd2 < 0){ + close(fd); + return nil; + } + size = 0; + res = nil; + while((rc = readn(fd2, buf, sizeof buf)) > 0){ + res = realloc(res, size + rc + 1); + memcpy(res + size, buf, rc); + size += rc; + res[size] = 0; + } + close(fd); + close(fd2); + if(rc < 0){ + free(res); + return nil; + } + return res; +} + +static void +parsetx(String *str, JSON *j, JSON *l) +{ + JSONEl *e; + JSON *k; + char buf[512]; + + for(e = j->first; e != nil; e = e->next){ + k = jsonbyname(e->val, "prev_out"); + sprint(buf, "%s %lld ", jsonstr(jsonbyname(k, "addr")), (vlong)jsonbyname(k, "value")->n); + s_append(str, buf); + } + s_append(str, "| "); + for(e = l->first; e != nil; e = e->next){ + sprint(buf, "%s %lld ", jsonstr(jsonbyname(e->val, "addr")), (vlong)jsonbyname(e->val, "value")->n); + s_append(str, buf); + } +} + +char * +balancestr(DirEntry *, Aux *a) +{ + char *s; + char buf[512]; + + sprint(buf, "http://blockchain.info/q/addressbalance/%s", a->addr); + s = graburl(buf); + if(s == nil) + return nil; + return s; +} + +char * +txstr(DirEntry *, Aux *a) +{ + char *s; + char buf[512]; + JSON *j, *k; + JSONEl *e; + String *str; + + sprint(buf, "http://blockchain.info/rawaddr/%s", a->addr); + s = graburl(buf); + if(s == nil) + return nil; + j = jsonparse(s); + free(s); + if(j == nil) + return nil; + str = s_new(); + k = jsonbyname(j, "txs"); + if(k == nil) + goto err; + for(e = k->first; e != nil; e = e->next){ + sprint(buf, "%d %s %d ", (int)(jsonbyname(e->val, "time")->n), jsonstr(jsonbyname(e->val, "hash")), (int)(jsonbyname(e->val, "block_height")->n)); + s_append(str, buf); + parsetx(str, jsonbyname(e->val, "inputs"), jsonbyname(e->val, "out")); + s_putc(str, '\n'); + } + s_terminate(str); + s = str->base; + free(str); + jsonfree(j); + return s; +err: + s_free(str); + jsonfree(j); + return nil; +} + +void +threadmain() +{ + gofs(); +} diff --git a/sys/src/cmd/btc/json.c b/sys/src/cmd/btc/json.c new file mode 100644 index 000000000..988a7d751 --- /dev/null +++ b/sys/src/cmd/btc/json.c @@ -0,0 +1,326 @@ +#include <u.h> +#include <libc.h> +#include <ctype.h> +#include "json.h" + +typedef struct Lex Lex; + +enum { + TEOF, + TSTRING = (1<<(8*sizeof(Rune)))+1, + TNUM, + TNULL, + TFALSE, + TTRUE, +}; + +struct Lex +{ + char *s; + int t; + double n; + char buf[4096]; + Rune peeked; + jmp_buf jmp; + int canjmp; +}; + +static Rune +getch(Lex *l) +{ + Rune r; + + if(l->peeked){ + r = l->peeked; + l->peeked = 0; + return r; + } + l->s += chartorune(&r, l->s); + return r; +} + +static Rune +peekch(Lex *l) +{ + if(!l->peeked) + l->peeked = getch(l); + return l->peeked; +} + +static int +lex(Lex *l) +{ + Rune r; + char *t; + + for(;;){ + r = peekch(l); + if(r != 0x20 && r != 0x09 && r != 0x0A && r != 0x0D) + break; + getch(l); + } + r = getch(l); + if(r == ']' && l->canjmp) + longjmp(l->jmp, 1); + l->canjmp = 0; + if(r == 0 || r == '{' || r == '[' || r == ']' || r == '}' || r == ':' || r == ','){ + l->t = r; + return 0; + } + if(r >= 0x80 || isalpha(r)){ + t = l->buf; + for(;;){ + t += runetochar(t, &r); + if(t >= l->buf + sizeof(l->buf)){ + werrstr("json: literal too long"); + return -1; + } + r = peekch(l); + if(r < 0x80 && !isalpha(r)) + break; + getch(l); + } + *t = 0; + if(strcmp(l->buf, "true") == 0) + l->t = TTRUE; + else if(strcmp(l->buf, "false") == 0) + l->t = TFALSE; + else if(strcmp(l->buf, "null") == 0) + l->t = TNULL; + else{ + werrstr("json: invalid literal"); + return -1; + } + return 0; + } + if(isdigit(r) || r == '-'){ + l->n = strtod(l->s-1, &l->s); + l->t = TNUM; + return 0; + } + if(r == '"'){ + t = l->buf; + for(;;){ + r = getch(l); + if(r == '"') + break; + if(r < ' '){ + werrstr("json: invalid char in string %x", r); + return -1; + } + if(r == '\\'){ + r = getch(l); + switch(r){ + case 'n': + r = '\n'; + break; + case 'r': + r = '\r'; + break; + case 't': + r = '\t'; + break; + case 'f': + r = '\f'; + break; + case 'b': + r = '\b'; + break; + case '"': case '/': case '\\': + break; + default: + werrstr("json: invalid escape sequence \\%C", r); + return -1; + } + } + t += runetochar(t, &r); + if(t >= l->buf + sizeof(l->buf)){ + werrstr("json: string too long"); + return -1; + } + } + *t = 0; + l->t = TSTRING; + return 0; + } + werrstr("json: invalid char %C", peekch(l)); + return -1; +} + +static JSON* +jsonobj(Lex *l) +{ + JSON *j; + JSONEl *e; + JSONEl **ln; + int obj; + + j = mallocz(sizeof(*j), 1); + if(j == nil) + return nil; + if(lex(l) < 0){ +error: + free(j); + return nil; + } + switch(l->t){ + case TEOF: + werrstr("json: unexpected eof"); + goto error; + case TNULL: + j->t = JSONNull; + break; + case TTRUE: + j->t = JSONBool; + j->n = 1; + break; + case TFALSE: + j->t = JSONBool; + j->n = 0; + break; + case TSTRING: + j->t = JSONString; + j->s = strdup(l->buf); + if(j->s == nil) + goto error; + break; + case TNUM: + j->t = JSONNumber; + j->n = l->n; + break; + case '{': + case '[': + obj = l->t == '{'; + ln = &j->first; + e = nil; + if(obj){ + j->t = JSONObject; + if(lex(l) < 0) + goto abort; + if(l->t == '}') + return j; + goto firstobj; + }else{ + j->t = JSONArray; + l->canjmp = 1; + if(setjmp(l->jmp) > 0){ + free(e); + return j; + } + } + for(;;){ + if(obj){ + if(lex(l) < 0) + goto abort; + firstobj: + if(l->t != TSTRING){ + werrstr("json: syntax error, not string"); + goto abort; + } + e = mallocz(sizeof(*e), 1); + if(e == nil) + goto abort; + e->name = strdup(l->buf); + if(e->name == nil || lex(l) < 0){ + free(e); + goto abort; + } + if(l->t != ':'){ + werrstr("json: syntax error, not colon"); + free(e); + goto abort; + } + }else{ + e = mallocz(sizeof(*e), 1); + if(e == nil) + goto abort; + } + e->val = jsonobj(l); + if(e->val == nil){ + free(e); + goto abort; + } + *ln = e; + ln = &e->next; + if(lex(l) < 0) + goto abort; + if(l->t == (obj ? '}' : ']')) + break; + if(l->t != ','){ + werrstr("json: syntax error, neither comma nor ending paren"); + goto abort; + } + } + break; + abort: + jsonfree(j); + return nil; + case ']': case '}': case ',': case ':': + werrstr("json: unexpected %C", l->t); + goto error; + default: + werrstr("json: the front fell off"); + goto error; + } + return j; +} + +JSON* +jsonparse(char *s) +{ + Lex l; + + memset(&l, 0, sizeof(l)); + l.s = s; + return jsonobj(&l); +} + +void +jsonfree(JSON *j) +{ + JSONEl *e, *f; + + switch(j->t){ + case JSONString: + if(j->s) + free(j->s); + break; + case JSONArray: case JSONObject: + for(e = j->first; e != nil; e = f){ + if(e->name) + free(e->name); + jsonfree(e->val); + f = e->next; + free(e); + } + } + free(j); +} + +JSON * +jsonbyname(JSON *j, char *n) +{ + JSONEl *e; + + if(j->t != JSONObject){ + werrstr("not an object"); + return nil; + } + for(e = j->first; e != nil; e = e->next) + if(strcmp(e->name, n) == 0) + return e->val; + werrstr("key '%s' not found", n); + return nil; +} + +char * +jsonstr(JSON *j) +{ + if(j == nil) + return nil; + if(j->t != JSONString){ + werrstr("not a string"); + return nil; + } + return j->s; +} diff --git a/sys/src/cmd/btc/json.h b/sys/src/cmd/btc/json.h new file mode 100644 index 000000000..75b2a8ea7 --- /dev/null +++ b/sys/src/cmd/btc/json.h @@ -0,0 +1,32 @@ +typedef struct JSONEl JSONEl; +typedef struct JSON JSON; + +enum { + JSONNull, + JSONBool, + JSONNumber, + JSONString, + JSONArray, + JSONObject, +}; + +struct JSONEl { + char *name; + JSON *val; + JSONEl *next; +}; + +struct JSON +{ + int t; + union { + double n; + char *s; + JSONEl *first; + }; +}; + +JSON* jsonparse(char *); +void jsonfree(JSON *); +JSON* jsonbyname(JSON *, char *); +char* jsonstr(JSON *); diff --git a/sys/src/cmd/btc/mkfile b/sys/src/cmd/btc/mkfile new file mode 100644 index 000000000..46510b732 --- /dev/null +++ b/sys/src/cmd/btc/mkfile @@ -0,0 +1,17 @@ +</$objtype/mkfile + +TARG=httpfs sign + +BIN=/$objtype/bin/btc + +OFILES=\ + json.$O\ + +HFILES=\ + dat.h\ + +default:V: all + +</sys/src/cmd/mkmany + +$O.httpfs: fs.$O diff --git a/sys/src/cmd/btc/sign.c b/sys/src/cmd/btc/sign.c new file mode 100644 index 000000000..09ff86651 --- /dev/null +++ b/sys/src/cmd/btc/sign.c @@ -0,0 +1,412 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <mp.h> +#include <ctype.h> +#include <libsec.h> +#include <auth.h> + +typedef struct TxIn TxIn; +typedef struct TxOut TxOut; +typedef struct Sig Sig; +typedef struct Word Word; + +int afd; +AuthRpc *rpc; + +struct Word { + char *name; + int val; +} words[]; + +struct Sig { + char *priv; + int loc; + Sig *n; +}; + +struct TxIn { + uchar prev[36]; + int scoldlen, sclen; + uchar scold[10000]; + uchar sc[10000]; + Sig *sig; +}; + +struct TxOut { + uvlong val; + int sclen; + uchar sc[10000]; +}; + +Biobuf *bp; + +int nin, nout; +TxIn *in[0xFD]; +TxOut *out[0xFD]; +uchar buf[65536]; + +void +varenc(uint i, uchar **b) +{ + if(i < 0xfd) + *(*b)++ = i; + else if(i <= 0xffff){ + *(*b)++ = 0xfd; + *(*b)++ = i; + *(*b)++ = i >> 8; + }else{ + *(*b)++ = 0xfe; + *(*b)++ = i; + *(*b)++ = i >> 8; + *(*b)++ = i >> 16; + *(*b)++ = i >> 24; + } +} + + +int +hexdec(char *c, uchar *u, int n) +{ + int i, v; + char *b; + static char *hexdig = "0123456789abcdef"; + + memset(u, 0, n); + for(i = 0; i < 2 * n; i++){ + b = strchr(hexdig, c[i]); + if(b == nil) + return -1; + v = b - hexdig; + if(i & 1) + u[i>>1] |= v; + else + u[i>>1] |= v << 4; + } + return 0; +} + +void +pushdat(uchar **scr, uchar *d, int n) +{ + if(n <= 0x4b) + *(*scr)++ = n; + else if(n <= 0xff){ + *(*scr)++ = 0x4c; + *(*scr)++ = n; + }else if(n <= 0xffff){ + *(*scr)++ = 0x4d; + *(*scr)++ = n; + *(*scr)++ = n >> 8; + }else{ + *(*scr)++ = 0x4e; + *(*scr)++ = n; + *(*scr)++ = n >> 8; + *(*scr)++ = n >> 16; + *(*scr)++ = n >> 24; + } + memcpy(*scr, d, n); + *scr += n; +} + +void +doscript(char **args, int n, uchar *script, int *len, TxIn *ti) +{ + int i, k; + Word *w; + uchar *scr; + uchar *b; + char *s; + Sig *si; + + scr = script; + for(i = 0; i < n; i++){ + for(w = words; w->name; w++) + if(strcmp(w->name, args[i]) == 0){ + *scr++ = w->val; + goto next; + } + if(strncmp(args[i], "sig(", 4) == 0){ + if(in == nil) + sysfatal("sig in out script"); + si = malloc(sizeof(*si)); + args[i][strlen(args[i])-1] = 0; + si->priv = strdup(args[i] + 4); + si->loc = scr - script; + si->n = ti->sig; + ti->sig = si; + continue; + } + if(strncmp(args[i], "h160(", 5) == 0){ + b = mallocz(25, 1); + args[i][strlen(args[i])-1] = 0; + base58dec(args[i] + 5, b, 25); + pushdat(&scr, b+1, 20); + free(b); + continue; + } + if(args[i][0] == '('){ + k = strtol(args[i] + 1, &s, 0); + b = mallocz(k, 1); + base58dec(s+1, b, k); + pushdat(&scr, b, k); + free(b); + continue; + } + sysfatal("invalid word %s", args[i]); +next: ; + } + *len = scr - script; +} + +int +serialize(uchar *buf, int sig) +{ + uchar *s; + TxIn *ti; + TxOut *to; + int i; + + s = buf; + *s++ = 1; + *s++ = 0; + *s++ = 0; + *s++ = 0; + *s++ = nin; + for(i = 0; i < nin; i++){ + ti = in[i]; + memcpy(s, ti->prev, 36); + s += 36; + if(sig == -1){ + varenc(ti->sclen, &s); + memcpy(s, ti->sc, ti->sclen); + s += ti->sclen; + } + if(sig == i){ + memcpy(s, ti->scold, ti->scoldlen); + s += ti->scoldlen; + } + *s++ = 0xff; + *s++ = 0xff; + *s++ = 0xff; + *s++ = 0xff; + } + *s++ = nout; + for(i = 0; i < nout; i++){ + to = out[i]; + *s++ = to->val; + *s++ = to->val >> 8; + *s++ = to->val >> 16; + *s++ = to->val >> 24; + *s++ = to->val >> 32; + *s++ = to->val >> 40; + *s++ = to->val >> 48; + *s++ = to->val >> 56; + varenc(to->sclen, &s); + memcpy(s, to->sc, to->sclen); + s += to->sclen; + } + *s++ = 0; + *s++ = 0; + *s++ = 0; + *s++ = 0; + if(sig != -1){ + *s++ = 0; + *s++ = 0; + *s++ = 0; + *s++ = 0; + } + return s - buf; +} + +void +sign(uchar *hash, char *priv, uchar *tar, uint *n) +{ + char buf[512]; + int rc; + +again: + sprint(buf, "proto=ecdsa role=client key=%s", priv); + rc = auth_rpc(rpc, "start", buf, strlen(buf)); + if(rc == ARneedkey || rc == ARbadkey){ + rerrstr(buf, sizeof buf); + if(auth_getkey(buf + 8) < 0) + sysfatal("auth_getkey: %r"); + goto again; + } + if(rc != ARok) + sysfatal("factotum start: %r"); + if(auth_rpc(rpc, "write", hash, 32) != ARok) + sysfatal("factotum write: %r"); + if(auth_rpc(rpc, "read", "", 0) != ARok) + sysfatal("factotum read: %r"); + memcpy(tar, rpc->arg, *n = rpc->narg); +} + +void +main() +{ + char *line; + int linenum; + uint i, n; + char *args[256]; + TxOut *to; + TxIn *ti; + Sig *si; + uchar hash[32]; + uchar sig[100]; + + afd = open("/mnt/factotum/rpc", ORDWR); + if(afd < 0) + sysfatal("open: %r"); + rpc = auth_allocrpc(afd); + + bp = malloc(sizeof(*bp)); + Binit(bp, 0, OREAD); + linenum = 0; + for(;;){ + line = Brdstr(bp, '\n', 1); + linenum++; + if(strcmp(line, "-") == 0) + break; + if(++nin >= 0xFD) + sysfatal("too many inputs"); + ti = malloc(sizeof(*ti)); + in[nin-1] = ti; + if(tokenize(line, args, nelem(args)) != 2) + sysfatal("line %d: invalid data", linenum); + hexdec(args[0], ti->prev, 32); + i = atoi(args[1]); + ti->prev[32] = i; + ti->prev[33] = i >> 8; + ti->prev[34] = i >> 16; + ti->prev[35] = i >> 24; + line = Brdstr(bp, '\n', 1); + linenum++; + i = tokenize(line, args, nelem(args)); + doscript(args, i, ti->scold, &ti->scoldlen, nil); + line = Brdstr(bp, '\n', 1); + linenum++; + i = tokenize(line, args, nelem(args)); + doscript(args, i, ti->sc, &ti->sclen, ti); + } + for(;;){ + line = Brdstr(bp, '\n', 1); + if(line == nil) + break; + linenum++; + if(++nout >= 0xFD) + sysfatal("too many outputs"); + to = malloc(sizeof(*to)); + out[nout-1] = to; + to->val = atoll(line); + line = Brdstr(bp, '\n', 1); + linenum++; + i = tokenize(line, args, nelem(args)); + doscript(args, i, to->sc, &to->sclen, nil); + } + for(i = 0; i < nin; i++){ + ti = in[i]; + if(ti->sig == nil) + continue; + n = serialize(buf, i); + sha2_256(buf, n, hash, nil); + sha2_256(hash, 32, hash, nil); + for(si = ti->sig; si != nil; si = si->n){ + sign(hash, ti->sig->priv, sig + 1, &n); + print("%d\n", n); + sig[0] = n++; + memmove(ti->sc + si->loc + n, ti->sc + si->loc, ti->sclen - si->loc); + memmove(ti->sc + si->loc, sig, n); + ti->sclen += n; + } + } + n = serialize(buf, -1); + for(i = 0; i < n; i++){ + print("%.2x", buf[i]); + if((i%4)==3) + print(" "); + if((i%32)==31) + print("\n"); + } + if((i%16)!=0) + print("\n"); +} + +Word words[] = { + {"nop", 97}, + {"if", 99}, + {"notif", 100}, + {"else", 103}, + {"endif", 104}, + {"verify", 105}, + {"return", 106}, + {"toaltstack", 107}, + {"fromaltstack", 108}, + {"2drop", 109}, + {"2dup", 110}, + {"3dup", 111}, + {"2over", 112}, + {"2rot", 113}, + {"2swap", 114}, + {"ifdup", 115}, + {"depth", 116}, + {"drop", 117}, + {"dup", 118}, + {"nip", 119}, + {"over", 120}, + {"pick", 121}, + {"roll", 122}, + {"rot", 123}, + {"swap", 124}, + {"tuck", 125}, + {"cat", 126}, + {"substr", 127}, + {"left", 128}, + {"right", 129}, + {"size", 130}, + {"invert", 131}, + {"and", 132}, + {"or", 133}, + {"xor", 134}, + {"equal", 135}, + {"equalverify", 136}, + {"1add", 139}, + {"1sub", 140}, + {"2mul", 141}, + {"2div", 142}, + {"negate", 143}, + {"abs", 144}, + {"not", 145}, + {"0notequal", 146}, + {"add", 147}, + {"sub", 148}, + {"mul", 149}, + {"div", 150}, + {"mod", 151}, + {"lshift", 152}, + {"rshift", 153}, + {"booland", 154}, + {"boolor", 155}, + {"numequal", 156}, + {"numequalverify", 157}, + {"numnotequal", 158}, + {"lessthan", 159}, + {"greaterthan", 160}, + {"lessthanorequal", 161}, + {"greaterthanorequal", 162}, + {"min", 163}, + {"max", 164}, + {"within", 165}, + {"ripemd160", 166}, + {"sha1", 167}, + {"sha256", 168}, + {"hash160", 169}, + {"hash256", 170}, + {"codeseparator", 171}, + {"checksig", 172}, + {"checksigverify", 173}, + {"checkmultisig", 174}, + {"checkmultisigverify", 175}, + {nil, 0}, +}; |