remove dunning-krugerrand code

This commit is contained in:
aiju 2015-11-20 22:26:20 +01:00
parent 51bedde447
commit b677ab0c59
7 changed files with 0 additions and 1035 deletions

View file

@ -1,29 +0,0 @@
typedef struct Aux Aux;
typedef struct DirEntry DirEntry;
enum
{
TROOT,
TBLOCKS,
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;
};

View file

@ -1,263 +0,0 @@
#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 *);
char *blocksstr(DirEntry *, Aux *);
DirEntry entr[] = {
[TROOT] = {
.name = "",
.qid = {TROOT, 0, QTDIR},
.par = TROOT,
.sub = {TADDR, TBLOCKS},
},
[TBLOCKS] = {
.name = "blocks",
.qid = {TBLOCKS, 0, 0},
.par = TROOT,
.str = blocksstr,
},
[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((req->fid->qid.type & QTDIR) != 0)
break;
if(d->str == nil)
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);
}

View file

@ -1,133 +0,0 @@
#include <u.h>
#include <libc.h>
#include <mp.h>
#include <libsec.h>
ECdomain dom;
void readln(char *prompt, char *line, int len, int raw);
void
address(ECpub *p, char *buf)
{
uchar buf1[65], buf2[25], buf3[25];
buf1[0] = 4;
buf3[0] = 0;
mptobe(p->x, buf1 + 1, 32, nil);
mptobe(p->y, buf1 + 33, 32, nil);
sha2_256(buf1, 65, buf2, nil);
ripemd160(buf2, 32, buf3 + 1, nil);
sha2_256(buf3, 21, buf2, nil);
sha2_256(buf2, 32, buf2, nil);
memcpy(buf3 + 21, buf2, 4);
memset(buf, 0, 100);
base58enc(buf3, buf, 25);
}
void
pubkey(ECpub *b, char *buf)
{
uchar buf1[65];
buf1[0] = 4;
mptobe(b->x, buf1 + 1, 32, nil);
mptobe(b->y, buf1 + 33, 32, nil);
memset(buf, 0, 100);
base58enc(buf1, buf, 65);
}
void
privkey(ECpriv *p, char *buf, char *pw)
{
uchar buf1[53], buf2[32];
AESstate st;
buf1[0] = 0x80;
mptobe(p->d, buf1 + 1, 32, nil);
sha2_256(buf1, 33, buf2, nil);
sha2_256(buf2, 32, buf2, nil);
memcpy(buf1 + 33, buf2, 4);
sha2_256((uchar *) pw, strlen(pw), buf2, nil);
sha2_256(buf2, 32, buf2, nil);
genrandom(buf1 + 37, 16);
setupAESstate(&st, buf2, 32, buf1+37);
aesCBCencrypt(buf1, 37, &st);
memset(buf, 0, 100);
base58enc(buf1, buf, 53);
}
void
main()
{
ECpriv *p;
char addr[100], pub[100], priv[100], pw[256], pw2[256];
dom.p = strtomp("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", nil, 16, nil);
dom.a = uitomp(0, nil);
dom.b = uitomp(7, nil);
dom.n = strtomp("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", nil, 16, nil);
dom.h = uitomp(1, nil);
dom.G = strtoec(&dom, "0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", nil, nil);
p = ecgen(&dom, nil);
readln("password: ", pw, sizeof pw, 1);
readln("repeat: ", pw2, sizeof pw2, 1);
if(strcmp(pw, pw2) != 0)
sysfatal("passwords don't match");
address(p, addr);
pubkey(p, pub);
privkey(p, priv, pw);
print("%s %s %s\n", addr, pub, priv);
}
void
readln(char *prompt, char *line, int len, int raw)
{
char *p;
int fdin, fdout, ctl, n, nr;
fdin = open("/dev/cons", OREAD);
fdout = open("/dev/cons", OWRITE);
fprint(fdout, "%s", prompt);
if(raw){
ctl = open("/dev/consctl", OWRITE);
if(ctl < 0)
sysfatal("couldn't set raw mode");
write(ctl, "rawon", 5);
} else
ctl = -1;
nr = 0;
p = line;
for(;;){
n = read(fdin, p, 1);
if(n < 0){
close(ctl);
sysfatal("can't read cons\n");
}
if(*p == 0x7f)
exits(0);
if(n == 0 || *p == '\n' || *p == '\r'){
*p = '\0';
if(raw){
write(ctl, "rawoff", 6);
write(fdout, "\n", 1);
}
close(ctl);
return;
}
if(*p == '\b'){
if(nr > 0){
nr--;
p--;
}
}else{
nr++;
p++;
}
if(nr == len){
fprint(fdout, "line too long; try again\n");
nr = 0;
p = line;
}
}
}

View file

@ -1,131 +0,0 @@
#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
#include <String.h>
#include <json.h>
#include "dat.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 buf[512];
sprint(buf, "http://blockchain.info/q/addressbalance/%s", a->addr);
return graburl(buf);
}
char *
blocksstr(DirEntry *, Aux *)
{
return graburl("http://blockchain.info/q/getblockcount");
}
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();
}

View file

@ -1,19 +0,0 @@
</$objtype/mkfile
TARG=httpfs sign genkey
BIN=/$objtype/bin/btc
HFILES=\
dat.h\
default:V: all
$BIN:
mkdir -p $BIN
install: $BIN
</sys/src/cmd/mkmany
$O.httpfs: fs.$O

View file

@ -1,429 +0,0 @@
#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, *bpout;
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;
}
if(args[i][0] == '['){
k = strtol(args[i] + 1, &s, 0);
b = mallocz(k, 1);
hexdec(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){
varenc(ti->scoldlen, &s);
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++ = 1;
*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], c;
afd = open("/mnt/factotum/rpc", ORDWR);
if(afd < 0)
sysfatal("open: %r");
rpc = auth_allocrpc(afd);
bp = malloc(sizeof(*bp));
Binit(bp, 0, OREAD);
bpout = malloc(sizeof(*bpout));
Binit(bpout, 1, OWRITE);
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);
for(n = 0; n < 16; n++){
c = ti->prev[n];
ti->prev[n] = ti->prev[31-n];
ti->prev[31-n] = c;
}
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);
sig[0] = ++n;
sig[n++] = 1;
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++){
Bprint(bpout, "%.2x", buf[i]);
if((i%4)==3)
Bputc(bpout, ' ');
if((i%32)==31)
Bputc(bpout, '\n');
}
if((i%32)!=0)
Bputc(bpout, '\n');
Bterm(bpout);
}
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},
};

View file

@ -1,31 +0,0 @@
#!/bin/rc
wallet=$home/lib/wallet
btc=/mnt/btc
addr=`{awk ' { print $1 } ' < $wallet}
last=`{cat $btc/blocks}
cat $btc/addr/$addr/tx | sort +0n | uniq | tail -n 10 | awk '
BEGIN {
split("'^$"addr^'", addrs)
for(x in addrs)
my[addrs[x]] = 1
last='^$last^'
}
{
f = -1
bal = 0
for(i = 4; i <= NF; i++){
if($i == "|")
f = 1
else if(my[$i]){
i++
bal += f*$i
}else
i++
}
d = last - $3
printf "%s %+15.4f %c %s\n", $1, bal/1e8, (d > 9) ? 32 : (d + 48), $2
}
'
echo '================================================================================================'
cat $btc/addr/$addr/balance | awk ' { a += $1 } END { printf "balance %18.4f BTC\n", a/1e8 } '