Created
August 4, 2018 12:18
-
-
Save AdamISZ/b378cd9ef84990154a4ea737f285c47c to your computer and use it in GitHub Desktop.
Elementary test of co-signing/coinjoin in lnd
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This tests a simple 2 party of coinjoin by first funding a utxo | |
// for both of Bob and Alice, and then spending both into one | |
// transaction. The purpose is to test construction and broadcast, | |
// not sophisticated validation. | |
func testCospend(r *rpctest.Harness, | |
alice, bob *lnwallet.LightningWallet, t *testing.T) { | |
// Start building the coinjoin transaction; we'll append | |
// inputs and outputs as we build | |
tx1 := wire.NewMsgTx(2) | |
// The (equal) amount we'll use for each input to the coinjoin | |
const fundingAmt = btcutil.SatoshiPerBitcoin | |
// We'll repeat the same actions for both wallets, so | |
// construct arrays of wallets | |
ws := []*lnwallet.LightningWallet{alice, bob} | |
// Some values must be stored for Alice and Bob for the next step | |
var outputValues []int64 | |
var pubKeys []keychain.KeyDescriptor | |
var keyScripts [][]byte | |
var txs []*wire.MsgTx | |
var outputIndexes []uint32 | |
// For each wallet: get an address, pay fundingAmt to it, | |
// then find its output index, then construct a TxIn paying | |
// from it, and append it to the coinjoin transaction. | |
for _, w := range ws[:] { | |
pubKey, err := w.DeriveNextKey(keychain.KeyFamilyMultiSig) | |
if err != nil { | |
t.Fatalf("unable to obtain pubkey: %v", err) | |
} | |
pubKeys = append(pubKeys, pubKey) | |
pubkeyHash := btcutil.Hash160(pubKey.PubKey.SerializeCompressed()) | |
keyAddr, err := btcutil.NewAddressWitnessPubKeyHash(pubkeyHash, | |
&chaincfg.RegressionNetParams) | |
if err != nil { | |
t.Fatalf("unable to create addr: %v", err) | |
} | |
keyScript, err := txscript.PayToAddrScript(keyAddr) | |
if err != nil { | |
t.Fatalf("unable to generate script: %v", err) | |
} | |
keyScripts = append(keyScripts, keyScript) | |
// We plan to pay to the above script, for this step we can use | |
// the (badly named!) utility function "SendOutputs" | |
newOutput := &wire.TxOut{ | |
Value: fundingAmt, | |
PkScript: keyScript, | |
} | |
txid, err := w.SendOutputs([]*wire.TxOut{newOutput}, 10) | |
if err != nil { | |
t.Fatalf("unable to create output: %v", err) | |
} | |
err = waitForMempoolTx(r, txid) | |
if err != nil { | |
t.Fatalf("tx not relayed to miner: %v", err) | |
} | |
txr, err := r.Node.GetRawTransaction(txid) | |
if err != nil { | |
t.Fatalf("unable to query for tx: %v", err) | |
} | |
tx := txr.MsgTx() | |
txs = append(txs, tx) | |
//Need to identify the outpoint in tx, which is the | |
//one we want to spend | |
var outputIndex uint32 | |
if len(tx.TxOut) == 1 || bytes.Equal(tx.TxOut[0].PkScript, keyScript) { | |
outputIndex = 0 | |
} else { | |
outputIndex = 1 | |
} | |
outputIndexes = append(outputIndexes, outputIndex) | |
outputValues = append(outputValues, tx.TxOut[outputIndex].Value) | |
tx1.AddTxIn(&wire.TxIn{ | |
PreviousOutPoint: wire.OutPoint{ | |
Hash: tx.TxHash(), | |
Index: outputIndex, | |
}, | |
Sequence: wire.MaxTxInSequenceNum, | |
}) | |
} | |
//prepare destinations for the transaction | |
var destinationAddresses []btcutil.Address | |
for i, w := range ws[:] { | |
addr, err := w.NewAddress(lnwallet.WitnessPubKey, false) | |
if err != nil { | |
t.Fatalf("unable to create new address: %v", err) | |
} | |
destinationAddresses = append(destinationAddresses, addr) | |
//Find the script we're paying to (so it'll be output of tx1) | |
payToScript, err := txscript.PayToAddrScript(addr) | |
if err != nil { | |
t.Fatalf("unable to create output script: %v", err) | |
} | |
txFee := btcutil.Amount(0.001 * btcutil.SatoshiPerBitcoin) | |
tx1.AddTxOut(&wire.TxOut{ | |
Value: outputValues[i] - int64(txFee), | |
PkScript: payToScript, | |
}) | |
} | |
for i, w := range ws[:] { | |
// Now we can populate the sign descriptor which we'll use to | |
// generate the signature. | |
signDesc := &lnwallet.SignDescriptor{ | |
KeyDesc: keychain.KeyDescriptor{ | |
PubKey: pubKeys[i].PubKey, | |
}, | |
WitnessScript: keyScripts[i], | |
Output: txs[i].TxOut[outputIndexes[i]], | |
HashType: txscript.SigHashAll, | |
SigHashes: txscript.NewTxSigHashes(tx1), | |
InputIndex: i, | |
} | |
spendSig, err := w.Cfg.Signer.SignOutputRaw(tx1, signDesc) | |
if err != nil { | |
t.Fatalf("unable to generate signature on tx1: %v", err) | |
} | |
//Construct the witness for this input | |
witness := make([][]byte, 2) | |
witness[0] = append(spendSig, byte(txscript.SigHashAll)) | |
witness[1] = pubKeys[i].PubKey.SerializeCompressed() | |
tx1.TxIn[i].Witness = witness | |
// Finally, attempt to validate the completed transaction. This | |
// should succeed if the wallet was able to properly generate | |
// the proper private key. | |
vm, err := txscript.NewEngine(keyScripts[i], | |
tx1, i, txscript.StandardVerifyFlags, nil, | |
nil, outputValues[i]) | |
if err != nil { | |
t.Fatalf("unable to create engine: %v", err) | |
} | |
if err := vm.Execute(); err != nil { | |
t.Fatalf("spend is invalid: %v", err) | |
} | |
} | |
//Finally, check that we can broadcast the coinjoin: | |
if err := alice.PublishTransaction(tx1); err != nil { | |
t.Fatalf("unable to publish: %v", err) | |
} | |
t.Log(spew.Sdump(tx1)) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment