8 changed files with 393 additions and 6 deletions
@ -0,0 +1,99 @@ |
|||||
|
#include "bitcoin/pubkey.h" |
||||
|
#include "bitcoin/script.h" |
||||
|
#include "bitcoin/shadouble.h" |
||||
|
#include "bitcoin/tx.h" |
||||
|
#include "escape_tx.h" |
||||
|
#include "protobuf_convert.h" |
||||
|
|
||||
|
static struct bitcoin_tx *escape_tx(const tal_t *ctx, |
||||
|
const u8 *redeemscript, |
||||
|
const struct sha256_double *anchor_txid, |
||||
|
unsigned int anchor_index, |
||||
|
uint64_t input_amount, |
||||
|
uint64_t escape_fee) |
||||
|
{ |
||||
|
struct bitcoin_tx *tx = bitcoin_tx(ctx, 1, 1); |
||||
|
|
||||
|
/* Our input spends the anchor tx output. */ |
||||
|
tx->input[0].txid = *anchor_txid; |
||||
|
tx->input[0].index = anchor_index; |
||||
|
tx->input[0].input_amount = input_amount; |
||||
|
|
||||
|
/* Escape fee must be sane. */ |
||||
|
tx->fee = escape_fee; |
||||
|
if (tx->fee > input_amount) |
||||
|
return tal_free(tx); |
||||
|
tx->output[0].amount = input_amount - tx->fee; |
||||
|
tx->output[0].script = scriptpubkey_p2sh(tx, redeemscript); |
||||
|
tx->output[0].script_length = tal_count(tx->output[0].script); |
||||
|
|
||||
|
return tx; |
||||
|
} |
||||
|
|
||||
|
struct bitcoin_tx *create_escape_tx(const tal_t *ctx, |
||||
|
OpenChannel *ours, |
||||
|
OpenChannel *theirs, |
||||
|
const struct sha256_double *anchor_txid, |
||||
|
unsigned int anchor_index, |
||||
|
uint64_t input_amount, |
||||
|
uint64_t escape_fee) |
||||
|
{ |
||||
|
struct bitcoin_tx *tx; |
||||
|
const u8 *redeemscript; |
||||
|
struct pubkey ourkey, theirkey; |
||||
|
struct sha256 rhash; |
||||
|
u32 locktime; |
||||
|
|
||||
|
/* Outputs goes to final pubkey */ |
||||
|
if (!proto_to_pubkey(ours->final, &ourkey)) |
||||
|
return NULL; |
||||
|
if (!proto_to_pubkey(theirs->final, &theirkey)) |
||||
|
return NULL;; |
||||
|
if (!proto_to_locktime(theirs, &locktime)) |
||||
|
return NULL; |
||||
|
|
||||
|
/* They can have it if they they present revocation preimage. */ |
||||
|
proto_to_sha256(ours->escape_hash, &rhash); |
||||
|
|
||||
|
redeemscript = bitcoin_redeem_secret_or_delay(ctx, &ourkey, locktime, |
||||
|
&theirkey, &rhash); |
||||
|
|
||||
|
tx = escape_tx(ctx, redeemscript, anchor_txid, anchor_index, |
||||
|
input_amount, escape_fee); |
||||
|
tal_free(redeemscript); |
||||
|
return tx; |
||||
|
} |
||||
|
|
||||
|
struct bitcoin_tx *create_fast_escape_tx(const tal_t *ctx, |
||||
|
OpenChannel *ours, |
||||
|
OpenChannel *theirs, |
||||
|
const struct sha256_double *anchor_txid, |
||||
|
unsigned int anchor_index, |
||||
|
uint64_t input_amount, |
||||
|
uint64_t escape_fee) |
||||
|
{ |
||||
|
struct bitcoin_tx *tx; |
||||
|
const u8 *redeemscript; |
||||
|
struct pubkey ourkey, theirkey; |
||||
|
struct sha256 ehash; |
||||
|
u32 locktime; |
||||
|
|
||||
|
/* Outputs goes to final pubkey */ |
||||
|
if (!proto_to_pubkey(ours->final, &ourkey)) |
||||
|
return NULL; |
||||
|
if (!proto_to_pubkey(theirs->final, &theirkey)) |
||||
|
return NULL; |
||||
|
if (!proto_to_locktime(theirs, &locktime)) |
||||
|
return NULL; |
||||
|
|
||||
|
/* We can have it if we present their escape preimage. */ |
||||
|
proto_to_sha256(theirs->escape_hash, &ehash); |
||||
|
|
||||
|
redeemscript = bitcoin_redeem_secret_or_delay(ctx, &theirkey, locktime, |
||||
|
&ourkey, &ehash); |
||||
|
|
||||
|
tx = escape_tx(ctx, redeemscript, anchor_txid, anchor_index, |
||||
|
input_amount, escape_fee); |
||||
|
tal_free(redeemscript); |
||||
|
return tx; |
||||
|
} |
@ -0,0 +1,28 @@ |
|||||
|
#ifndef LIGHTNING_ESCAPE_TX_H |
||||
|
#define LIGHTNING_ESCAPE_TX_H |
||||
|
#include <ccan/tal/tal.h> |
||||
|
#include "lightning.pb-c.h" |
||||
|
|
||||
|
struct sha256_double; |
||||
|
struct sha256; |
||||
|
|
||||
|
/* Create escape tx to spend our anchor tx output; doesn't fill in
|
||||
|
* input scriptsig. */ |
||||
|
struct bitcoin_tx *create_escape_tx(const tal_t *ctx, |
||||
|
OpenChannel *ours, |
||||
|
OpenChannel *theirs, |
||||
|
const struct sha256_double *anchor_txid, |
||||
|
unsigned int anchor_index, |
||||
|
uint64_t input_amount, |
||||
|
uint64_t escape_fee); |
||||
|
|
||||
|
/* Create fast escape tx to spend our anchor tx output; doesn't fill in
|
||||
|
* input scriptsig. */ |
||||
|
struct bitcoin_tx *create_fast_escape_tx(const tal_t *ctx, |
||||
|
OpenChannel *ours, |
||||
|
OpenChannel *theirs, |
||||
|
const struct sha256_double *anchor_txid, |
||||
|
unsigned int anchor_index, |
||||
|
uint64_t input_amount, |
||||
|
uint64_t escape_fee); |
||||
|
#endif |
@ -0,0 +1,124 @@ |
|||||
|
#include <ccan/crypto/shachain/shachain.h> |
||||
|
#include <ccan/short_types/short_types.h> |
||||
|
#include <ccan/tal/tal.h> |
||||
|
#include <ccan/opt/opt.h> |
||||
|
#include <ccan/str/hex/hex.h> |
||||
|
#include <ccan/structeq/structeq.h> |
||||
|
#include <ccan/err/err.h> |
||||
|
#include <ccan/read_write_all/read_write_all.h> |
||||
|
#include "lightning.pb-c.h" |
||||
|
#include "anchor.h" |
||||
|
#include "bitcoin/base58.h" |
||||
|
#include "pkt.h" |
||||
|
#include "bitcoin/script.h" |
||||
|
#include "permute_tx.h" |
||||
|
#include "bitcoin/signature.h" |
||||
|
#include "escape_tx.h" |
||||
|
#include "bitcoin/pubkey.h" |
||||
|
#include "bitcoin/privkey.h" |
||||
|
#include "protobuf_convert.h" |
||||
|
#include <unistd.h> |
||||
|
|
||||
|
int main(int argc, char *argv[]) |
||||
|
{ |
||||
|
const tal_t *ctx = tal_arr(NULL, char, 0); |
||||
|
OpenChannel *o1, *o2; |
||||
|
OpenAnchor *oa1; |
||||
|
OpenEscapeSigs *oe2; |
||||
|
struct bitcoin_tx *tx; |
||||
|
struct sha256 escape_secret, escape_hash1, escape_hash2, expect; |
||||
|
struct sha256_double anchor_txid1; |
||||
|
struct bitcoin_signature our_sig, their_sig; |
||||
|
struct privkey privkey; |
||||
|
u8 *redeemscript; |
||||
|
bool testnet; |
||||
|
bool fast_escape = false; |
||||
|
struct pubkey pubkey1, pubkey2, final2; |
||||
|
|
||||
|
err_set_progname(argv[0]); |
||||
|
|
||||
|
opt_register_noarg("--help|-h", opt_usage_and_exit, |
||||
|
"<open-channel-file1> <open-channel-file2> <open-anchor-file1> <escape-sigs-file2> <escape-privkey> <escape-secret>\n" |
||||
|
"Create the escape transaction", |
||||
|
"Print this message."); |
||||
|
opt_register_noarg("--fast", opt_set_bool, &fast_escape, |
||||
|
"Generate fast escape transaction" |
||||
|
" (instead of normal escape)"); |
||||
|
|
||||
|
opt_parse(&argc, argv, opt_log_stderr_exit); |
||||
|
|
||||
|
if (argc != 7) |
||||
|
opt_usage_exit_fail("Expected 6 arguments"); |
||||
|
|
||||
|
o1 = pkt_from_file(argv[1], PKT__PKT_OPEN)->open; |
||||
|
proto_to_sha256(o1->escape_hash, &escape_hash1); |
||||
|
o2 = pkt_from_file(argv[2], PKT__PKT_OPEN)->open; |
||||
|
proto_to_sha256(o2->escape_hash, &escape_hash2); |
||||
|
if (!proto_to_pubkey(o2->commitkey, &pubkey2)) |
||||
|
errx(1, "Invalid o2 commit pubkey"); |
||||
|
if (!proto_to_pubkey(o2->final, &final2)) |
||||
|
errx(1, "Invalid o2 final pubkey"); |
||||
|
oa1 = pkt_from_file(argv[3], PKT__PKT_OPEN_ANCHOR)->open_anchor; |
||||
|
oe2 = pkt_from_file(argv[4], PKT__PKT_OPEN_ESCAPE_SIGS)->open_escape_sigs; |
||||
|
|
||||
|
if (!key_from_base58(argv[5], strlen(argv[5]), &testnet, &privkey, &pubkey1)) |
||||
|
errx(1, "Invalid private key '%s'", argv[5]); |
||||
|
if (!testnet) |
||||
|
errx(1, "Private key '%s' not on testnet!", argv[5]); |
||||
|
|
||||
|
if (!hex_decode(argv[6], strlen(argv[6]), &escape_secret, |
||||
|
sizeof(escape_secret))) |
||||
|
errx(1, "Invalid escape secret '%s' - need 256 hex bits", argv[6]); |
||||
|
|
||||
|
/* Make sure secret is correct. */ |
||||
|
sha256(&expect, &escape_secret, sizeof(escape_secret)); |
||||
|
if (!structeq(&expect, &escape_hash1)) |
||||
|
errx(1, "Escape secret not what we promised"); |
||||
|
|
||||
|
proto_to_sha256(oa1->anchor_txid, &anchor_txid1.sha); |
||||
|
|
||||
|
their_sig.stype = our_sig.stype = SIGHASH_ALL; |
||||
|
if (fast_escape) { |
||||
|
if (!proto_to_signature(oe2->fast_escape, &their_sig.sig)) |
||||
|
errx(1, "Bad fast escape signature in oe2"); |
||||
|
tx = create_fast_escape_tx(ctx, o1, o2, |
||||
|
&anchor_txid1, oa1->index, |
||||
|
o1->total_input, o1->escape_fee); |
||||
|
} else { |
||||
|
if (!proto_to_signature(oe2->escape, &their_sig.sig)) |
||||
|
errx(1, "Bad escape signature in oe2"); |
||||
|
tx = create_escape_tx(ctx, o1, o2, |
||||
|
&anchor_txid1, oa1->index, |
||||
|
o1->total_input, o1->escape_fee); |
||||
|
} |
||||
|
if (!tx) |
||||
|
errx(1, "Could not create transaction"); |
||||
|
|
||||
|
/* Sign input for the anchor. */ |
||||
|
redeemscript = bitcoin_redeem_anchor(ctx, &pubkey1, &pubkey2, |
||||
|
&final2, &escape_hash1); |
||||
|
if (!check_tx_sig(tx, 0, redeemscript, tal_count(redeemscript), |
||||
|
&final2, &their_sig)) |
||||
|
errx(1, "Invalid %sescape signature in oe2", |
||||
|
fast_escape ? "fast-" : ""); |
||||
|
|
||||
|
if (!sign_tx_input(ctx, tx, 0, |
||||
|
redeemscript, tal_count(redeemscript), |
||||
|
&privkey, &pubkey1, &our_sig.sig)) |
||||
|
errx(1, "Could not sign transaction"); |
||||
|
|
||||
|
/* Now create script. */ |
||||
|
tx->input[0].script |
||||
|
= scriptsig_p2sh_anchor_escape(ctx, &their_sig, &our_sig, |
||||
|
&escape_secret, |
||||
|
redeemscript, |
||||
|
tal_count(redeemscript)); |
||||
|
tx->input[0].script_length = tal_count(tx->input[0].script); |
||||
|
|
||||
|
/* Print it out in hex. */ |
||||
|
if (!bitcoin_tx_write(STDOUT_FILENO, tx)) |
||||
|
err(1, "Writing out transaction"); |
||||
|
|
||||
|
tal_free(ctx); |
||||
|
return 0; |
||||
|
} |
@ -0,0 +1,106 @@ |
|||||
|
#include <ccan/crypto/shachain/shachain.h> |
||||
|
#include <ccan/short_types/short_types.h> |
||||
|
#include <ccan/tal/tal.h> |
||||
|
#include <ccan/opt/opt.h> |
||||
|
#include <ccan/str/hex/hex.h> |
||||
|
#include <ccan/err/err.h> |
||||
|
#include <ccan/read_write_all/read_write_all.h> |
||||
|
#include "lightning.pb-c.h" |
||||
|
#include "anchor.h" |
||||
|
#include "bitcoin/base58.h" |
||||
|
#include "pkt.h" |
||||
|
#include "bitcoin/script.h" |
||||
|
#include "permute_tx.h" |
||||
|
#include "bitcoin/signature.h" |
||||
|
#include "escape_tx.h" |
||||
|
#include "bitcoin/pubkey.h" |
||||
|
#include "bitcoin/privkey.h" |
||||
|
#include "protobuf_convert.h" |
||||
|
#include <unistd.h> |
||||
|
|
||||
|
int main(int argc, char *argv[]) |
||||
|
{ |
||||
|
const tal_t *ctx = tal_arr(NULL, char, 0); |
||||
|
OpenChannel *o1, *o2; |
||||
|
OpenAnchor *oa2; |
||||
|
struct bitcoin_tx *escape; |
||||
|
struct sha256 escape_hash2; |
||||
|
struct sha256_double anchor_txid2; |
||||
|
struct pkt *pkt; |
||||
|
struct signature escape_sig, fast_escape_sig; |
||||
|
struct privkey privkey; |
||||
|
u8 *redeemscript; |
||||
|
bool testnet; |
||||
|
struct pubkey pubkey1, pubkey2, final1, final2; |
||||
|
|
||||
|
err_set_progname(argv[0]); |
||||
|
|
||||
|
opt_register_noarg("--help|-h", opt_usage_and_exit, |
||||
|
"<open-channel-file1> <open-channel-file2> <open-anchor-file2> <escape-privkey>\n" |
||||
|
"Create the signatures needed for the other side's escape transaction", |
||||
|
"Print this message."); |
||||
|
|
||||
|
opt_parse(&argc, argv, opt_log_stderr_exit); |
||||
|
|
||||
|
if (argc != 5) |
||||
|
opt_usage_exit_fail("Expected 4 arguments"); |
||||
|
|
||||
|
o1 = pkt_from_file(argv[1], PKT__PKT_OPEN)->open; |
||||
|
if (!proto_to_pubkey(o1->commitkey, &pubkey1)) |
||||
|
errx(1, "Invalid o1 commit pubkey"); |
||||
|
o2 = pkt_from_file(argv[2], PKT__PKT_OPEN)->open; |
||||
|
proto_to_sha256(o2->escape_hash, &escape_hash2); |
||||
|
if (!proto_to_pubkey(o2->final, &final2)) |
||||
|
errx(1, "Invalid o2 final pubkey"); |
||||
|
if (!proto_to_pubkey(o2->commitkey, &pubkey2)) |
||||
|
errx(1, "Invalid o2 commit pubkey"); |
||||
|
oa2 = pkt_from_file(argv[3], PKT__PKT_OPEN_ANCHOR)->open_anchor; |
||||
|
|
||||
|
if (!key_from_base58(argv[4], strlen(argv[4]), &testnet, &privkey, &final1)) |
||||
|
errx(1, "Invalid private key '%s'", argv[4]); |
||||
|
if (!testnet) |
||||
|
errx(1, "Private key '%s' not on testnet!", argv[4]); |
||||
|
|
||||
|
proto_to_sha256(oa2->anchor_txid, &anchor_txid2.sha); |
||||
|
|
||||
|
/* Now create THEIR escape tx to spend output of their anchor. */ |
||||
|
escape = create_escape_tx(ctx, o2, o1, &anchor_txid2, oa2->index, |
||||
|
o2->total_input, o2->escape_fee); |
||||
|
|
||||
|
/* If contributions don't exceed fees, this fails. */ |
||||
|
if (!escape) |
||||
|
errx(1, "Input %llu vs fees %llu", |
||||
|
(long long)o2->total_input, |
||||
|
(long long)o2->escape_fee); |
||||
|
|
||||
|
/* Sign input for their anchor. */ |
||||
|
redeemscript = bitcoin_redeem_anchor(ctx, &pubkey2, &pubkey1, |
||||
|
&final1, &escape_hash2); |
||||
|
if (!sign_tx_input(ctx, escape, 0, |
||||
|
redeemscript, tal_count(redeemscript), |
||||
|
&privkey, &final1, &escape_sig)) |
||||
|
errx(1, "Could not sign their escape tx"); |
||||
|
|
||||
|
/* Now create THEIR fast escape tx to spend output of their anchor. */ |
||||
|
escape = create_fast_escape_tx(ctx, o2, o1, &anchor_txid2, oa2->index, |
||||
|
o2->total_input, o2->escape_fee); |
||||
|
/* If contributions don't exceed fees, this fails. */ |
||||
|
if (!escape) |
||||
|
errx(1, "Input %llu vs fees %llu", |
||||
|
(long long)o2->total_input, |
||||
|
(long long)o2->escape_fee); |
||||
|
|
||||
|
/* It's spending the same output, so same redeemscript */ |
||||
|
if (!sign_tx_input(ctx, escape, 0, |
||||
|
redeemscript, tal_count(redeemscript), |
||||
|
&privkey, &final1, &fast_escape_sig)) |
||||
|
errx(1, "Could not sign their fast escape tx"); |
||||
|
|
||||
|
pkt = open_escape_sig_pkt(ctx, &escape_sig, &fast_escape_sig); |
||||
|
if (!write_all(STDOUT_FILENO, pkt, pkt_totlen(pkt))) |
||||
|
err(1, "Writing out packet"); |
||||
|
|
||||
|
tal_free(ctx); |
||||
|
return 0; |
||||
|
} |
||||
|
|
Loading…
Reference in new issue