open-commit-sig: create signature for commit tx.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2015-05-30 20:11:10 +09:30
parent 63986e5b2d
commit bf38ca052e
14 changed files with 313 additions and 36 deletions

View File

@ -3,7 +3,7 @@
# Needs to have oneof support: Ubuntu vivid's is too old :(
PROTOCC:=protoc-c
PROGRAMS := open-channel open-anchor-sig leak-anchor-sigs
PROGRAMS := open-channel open-anchor-sig leak-anchor-sigs open-commit-sig
HELPER_OBJS := base58.o lightning.pb-c.o shadouble.o pkt.o bitcoin_script.o permute_tx.o signature.o bitcoin_tx.o bitcoin_address.o anchor.o
@ -12,6 +12,7 @@ CCAN_OBJS := ccan-crypto-sha256.o ccan-crypto-shachain.o ccan-err.o ccan-tal.o c
OPEN_CHANNEL_OBJS := open-channel.o
OPEN_ANCHOR_SIG_OBJS := open-anchor-sig.o
LEAK_ANCHOR_SIGS_OBJS := leak-anchor-sigs.o
OPEN_COMMIT_SIG_OBJS := open-commit-sig.o
HEADERS := $(wildcard *.h)
@ -33,12 +34,15 @@ $(OPEN_ANCHOR_SIG_OBJS): $(HEADERS)
leak-anchor-sigs: $(LEAK_ANCHOR_SIGS_OBJS) $(HELPER_OBJS) $(CCAN_OBJS)
$(LEAK_ANCHOR_SIGS_OBJS): $(HEADERS)
open-commit-sig: $(OPEN_COMMIT_SIG_OBJS) $(HELPER_OBJS) $(CCAN_OBJS)
$(OPEN_COMMIT_SIG_OBJS): $(HEADERS)
distclean: clean
$(RM) lightning.pb-c.c lightning.pb-c.h
clean:
$(RM) $(PROGRAMS)
$(RM) $(OPEN_CHANNEL_OBJS) $(OPEN_ANCHOR_SIG_OBJS) $(LEAK_ANCHOR_SIGS_OBJS) $(HELPER_OBJS) $(CCAN_OBJS)
$(RM) $(OPEN_CHANNEL_OBJS) $(OPEN_ANCHOR_SIG_OBJS) $(LEAK_ANCHOR_SIGS_OBJS) $(OPEN_COMMIT_SIG_OBJS) $(HELPER_OBJS) $(CCAN_OBJS)
ccan-tal.o: $(CCANDIR)/ccan/tal/tal.c
$(CC) $(CFLAGS) -c -o $@ $<

View File

