Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: rustyrussell/pettycoin
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 063903453ee7
Choose a base ref
...
head repository: rustyrussell/pettycoin
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: e27a7e2998a2
Choose a head ref
  • 2 commits
  • 3 files changed
  • 1 contributor

Commits on Sep 10, 2014

  1. pettycoin-gateway: refund transactions which are too large.

    The pettycoin protocol has a limit of ~5BTC, and gateways probably don't
    want to deal with anything like that amount anyway.  It's set here to the
    theoretical max on the test network, and 0.002 BTC on the non-test net.
    
    Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
    rustyrussell committed Sep 10, 2014
    Copy the full SHA
    e5a62ba View commit details
  2. tools/gateway-report.sh: simple inetd serving of gateway logs.

    Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
    rustyrussell committed Sep 10, 2014
    Copy the full SHA
    e27a7e2 View commit details
Showing with 171 additions and 24 deletions.
  1. +91 −12 pettycoin-gateway.c
  2. +45 −12 test/run-20-gateway.c
  3. +35 −0 tools/gateway-report.sh
103 changes: 91 additions & 12 deletions pettycoin-gateway.c
Original file line number Diff line number Diff line change
@@ -24,11 +24,22 @@
#include <openssl/ec.h>
#include <openssl/err.h>
#include <openssl/obj_mac.h>
#include <stdarg.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

static inline u64 max_satoshi_accepted(bool testnet)
{
/* Accept any valid amount on testnet */
if (testnet)
return PROTOCOL_MAX_SATOSHI;

/* $1 limit ~= 0.002 BTC */
return 200000;
}

/* Whee, we're on testnet, who cares? */
#define REQUIRED_CONFIRMATIONS 1

@@ -440,11 +451,9 @@ static const char *get_first_input_addr(const tal_t *ctx,
}

