mirror of
https://github.com/lightninglabs/loop.git
synced 2026-08-13 12:33:03 +02:00
utils: remove utils package in favor of new swap package
This commit is contained in:
parent
cdcb9f8345
commit
f552bc06b1
8 changed files with 147 additions and 174 deletions
46
swap/fees.go
Normal file
46
swap/fees.go
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
package swap
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/lightningnetwork/lnd/zpay32"
|
||||
)
|
||||
|
||||
const (
|
||||
// FeeRateTotalParts defines the granularity of the fee rate.
|
||||
// Throughout the codebase, we'll use fix based arithmetic to compute
|
||||
// fees.
|
||||
FeeRateTotalParts = 1e6
|
||||
)
|
||||
|
||||
// CalcFee returns the swap fee for a given swap amount.
|
||||
func CalcFee(amount, feeBase btcutil.Amount, feeRate int64) btcutil.Amount {
|
||||
return feeBase + amount*btcutil.Amount(feeRate)/
|
||||
btcutil.Amount(FeeRateTotalParts)
|
||||
}
|
||||
|
||||
// FeeRateAsPercentage converts a feerate to a percentage.
|
||||
func FeeRateAsPercentage(feeRate int64) float64 {
|
||||
return float64(feeRate) / (FeeRateTotalParts / 100)
|
||||
}
|
||||
|
||||
// GetInvoiceAmt gets the invoice amount. It requires an amount to be
|
||||
// specified.
|
||||
func GetInvoiceAmt(params *chaincfg.Params,
|
||||
payReq string) (btcutil.Amount, error) {
|
||||
|
||||
swapPayReq, err := zpay32.Decode(
|
||||
payReq, params,
|
||||
)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if swapPayReq.MilliSat == nil {
|
||||
return 0, errors.New("no amount in invoice")
|
||||
}
|
||||
|
||||
return swapPayReq.MilliSat.ToSatoshis(), nil
|
||||
}
|
||||
180
swap/htlc.go
Normal file
180
swap/htlc.go
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
package swap
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/lightningnetwork/lnd/input"
|
||||
"github.com/lightningnetwork/lnd/lntypes"
|
||||
)
|
||||
|
||||
// Htlc contains relevant htlc information from the receiver perspective.
|
||||
type Htlc struct {
|
||||
Script []byte
|
||||
ScriptHash []byte
|
||||
Hash lntypes.Hash
|
||||
MaxSuccessWitnessSize int
|
||||
MaxTimeoutWitnessSize int
|
||||
}
|
||||
|
||||
var (
|
||||
quoteKey [33]byte
|
||||
|
||||
quoteHash lntypes.Hash
|
||||
|
||||
// QuoteHtlc is a template script just used for fee estimation. It uses
|
||||
// the maximum value for cltv expiry to get the maximum (worst case)
|
||||
// script size.
|
||||
QuoteHtlc, _ = NewHtlc(
|
||||
^int32(0), quoteKey, quoteKey, quoteHash,
|
||||
)
|
||||
)
|
||||
|
||||
// NewHtlc returns a new instance.
|
||||
func NewHtlc(cltvExpiry int32, senderKey, receiverKey [33]byte,
|
||||
hash lntypes.Hash) (*Htlc, error) {
|
||||
|
||||
script, err := swapHTLCScript(
|
||||
cltvExpiry, senderKey, receiverKey, hash,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
scriptHash, err := input.WitnessScriptHash(script)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 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: 33 bytes
|
||||
// - witness_script_length: 1 byte
|
||||
// - witness_script: len(script) bytes
|
||||
maxSuccessWitnessSize := 1 + 1 + 73 + 1 + 33 + 1 + len(script)
|
||||
|
||||
// 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
|
||||
maxTimeoutWitnessSize := 1 + 1 + 73 + 1 + 1 + 1 + len(script)
|
||||
|
||||
return &Htlc{
|
||||
Hash: hash,
|
||||
Script: script,
|
||||
ScriptHash: scriptHash,
|
||||
MaxSuccessWitnessSize: maxSuccessWitnessSize,
|
||||
MaxTimeoutWitnessSize: maxTimeoutWitnessSize,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// SwapHTLCScript returns the on-chain HTLC witness script.
|
||||
//
|
||||
// OP_SIZE 32 OP_EQUAL
|
||||
// OP_IF
|
||||
// OP_HASH160 <ripemd160(swap_hash)> OP_EQUALVERIFY
|
||||
// <recvr key>
|
||||
// OP_ELSE
|
||||
// OP_DROP
|
||||
// <cltv timeout> OP_CHECKLOCKTIMEVERIFY OP_DROP
|
||||
// <sender key>
|
||||
// OP_ENDIF
|
||||
// OP_CHECKSIG
|
||||
func swapHTLCScript(cltvExpiry int32, senderHtlcKey,
|
||||
receiverHtlcKey [33]byte, swapHash lntypes.Hash) ([]byte, error) {
|
||||
|
||||
builder := txscript.NewScriptBuilder()
|
||||
|
||||
builder.AddOp(txscript.OP_SIZE)
|
||||
builder.AddInt64(32)
|
||||
builder.AddOp(txscript.OP_EQUAL)
|
||||
|
||||
builder.AddOp(txscript.OP_IF)
|
||||
|
||||
builder.AddOp(txscript.OP_HASH160)
|
||||
builder.AddData(input.Ripemd160H(swapHash[:]))
|
||||
builder.AddOp(txscript.OP_EQUALVERIFY)
|
||||
|
||||
builder.AddData(receiverHtlcKey[:])
|
||||
|
||||
builder.AddOp(txscript.OP_ELSE)
|
||||
|
||||
builder.AddOp(txscript.OP_DROP)
|
||||
|
||||
builder.AddInt64(int64(cltvExpiry))
|
||||
builder.AddOp(txscript.OP_CHECKLOCKTIMEVERIFY)
|
||||
builder.AddOp(txscript.OP_DROP)
|
||||
|
||||
builder.AddData(senderHtlcKey[:])
|
||||
|
||||
builder.AddOp(txscript.OP_ENDIF)
|
||||
|
||||
builder.AddOp(txscript.OP_CHECKSIG)
|
||||
|
||||
return builder.Script()
|
||||
}
|
||||
|
||||
// Address returns the p2wsh address of the htlc.
|
||||
func (h *Htlc) Address(chainParams *chaincfg.Params) (
|
||||
btcutil.Address, error) {
|
||||
|
||||
// Skip OP_0 and data length.
|
||||
return btcutil.NewAddressWitnessScriptHash(
|
||||
h.ScriptHash[2:],
|
||||
chainParams,
|
||||
)
|
||||
}
|
||||
|
||||
// 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")
|
||||
}
|
||||
|
||||
witnessStack := make(wire.TxWitness, 3)
|
||||
witnessStack[0] = append(receiverSig, byte(txscript.SigHashAll))
|
||||
witnessStack[1] = preimage[:]
|
||||
witnessStack[2] = h.Script
|
||||
|
||||
return witnessStack, nil
|
||||
}
|
||||
|
||||
// IsSuccessWitness checks whether the given stack is valid for redeeming the
|
||||
// htlc.
|
||||
func (h *Htlc) IsSuccessWitness(witness wire.TxWitness) bool {
|
||||
if len(witness) != 3 {
|
||||
return false
|
||||
}
|
||||
|
||||
isTimeoutTx := bytes.Equal([]byte{0}, witness[1])
|
||||
|
||||
return !isTimeoutTx
|
||||
}
|
||||
|
||||
// GenTimeoutWitness returns the timeout script to spend this htlc after
|
||||
// timeout.
|
||||
func (h *Htlc) GenTimeoutWitness(senderSig []byte) (wire.TxWitness, error) {
|
||||
|
||||
witnessStack := make(wire.TxWitness, 3)
|
||||
witnessStack[0] = append(senderSig, byte(txscript.SigHashAll))
|
||||
witnessStack[1] = []byte{0}
|
||||
witnessStack[2] = h.Script
|
||||
|
||||
return witnessStack, nil
|
||||
}
|
||||
9
swap/keychain.go
Normal file
9
swap/keychain.go
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
package swap
|
||||
|
||||
var (
|
||||
// KeyFamily is the key family used to generate keys that allow
|
||||
// spending of the htlc.
|
||||
//
|
||||
// TODO(joost): decide on actual value
|
||||
KeyFamily = int32(99)
|
||||
)
|
||||
27
swap/net.go
Normal file
27
swap/net.go
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
package swap
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
)
|
||||
|
||||
// ChainParamsFromNetwork returns chain parameters based on a network name.
|
||||
func ChainParamsFromNetwork(network string) (*chaincfg.Params, error) {
|
||||
switch network {
|
||||
case "mainnet":
|
||||
return &chaincfg.MainNetParams, nil
|
||||
|
||||
case "testnet":
|
||||
return &chaincfg.TestNet3Params, nil
|
||||
|
||||
case "regtest":
|
||||
return &chaincfg.RegressionNetParams, nil
|
||||
|
||||
case "simnet":
|
||||
return &chaincfg.SimNetParams, nil
|
||||
|
||||
default:
|
||||
return nil, errors.New("unknown network")
|
||||
}
|
||||
}
|
||||
64
swap/tx.go
Normal file
64
swap/tx.go
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
package swap
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
)
|
||||
|
||||
// EncodeTx encodes a tx to raw bytes.
|
||||
func EncodeTx(tx *wire.MsgTx) ([]byte, error) {
|
||||
var buffer bytes.Buffer
|
||||
err := tx.BtcEncode(&buffer, 0, wire.WitnessEncoding)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rawTx := buffer.Bytes()
|
||||
|
||||
return rawTx, nil
|
||||
}
|
||||
|
||||
// DecodeTx decodes raw tx bytes.
|
||||
func DecodeTx(rawTx []byte) (*wire.MsgTx, error) {
|
||||
tx := wire.MsgTx{}
|
||||
r := bytes.NewReader(rawTx)
|
||||
err := tx.BtcDecode(r, 0, wire.WitnessEncoding)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &tx, nil
|
||||
}
|
||||
|
||||
// GetScriptOutput locates the given script in the outputs of a transaction and
|
||||
// returns its outpoint and value.
|
||||
func GetScriptOutput(htlcTx *wire.MsgTx, scriptHash []byte) (
|
||||
*wire.OutPoint, btcutil.Amount, error) {
|
||||
|
||||
for idx, output := range htlcTx.TxOut {
|
||||
if bytes.Equal(output.PkScript, scriptHash) {
|
||||
return &wire.OutPoint{
|
||||
Hash: htlcTx.TxHash(),
|
||||
Index: uint32(idx),
|
||||
}, btcutil.Amount(output.Value), nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, 0, fmt.Errorf("cannot determine outpoint")
|
||||
}
|
||||
|
||||
// GetTxInputByOutpoint returns a tx input based on a given input outpoint.
|
||||
func GetTxInputByOutpoint(tx *wire.MsgTx, input *wire.OutPoint) (
|
||||
*wire.TxIn, error) {
|
||||
|
||||
for _, in := range tx.TxIn {
|
||||
if in.PreviousOutPoint == *input {
|
||||
return in, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.New("input not found")
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue