Browse Source

BIP68 support (nSequence enforcement)

The latest version of the BIP doesn't use inversion, but does use
bitshifts.

It also uncovered a bug in the test scripts: the block timestamps
creep forward when we generate large numbers of blocks (UpdateTime
insists it be > GetMedianTimePast() so it's valid).  We need to take
this into account when waiting for the median to move (reduced it from
60 to 30 seconds, since that adds about 14 seconds).

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
ppa-0.6.1
Rusty Russell 9 years ago
parent
commit
454a3867e5
  1. 4
      Makefile
  2. 22
      bitcoin/tx.c
  3. 2
      bitcoin/tx.h
  4. 3
      test-cli/create-commit-spend-tx.c
  5. 6
      test-cli/create-htlc-spend-tx.c
  6. 6
      test-cli/scripts/generate-block.sh
  7. 29
      test-cli/scripts/test.sh
  8. 6
      test-cli/scripts/vars.sh

4
Makefile

@ -6,8 +6,8 @@ PROTOCC:=protoc-c
# Alpha has checksequenceverify, segregated witness+input-amount-in-sig+confidentual-transactions, schnorr, checklocktimeverify # 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 FEATURES := -DHAS_CSV=1 -DALPHA_TXSTYLE=1 -DUSE_SCHNORR=1 -DHAS_CLTV=1
# Bitcoin uses DER for signatures # Bitcoin uses DER for signatures (Add BIP68 if it's supported)
#FEATURES := -DSCRIPTS_USE_DER #FEATURES := -DSCRIPTS_USE_DER #-DHAS_BIP68
TEST_CLI_PROGRAMS := \ TEST_CLI_PROGRAMS := \
test-cli/check-commit-sig \ test-cli/check-commit-sig \

22
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->input[i].sequence_number = 0xFFFFFFFF;
} }
tx->lock_time = 0; tx->lock_time = 0;
#ifdef HAS_BIP68
tx->version = 2;
#else
tx->version = 1; tx->version = 1;
#endif
return tx; return tx;
} }
@ -543,3 +546,20 @@ bool bitcoin_tx_write(int fd, const struct bitcoin_tx *tx)
tal_free(tx_arr); tal_free(tx_arr);
return ok; 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
}

2
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, bool bitcoin_txid_to_hex(const struct sha256_double *txid,
char *hexstr, size_t hexstr_len); char *hexstr, size_t hexstr_len);
/* Get sequence number for a given locktime. */
u32 bitcoin_nsequence(u32 locktime);
#endif /* LIGHTNING_BITCOIN_TX_H */ #endif /* LIGHTNING_BITCOIN_TX_H */

3
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->input[0].input_amount = commit->output[p2sh_out].amount;
tx->fee = fee; tx->fee = fee;
/* Sequence number is inverted timeout. */ tx->input[0].sequence_number = bitcoin_nsequence(locktime);
tx->input[0].sequence_number = ~locktime;
if (commit->output[p2sh_out].amount <= fee) if (commit->output[p2sh_out].amount <= fee)
errx(1, "Amount of %llu won't exceed fee", errx(1, "Amount of %llu won't exceed fee",

6
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 it's our own commit tx, we also need delay. */
if (own_commit_tx) { if (own_commit_tx)
/* Sequence number is inverted timeout. */ tx->input[0].sequence_number = bitcoin_nsequence(locktime);
tx->input[0].sequence_number = ~locktime;
}
/* Leave 10,000 satoshi as fee (if we can!). */ /* Leave 10,000 satoshi as fee (if we can!). */
tx->fee = 10000; tx->fee = 10000;

6
test-cli/scripts/generate-block.sh

@ -1,5 +1,5 @@
#! /bin/sh #! /bin/sh
# Query bitcoind to get (first) unspent output to spend. # Generate a block.
set -e set -e
@ -10,6 +10,10 @@ case $STYLE in
alpha) alpha)
# This is a one-shot in alpha, it seems. # This is a one-shot in alpha, it seems.
$CLI setgenerate true $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) bitcoin)
# Initially we need 100 blocks so coinbase matures, giving us funds. # Initially we need 100 blocks so coinbase matures, giving us funds.

29
test-cli/scripts/test.sh

@ -4,6 +4,9 @@ set -e
# Expect to be run from test-cli dir. # Expect to be run from test-cli dir.
. scripts/vars.sh . scripts/vars.sh
# How long to lock transactions (unrealistically short, for testing)
TEST_LOCKTIME=30
getpubkey() getpubkey()
{ {
$CLI validateaddress $1 | sed -n 's/.*"pubkey" *: "\([0-9a-f]*\)".*/\1/p' $CLI validateaddress $1 | sed -n 's/.*"pubkey" *: "\([0-9a-f]*\)".*/\1/p'
@ -17,11 +20,9 @@ getprivkey()
send_after_delay() send_after_delay()
{ {
# For bitcoin testing, OP_CHECKSEQUENCEVERIFY is a NOP. # For bitcoin testing, OP_CHECKSEQUENCEVERIFY is a NOP.
if [ $STYLE = alpha ]; then # But nSequence enforcement is enough to stop it.
# Alpha has a median time bug (which can't be triggered in bitcoin), if [ $SEQ_ENFORCEMENT = true ]; then
# triggered if we have < 11 blocks. Generate them now. # OP_CHECKSEQUENCEVERIFY will stop us spending for $TEST_LOCKTIME seconds.
for i in `seq 11`; do scripts/generate-block.sh; done
# OP_CHECKSEQUENCEVERIFY will stop us spending for 60 seconds.
for tx; do for tx; do
if $CLI sendrawtransaction $tx 2>/dev/null; then if $CLI sendrawtransaction $tx 2>/dev/null; then
echo OP_CHECKSEQUENCEVERIFY broken! >&2 echo OP_CHECKSEQUENCEVERIFY broken! >&2
@ -34,10 +35,14 @@ send_after_delay()
# Confirm them. # Confirm them.
scripts/generate-block.sh 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! # 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 for tx; do
$CLI sendrawtransaction $tx $CLI sendrawtransaction $tx
@ -101,7 +106,7 @@ B_FINALPUBKEY=`getpubkey $B_FINALADDR`
# Both sides say what they want from channel (A offers anchor) # 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 $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. # 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!) # 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 $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 $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. # 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" A_UPDATE_PKTS="$A_UPDATE_PKTS +A-update-htlc-3.pb"
B_UPDATE_PKTS="$B_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 $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. # 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" A_UPDATE_PKTS="$A_UPDATE_PKTS -B-update-htlc-4.pb"
B_UPDATE_PKTS="$B_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-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 +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 $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. # 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 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 exit 0

6
test-cli/scripts/vars.sh

@ -6,12 +6,18 @@ if grep -q ^FEATURES.*ALPHA ../Makefile; then
REGTESTDIR=alpharegtest REGTESTDIR=alpharegtest
CLI="alpha-cli -datadir=$DATADIR -regtest -testnet=0" CLI="alpha-cli -datadir=$DATADIR -regtest -testnet=0"
DAEMON="alphad -datadir=$DATADIR" DAEMON="alphad -datadir=$DATADIR"
SEQ_ENFORCEMENT=true
else else
STYLE=bitcoin STYLE=bitcoin
CLI="bitcoin-cli -regtest" CLI="bitcoin-cli -regtest"
DATADIR=$HOME/.bitcoin DATADIR=$HOME/.bitcoin
REGTESTDIR=regtest REGTESTDIR=regtest
DAEMON="bitcoind -regtest" DAEMON="bitcoind -regtest"
if grep ^FEATURES ../Makefile | cut -d'#' -f1 | grep -q BIP68; then
SEQ_ENFORCEMENT=true
else
SEQ_ENFORCEMENT=false
fi
fi fi
#PREFIX="valgrind --vgdb-error=1" #PREFIX="valgrind --vgdb-error=1"

Loading…
Cancel
Save