mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-01-19 05:45:21 +01:00
lnwallet: update public key scripts for the HTLC sender's commitment tx
This commit is contained in:
parent
0b9c117bbd
commit
8f5129e08f
@ -8,6 +8,7 @@ import (
|
|||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"golang.org/x/crypto/hkdf"
|
"golang.org/x/crypto/hkdf"
|
||||||
|
"golang.org/x/crypto/ripemd160"
|
||||||
|
|
||||||
"github.com/roasbeef/btcd/btcec"
|
"github.com/roasbeef/btcd/btcec"
|
||||||
"github.com/roasbeef/btcd/chaincfg/chainhash"
|
"github.com/roasbeef/btcd/chaincfg/chainhash"
|
||||||
@ -153,92 +154,116 @@ func FindScriptOutputIndex(tx *wire.MsgTx, script []byte) (bool, uint32) {
|
|||||||
return found, index
|
return found, index
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ripemd160H calculates the ripemd160 of the passed byte slice. This is used to
|
||||||
|
// calculate the intermediate hash for payment pre-images. Payment hashes are
|
||||||
|
// the result of ripemd160(sha256(paymentPreimage)). As a result, the value
|
||||||
|
// passed in should be the sha256 of the payment hash.
|
||||||
|
func ripemd160H(d []byte) []byte {
|
||||||
|
h := ripemd160.New()
|
||||||
|
h.Write(d)
|
||||||
|
return h.Sum(nil)
|
||||||
|
}
|
||||||
|
|
||||||
// senderHTLCScript constructs the public key script for an outgoing HTLC
|
// senderHTLCScript constructs the public key script for an outgoing HTLC
|
||||||
// output payment for the sender's version of the commitment transaction:
|
// output payment for the sender's version of the commitment transaction. The
|
||||||
|
// possible script paths from this output include:
|
||||||
|
//
|
||||||
|
// * The sender timing out the HTLC using the second level HTLC timeout
|
||||||
|
// transaction.
|
||||||
|
// * The receiver of the HTLC claiming the output on-chain with the payment
|
||||||
|
// preimage.
|
||||||
|
// * The receiver of the HTLC sweeping all the funds in the case that a
|
||||||
|
// revoked commitment transaction bearing this HTLC was broadcast.
|
||||||
//
|
//
|
||||||
// Possible Input Scripts:
|
// Possible Input Scripts:
|
||||||
// SENDR: <sig> 0
|
// SENDR: <0> <sendr sig> <recvr sig> <0> (spend using HTLC timeout transaction)
|
||||||
// RECVR: <sig> <preimage> 0 1
|
// RECVR: <recvr sig> <preimage>
|
||||||
// REVOK: <sig <preimage> 1 1
|
// REVOK: <revoke sig> <revoke key>
|
||||||
// * receiver revoke
|
// * receiver revoke
|
||||||
//
|
//
|
||||||
|
// OP_DUP OP_HASH160 <revocation key hash160> OP_EQUAL
|
||||||
// OP_IF
|
// OP_IF
|
||||||
// //Receiver
|
// OP_CHECKSIG
|
||||||
// OP_IF
|
|
||||||
// //Revoke
|
|
||||||
// <revocation hash>
|
|
||||||
// OP_ELSE
|
|
||||||
// //Receive
|
|
||||||
// OP_SIZE 32 OP_EQUALVERIFY
|
|
||||||
// <payment hash>
|
|
||||||
// OP_ENDIF
|
|
||||||
// OP_SWAP
|
|
||||||
// OP_SHA256 OP_EQUALVERIFY
|
|
||||||
// <recv key> OP_CHECKSIG
|
|
||||||
// OP_ELSE
|
// OP_ELSE
|
||||||
// //Sender
|
// <recv key>
|
||||||
// <absolute blockheight> OP_CHECKLOCKTIMEVERIFY
|
// OP_SWAP OP_SIZE 32 OP_EQUAL
|
||||||
// <relative blockheight> OP_CHECKSEQUENCEVERIFY
|
// OP_NOTIF
|
||||||
// OP_2DROP
|
// OP_DROP 2 OP_SWAP <sender key> 2 OP_CHECKMULTISIG
|
||||||
// <sendr key> OP_CHECKSIG
|
// OP_ELSE
|
||||||
|
// OP_HASH160 <ripemd160(payment hash)> OP_EQUALVERIFY
|
||||||
|
// OP_ENDIF
|
||||||
// OP_ENDIF
|
// OP_ENDIF
|
||||||
func senderHTLCScript(absoluteTimeout, relativeTimeout uint32, senderKey,
|
func senderHTLCScript(senderKey, receiverKey, revocationKey *btcec.PublicKey,
|
||||||
receiverKey *btcec.PublicKey, revokeHash, paymentHash []byte) ([]byte, error) {
|
paymentHash []byte) ([]byte, error) {
|
||||||
|
|
||||||
builder := txscript.NewScriptBuilder()
|
builder := txscript.NewScriptBuilder()
|
||||||
|
|
||||||
// The receiver of the HTLC places a 1 as the first item in the witness
|
// The opening operations are used to determine if this is the receiver
|
||||||
// stack, forcing Script execution to enter the "if" clause within the
|
// of the HTLC attempting to sweep all the funds due to a contract
|
||||||
// main body of the script.
|
// breach. In this case, they'll place the revocation key at the top of
|
||||||
builder.AddOp(txscript.OP_IF)
|
// the stack.
|
||||||
|
builder.AddOp(txscript.OP_DUP)
|
||||||
|
builder.AddOp(txscript.OP_HASH160)
|
||||||
|
builder.AddData(btcutil.Hash160(revocationKey.SerializeCompressed()))
|
||||||
|
builder.AddOp(txscript.OP_EQUAL)
|
||||||
|
|
||||||
// The receiver will place a 1 as the second item of the witness stack
|
// If the hash matches, then this is the revocation clause. The output
|
||||||
// in the case the sender broadcasts a revoked commitment transaction.
|
// can be spent if the check sig operation passes.
|
||||||
// Executing this branch allows the receiver to claim the sender's
|
|
||||||
// funds as a result of their contract violation.
|
|
||||||
builder.AddOp(txscript.OP_IF)
|
builder.AddOp(txscript.OP_IF)
|
||||||
builder.AddData(revokeHash)
|
builder.AddOp(txscript.OP_CHECKSIG)
|
||||||
|
|
||||||
// Alternatively, the receiver can place a 0 as the second item of the
|
// Otherwise, this may either be the receiver of the HTLC claiming with
|
||||||
// witness stack if they wish to claim the HTLC with the proper
|
// the pre-image, or the sender of the HTLC sweeping the output after
|
||||||
// preimage as normal. In order to prevent an over-sized preimage
|
// it has timed out.
|
||||||
// attack (which can create undesirable redemption asymmetries), we
|
|
||||||
// strongly require that all HTLC preimages are exactly 32 bytes.
|
|
||||||
builder.AddOp(txscript.OP_ELSE)
|
builder.AddOp(txscript.OP_ELSE)
|
||||||
builder.AddOp(txscript.OP_SIZE)
|
|
||||||
builder.AddInt64(32)
|
|
||||||
builder.AddOp(txscript.OP_EQUALVERIFY)
|
|
||||||
builder.AddData(paymentHash)
|
|
||||||
builder.AddOp(txscript.OP_ENDIF)
|
|
||||||
|
|
||||||
|
// We'll do a bit of set up by pushing the receiver's key on the top of
|
||||||
|
// the stack. This will be needed later if we decide that this is the
|
||||||
|
// sender activating the time out clause with the HTLC timeout
|
||||||
|
// transaction.
|
||||||
|
builder.AddData(receiverKey.SerializeCompressed())
|
||||||
|
|
||||||
|
// Atm, the top item of the stack is the receiverKey's so we use a swap
|
||||||
|
// to expose what is either the payment pre-image or a signature.
|
||||||
builder.AddOp(txscript.OP_SWAP)
|
builder.AddOp(txscript.OP_SWAP)
|
||||||
|
|
||||||
builder.AddOp(txscript.OP_SHA256)
|
// With the top item swapped, check if it's 32 bytes. If so, then this
|
||||||
builder.AddOp(txscript.OP_EQUALVERIFY)
|
// *may* be the payment pre-image.
|
||||||
|
builder.AddOp(txscript.OP_SIZE)
|
||||||
|
builder.AddInt64(32)
|
||||||
|
builder.AddOp(txscript.OP_EQUAL)
|
||||||
|
|
||||||
// In either case, we require a valid signature by the receiver.
|
// If it isn't then this might be the sender of the HTLC activating the
|
||||||
builder.AddData(receiverKey.SerializeCompressed())
|
// time out clause.
|
||||||
builder.AddOp(txscript.OP_CHECKSIG)
|
builder.AddOp(txscript.OP_NOTIF)
|
||||||
|
|
||||||
// Otherwise, the sender of the HTLC will place a 0 as the first item
|
// We'll drop the OP_IF return value off the top of the stack so we can
|
||||||
// of the witness stack in order to sweep the funds back after the HTLC
|
// reconstruct the multi-sig script used as an off-chain covenant. If
|
||||||
// times out.
|
// two valid signatures are provided, ten then output will be deemed as
|
||||||
|
// spendable.
|
||||||
|
builder.AddOp(txscript.OP_DROP)
|
||||||
|
builder.AddOp(txscript.OP_2)
|
||||||
|
builder.AddOp(txscript.OP_SWAP)
|
||||||
|
builder.AddData(senderKey.SerializeCompressed())
|
||||||
|
builder.AddOp(txscript.OP_2)
|
||||||
|
builder.AddOp(txscript.OP_CHECKMULTISIG)
|
||||||
|
|
||||||
|
// Otherwise, then the only other case is that this is the receiver of
|
||||||
|
// the HTLC sweeping it on-chain with the payment pre-image.
|
||||||
builder.AddOp(txscript.OP_ELSE)
|
builder.AddOp(txscript.OP_ELSE)
|
||||||
|
|
||||||
// In this case, the sender will need to wait for an absolute HTLC
|
// Hash the top item of the stack and compare it with the hash160 of
|
||||||
// timeout, then afterwards a relative timeout before we claim re-claim
|
// the payment hash, which is already the sha256 of the payment
|
||||||
// the unsettled funds. This delay gives the other party a chance to
|
// pre-image. By using this little trick we're able save space on-chain
|
||||||
// present the preimage to the revocation hash in the event that the
|
// as the witness includes a 20-byte hash rather than a 32-byte hash.
|
||||||
// sender (at this time) broadcasts this commitment transaction after
|
builder.AddOp(txscript.OP_HASH160)
|
||||||
// it has been revoked.
|
builder.AddData(ripemd160H(paymentHash))
|
||||||
builder.AddInt64(int64(absoluteTimeout))
|
builder.AddOp(txscript.OP_EQUALVERIFY)
|
||||||
builder.AddOp(txscript.OP_CHECKLOCKTIMEVERIFY)
|
|
||||||
builder.AddInt64(int64(relativeTimeout))
|
|
||||||
builder.AddOp(txscript.OP_CHECKSEQUENCEVERIFY)
|
|
||||||
builder.AddOp(txscript.OP_2DROP)
|
|
||||||
builder.AddData(senderKey.SerializeCompressed())
|
|
||||||
builder.AddOp(txscript.OP_CHECKSIG)
|
|
||||||
|
|
||||||
|
// Close out the OP_IF statement above.
|
||||||
|
builder.AddOp(txscript.OP_ENDIF)
|
||||||
|
|
||||||
|
// Close out the OP_IF statement at the top of the script.
|
||||||
builder.AddOp(txscript.OP_ENDIF)
|
builder.AddOp(txscript.OP_ENDIF)
|
||||||
|
|
||||||
return builder.Script()
|
return builder.Script()
|
||||||
|
Loading…
Reference in New Issue
Block a user