/* See "https://en.bitcoin.it/wiki/Proper_Money_Handling_(JSON-RPC)" */
static bool pettycoin_tx(const tal_t *ctx, const char *privkey,
const char *destaddr, const char *amount,
size_t amount_len)
static u64 amount_in_satoshis(const char *amount, size_t amount_len)
{
char *out, *end, *amountstr, *pettyaddr;
char *end;
u64 amt;

/* We expect <number>.<number>. */
@@ -456,6 +465,14 @@ static bool pettycoin_tx(const tal_t *ctx, const char *privkey,
if (end != amount + amount_len)
errx(1, "Bad amount '%.*s'", (int)amount_len, amount);

return amt;
}

static bool pettycoin_tx(const tal_t *ctx, const char *privkey,
const char *destaddr, u64 amt)
{
char *out, *amountstr, *pettyaddr;

amountstr = tal_fmt(ctx, "%llu", (unsigned long long)amt);
pettyaddr = tal_fmt(ctx, "P-%s", destaddr);

@@ -474,6 +491,55 @@ static bool pettycoin_tx(const tal_t *ctx, const char *privkey,
return true;
}

static u64 get_txfee(const tal_t *ctx)
{
jsmntok_t *toks;
const jsmntok_t *fee;
const char *buffer;
unsigned int fee_amount;

toks = json_bitcoind(ctx, &buffer, "getinfo", NULL, NULL, NULL);
if (!toks)
err(1, "getinfo failed");

fee = json_get_member(buffer, toks, "paytxfee");
if (!fee)
errx(1, "getinfo failed to give paytxfee: '%s'", buffer);
fee_amount = amount_in_satoshis(buffer + fee->start,
fee->end - fee->start);
tal_free(toks);
return fee_amount;
}

static bool PRINTF_FMT(5, 6)
bitcoin_tx(const tal_t *ctx,
const char *destaddr, u64 txfee, u64 amt,
const char *fmt, ...)
{
va_list ap;
char *comment, *amountstr, *ret;

va_start(ap, fmt);
comment = tal_vfmt(ctx, fmt, ap);
va_end(ap);
if (amt <= txfee) {
printf("Not refunding tiny amount. Thanks for donation!\n");
return false;
}

amt -= txfee;
amountstr = tal_fmt(comment, "%.8Lf", amt / (long double)100000000);

ret = ask_bitcoind(comment, "sendtoaddress",
destaddr, amountstr, comment);
if (!ret)
err(1, "Doing sendtoaddress '%s' '%s' '%s'",
destaddr, amountstr, comment);

tal_free(comment);
return true;
}

static void destroy_thash(struct thash *thash)
{
thash_clear(thash);
@@ -486,6 +552,7 @@ int main(int argc, char *argv[])
char *pettycoin_dir, *rpc_filename;
bool setup = false;
struct thash *thash;
u64 txfee;
int fd;

err_set_progname(argv[0]);
@@ -523,6 +590,8 @@ int main(int argc, char *argv[])
if (!privkey)
err(1, "Reading gateway-privkey");

txfee = get_txfee(ctx);

for (;;) {
jsmntok_t *toks;
const jsmntok_t *t, *end;
@@ -561,6 +630,7 @@ int main(int argc, char *argv[])
const jsmntok_t *val, *amount;
const char *destaddr;
struct protocol_double_sha txid;
u64 amt;

if (t->type != JSMN_OBJECT)
errx(1, "Unexpected type in"
@@ -591,17 +661,26 @@ int main(int argc, char *argv[])
if (thash_get(thash, &txid))
continue;

amount = json_get_member(buffer, t, "amount");
if (!amount)
errx(1, "No amount in '%s'", buffer);

/* Get address of first input. */
destaddr = get_first_input_addr(this_ctx, buffer, val);

/* OK, inject new from_gateway tx. */
if (!pettycoin_tx(ctx, privkey, destaddr,
buffer + amount->start,
amount->end - amount->start)) {
/* Get amount */
amount = json_get_member(buffer, t, "amount");
if (!amount)
errx(1, "No amount in '%s'", buffer);
amt = amount_in_satoshis(buffer + amount->start,
amount->end - amount->start);

if (amt > max_satoshi_accepted(true)) {
printf("Returning giant tx %.*s (%llu)\n",
json_tok_len(val),
json_tok_contents(buffer, val),
(unsigned long long)amt);
bitcoin_tx(ctx, destaddr, txfee, amt,
"Refund for giant tx %.*s",
json_tok_len(val),
json_tok_contents(buffer, val));
} else if (!pettycoin_tx(ctx, privkey, destaddr, amt)) {
err(1, "Pettcoin injection failed for"
" from-gateway %s P-%s %.*s",
privkey, destaddr,
57 changes: 45 additions & 12 deletions test/run-20-gateway.c
Original file line number Diff line number Diff line change
@@ -574,10 +574,25 @@ struct expected_payment {
static struct expected_payment payments[] = {
{ "P-muzsCLFmS1A2ppe8V4Gxro89a4UvZZzgMn", 12340000, false },
{ "P-mvASkCxYqjMh3JK5VV52CrTWTwhe2T875G", 1000000, false },
{ "P-mzDLJTAWGVuZwwofZLXBxa676gHNU8QtHs", 2000000000, false },
/* Refund payment to bitcoin */
{ "mzDLJTAWGVuZwwofZLXBxa676gHNU8QtHs", 2000000000 - 1000, false },
{ "P-mhe17cf9VGsZH6G6DhsGTQre9LM53qMXDs", 1200000, false }
};

static bool mark_off_payment(const char *addr, u64 satoshis)
{
unsigned int i;

for (i = 0; i < ARRAY_SIZE(payments); i++) {
if (streq(addr, payments[i].address)) {
assert(!payments[i].complete);
assert(satoshis == payments[i].satoshis);
payments[i].complete = true;
return true;
}
}
return false;
}

static void fake_sleep(int seconds)
{
@@ -598,6 +613,25 @@ static void fake_sleep(int seconds)
exit(0);
}

static const char getinfo_response[] =
"{\n"
" \"version\" : 90201,\n"
" \"protocolversion\" : 70002,\n"
" \"walletversion\" : 60000,\n"
" \"balance\" : 0.25440000,\n"
" \"blocks\" : 279418,\n"
" \"timeoffset\" : 0,\n"
" \"connections\" : 0,\n"
" \"proxy\" : \"\",\n"
" \"difficulty\" : 1.00000000,\n"
" \"testnet\" : true,\n"
" \"keypoololdest\" : 1386823067,\n"
" \"keypoolsize\" : 101,\n"
" \"paytxfee\" : 0.00001000,\n"
" \"relayfee\" : 0.00001000,\n"
" \"errors\" : \"\"\n"
"}\n";

static char *ask_process(const tal_t *ctx,
const char *name,
const char *arg1,
@@ -655,22 +689,21 @@ static char *ask_process(const tal_t *ctx,
return tal_strdup(ctx,
getrawtxs_response[i]);
}
} else if (streq(arg2, "getinfo")) {
return tal_strdup(ctx, getinfo_response);
} else if (streq(arg2, "sendtoaddress")) {
if (mark_off_payment(arg3,
amount_in_satoshis(arg4,
strlen(arg4))))
return tal_strdup(ctx, "some-new-bitcoin-txid");
}
} else if (streq(name, "pettycoin-tx")) {
unsigned int i;

assert(streq(arg1, "--no-fee"));
assert(streq(arg2, "from-gateway"));
assert(streq(arg3, "FAKE-gateway-privkey"));

for (i = 0; i < ARRAY_SIZE(payments); i++) {
if (streq(arg4, payments[i].address)) {
assert(!payments[i].complete);
assert(atoi(arg5) == payments[i].satoshis);
payments[i].complete = true;
return tal_fmt(ctx, "raw-transaction-%s", arg4);
}
}

if (mark_off_payment(arg4, atol(arg5)))
return tal_fmt(ctx, "raw-transaction-%s", arg4);
} else if (streq(name, "pettycoin-query")) {
assert(streq(arg1, "sendrawtransaction"));
assert(strstarts(arg2, "raw-transaction-"));
35 changes: 35 additions & 0 deletions tools/gateway-report.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#! /bin/sh

# Read request.
while read LINE; do
LINE=$(echo "$LINE" | tr -d '\015')
if [ x"$LINE" = x ]; then
break
fi
done

# Whatever they said, give canned answer.
echo 'HTTP/1.1 200 OK'
echo 'Content-Type: text/html'
echo
echo '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">'
echo '<html><head>'
echo '<title>Pettycoin Gateway Status Page</title>'
echo '</head><body>'
echo '<h1>Pettycoin Gateway Status Page</h1>'

if pidof pettycoin >/dev/null; then
echo '<p>Gateway is active</p>'
else
echo '<p>Gateway is <b>DOWN</b></p>'
fi

if [ -r /home/pettycoin/gateway-info.html ]; then
cat /home/pettycoin/gateway-info
fi

LOG=`ls /home/pettycoin/pettycoin-gateway.log* | tail -n1`
echo "<p>Log $LOG</p> <pre>"
cat $LOG
echo '</pre></body>'