Browse Source

locktime: nice abstractions for absolute and relative locktimes.

I got confused navigating these, especially since Alpha and Bitcoin
have diverged (BIP68 was proposed after Elements Alpha).

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
ppa-0.6.1
Rusty Russell 9 years ago
parent
commit
4b02c6f558
  1. 1
      Makefile
  2. 101
      bitcoin/locktime.c
  3. 27
      bitcoin/locktime.h
  4. 21
      bitcoin/script.c
  5. 12
      bitcoin/script.h
  6. 14
      bitcoin/tx.c
  7. 2
      bitcoin/tx.h
  8. 19
      commit_tx.c
  9. 41
      protobuf_convert.c
  10. 6
      protobuf_convert.h
  11. 7
      test-cli/create-commit-spend-tx.c
  12. 16
      test-cli/create-htlc-spend-tx.c
  13. 7
      test-cli/create-steal-tx.c

1
Makefile

@ -39,6 +39,7 @@ TEST_PROGRAMS := \
BITCOIN_OBJS := \
bitcoin/address.o \
bitcoin/base58.o \
bitcoin/locktime.o \
bitcoin/pubkey.o \
bitcoin/script.o \
bitcoin/shadouble.o \

101
bitcoin/locktime.c

@ -0,0 +1,101 @@
#include "bitcoin/locktime.h"
#include <assert.h>
/* Alpha uses simple locktimes a-la the tx locktime field; BIP68 uses
* a bitmask */
#define SECONDS_POINT 500000000
#define BIP68_SECONDS_FLAG (1<<22)
#define BIP68_LOCKTIME_MASK (0x0000FFFF)
#define BIP68_SECONDS_SHIFT 9
static bool abs_seconds_to_locktime(u32 seconds, u32 *locktime)
{
*locktime = seconds;
if (*locktime < SECONDS_POINT)
return false;
return true;
}
static bool abs_blocks_to_locktime(u32 blocks, u32 *locktime)
{
*locktime = blocks;
if (*locktime >= SECONDS_POINT)
return false;
return true;
}
static bool abs_is_seconds(u32 locktime)
{
return locktime >= SECONDS_POINT;
}
bool seconds_to_rel_locktime(u32 seconds, struct rel_locktime *rel)
{
#ifdef HAS_BIP68
if ((seconds >> BIP68_SECONDS_SHIFT) > BIP68_LOCKTIME_MASK)
return false;
rel->locktime = BIP68_SECONDS_FLAG | (seconds >> BIP68_SECONDS_SHIFT);
return true;
#else
/* Make abs-style time by adding SECONDS_POINT. */
return abs_seconds_to_locktime(seconds + SECONDS_POINT, &rel->locktime);
#endif
}
bool blocks_to_rel_locktime(u32 blocks, struct rel_locktime *rel)
{
#ifdef HAS_BIP68
if (blocks > BIP68_LOCKTIME_MASK)
return false;
#endif
rel->locktime = blocks;
return true;
}
bool rel_locktime_is_seconds(const struct rel_locktime *rel)
{
#ifdef HAS_BIP68
return rel->locktime & BIP68_SECONDS_FLAG;
#else
return abs_is_seconds(rel->locktime);
#endif
}
u32 rel_locktime_to_seconds(const struct rel_locktime *rel)
{
assert(rel_locktime_is_seconds(rel));
#ifdef HAS_BIP68
return rel->locktime & BIP68_LOCKTIME_MASK;
#else
return rel->locktime - SECONDS_POINT;
#endif
}
u32 bitcoin_nsequence(const struct rel_locktime *rel)
{
#ifdef HAS_BIP68
/* Can't set disable bit, or other bits except low 16 and bit 22 */
assert(!(rel->locktime & ~(BIP68_SECONDS_FLAG|BIP68_LOCKTIME_MASK)));
return rel->locktime;
#else
/* Alpha uses the original proposal: simply invert the bits. */
return ~rel->locktime;
#endif
}
bool seconds_to_abs_locktime(u32 seconds, struct abs_locktime *abs)
{
return abs_seconds_to_locktime(seconds, &abs->locktime);
}
bool blocks_to_abs_locktime(u32 blocks, struct abs_locktime *abs)
{
return abs_blocks_to_locktime(blocks, &abs->locktime);
}
bool abs_locktime_is_seconds(const struct abs_locktime *abs)
{
return abs_is_seconds(abs->locktime);
}

27
bitcoin/locktime.h

