diff --git a/Makefile b/Makefile index b4fad4bc0..91b71bbb8 100644 --- a/Makefile +++ b/Makefile @@ -6,8 +6,8 @@ PROTOCC:=protoc-c # Alpha has checksequenceverify, segregated witness+input-amount-in-sig+confidentual-transactions, schnorr, checklocktimeverify FEATURES := -DHAS_CSV=1 -DALPHA_TXSTYLE=1 -DUSE_SCHNORR=1 -DHAS_CLTV=1 -# Bitcoin uses DER for signatures -#FEATURES := -DSCRIPTS_USE_DER +# Bitcoin uses DER for signatures (Add BIP68 if it's supported) +#FEATURES := -DSCRIPTS_USE_DER #-DHAS_BIP68 TEST_CLI_PROGRAMS := \ test-cli/check-commit-sig \ diff --git a/bitcoin/tx.c b/bitcoin/tx.c index a7a3dcfbc..97c59f26c 100644 --- a/bitcoin/tx.c +++ b/bitcoin/tx.c @@ -253,8 +253,11 @@ struct bitcoin_tx *bitcoin_tx(const tal_t *ctx, varint_t input_count, tx->input[i].sequence_number = 0xFFFFFFFF; } tx->lock_time = 0; +#ifdef HAS_BIP68 + tx->version = 2; +#else tx->version = 1; - +#endif return tx; } @@ -543,3 +546,20 @@ bool bitcoin_tx_write(int fd, const struct bitcoin_tx *tx) tal_free(tx_arr); return ok; } + +u32 bitcoin_nsequence(u32 locktime) +{ +#ifdef HAS_BIP68 + /* BIP66 style sequence numbers */ + if (locktime >= 500000000) + /* A relative time. Set bit 30, shift by 5. */ + return 0x40000000 | ((locktime - 500000000) << 5); + else + /* A block height. Shift by 14. */ + return locktime << 14; +#else + /* Alpha uses the original proposal: simply invert the bits. */ + return ~locktime; +#endif +} + diff --git a/bitcoin/tx.h b/bitcoin/tx.h index ad8ecd51e..841100fac 100644 --- a/bitcoin/tx.h +++ b/bitcoin/tx.h @@ -66,4 +66,6 @@ bool bitcoin_txid_from_hex(const char *hexstr, size_t hexstr_len, bool bitcoin_txid_to_hex(const struct sha256_double *txid, char *hexstr, size_t hexstr_len); +/* Get sequence number for a given locktime. */ +u32 bitcoin_nsequence(u32 locktime); #endif /* LIGHTNING_BITCOIN_TX_H */ diff --git a/test-cli/create-commit-spend-tx.c b/test-cli/create-commit-spend-tx.c index bb20df03e..79755044d 100644 --- a/test-cli/create-commit-spend-tx.c +++ b/test-cli/create-commit-spend-tx.c @@ -99,8 +99,7 @@ int main(int argc, char *argv[]) tx->input[0].input_amount = commit->output[p2sh_out].amount; tx->fee = fee; - /* Sequence number is inverted timeout. */ - tx->input[0].sequence_number = ~locktime; + tx->input[0].sequence_number = bitcoin_nsequence(locktime); if (commit->output[p2sh_out].amount <= fee) errx(1, "Amount of %llu won't exceed fee", diff --git a/test-cli/create-htlc-spend-tx.c b/test-cli/create-htlc-spend-tx.c index 290220c3d..82b46baf0 100644 --- a/test-cli/create-htlc-spend-tx.c +++ b/test-cli/create-htlc-spend-tx.c @@ -171,10 +171,8 @@ int main(int argc, char *argv[]) } /* If it's our own commit tx, we also need delay. */ - if (own_commit_tx) { - /* Sequence number is inverted timeout. */ - tx->input[0].sequence_number = ~locktime; - } + if (own_commit_tx) + tx->input[0].sequence_number = bitcoin_nsequence(locktime); /* Leave 10,000 satoshi as fee (if we can!). */ tx->fee = 10000; diff --git a/test-cli/scripts/generate-block.sh b/test-cli/scripts/generate-block.sh index 1d53ee41d..8975723c1 100755 --- a/test-cli/scripts/generate-block.sh +++ b/test-cli/scripts/generate-block.sh @@ -1,5 +1,5 @@ #! /bin/sh -# Query bitcoind to get (first) unspent output to spend. +# Generate a block. set -e @@ -10,6 +10,10 @@ case $STYLE in alpha) # This is a one-shot in alpha, it seems. $CLI setgenerate true + # Avoid median time bug by generating 11 blocks + if [ -n "$INIT" ]; then + for i in `seq 10`; do $CLI setgenerate true; done + fi ;; bitcoin) # Initially we need 100 blocks so coinbase matures, giving us funds. diff --git a/test-cli/scripts/test.sh b/test-cli/scripts/test.sh index 991f9accd..1b6e063fb 100755 --- a/test-cli/scripts/test.sh +++ b/test-cli/scripts/test.sh @@ -4,6 +4,9 @@ set -e # Expect to be run from test-cli dir. . scripts/vars.sh +# How long to lock transactions (unrealistically short, for testing) +TEST_LOCKTIME=30 + getpubkey() { $CLI validateaddress $1 | sed -n 's/.*"pubkey" *: "\([0-9a-f]*\)".*/\1/p' @@ -17,11 +20,9 @@ getprivkey() send_after_delay() { # For bitcoin testing, OP_CHECKSEQUENCEVERIFY is a NOP. - if [ $STYLE = alpha ]; then - # Alpha has a median time bug (which can't be triggered in bitcoin), - # triggered if we have < 11 blocks. Generate them now. - for i in `seq 11`; do scripts/generate-block.sh; done - # OP_CHECKSEQUENCEVERIFY will stop us spending for 60 seconds. + # But nSequence enforcement is enough to stop it. + if [ $SEQ_ENFORCEMENT = true ]; then + # OP_CHECKSEQUENCEVERIFY will stop us spending for $TEST_LOCKTIME seconds. for tx; do if $CLI sendrawtransaction $tx 2>/dev/null; then echo OP_CHECKSEQUENCEVERIFY broken! >&2 @@ -34,11 +35,15 @@ send_after_delay() # Confirm them. scripts/generate-block.sh - echo Waiting for CSV timeout. >&2 - sleep 61 + + # Bitcoin bumps block times so that blocks are valid. + TIME=$($CLI getblockheader $($CLI getbestblockhash) | sed -n 's/.*"time": \([0-9]*\),/\1/p') + echo Waiting for CSV timeout $(( $TIME + $TEST_LOCKTIME - $(date -u +%s) )) seconds. >&2 + # Move median time, for sure! - for i in `seq 11`; do scripts/generate-block.sh; done - + while [ `date -u +%s` -lt $(($TIME + $TEST_LOCKTIME)) ]; do sleep 1; done + for i in `seq 6`; do scripts/generate-block.sh; done + for tx; do $CLI sendrawtransaction $tx done @@ -101,7 +106,7 @@ B_FINALPUBKEY=`getpubkey $B_FINALADDR` # Both sides say what they want from channel (A offers anchor) $PREFIX ./open-channel --offer-anchor $A_SEED $A_TMPPUBKEY $A_FINALPUBKEY > A-open.pb # B asks for a (dangerously) short locktime, for testing unilateral close. -$PREFIX ./open-channel --locktime=60 $B_SEED $B_TMPPUBKEY $B_FINALPUBKEY > B-open.pb +$PREFIX ./open-channel --locktime=$TEST_LOCKTIME $B_SEED $B_TMPPUBKEY $B_FINALPUBKEY > B-open.pb # Now A creates anchor (does not broadcast!) $PREFIX ./create-anchor-tx A-open.pb B-open.pb $A_AMOUNT $A_CHANGEPUBKEY $A_TXIN > A-anchor.tx @@ -175,7 +180,7 @@ $PREFIX ./create-commit-tx A-open.pb B-open.pb A-anchor.pb $A_TMPKEY $A_UPDATE_P $PREFIX ./create-commit-tx B-open.pb A-open.pb A-anchor.pb $B_TMPKEY $B_UPDATE_PKTS > B-commit-2.tx # Now, A offers an HTLC for 10001 satoshi. -$PREFIX ./update-channel-htlc $A_SEED 3 10001 $A_HTLC1 $((`date +%s` + 60)) > A-update-htlc-3.pb +$PREFIX ./update-channel-htlc $A_SEED 3 10001 $A_HTLC1 $((`date +%s` + $TEST_LOCKTIME)) > A-update-htlc-3.pb A_UPDATE_PKTS="$A_UPDATE_PKTS +A-update-htlc-3.pb" B_UPDATE_PKTS="$B_UPDATE_PKTS -A-update-htlc-3.pb" @@ -196,7 +201,7 @@ $PREFIX ./create-commit-tx A-open.pb B-open.pb A-anchor.pb $A_TMPKEY $A_UPDATE_P $PREFIX ./create-commit-tx B-open.pb A-open.pb A-anchor.pb $B_TMPKEY $B_UPDATE_PKTS > B-commit-3.tx # Now, B offers an HTLC for 10002 satoshi. -$PREFIX ./update-channel-htlc $B_SEED 4 10002 $B_HTLC1 $((`date +%s` + 60)) > B-update-htlc-4.pb +$PREFIX ./update-channel-htlc $B_SEED 4 10002 $B_HTLC1 $((`date +%s` + $TEST_LOCKTIME)) > B-update-htlc-4.pb A_UPDATE_PKTS="$A_UPDATE_PKTS -B-update-htlc-4.pb" B_UPDATE_PKTS="$B_UPDATE_PKTS +B-update-htlc-4.pb" @@ -237,7 +242,7 @@ if [ x"$1" = x--unilateral ]; then $PREFIX ./create-commit-spend-tx A-commit-4.tx A-open.pb B-open.pb A-anchor.pb $A_FINALKEY $A_CHANGEPUBKEY $A_UPDATE_PKTS > A-spend.tx $PREFIX ./create-htlc-spend-tx A-open.pb B-open.pb A-commit-4.tx +A-update-htlc-3.pb A-update-accept-4.pb $A_FINALKEY $A_CHANGEPUBKEY > A-htlc-3-spend.tx $PREFIX ./create-htlc-spend-tx -- A-open.pb B-open.pb A-commit-4.tx -B-update-htlc-4.pb A-update-accept-4.pb $B_FINALKEY $B_CHANGEPUBKEY > B-htlc-4-spend.tx - # HTLCs conveniently set to 60 seconds, though absolute. Script + # HTLCs conveniently set to $TEST_LOCKTIME seconds, though absolute. Script # shouldn't be that slow, so they should be unspendable to start. send_after_delay `cut -d: -f1 A-spend.tx` `cut -d: -f1 A-htlc-3-spend.tx` `cut -d: -f1 B-htlc-4-spend.tx` > A-spend.txids exit 0 diff --git a/test-cli/scripts/vars.sh b/test-cli/scripts/vars.sh index 60a882e2e..bbde8f7c4 100644 --- a/test-cli/scripts/vars.sh +++ b/test-cli/scripts/vars.sh @@ -6,12 +6,18 @@ if grep -q ^FEATURES.*ALPHA ../Makefile; then REGTESTDIR=alpharegtest CLI="alpha-cli -datadir=$DATADIR -regtest -testnet=0" DAEMON="alphad -datadir=$DATADIR" + SEQ_ENFORCEMENT=true else STYLE=bitcoin CLI="bitcoin-cli -regtest" DATADIR=$HOME/.bitcoin REGTESTDIR=regtest DAEMON="bitcoind -regtest" + if grep ^FEATURES ../Makefile | cut -d'#' -f1 | grep -q BIP68; then + SEQ_ENFORCEMENT=true + else + SEQ_ENFORCEMENT=false + fi fi #PREFIX="valgrind --vgdb-error=1"