summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoraiju <aiju@phicode.de>2012-06-08 23:48:41 +0200
committeraiju <aiju@phicode.de>2012-06-08 23:48:41 +0200
commit42656f38efa50235c363ab1522021cf6726b8e93 (patch)
tree1eab38291d041040e94766721c3169b069cd9256
parentbaea7ec1e2c4175d00b0aa9f4208d103e38b2323 (diff)
more bitcoin
-rw-r--r--sys/src/cmd/auth/factotum/ecdsa.c42
-rw-r--r--sys/src/cmd/btc/dat.h28
-rw-r--r--sys/src/cmd/btc/fs.c254
-rw-r--r--sys/src/cmd/btc/httpfs.c129
-rw-r--r--sys/src/cmd/btc/json.c326
-rw-r--r--sys/src/cmd/btc/json.h32
-rw-r--r--sys/src/cmd/btc/mkfile17
-rw-r--r--sys/src/cmd/btc/sign.c412
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},
+};