mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-20 02:27:21 +01:00
fcff17c336
This commit will allow the general public to build lnd without jumping through hoops setting up their local git branches nicely with all of our forks.
282 lines
9.7 KiB
Go
282 lines
9.7 KiB
Go
package lnwallet
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
|
|
"github.com/btcsuite/fastsha256"
|
|
"github.com/roasbeef/btcd/btcec"
|
|
"github.com/roasbeef/btcd/txscript"
|
|
"github.com/roasbeef/btcd/wire"
|
|
"github.com/roasbeef/btcutil"
|
|
)
|
|
|
|
var (
|
|
// TODO(roasbeef): remove these and use the one's defined in txscript
|
|
// within testnet-L.
|
|
SequenceLockTimeSeconds = uint32(1 << 22)
|
|
SequenceLockTimeMask = uint32(0x0000ffff)
|
|
OP_CHECKSEQUENCEVERIFY byte = txscript.OP_NOP3
|
|
)
|
|
|
|
// witnessScriptHash generates a pay-to-witness-script-hash public key script
|
|
// paying to a version 0 witness program paying to the passed redeem script.
|
|
func witnessScriptHash(redeemScript []byte) ([]byte, error) {
|
|
bldr := txscript.NewScriptBuilder()
|
|
|
|
bldr.AddOp(txscript.OP_0)
|
|
scriptHash := fastsha256.Sum256(redeemScript)
|
|
bldr.AddData(scriptHash[:])
|
|
return bldr.Script()
|
|
}
|
|
|
|
// genMultiSigScript generates the non-p2sh'd multisig script for 2 of 2
|
|
// pubkeys.
|
|
func genMultiSigScript(aPub, bPub []byte) ([]byte, error) {
|
|
if len(aPub) != 33 || len(bPub) != 33 {
|
|
return nil, fmt.Errorf("Pubkey size error. Compressed pubkeys only")
|
|
}
|
|
|
|
// Swap to sort pubkeys if needed. Keys are sorted in lexicographical
|
|
// order. The signatures within the scriptSig must also adhere to the
|
|
// order, ensuring that the signatures for each public key appears
|
|
// in the proper order on the stack.
|
|
if bytes.Compare(aPub, bPub) == -1 {
|
|
aPub, bPub = bPub, aPub
|
|
}
|
|
|
|
bldr := txscript.NewScriptBuilder()
|
|
bldr.AddOp(txscript.OP_2)
|
|
bldr.AddData(aPub) // Add both pubkeys (sorted).
|
|
bldr.AddData(bPub)
|
|
bldr.AddOp(txscript.OP_2)
|
|
bldr.AddOp(txscript.OP_CHECKMULTISIG)
|
|
return bldr.Script()
|
|
}
|
|
|
|
// genFundingPkScript creates a redeem script, and its matching p2wsh
|
|
// output for the funding transaction.
|
|
func genFundingPkScript(aPub, bPub []byte, amt int64) ([]byte, *wire.TxOut, error) {
|
|
// As a sanity check, ensure that the passed amount is above zero.
|
|
if amt <= 0 {
|
|
return nil, nil, fmt.Errorf("can't create FundTx script with " +
|
|
"zero, or negative coins")
|
|
}
|
|
|
|
// First, create the 2-of-2 multi-sig script itself.
|
|
redeemScript, err := genMultiSigScript(aPub, bPub)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
// With the 2-of-2 script in had, generate a p2wsh script which pays
|
|
// to the funding script.
|
|
pkScript, err := witnessScriptHash(redeemScript)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return redeemScript, wire.NewTxOut(amt, pkScript), nil
|
|
}
|
|
|
|
// spendMultiSig generates the witness stack required to redeem the 2-of-2 p2wsh
|
|
// multi-sig output.
|
|
func spendMultiSig(redeemScript, pubA, sigA, pubB, sigB []byte) [][]byte {
|
|
witness := make([][]byte, 4)
|
|
|
|
// When spending a p2wsh multi-sig script, rather than an OP_0, we add
|
|
// a nil stack element to eat the extra pop.
|
|
witness[0] = nil
|
|
|
|
// When initially generating the redeemScript, we sorted the serialized
|
|
// public keys in descending order. So we do a quick comparison in order
|
|
// ensure the signatures appear on the Script Virual Machine stack in
|
|
// the correct order.
|
|
if bytes.Compare(pubA, pubB) == -1 {
|
|
witness[1] = sigB
|
|
witness[2] = sigA
|
|
} else {
|
|
witness[1] = sigA
|
|
witness[2] = sigB
|
|
}
|
|
|
|
// Finally, add the pre-image as the last witness element.
|
|
witness[3] = redeemScript
|
|
|
|
return witness
|
|
}
|
|
|
|
// findScriptOutputIndex finds the index of the public key script output
|
|
// matching 'script'. Additionally, a boolean is returned indicating if
|
|
// a matching output was found at all.
|
|
// NOTE: The search stops after the first matching script is found.
|
|
func findScriptOutputIndex(tx *wire.MsgTx, script []byte) (bool, uint32) {
|
|
found := false
|
|
index := uint32(0)
|
|
for i, txOut := range tx.TxOut {
|
|
if bytes.Equal(txOut.PkScript, script) {
|
|
found = true
|
|
index = uint32(i)
|
|
break
|
|
}
|
|
}
|
|
|
|
return found, index
|
|
}
|
|
|
|
// senderHTLCScript constructs the public key script for an outgoing HTLC
|
|
// output payment for the sender's commitment transaction.
|
|
func senderHTLCScript(absoluteTimeout, relativeTimeout uint32, senderKey,
|
|
receiverKey *btcec.PublicKey, revokeHash, paymentHash []byte) ([]byte, error) {
|
|
|
|
builder := txscript.NewScriptBuilder()
|
|
|
|
// Was the pre-image to the payment hash presented?
|
|
builder.AddOp(txscript.OP_HASH160)
|
|
builder.AddOp(txscript.OP_DUP)
|
|
builder.AddData(paymentHash)
|
|
builder.AddOp(txscript.OP_EQUAL)
|
|
|
|
// How about the pre-image for our commitment revocation hash?
|
|
builder.AddOp(txscript.OP_SWAP)
|
|
builder.AddData(revokeHash)
|
|
builder.AddOp(txscript.OP_EQUAL)
|
|
builder.AddOp(txscript.OP_SWAP)
|
|
builder.AddOp(txscript.OP_ADD)
|
|
|
|
// If either is present, then the receiver can claim immediately.
|
|
builder.AddOp(txscript.OP_IF)
|
|
builder.AddData(receiverKey.SerializeCompressed())
|
|
|
|
// Otherwise, we (the sender) need to wait for an absolute HTLC
|
|
// timeout, then afterwards a relative timeout before we claim re-claim
|
|
// the unsettled funds. This delay gives the other party a chance to
|
|
// present the pre-image to the revocation hash in the event that the
|
|
// sender (at this time) broadcasts this commitment transaction after
|
|
// it has been revoked.
|
|
builder.AddOp(txscript.OP_ELSE)
|
|
builder.AddInt64(int64(absoluteTimeout))
|
|
builder.AddOp(txscript.OP_CHECKLOCKTIMEVERIFY)
|
|
builder.AddInt64(int64(relativeTimeout))
|
|
builder.AddOp(OP_CHECKSEQUENCEVERIFY)
|
|
builder.AddOp(txscript.OP_2DROP)
|
|
builder.AddData(senderKey.SerializeCompressed())
|
|
builder.AddOp(txscript.OP_ENDIF)
|
|
builder.AddOp(txscript.OP_CHECKSIG)
|
|
|
|
return builder.Script()
|
|
}
|
|
|
|
// senderHTLCScript constructs the public key script for an incoming HTLC
|
|
// output payment for the receiver's commitment transaction.
|
|
func receiverHTLCScript(absoluteTimeout, relativeTimeout uint32, senderKey,
|
|
receiverKey *btcec.PublicKey, revokeHash, paymentHash []byte) ([]byte, error) {
|
|
|
|
builder := txscript.NewScriptBuilder()
|
|
|
|
// Was the pre-image to the payment hash presented?
|
|
builder.AddOp(txscript.OP_HASH160)
|
|
builder.AddOp(txscript.OP_DUP)
|
|
builder.AddData(paymentHash)
|
|
builder.AddOp(txscript.OP_EQUAL)
|
|
builder.AddOp(txscript.OP_IF)
|
|
|
|
// If so, let the receiver redeem after a relative timeout. This added
|
|
// delay gives the sender (at this time) an opportunity to re-claim the
|
|
// pending HTLC in the event that the receiver (at this time) broadcasts
|
|
// this old commitment transaction after it has been revoked.
|
|
builder.AddInt64(int64(relativeTimeout))
|
|
builder.AddOp(OP_CHECKSEQUENCEVERIFY)
|
|
builder.AddOp(txscript.OP_2DROP)
|
|
builder.AddData(receiverKey.SerializeCompressed())
|
|
|
|
// Otherwise, if the sender has the revocation pre-image to the
|
|
// receiver's commitment transactions, then let them claim the
|
|
// funds immediately.
|
|
builder.AddOp(txscript.OP_ELSE)
|
|
builder.AddData(revokeHash)
|
|
builder.AddOp(txscript.OP_EQUAL)
|
|
|
|
// If not, then the sender needs to wait for the HTLC timeout. This
|
|
// clause may be executed if the receiver fails to present the r-value
|
|
// in time. This prevents the pending funds from being locked up
|
|
// indefinately.
|
|
builder.AddOp(txscript.OP_NOTIF)
|
|
builder.AddInt64(int64(absoluteTimeout))
|
|
builder.AddOp(txscript.OP_CHECKLOCKTIMEVERIFY)
|
|
builder.AddOp(txscript.OP_DROP)
|
|
builder.AddOp(txscript.OP_ENDIF)
|
|
|
|
builder.AddData(senderKey.SerializeCompressed())
|
|
|
|
builder.AddOp(txscript.OP_ENDIF)
|
|
builder.AddOp(txscript.OP_CHECKSIG)
|
|
|
|
return builder.Script()
|
|
}
|
|
|
|
// lockTimeToSequence converts the passed relative locktime to a sequence
|
|
// number in accordance to BIP-68.
|
|
// See: https://github.com/bitcoin/bips/blob/master/bip-0068.mediawiki
|
|
// * (Compatibility)
|
|
func lockTimeToSequence(isSeconds bool, locktime uint32) uint32 {
|
|
if !isSeconds {
|
|
// The locktime is to be expressed in confirmations. Apply the
|
|
// mask to restrict the number of confirmations to 65,535 or
|
|
// 1.25 years.
|
|
return SequenceLockTimeMask & locktime
|
|
}
|
|
|
|
// Set the 22nd bit which indicates the lock time is in seconds, then
|
|
// shift the locktime over by 9 since the time granularity is in
|
|
// 512-second intervals (2^9). This results in a max lock-time of
|
|
// 33,554,431 seconds, or 1.06 years.
|
|
return SequenceLockTimeSeconds | (locktime >> 9)
|
|
}
|
|
|
|
// commitScriptToSelf constructs the public key script for the output on the
|
|
// commitment transaction paying to the "owner" of said commitment transaction.
|
|
// If the other party learns of the pre-image to the revocation hash, then they
|
|
// can claim all the settled funds in the channel, plus the unsettled funds.
|
|
func commitScriptToSelf(csvTimeout uint32, selfKey, theirKey *btcec.PublicKey, revokeHash []byte) ([]byte, error) {
|
|
// This script is spendable under two conditions: either the 'csvTimeout'
|
|
// has passed and we can redeem our funds, or they have the pre-image
|
|
// to 'revokeHash'.
|
|
builder := txscript.NewScriptBuilder()
|
|
|
|
// If the pre-image for the revocation hash is presented, then allow a
|
|
// spend provided the proper signature.
|
|
builder.AddOp(txscript.OP_HASH160)
|
|
builder.AddData(revokeHash)
|
|
builder.AddOp(txscript.OP_EQUAL)
|
|
builder.AddOp(txscript.OP_IF)
|
|
builder.AddData(theirKey.SerializeCompressed())
|
|
builder.AddOp(txscript.OP_ELSE)
|
|
|
|
// Otherwise, we can re-claim our funds after a CSV delay of
|
|
// 'csvTimeout' timeout blocks, and a valid signature.
|
|
builder.AddInt64(int64(csvTimeout))
|
|
builder.AddOp(OP_CHECKSEQUENCEVERIFY)
|
|
builder.AddOp(txscript.OP_DROP)
|
|
builder.AddData(selfKey.SerializeCompressed())
|
|
builder.AddOp(txscript.OP_ENDIF)
|
|
builder.AddOp(txscript.OP_CHECKSIG)
|
|
|
|
return builder.Script()
|
|
}
|
|
|
|
// commitScriptUnencumbered constructs the public key script on the commitment
|
|
// transaction paying to the "other" party. This output is spendable
|
|
// immediately, requiring no contestation period.
|
|
func commitScriptUnencumbered(key *btcec.PublicKey) ([]byte, error) {
|
|
// This script goes to the "other" party, and it spendable immediately.
|
|
builder := txscript.NewScriptBuilder()
|
|
builder.AddOp(txscript.OP_DUP)
|
|
builder.AddOp(txscript.OP_HASH160)
|
|
builder.AddData(btcutil.Hash160(key.SerializeCompressed()))
|
|
builder.AddOp(txscript.OP_EQUALVERIFY)
|
|
builder.AddOp(txscript.OP_CHECKSIG)
|
|
|
|
return builder.Script()
|
|
}
|