swap: extend swap to be able to create the V2 htlc

pull/255/head
Andras Banki-Horvath 4 years ago
parent 9fdd531130
commit cd0a5f9f06
No known key found for this signature in database
GPG Key ID: 80E5375C094198D8

@ -756,7 +756,7 @@ func (s *loopInSwap) publishTimeoutTx(ctx context.Context,
} }
witnessFunc := func(sig []byte) (wire.TxWitness, error) { witnessFunc := func(sig []byte) (wire.TxWitness, error) {
return s.htlc.GenTimeoutWitness(sig) return s.htlc.GenTimeoutWitness(sig), nil
} }
timeoutTx, err := s.sweeper.CreateSweepTx( timeoutTx, err := s.sweeper.CreateSweepTx(

@ -32,12 +32,46 @@ type ScriptVersion uint8
const ( const (
// HtlcV1 refers to the original version of the HTLC script. // HtlcV1 refers to the original version of the HTLC script.
HtlcV1 ScriptVersion = iota HtlcV1 ScriptVersion = iota
// HtlcV2 refers to the improved version of the HTLC script.
HtlcV2
) )
// htlcScript defines an interface for the different HTLC implementations.
type HtlcScript interface {
// genSuccessWitness returns the success script to spend this htlc with
// the preimage.
genSuccessWitness(receiverSig []byte, preimage lntypes.Preimage) wire.TxWitness
// GenTimeoutWitness returns the timeout script to spend this htlc after
// timeout.
GenTimeoutWitness(senderSig []byte) wire.TxWitness
// IsSuccessWitness checks whether the given stack is valid for
// redeeming the htlc.
IsSuccessWitness(witness wire.TxWitness) bool
// Script returns the htlc script.
Script() []byte
// MaxSuccessWitnessSize returns the maximum witness size for the
// success case witness.
MaxSuccessWitnessSize() int
// MaxTimeoutWitnessSize returns the maximum witness size for the
// timeout case witness.
MaxTimeoutWitnessSize() int
// SuccessSequence returns the sequence to spend this htlc in the
// success case.
SuccessSequence() uint32
}
// Htlc contains relevant htlc information from the receiver perspective. // Htlc contains relevant htlc information from the receiver perspective.
type Htlc struct { type Htlc struct {
HtlcScript
Version ScriptVersion Version ScriptVersion
Script []byte
PkScript []byte PkScript []byte
Hash lntypes.Hash Hash lntypes.Hash
OutputType HtlcOutputType OutputType HtlcOutputType
@ -55,10 +89,12 @@ var (
// the maximum value for cltv expiry to get the maximum (worst case) // the maximum value for cltv expiry to get the maximum (worst case)
// script size. // script size.
QuoteHtlc, _ = NewHtlc( QuoteHtlc, _ = NewHtlc(
HtlcV1, HtlcV2,
^int32(0), quoteKey, quoteKey, quoteHash, HtlcP2WSH, ^int32(0), quoteKey, quoteKey, quoteHash, HtlcP2WSH,
&chaincfg.MainNetParams, &chaincfg.MainNetParams,
) )
ErrInvalidScriptVersion = fmt.Errorf("invalid script version")
) )
// String returns the string value of HtlcOutputType. // String returns the string value of HtlcOutputType.
@ -82,25 +118,30 @@ func NewHtlc(version ScriptVersion, cltvExpiry int32,
chainParams *chaincfg.Params) (*Htlc, error) { chainParams *chaincfg.Params) (*Htlc, error) {
var ( var (
err error err error
script []byte htlc HtlcScript
) )
switch version { switch version {
case HtlcV1: case HtlcV1:
script, err = swapHTLCScriptV1( htlc, err = newHTLCScriptV1(
cltvExpiry, senderKey, receiverKey, hash,
)
case HtlcV2:
htlc, err = newHTLCScriptV2(
cltvExpiry, senderKey, receiverKey, hash, cltvExpiry, senderKey, receiverKey, hash,
) )
default: default:
return nil, fmt.Errorf("unknown script version: %v", version) return nil, ErrInvalidScriptVersion
} }
if err != nil { if err != nil {
return nil, err return nil, err
} }
p2wshPkScript, err := input.WitnessScriptHash(script) p2wshPkScript, err := input.WitnessScriptHash(htlc.Script())
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -158,9 +199,9 @@ func NewHtlc(version ScriptVersion, cltvExpiry int32,
} }
return &Htlc{ return &Htlc{
HtlcScript: htlc,
Hash: hash, Hash: hash,
Version: version, Version: version,
Script: script,
PkScript: pkScript, PkScript: pkScript,
OutputType: outputType, OutputType: outputType,
ChainParams: chainParams, ChainParams: chainParams,
@ -169,7 +210,50 @@ func NewHtlc(version ScriptVersion, cltvExpiry int32,
}, nil }, nil
} }
// SwapHTLCScriptV1 returns the on-chain HTLC witness script. // GenSuccessWitness returns the success script to spend this htlc with
// the preimage.
func (h *Htlc) GenSuccessWitness(receiverSig []byte,
preimage lntypes.Preimage) (wire.TxWitness, error) {
if h.Hash != preimage.Hash() {
return nil, errors.New("preimage doesn't match hash")
}
return h.genSuccessWitness(receiverSig, preimage), nil
}
// AddSuccessToEstimator adds a successful spend to a weight estimator.
func (h *Htlc) AddSuccessToEstimator(estimator *input.TxWeightEstimator) {
maxSuccessWitnessSize := h.MaxSuccessWitnessSize()
switch h.OutputType {
case HtlcP2WSH:
estimator.AddWitnessInput(maxSuccessWitnessSize)
case HtlcNP2WSH:
estimator.AddNestedP2WSHInput(maxSuccessWitnessSize)
}
}
// AddTimeoutToEstimator adds a timeout spend to a weight estimator.
func (h *Htlc) AddTimeoutToEstimator(estimator *input.TxWeightEstimator) {
maxTimeoutWitnessSize := h.MaxTimeoutWitnessSize()
switch h.OutputType {
case HtlcP2WSH:
estimator.AddWitnessInput(maxTimeoutWitnessSize)
case HtlcNP2WSH:
estimator.AddNestedP2WSHInput(maxTimeoutWitnessSize)
}
}
// HtlcScriptV1 encapsulates the htlc v1 script.
type HtlcScriptV1 struct {
script []byte
}
// newHTLCScriptV1 constructs an HtlcScript with the HTLC V1 witness script.
// //
// OP_SIZE 32 OP_EQUAL // OP_SIZE 32 OP_EQUAL
// OP_IF // OP_IF
@ -181,8 +265,8 @@ func NewHtlc(version ScriptVersion, cltvExpiry int32,
// <senderHtlcKey> // <senderHtlcKey>
// OP_ENDIF // OP_ENDIF
// OP_CHECKSIG // OP_CHECKSIG
func swapHTLCScriptV1(cltvExpiry int32, senderHtlcKey, func newHTLCScriptV1(cltvExpiry int32, senderHtlcKey,
receiverHtlcKey [33]byte, swapHash lntypes.Hash) ([]byte, error) { receiverHtlcKey [33]byte, swapHash lntypes.Hash) (*HtlcScriptV1, error) {
builder := txscript.NewScriptBuilder() builder := txscript.NewScriptBuilder()
@ -212,90 +296,219 @@ func swapHTLCScriptV1(cltvExpiry int32, senderHtlcKey,
builder.AddOp(txscript.OP_CHECKSIG) builder.AddOp(txscript.OP_CHECKSIG)
return builder.Script() script, err := builder.Script()
} if err != nil {
return nil, err
}
// GenSuccessWitness returns the success script to spend this htlc with the return &HtlcScriptV1{
// preimage. script: script,
func (h *Htlc) GenSuccessWitness(receiverSig []byte, }, nil
preimage lntypes.Preimage) (wire.TxWitness, error) { }
if h.Hash != preimage.Hash() { // genSuccessWitness returns the success script to spend this htlc with
return nil, errors.New("preimage doesn't match hash") // the preimage.
} func (h *HtlcScriptV1) genSuccessWitness(receiverSig []byte,
preimage lntypes.Preimage) wire.TxWitness {
witnessStack := make(wire.TxWitness, 3) witnessStack := make(wire.TxWitness, 3)
witnessStack[0] = append(receiverSig, byte(txscript.SigHashAll)) witnessStack[0] = append(receiverSig, byte(txscript.SigHashAll))
witnessStack[1] = preimage[:] witnessStack[1] = preimage[:]
witnessStack[2] = h.Script witnessStack[2] = h.script
return witnessStack, nil return witnessStack
}
// GenTimeoutWitness returns the timeout script to spend this htlc after
// timeout.
func (h *HtlcScriptV1) GenTimeoutWitness(senderSig []byte) wire.TxWitness {
witnessStack := make(wire.TxWitness, 3)
witnessStack[0] = append(senderSig, byte(txscript.SigHashAll))
witnessStack[1] = []byte{0}
witnessStack[2] = h.script
return witnessStack
} }
// IsSuccessWitness checks whether the given stack is valid for redeeming the // IsSuccessWitness checks whether the given stack is valid for redeeming the
// htlc. // htlc.
func (h *Htlc) IsSuccessWitness(witness wire.TxWitness) bool { func (h *HtlcScriptV1) IsSuccessWitness(witness wire.TxWitness) bool {
if len(witness) != 3 { if len(witness) != 3 {
return false return false
} }
isTimeoutTx := bytes.Equal([]byte{0}, witness[1]) isTimeoutTx := bytes.Equal([]byte{0}, witness[1])
return !isTimeoutTx
}
// Script returns the htlc script.
func (h *HtlcScriptV1) Script() []byte {
return h.script
}
// MaxSuccessWitnessSize returns the maximum success witness size.
func (h *HtlcScriptV1) MaxSuccessWitnessSize() int {
// Calculate maximum success witness size
//
// - number_of_witness_elements: 1 byte
// - receiver_sig_length: 1 byte
// - receiver_sig: 73 bytes
// - preimage_length: 1 byte
// - preimage: 32 bytes
// - witness_script_length: 1 byte
// - witness_script: len(script) bytes
return 1 + 1 + 73 + 1 + 32 + 1 + len(h.script)
}
// MaxTimeoutWitnessSize return the maximum timeout witness size.
func (h *HtlcScriptV1) MaxTimeoutWitnessSize() int {
// Calculate maximum timeout witness size
//
// - number_of_witness_elements: 1 byte
// - sender_sig_length: 1 byte
// - sender_sig: 73 bytes
// - zero_length: 1 byte
// - zero: 1 byte
// - witness_script_length: 1 byte
// - witness_script: len(script) bytes
return 1 + 1 + 73 + 1 + 1 + 1 + len(h.script)
}
// SuccessSequence returns the sequence to spend this htlc in the success case.
func (h *HtlcScriptV1) SuccessSequence() uint32 {
return 0
}
// HtlcScriptV2 encapsulates the htlc v2 script.
type HtlcScriptV2 struct {
script []byte
senderKey [33]byte
}
// newHTLCScriptV2 construct an HtlcScipt with the HTLC V2 witness script.
//
// <receiverHtlcKey> OP_CHECKSIG OP_NOTIF
// OP_DUP OP_HASH160 <HASH160(senderHtlcKey)> OP_EQUALVERIFY OP_CHECKSIGVERIFY
// <cltv timeout> OP_CHECKLOCKTIMEVERIFY
// OP_ELSE
// OP_SIZE <20> OP_EQUALVERIFY OP_HASH160 <ripemd(swapHash)> OP_EQUALVERIFY 1
// OP_CHECKSEQUENCEVERIFY
// OP_ENDIF
func newHTLCScriptV2(cltvExpiry int32, senderHtlcKey,
receiverHtlcKey [33]byte, swapHash lntypes.Hash) (*HtlcScriptV2, error) {
builder := txscript.NewScriptBuilder()
builder.AddData(receiverHtlcKey[:])
builder.AddOp(txscript.OP_CHECKSIG)
builder.AddOp(txscript.OP_NOTIF)
builder.AddOp(txscript.OP_DUP)
builder.AddOp(txscript.OP_HASH160)
senderHtlcKeyHash := sha256.Sum256(senderHtlcKey[:])
builder.AddData(input.Ripemd160H(senderHtlcKeyHash[:]))
builder.AddOp(txscript.OP_EQUALVERIFY)
builder.AddOp(txscript.OP_CHECKSIGVERIFY)
builder.AddInt64(int64(cltvExpiry))
builder.AddOp(txscript.OP_CHECKLOCKTIMEVERIFY)
builder.AddOp(txscript.OP_ELSE)
builder.AddOp(txscript.OP_SIZE)
builder.AddInt64(0x20)
builder.AddOp(txscript.OP_EQUALVERIFY)
builder.AddOp(txscript.OP_HASH160)
builder.AddData(input.Ripemd160H(swapHash[:]))
builder.AddOp(txscript.OP_EQUALVERIFY)
builder.AddOp(txscript.OP_1)
builder.AddOp(txscript.OP_CHECKSEQUENCEVERIFY)
builder.AddOp(txscript.OP_ENDIF)
script, err := builder.Script()
if err != nil {
return nil, err
}
return &HtlcScriptV2{
script: script,
senderKey: senderHtlcKey,
}, nil
}
// genSuccessWitness returns the success script to spend this htlc with
// the preimage.
func (h *HtlcScriptV2) genSuccessWitness(receiverSig []byte,
preimage lntypes.Preimage) wire.TxWitness {
witnessStack := make(wire.TxWitness, 3)
witnessStack[0] = preimage[:]
witnessStack[1] = append(receiverSig, byte(txscript.SigHashAll))
witnessStack[2] = h.script
return witnessStack
}
// IsSuccessWitness checks whether the given stack is valid for redeeming the
// htlc.
func (h *HtlcScriptV2) IsSuccessWitness(witness wire.TxWitness) bool {
isTimeoutTx := len(witness) == 4
return !isTimeoutTx return !isTimeoutTx
} }
// GenTimeoutWitness returns the timeout script to spend this htlc after // GenTimeoutWitness returns the timeout script to spend this htlc after
// timeout. // timeout.
func (h *Htlc) GenTimeoutWitness(senderSig []byte) (wire.TxWitness, error) { func (h *HtlcScriptV2) GenTimeoutWitness(senderSig []byte) wire.TxWitness {
witnessStack := make(wire.TxWitness, 3) witnessStack := make(wire.TxWitness, 4)
witnessStack[0] = append(senderSig, byte(txscript.SigHashAll)) witnessStack[0] = append(senderSig, byte(txscript.SigHashAll))
witnessStack[1] = []byte{0} witnessStack[1] = h.senderKey[:]
witnessStack[2] = h.Script witnessStack[2] = []byte{}
witnessStack[3] = h.script
return witnessStack, nil return witnessStack
} }
// AddSuccessToEstimator adds a successful spend to a weight estimator. // Script returns the htlc script.
func (h *Htlc) AddSuccessToEstimator(estimator *input.TxWeightEstimator) { func (h *HtlcScriptV2) Script() []byte {
return h.script
}
// MaxSuccessWitnessSize returns maximum success witness size.
func (h *HtlcScriptV2) MaxSuccessWitnessSize() int {
// Calculate maximum success witness size // Calculate maximum success witness size
// //
// - number_of_witness_elements: 1 byte // - number_of_witness_elements: 1 byte
// - receiver_sig_length: 1 byte // - receiver_sig_length: 1 byte
// - receiver_sig: 73 bytes // - receiver_sig: 73 bytes
// - preimage_length: 1 byte // - preimage_length: 1 byte
// - preimage: 33 bytes // - preimage: 32 bytes
// - witness_script_length: 1 byte // - witness_script_length: 1 byte
// - witness_script: len(script) bytes // - witness_script: len(script) bytes
maxSuccessWitnessSize := 1 + 1 + 73 + 1 + 33 + 1 + len(h.Script) return 1 + 1 + 73 + 1 + 32 + 1 + len(h.script)
switch h.OutputType {
case HtlcP2WSH:
estimator.AddWitnessInput(maxSuccessWitnessSize)
case HtlcNP2WSH:
estimator.AddNestedP2WSHInput(maxSuccessWitnessSize)
}
} }
// AddTimeoutToEstimator adds a timeout spend to a weight estimator. // MaxTimeoutWitnessSize returns maximum timeout witness size.
func (h *Htlc) AddTimeoutToEstimator(estimator *input.TxWeightEstimator) { func (h *HtlcScriptV2) MaxTimeoutWitnessSize() int {
// Calculate maximum timeout witness size // Calculate maximum timeout witness size
// //
// - number_of_witness_elements: 1 byte // - number_of_witness_elements: 1 byte
// - sender_sig_length: 1 byte // - sender_sig_length: 1 byte
// - sender_sig: 73 bytes // - sender_sig: 73 bytes
// - zero_length: 1 byte // - sender_key_length: 1 byte
// - sender_key: 33 bytes
// - zero: 1 byte // - zero: 1 byte
// - witness_script_length: 1 byte // - witness_script_length: 1 byte
// - witness_script: len(script) bytes // - witness_script: len(script) bytes
maxTimeoutWitnessSize := 1 + 1 + 73 + 1 + 1 + 1 + len(h.Script) return 1 + 1 + 73 + 1 + 33 + 1 + 1 + len(h.script)
}
switch h.OutputType {
case HtlcP2WSH:
estimator.AddWitnessInput(maxTimeoutWitnessSize)
case HtlcNP2WSH: // SuccessSequence returns the sequence to spend this htlc in the success case.
estimator.AddNestedP2WSHInput(maxTimeoutWitnessSize) func (h *HtlcScriptV2) SuccessSequence() uint32 {
} return 1
} }

@ -58,7 +58,7 @@ func (s *Sweeper) CreateSweepTx(
} }
signDesc := lndclient.SignDescriptor{ signDesc := lndclient.SignDescriptor{
WitnessScript: htlc.Script, WitnessScript: htlc.Script(),
Output: &wire.TxOut{ Output: &wire.TxOut{
Value: int64(amount), Value: int64(amount),
}, },

Loading…
Cancel
Save