@ -0,0 +1,27 @@
#ifndef LIGHTNING_BITCOIN_LOCKTIME_H
#define LIGHTNING_BITCOIN_LOCKTIME_H
#include <ccan/short_types/short_types.h>
#include <stdbool.h>
/* As used by nSequence and OP_CHECKSEQUENCEVERIFY (BIP68) */
struct rel_locktime {
u32 locktime;
};
bool seconds_to_rel_locktime(u32 seconds, struct rel_locktime *rel);
bool blocks_to_rel_locktime(u32 blocks, struct rel_locktime *rel);
bool rel_locktime_is_seconds(const struct rel_locktime *rel);
u32 rel_locktime_to_seconds(const struct rel_locktime *rel);
u32 bitcoin_nsequence(const struct rel_locktime *rel);
/* As used by nLocktime and OP_CHECKLOCKTIMEVERIFY (BIP65) */
struct abs_locktime {
u32 locktime;
};
bool seconds_to_abs_locktime(u32 seconds, struct abs_locktime *abs);
bool blocks_to_abs_locktime(u32 blocks, struct abs_locktime *abs);
bool abs_locktime_is_seconds(const struct abs_locktime *abs);
#endif /* LIGHTNING_BITCOIN_LOCKTIME_H */

21
bitcoin/script.c

@ -3,6 +3,7 @@
#include <ccan/endian/endian.h>
#include <assert.h>
#include "address.h"
#include "locktime.h"
#include "pubkey.h"
#include "script.h"
#include "signature.h"
@ -179,8 +180,8 @@ u8 *scriptpubkey_p2sh(const tal_t *ctx, const u8 *redeemscript)
u8 *scriptpubkey_htlc_send(const tal_t *ctx,
const struct pubkey *ourkey,
const struct pubkey *theirkey,
uint32_t htlc_abstimeout,
uint32_t locktime,
const struct abs_locktime *htlc_abstimeout,
const struct rel_locktime *locktime,
const struct sha256 *commit_revoke,
const struct sha256 *rhash)
{
@ -210,9 +211,9 @@ u8 *scriptpubkey_htlc_send(const tal_t *ctx,
add_op(&script, OP_ELSE);
/* If HTLC times out, they can collect after a delay. */
add_number(&script, htlc_abstimeout);
add_number(&script, htlc_abstimeout->locktime);
add_op(&script, OP_CHECKLOCKTIMEVERIFY);
add_number(&script, locktime);
add_number(&script, locktime->locktime);
add_op(&script, OP_CHECKSEQUENCEVERIFY);
add_op(&script, OP_2DROP);
add_push_key(&script, ourkey);
@ -227,8 +228,8 @@ u8 *scriptpubkey_htlc_send(const tal_t *ctx,
u8 *scriptpubkey_htlc_recv(const tal_t *ctx,
const struct pubkey *ourkey,
const struct pubkey *theirkey,
uint32_t htlc_abstimeout,
uint32_t locktime,
const struct abs_locktime *htlc_abstimeout,
const struct rel_locktime *locktime,
const struct sha256 *commit_revoke,
const struct sha256 *rhash)
{
@ -247,7 +248,7 @@ u8 *scriptpubkey_htlc_recv(const tal_t *ctx,
add_op(&script, OP_EQUAL);
add_op(&script, OP_IF);
add_number(&script, locktime);
add_number(&script, locktime->locktime);
add_op(&script, OP_CHECKSEQUENCEVERIFY);
/* Drop extra hash as well as locktime. */
add_op(&script, OP_2DROP);
@ -264,7 +265,7 @@ u8 *scriptpubkey_htlc_recv(const tal_t *ctx,
add_op(&script, OP_NOTIF);
/* Otherwise, they must wait for HTLC timeout. */
add_number(&script, htlc_abstimeout);
add_number(&script, htlc_abstimeout->locktime);
add_op(&script, OP_CHECKLOCKTIMEVERIFY);
add_op(&script, OP_DROP);
add_op(&script, OP_ENDIF);
@ -361,7 +362,7 @@ bool is_p2sh(const u8 *script, size_t script_len)
* it after delay. */
u8 *bitcoin_redeem_secret_or_delay(const tal_t *ctx,
const struct pubkey *delayed_key,
u32 locktime,
const struct rel_locktime *locktime,
const struct pubkey *key_if_secret_known,
const struct sha256 *hash_of_secret)
{
@ -382,7 +383,7 @@ u8 *bitcoin_redeem_secret_or_delay(const tal_t *ctx,
add_op(&script, OP_ELSE);
/* Other can collect after a delay. */
add_number(&script, locktime);
add_number(&script, locktime->locktime);
add_op(&script, OP_CHECKSEQUENCEVERIFY);
add_op(&script, OP_DROP);
add_push_key(&script, delayed_key);

12
bitcoin/script.h

@ -7,6 +7,8 @@
struct bitcoin_address;
struct pubkey;
struct sha256;
struct rel_locktime;
struct abs_locktime;
/* A bitcoin signature includes one byte for the type. */
struct bitcoin_signature {
@ -26,7 +28,7 @@ u8 *bitcoin_redeem_single(const tal_t *ctx, const struct pubkey *key);
* it after delay. */
u8 *bitcoin_redeem_secret_or_delay(const tal_t *ctx,
const struct pubkey *delayed_key,
u32 locktime,
const struct rel_locktime *locktime,
const struct pubkey *key_if_secret_known,
const struct sha256 *hash_of_secret);
@ -41,8 +43,8 @@ u8 *scriptsig_pay_to_pubkeyhash(const tal_t *ctx,
u8 *scriptpubkey_htlc_send(const tal_t *ctx,
const struct pubkey *ourkey,
const struct pubkey *theirkey,
uint32_t htlc_abstimeout,
uint32_t locktime,
const struct abs_locktime *htlc_abstimeout,
const struct rel_locktime *locktime,
const struct sha256 *commit_revoke,
const struct sha256 *rhash);
@ -50,8 +52,8 @@ u8 *scriptpubkey_htlc_send(const tal_t *ctx,
u8 *scriptpubkey_htlc_recv(const tal_t *ctx,
const struct pubkey *ourkey,
const struct pubkey *theirkey,
uint32_t htlc_abstimeout,
uint32_t locktime,
const struct abs_locktime *htlc_abstimeout,
const struct rel_locktime *locktime,
const struct sha256 *commit_revoke,
const struct sha256 *rhash);

14
bitcoin/tx.c

@ -546,17 +546,3 @@ 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
/* FIXME: return fail to caller. */
/* Can't set disable bit, or other bits except low 16 and bit 22 */
assert(!(locktime & ~((1 << 22) | 0xFFFF)));
return locktime;
#else
/* Alpha uses the original proposal: simply invert the bits. */
return ~locktime;
#endif
}

2
bitcoin/tx.h

@ -66,6 +66,4 @@ 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 */

19
commit_tx.c

@ -1,3 +1,4 @@
#include "bitcoin/locktime.h"
#include "bitcoin/pubkey.h"
#include "bitcoin/script.h"
#include "bitcoin/shadouble.h"
@ -14,16 +15,16 @@ static bool add_htlc(struct bitcoin_tx *tx, size_t n,
const struct pubkey *ourkey,
const struct pubkey *theirkey,
const struct sha256 *rhash,
u32 locktime,
const struct rel_locktime *locktime,
u8 *(*scriptpubkeyfn)(const tal_t *,
const struct pubkey *,
const struct pubkey *,
uint32_t,
uint32_t,
const struct abs_locktime *,
const struct rel_locktime *,
const struct sha256 *,
const struct sha256 *))
{
uint32_t htlc_abstime;
struct abs_locktime htlc_abstime;
struct sha256 htlc_rhash;
assert(!tx->output[n].script);
@ -35,7 +36,7 @@ static bool add_htlc(struct bitcoin_tx *tx, size_t n,
proto_to_sha256(h->r_hash, &htlc_rhash);
tx->output[n].script = scriptpubkey_p2sh(tx,
scriptpubkeyfn(tx, ourkey, theirkey,
htlc_abstime, locktime, rhash,
&htlc_abstime, locktime, rhash,
&htlc_rhash));
tx->output[n].script_length = tal_count(tx->output[n].script);
tx->output[n].amount = h->amount_msat / 1000;
@ -52,7 +53,7 @@ struct bitcoin_tx *create_commit_tx(const tal_t *ctx,
struct bitcoin_tx *tx;
const u8 *redeemscript;
struct pubkey ourkey, theirkey;
u32 locktime;
struct rel_locktime locktime;
size_t i, num;
uint64_t total;
@ -76,7 +77,7 @@ struct bitcoin_tx *create_commit_tx(const tal_t *ctx,
/* First output is a P2SH to a complex redeem script (usu. for me) */
redeemscript = bitcoin_redeem_secret_or_delay(tx, &ourkey,
locktime,
&locktime,
&theirkey,
rhash);
tx->output[0].script = scriptpubkey_p2sh(tx, redeemscript);
@ -97,14 +98,14 @@ struct bitcoin_tx *create_commit_tx(const tal_t *ctx,
/* HTLCs we've sent. */
for (i = 0; i < tal_count(cstate->a.htlcs); i++) {
if (!add_htlc(tx, num, cstate->a.htlcs[i], &ourkey, &theirkey,
rhash, locktime, scriptpubkey_htlc_send))
rhash, &locktime, scriptpubkey_htlc_send))
return tal_free(tx);
total += tx->output[num++].amount;
}
/* HTLCs we've received. */
for (i = 0; i < tal_count(cstate->b.htlcs); i++) {
if (!add_htlc(tx, num, cstate->b.htlcs[i], &ourkey, &theirkey,
rhash, locktime, scriptpubkey_htlc_recv))
rhash, &locktime, scriptpubkey_htlc_recv))
return tal_free(tx);
total += tx->output[num++].amount;
}

41
protobuf_convert.c

@ -1,4 +1,5 @@
#include <ccan/crypto/sha256/sha256.h>
#include "bitcoin/locktime.h"
#include "bitcoin/pubkey.h"
#include "bitcoin/signature.h"
#include "protobuf_convert.h"
@ -105,54 +106,26 @@ void proto_to_sha256(const Sha256Hash *pb, struct sha256 *hash)
memcpy(hash->u.u8 + 24, &pb->d, 8);
}
static bool proto_to_locktime(const Locktime *l, uint32_t off,
uint32_t *locktime)
bool proto_to_rel_locktime(const Locktime *l, struct rel_locktime *locktime)
{
switch (l->locktime_case) {
case LOCKTIME__LOCKTIME_SECONDS:
*locktime = off + l->seconds;
/* Check for wrap, or too low value */
if (*locktime < 500000000)
return false;
break;
return seconds_to_rel_locktime(l->seconds, locktime);
case LOCKTIME__LOCKTIME_BLOCKS:
if (l->blocks >= 500000000)
return false;
*locktime = l->blocks;
break;
return blocks_to_rel_locktime(l->blocks, locktime);
default:
return false;
}
return true;
}
bool proto_to_rel_locktime(const Locktime *l, uint32_t *locktime)
bool proto_to_abs_locktime(const Locktime *l, struct abs_locktime *locktime)
{
/* Original proposal from Elements Alpha was simply locktime. */
#ifdef HAS_BIP68
switch (l->locktime_case) {
case LOCKTIME__LOCKTIME_SECONDS:
*locktime = (1 << 22) | (l->seconds / 512);
if (l->seconds / 512 > 0xFFFF)
return false;
break;
return seconds_to_abs_locktime(l->seconds, locktime);
case LOCKTIME__LOCKTIME_BLOCKS:
*locktime = l->blocks;
if (l->blocks > 0xFFFF)
return false;
break;
return blocks_to_abs_locktime(l->blocks, locktime);
default:
return false;
}
/* No other bits should be set. */
assert((*locktime & ~((1 << 22) | 0xFFFF)) == 0);
return true;
#else
return proto_to_locktime(l, 500000000, locktime);
#endif
}
bool proto_to_abs_locktime(const Locktime *l, uint32_t *locktime)
{
return proto_to_locktime(l, 0, locktime);
}

6
protobuf_convert.h

@ -19,6 +19,8 @@ struct sha256;
Sha256Hash *sha256_to_proto(const tal_t *ctx, const struct sha256 *hash);
void proto_to_sha256(const Sha256Hash *pb, struct sha256 *hash);
bool proto_to_rel_locktime(const Locktime *l, uint32_t *locktime);
bool proto_to_abs_locktime(const Locktime *l, uint32_t *locktime);
struct rel_locktime;
struct abs_locktime;
bool proto_to_rel_locktime(const Locktime *l, struct rel_locktime *locktime);
bool proto_to_abs_locktime(const Locktime *l, struct abs_locktime *locktime);
#endif /* LIGHTNING_PROTOBUF_CONVERT_H */

7
test-cli/create-commit-spend-tx.c

@ -21,6 +21,7 @@
#include "test-cli/gather_updates.h"
#include "funding.h"
#include "version.h"
#include "bitcoin/locktime.h"
#include <unistd.h>
int main(int argc, char *argv[])
@ -37,7 +38,7 @@ int main(int argc, char *argv[])
struct sha256 rhash;
size_t p2sh_out;
u64 fee = 10000;
u32 locktime;
struct rel_locktime locktime;
err_set_progname(argv[0]);
@ -87,7 +88,7 @@ int main(int argc, char *argv[])
NULL, &rhash, NULL, NULL);
/* Create redeem script */
redeemscript = bitcoin_redeem_secret_or_delay(ctx, &pubkey1, locktime,
redeemscript = bitcoin_redeem_secret_or_delay(ctx, &pubkey1, &locktime,
&pubkey2, &rhash);
/* Now, create transaction to spend it. */
@ -98,7 +99,7 @@ int main(int argc, char *argv[])
tx->input[0].input_amount = commit->output[p2sh_out].amount;
tx->fee = fee;
tx->input[0].sequence_number = bitcoin_nsequence(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",

16
test-cli/create-htlc-spend-tx.c

@ -16,6 +16,7 @@
#include "bitcoin/privkey.h"
#include "protobuf_convert.h"
#include "find_p2sh_out.h"
#include "bitcoin/locktime.h"
#include "version.h"
#include <unistd.h>
@ -31,7 +32,8 @@ int main(int argc, char *argv[])
struct bitcoin_signature sig;
struct privkey privkey;
bool testnet;
u32 locktime, htlc_abstimeout;
struct rel_locktime locktime;
struct abs_locktime htlc_abstimeout;
char *rvalue = NULL, *preimage = NULL;
bool received, own_commit_tx;
Pkt *pkt;
@ -114,13 +116,13 @@ int main(int argc, char *argv[])
if (received) {
redeemscript = scriptpubkey_htlc_recv(ctx, &pubkey1, &pubkey2,
htlc_abstimeout,
locktime, &revoke_hash,
&htlc_abstimeout,
&locktime, &revoke_hash,
&htlc_rhash);
} else {
redeemscript = scriptpubkey_htlc_send(ctx, &pubkey1, &pubkey2,
htlc_abstimeout,
locktime, &revoke_hash,
&htlc_abstimeout,
&locktime, &revoke_hash,
&htlc_rhash);
}
@ -163,14 +165,14 @@ int main(int argc, char *argv[])
if (!secret_len) {
/* We must be relying on HTLC timeout. */
tx->lock_time = htlc_abstimeout;
tx->lock_time = htlc_abstimeout.locktime;
/* Locktime only applies if an input has seq != ffffffff... */
tx->input[0].sequence_number = 0;
}
/* If it's our own commit tx, we also need delay. */
if (own_commit_tx)
tx->input[0].sequence_number = bitcoin_nsequence(locktime);
tx->input[0].sequence_number = bitcoin_nsequence(&locktime);
/* Leave 10,000 satoshi as fee (if we can!). */
tx->fee = 10000;

7
test-cli/create-steal-tx.c

@ -15,6 +15,7 @@
#include "bitcoin/privkey.h"
#include "protobuf_convert.h"
#include "version.h"
#include "bitcoin/locktime.h"
#include <unistd.h>
int main(int argc, char *argv[])
@ -30,7 +31,7 @@ int main(int argc, char *argv[])
struct bitcoin_signature sig;
struct privkey privkey;
bool testnet;
u32 locktime_seconds;
struct rel_locktime locktime;
err_set_progname(argv[0]);
@ -68,7 +69,7 @@ int main(int argc, char *argv[])
o1 = pkt_from_file(argv[4], PKT__PKT_OPEN)->open;
o2 = pkt_from_file(argv[5], PKT__PKT_OPEN)->open;
if (!proto_to_rel_locktime(o1->delay, &locktime_seconds))
if (!proto_to_rel_locktime(o1->delay, &locktime))
errx(1, "Invalid locktime in o2");
if (!pubkey_from_hexstr(argv[6], &outpubkey))
@ -85,7 +86,7 @@ int main(int argc, char *argv[])
/* Now, which commit output? Match redeem script. */
sha256(&revoke_hash, &revoke_preimage, sizeof(revoke_preimage));
redeemscript = bitcoin_redeem_secret_or_delay(ctx, &pubkey2,
locktime_seconds,
&locktime,
&pubkey1, &revoke_hash);
p2sh = scriptpubkey_p2sh(ctx, redeemscript);

Loading…
Cancel
Save