@ -4,6 +4,7 @@
#include "pkt.h"
#include "permute_tx.h"
#include "bitcoin_script.h"
#include <ccan/err/err.h>
struct bitcoin_tx *anchor_tx_create(const tal_t *ctx,
const OpenChannel *o1,
@ -16,7 +17,7 @@ struct bitcoin_tx *anchor_tx_create(const tal_t *ctx,
size_t *inmap, *outmap;
if (add_overflows_size_t(o1->anchor->n_inputs, o2->anchor->n_inputs))
return tal_free(tx);
return NULL;
n_out = 1 + !!o1->anchor->change + !!o2->anchor->change;
tx = bitcoin_tx(ctx, o1->anchor->n_inputs+o2->anchor->n_inputs, n_out);
@ -88,4 +89,42 @@ struct bitcoin_tx *anchor_tx_create(const tal_t *ctx,
return tx;
}
void anchor_txid(struct bitcoin_tx *anchor,
const char *leakfile1, const char *leakfile2,
const size_t *inmap,
struct sha256_double *txid)
{
Pkt *p1, *p2;
LeakAnchorSigsAndPretendWeDidnt *leak1, *leak2;
size_t i;
struct sha256_ctx shactx;
p1 = pkt_from_file(leakfile1, PKT__PKT_OMG_FAIL);
p2 = pkt_from_file(leakfile2, PKT__PKT_OMG_FAIL);
leak1 = p1->omg_fail;
leak2 = p2->omg_fail;
if (leak1->sigs->n_script + leak2->sigs->n_script != anchor->input_count)
errx(1, "Expected %llu total inputs, not %zu + %zu",
(long long)anchor->input_count,
leak1->sigs->n_script, leak2->sigs->n_script);
for (i = 0; i < leak1->sigs->n_script; i++) {
anchor->input[i].script = leak1->sigs->script[i].data;
anchor->input[i].script_length = leak1->sigs->script[i].len;
}
for (i = 0; i < leak2->sigs->n_script; i++) {
anchor->input[leak1->sigs->n_script + i].script
= leak2->sigs->script[i].data;
anchor->input[leak1->sigs->n_script + i].script_length
= leak2->sigs->script[i].len;
}
sha256_init(&shactx);
sha256_tx(&shactx, anchor);
sha256_double_done(&shactx, txid);
pkt__free_unpacked(p1, NULL);
pkt__free_unpacked(p2, NULL);
}

View File

@ -3,6 +3,8 @@
#include <ccan/tal/tal.h>
#include "lightning.pb-c.h"
struct sha256_double;
/* Create an anchor transaction based on both sides' requests.
* The scriptSigs are left empty.
*
@ -16,4 +18,10 @@ struct bitcoin_tx *anchor_tx_create(const tal_t *ctx,
const OpenChannel *o1,
const OpenChannel *o2,
size_t **inmap, size_t **outmap);
/* We wouldn't need the leak files if we had normalized txids! */
void anchor_txid(struct bitcoin_tx *anchor,
const char *leakfile1, const char *leakfile2,
const size_t *inmap,
struct sha256_double *txid);
#endif /* LIGHTNING_ANCHOR_H */

View File

@ -1,5 +1,6 @@
#include "bitcoin_script.h"
#include "bitcoin_address.h"
#include "pkt.h"
#include <openssl/ripemd.h>
#include <ccan/endian/endian.h>
#include <ccan/crypto/sha256/sha256.h>
@ -11,12 +12,19 @@
#define OP_PUSHDATA2 0x4D
#define OP_PUSHDATA4 0x4E
#define OP_NOP 0x61
#define OP_IF 0x63
#define OP_ELSE 0x67
#define OP_ENDIF 0x68
#define OP_DEPTH 0x74
#define OP_DUP 0x76
#define OP_EQUAL 0x87
#define OP_EQUALVERIFY 0x88
#define OP_SIZE 0x82
#define OP_1SUB 0x8C
#define OP_CHECKSIG 0xAC
#define OP_CHECKMULTISIG 0xAE
#define OP_HASH160 0xA9
#define OP_CHECKSEQUENCEVERIFY 0xB2
static void add(u8 **scriptp, const void *mem, size_t len)
{
@ -160,3 +168,66 @@ bool is_pay_to_pubkey_hash(const ProtobufCBinaryData *script)
return false;
return true;
}
/* One of:
* mysig and theirsig, OR
* mysig and relative locktime passed, OR
* theirsig and hash preimage. */
u8 *bitcoin_redeem_revocable(const tal_t *ctx,
const BitcoinPubkey *mykey,
u32 locktime,
const BitcoinPubkey *theirkey,
const Sha256Hash *revocation_hash)
{
u8 *script = tal_arr(ctx, u8, 0);
struct sha256 rhash;
u8 rhash_ripemd[RIPEMD160_DIGEST_LENGTH];
le32 locktime_le = cpu_to_le32(locktime);
proto_to_sha256(revocation_hash, &rhash);
/* If there are two args: */
add_op(&script, OP_DEPTH);
add_op(&script, OP_1SUB);
add_op(&script, OP_IF);
/* If the top arg is a hashpreimage. */
add_op(&script, OP_SIZE);
add_op(&script, OP_LITERAL(32));
add_op(&script, OP_EQUAL);
add_op(&script, OP_IF);
/* Must hash to revocation_hash, and be signed by them. */
RIPEMD160(rhash.u.u8, sizeof(rhash.u), rhash_ripemd);
add_op(&script, OP_HASH160);
add_push_bytes(&script, rhash_ripemd, sizeof(rhash_ripemd));
add_op(&script, OP_EQUALVERIFY);
add_push_bytes(&script, theirkey->key.data, theirkey->key.len);
add_op(&script, OP_CHECKSIG);
/* Otherwise, it should be both our sigs. */
add_op(&script, OP_ELSE);
add_op(&script, OP_LITERAL(2));
/* This obscures whose key is whose. Probably unnecessary? */
if (key_less(mykey, theirkey)) {
add_push_bytes(&script, mykey->key.data, mykey->key.len);
add_push_bytes(&script, theirkey->key.data, theirkey->key.len);
} else {
add_push_bytes(&script, theirkey->key.data, theirkey->key.len);
add_push_bytes(&script, mykey->key.data, mykey->key.len);
}
add_op(&script, OP_LITERAL(2));
add_op(&script, OP_CHECKMULTISIG);
add_op(&script, OP_ENDIF);
/* Not two args? Must be us using timeout. */
add_op(&script, OP_ELSE);
add_push_bytes(&script, &locktime_le, sizeof(locktime_le));
add_op(&script, OP_CHECKSEQUENCEVERIFY);
add_push_bytes(&script, mykey->key.data, mykey->key.len);
add_op(&script, OP_CHECKSIG);
add_op(&script, OP_ENDIF);
return script;
}

View File

@ -15,6 +15,16 @@ u8 *bitcoin_redeem_2of2(const tal_t *ctx,
/* tal_count() gives the length of the script. */
u8 *bitcoin_redeem_single(const tal_t *ctx, const u8 *key, size_t keylen);
/* One of:
* mysig and theirsig, OR
* mysig and relative locktime passed, OR
* theirsig and hash preimage. */
u8 *bitcoin_redeem_revocable(const tal_t *ctx,
const BitcoinPubkey *mykey,
u32 locktime,
const BitcoinPubkey *theirkey,
const Sha256Hash *revocation_hash);
/* Create an output script using p2sh for this redeem script. */
u8 *scriptpubkey_p2sh(const tal_t *ctx, const u8 *redeemscript);

View File

@ -1579,12 +1579,12 @@ const ProtobufCMessageDescriptor open_anchor_sig__descriptor =
static const ProtobufCFieldDescriptor leak_anchor_sigs_and_pretend_we_didnt__field_descriptors[1] =
{
{
"anchor_scriptsigs",
"sigs",
1,
PROTOBUF_C_LABEL_REQUIRED,
PROTOBUF_C_TYPE_MESSAGE,
0, /* quantifier_offset */
offsetof(LeakAnchorSigsAndPretendWeDidnt, anchor_scriptsigs),
offsetof(LeakAnchorSigsAndPretendWeDidnt, sigs),
&open_anchor_sig__descriptor,
NULL,
0, /* flags */
@ -1592,7 +1592,7 @@ static const ProtobufCFieldDescriptor leak_anchor_sigs_and_pretend_we_didnt__fie
},
};
static const unsigned leak_anchor_sigs_and_pretend_we_didnt__field_indices_by_name[] = {
0, /* field[0] = anchor_scriptsigs */
0, /* field[0] = sigs */
};
static const ProtobufCIntRange leak_anchor_sigs_and_pretend_we_didnt__number_ranges[1 + 1] =
{

View File

@ -248,7 +248,7 @@ struct _OpenAnchorSig
struct _LeakAnchorSigsAndPretendWeDidnt
{
ProtobufCMessage base;
OpenAnchorSig *anchor_scriptsigs;
OpenAnchorSig *sigs;
};
#define LEAK_ANCHOR_SIGS_AND_PRETEND_WE_DIDNT__INIT \
{ PROTOBUF_C_MESSAGE_INIT (&leak_anchor_sigs_and_pretend_we_didnt__descriptor) \

View File

@ -99,7 +99,7 @@ message open_anchor_sig {
// malleability anyway). So for testing, we send the scriptsigs for the
// anchor transaction's inputs immediately.
message leak_anchor_sigs_and_pretend_we_didnt {
required open_anchor_sig anchor_scriptsigs = 1;
required open_anchor_sig sigs = 1;
}
// Indicates we've seen transaction reach min-depth.

View File

@ -18,33 +18,19 @@
#include <unistd.h>
/* All the input scripts are already set to 0. We just need to make this one. */
static u8 *sign_tx_input(const tal_t *ctx,
struct bitcoin_tx *tx,
unsigned int i,
const BitcoinInput *input,
EC_KEY *privkey,
const struct bitcoin_compressed_pubkey *pubkey)
static u8 *tx_scriptsig(const tal_t *ctx,
struct bitcoin_tx *tx,
unsigned int i,
const BitcoinInput *input,
EC_KEY *privkey,
const struct bitcoin_compressed_pubkey *pubkey)
{
struct sha256_double hash;
struct sha256_ctx shactx;
struct bitcoin_address addr;
u8 *sig;
struct bitcoin_address addr;
/* Transaction gets signed as if the output subscript is the
* only input script. */
tx->input[i].script_length = input->subscript.len;
tx->input[i].script = input->subscript.data;
sha256_init(&shactx);
sha256_tx(&shactx, tx);
sha256_le32(&shactx, SIGHASH_ALL);
sha256_double_done(&shactx, &hash);
/* Reset it for next time. */
tx->input[i].script_length = 0;
tx->input[i].script = NULL;
sig = sign_hash(ctx, privkey, &hash);
sig = sign_tx_input(ctx, tx, i,
input->subscript.data, input->subscript.len,
privkey);
if (!sig)
return NULL;
@ -102,9 +88,9 @@ int main(int argc, char *argv[])
if (!testnet)
errx(1, "Private key '%s' not on testnet!", argv[3+i]);
sigs[i] = sign_tx_input(sigs, anchor, map[i],
o1->anchor->inputs[i],
privkey, &pubkey);
sigs[i] = tx_scriptsig(sigs, anchor, map[i],
o1->anchor->inputs[i],
privkey, &pubkey);
}
pkt = open_anchor_sig_pkt(ctx, sigs, o1->anchor->n_inputs);

108
open-commit-sig.c Normal file
View File

@ -0,0 +1,108 @@
/* My example:
* ./open-commit-sig A-open.pb B-open.pb cUBCjrdJu8tfvM7FT8So6aqs6G6bZS1Cax6Rc9rFzYL6nYG4XNEC A-leak-anchor-sigs.pb B-leak-anchor-sigs.pb > A-commit-sig.pb
* ./open-commit-sig B-open.pb A-open.pb cQXhbUnNRsFcdzTQwjbCrud5yVskHTEas7tZPUWoJYNk5htGQrpi B-leak-anchor-sigs.pb A-leak-anchor-sigs.pb > B-commit-sig.pb
*/
#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 "base58.h"
#include "pkt.h"
#include "bitcoin_script.h"
#include "permute_tx.h"
#include "signature.h"
#include <openssl/ec.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
const tal_t *ctx = tal_arr(NULL, char, 0);
OpenChannel *o1, *o2;
struct bitcoin_tx *anchor, *commit;
struct sha256_double txid;
struct pkt *pkt;
u8 *redeemscript, *sig;
size_t *inmap, *outmap;
EC_KEY *privkey;
bool testnet;
struct bitcoin_compressed_pubkey pubkey;
err_set_progname(argv[0]);
opt_register_noarg("--help|-h", opt_usage_and_exit,
"<open-channel-file1> <open-channel-file2> <commit-privkey> <leak-anchor-sigs1> <leak-anchor-sigs2>\n"
"Create the signature needed for the commit transaction",
"Print this message.");
opt_parse(&argc, argv, opt_log_stderr_exit);
if (argc != 6)
opt_usage_and_exit(NULL);
o1 = pkt_from_file(argv[1], PKT__PKT_OPEN)->open;
o2 = pkt_from_file(argv[2], PKT__PKT_OPEN)->open;
privkey = key_from_base58(argv[3], strlen(argv[3]), &testnet, &pubkey);
if (!privkey)
errx(1, "Invalid private key '%s'", argv[3]);
if (!testnet)
errx(1, "Private key '%s' not on testnet!", argv[3]);
/* Create merged anchor transaction */
anchor = anchor_tx_create(ctx, o1, o2, &inmap, &outmap);
if (!anchor)
errx(1, "Failed transaction merge");
/* Get the transaction ID of the anchor. */
anchor_txid(anchor, argv[4], argv[5], inmap, &txid);
/* Now create commitment tx: one input, two outputs. */
commit = bitcoin_tx(ctx, 1, 2);
/* Our input spends the anchor tx output. */
commit->input[0].txid = txid;
commit->input[0].index = outmap[0];
/* First output is a P2SH to a complex redeem script */
redeemscript = bitcoin_redeem_revocable(ctx, o1->anchor->pubkey,
o1->locktime_seconds,
o2->anchor->pubkey,
o1->revocation_hash);
commit->output[0].script = scriptpubkey_p2sh(ctx, redeemscript);
commit->output[0].script_length = tal_count(commit->output[0].script);
if (o1->anchor->total < o1->commitment_fee)
errx(1, "Our contribution to channel %llu < fee %llu",
(long long)o1->anchor->total,
(long long)o1->commitment_fee);
commit->output[0].amount = o1->anchor->total - o1->commitment_fee;
/* Second output is a simple payment to them. */
commit->output[1].script = o2->script_to_me.data;
commit->output[1].script_length = o2->script_to_me.len;
if (o2->anchor->total < o2->commitment_fee)
errx(1, "Their contribution to channel %llu < fee %llu",
(long long)o2->anchor->total,
(long long)o2->commitment_fee);
commit->output[1].amount = o2->anchor->total - o2->commitment_fee;
permute_outputs(o1->seed, o2->seed, 1, commit->output, 2, NULL);
sig = sign_tx_input(ctx, commit, 0, anchor->output[outmap[0]].script,
anchor->output[outmap[0]].script_length, privkey);
pkt = open_commit_sig_pkt(ctx, sig, tal_count(sig));
if (!write_all(STDOUT_FILENO, pkt,
sizeof(pkt->len) + le32_to_cpu(pkt->len)))
err(1, "Writing out packet");
tal_free(ctx);
return 0;
}

15
pkt.c
View File

@ -1,6 +1,7 @@
#include <ccan/crypto/sha256/sha256.h>
#include <ccan/tal/grab_file/grab_file.h>
#include <ccan/err/err.h>
#include <ccan/cast/cast.h>
#include "pkt.h"
#include "bitcoin_tx.h"
#include "bitcoin_address.h"
@ -137,6 +138,18 @@ struct pkt *leak_anchor_sigs_and_pretend_we_didnt_pkt(const tal_t *ctx,
LeakAnchorSigsAndPretendWeDidnt omg_fail
= LEAK_ANCHOR_SIGS_AND_PRETEND_WE_DIDNT__INIT;
omg_fail.anchor_scriptsigs = s;
omg_fail.sigs = s;
return to_pkt(ctx, PKT__PKT_OMG_FAIL, &omg_fail);
}
struct pkt *open_commit_sig_pkt(const tal_t *ctx, const u8 *sig, size_t siglen)
{
OpenCommitSig o = OPEN_COMMIT_SIG__INIT;
BitcoinSignature s = BITCOIN_SIGNATURE__INIT;
o.sig = &s;
s.der_then_sigtype.len = siglen;
s.der_then_sigtype.data = cast_const(u8 *, sig);
return to_pkt(ctx, PKT__PKT_OPEN_COMMIT_SIG, &o);
}

8
pkt.h
View File

@ -58,6 +58,14 @@ struct pkt *leak_anchor_sigs_and_pretend_we_didnt_pkt(const tal_t *ctx,
OpenAnchorSig *s);
/**
* open_commit_sig_pkt - create an open_commit_sig message
* @ctx: tal context to allocate off.
* @sig: the der-encoded signature for the commit transaction input.
* @siglen: the length of @sig.
*/
struct pkt *open_commit_sig_pkt(const tal_t *ctx, const u8 *sig, size_t siglen);
/* Useful helper for allocating & populating a protobuf Sha256Hash */
Sha256Hash *sha256_to_proto(const tal_t *ctx, const struct sha256 *hash);
void proto_to_sha256(const Sha256Hash *pb, struct sha256 *hash);

View File

@ -1,8 +1,10 @@
#include "signature.h"
#include "shadouble.h"
#include "bitcoin_tx.h"
#include <openssl/bn.h>
#include <openssl/obj_mac.h>
#include <assert.h>
#include <ccan/cast/cast.h>
u8 *sign_hash(const tal_t *ctx, EC_KEY *private_key,
const struct sha256_double *h)
@ -43,3 +45,26 @@ u8 *sign_hash(const tal_t *ctx, EC_KEY *private_key,
OPENSSL_free(der);
return ret;
}
u8 *sign_tx_input(const tal_t *ctx, struct bitcoin_tx *tx, unsigned int in,
const u8 *subscript, size_t subscript_len, EC_KEY *privkey)
{
struct sha256_double hash;
struct sha256_ctx shactx;
/* Transaction gets signed as if the output subscript is the
* only input script. */
tx->input[in].script_length = subscript_len;
tx->input[in].script = cast_const(u8 *, subscript);
sha256_init(&shactx);
sha256_tx(&shactx, tx);
sha256_le32(&shactx, SIGHASH_ALL);
sha256_double_done(&shactx, &hash);
/* Reset it for next time. */
tx->input[in].script_length = 0;
tx->input[in].script = NULL;
return sign_hash(ctx, privkey, &hash);
}

View File

@ -12,8 +12,13 @@ enum sighash_type {
};
struct sha256_double;
struct bitcoin_tx;
u8 *sign_hash(const tal_t *ctx, EC_KEY *private_key,
const struct sha256_double *h);
/* All tx input scripts must be set to 0 len. */
u8 *sign_tx_input(const tal_t *ctx, struct bitcoin_tx *tx, unsigned int in,
const u8 *subscript, size_t subscript_len, EC_KEY *privkey);
#endif /* LIGHTNING_SIGNATURE_H */