2019-10-01 12:22:30 -03:00
|
|
|
package libwallet
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/hex"
|
2021-01-29 18:51:08 -03:00
|
|
|
"errors"
|
|
|
|
"fmt"
|
2019-10-01 12:22:30 -03:00
|
|
|
|
2020-11-09 10:05:29 -03:00
|
|
|
"github.com/muun/libwallet/addresses"
|
2021-11-12 19:06:13 -03:00
|
|
|
"github.com/muun/libwallet/btcsuitew/btcutilw"
|
|
|
|
"github.com/muun/libwallet/btcsuitew/txscriptw"
|
2020-11-09 10:05:29 -03:00
|
|
|
|
|
|
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
|
|
|
|
2019-10-01 12:22:30 -03:00
|
|
|
"github.com/btcsuite/btcd/wire"
|
2020-11-09 10:05:29 -03:00
|
|
|
"github.com/btcsuite/btcutil"
|
2019-10-01 12:22:30 -03:00
|
|
|
)
|
|
|
|
|
2020-11-09 10:05:29 -03:00
|
|
|
type SigningExpectations struct {
|
|
|
|
destination string
|
|
|
|
amount int64
|
|
|
|
change MuunAddress
|
|
|
|
fee int64
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewSigningExpectations(destination string, amount int64, change MuunAddress, fee int64) *SigningExpectations {
|
|
|
|
return &SigningExpectations{
|
|
|
|
destination,
|
|
|
|
amount,
|
|
|
|
change,
|
|
|
|
fee,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-01 12:22:30 -03:00
|
|
|
type MuunAddress interface {
|
|
|
|
Version() int
|
|
|
|
DerivationPath() string
|
|
|
|
Address() string
|
|
|
|
}
|
|
|
|
|
|
|
|
type Outpoint interface {
|
|
|
|
TxId() []byte
|
|
|
|
Index() int
|
|
|
|
Amount() int64
|
|
|
|
}
|
|
|
|
|
2019-12-16 17:59:11 -03:00
|
|
|
type InputSubmarineSwapV1 interface {
|
2019-10-01 12:22:30 -03:00
|
|
|
RefundAddress() string
|
|
|
|
PaymentHash256() []byte
|
|
|
|
ServerPublicKey() []byte
|
|
|
|
LockTime() int64
|
|
|
|
}
|
|
|
|
|
2019-12-16 17:59:11 -03:00
|
|
|
type InputSubmarineSwapV2 interface {
|
|
|
|
PaymentHash256() []byte
|
|
|
|
UserPublicKey() []byte
|
|
|
|
MuunPublicKey() []byte
|
|
|
|
ServerPublicKey() []byte
|
|
|
|
BlocksForExpiration() int64
|
|
|
|
ServerSignature() []byte
|
|
|
|
}
|
|
|
|
|
2020-11-09 10:05:29 -03:00
|
|
|
type InputIncomingSwap interface {
|
|
|
|
Sphinx() []byte
|
|
|
|
HtlcTx() []byte
|
|
|
|
PaymentHash256() []byte
|
|
|
|
SwapServerPublicKey() string
|
|
|
|
ExpirationHeight() int64
|
2021-01-29 18:51:08 -03:00
|
|
|
CollectInSats() int64
|
2020-11-09 10:05:29 -03:00
|
|
|
}
|
|
|
|
|
2019-10-01 12:22:30 -03:00
|
|
|
type Input interface {
|
|
|
|
OutPoint() Outpoint
|
|
|
|
Address() MuunAddress
|
|
|
|
UserSignature() []byte
|
|
|
|
MuunSignature() []byte
|
2019-12-16 17:59:11 -03:00
|
|
|
SubmarineSwapV1() InputSubmarineSwapV1
|
|
|
|
SubmarineSwapV2() InputSubmarineSwapV2
|
2020-11-09 10:05:29 -03:00
|
|
|
IncomingSwap() InputIncomingSwap
|
2021-11-12 19:06:13 -03:00
|
|
|
MuunPublicNonce() []byte
|
2019-10-01 12:22:30 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
type PartiallySignedTransaction struct {
|
|
|
|
tx *wire.MsgTx
|
|
|
|
inputs []Input
|
2021-11-12 19:06:13 -03:00
|
|
|
nonces *MusigNonces
|
2019-10-01 12:22:30 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
type Transaction struct {
|
|
|
|
Hash string
|
|
|
|
Bytes []byte
|
|
|
|
}
|
|
|
|
|
2020-11-09 10:05:29 -03:00
|
|
|
const dustThreshold = 546
|
2019-10-01 12:22:30 -03:00
|
|
|
|
2020-11-09 10:05:29 -03:00
|
|
|
type InputList struct {
|
|
|
|
inputs []Input
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *InputList) Add(input Input) {
|
|
|
|
l.inputs = append(l.inputs, input)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *InputList) Inputs() []Input {
|
|
|
|
return l.inputs
|
|
|
|
}
|
|
|
|
|
2021-11-12 19:06:13 -03:00
|
|
|
func NewPartiallySignedTransaction(
|
|
|
|
inputs *InputList, rawTx []byte, nonces *MusigNonces,
|
|
|
|
) (*PartiallySignedTransaction, error) {
|
2019-10-01 12:22:30 -03:00
|
|
|
|
|
|
|
tx := wire.NewMsgTx(0)
|
2020-11-09 10:05:29 -03:00
|
|
|
err := tx.Deserialize(bytes.NewReader(rawTx))
|
2019-10-01 12:22:30 -03:00
|
|
|
if err != nil {
|
2021-01-29 18:51:08 -03:00
|
|
|
return nil, fmt.Errorf("failed to decode tx: %w", err)
|
2019-10-01 12:22:30 -03:00
|
|
|
}
|
|
|
|
|
2021-11-12 19:06:13 -03:00
|
|
|
return &PartiallySignedTransaction{
|
|
|
|
tx: tx,
|
|
|
|
inputs: inputs.Inputs(),
|
|
|
|
nonces: nonces,
|
|
|
|
}, nil
|
2019-10-01 12:22:30 -03:00
|
|
|
}
|
|
|
|
|
2020-11-09 10:05:29 -03:00
|
|
|
func (p *PartiallySignedTransaction) coins(net *Network) ([]coin, error) {
|
|
|
|
var coins []coin
|
2021-11-12 19:06:13 -03:00
|
|
|
|
|
|
|
prevOuts, err := p.createPrevOuts(net)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO:
|
|
|
|
// Only taproot coins are going to use this kind of cache. SegWit v0 coins should use it too.
|
|
|
|
sigHashes := txscriptw.NewTaprootSigHashes(p.tx, prevOuts)
|
|
|
|
|
|
|
|
for i, input := range p.inputs {
|
|
|
|
coin, err := createCoin(i, input, net, sigHashes, p.nonces)
|
2020-11-09 10:05:29 -03:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
coins = append(coins, coin)
|
|
|
|
}
|
|
|
|
return coins, nil
|
2019-10-01 12:22:30 -03:00
|
|
|
}
|
|
|
|
|
2021-11-12 19:06:13 -03:00
|
|
|
func (p *PartiallySignedTransaction) createPrevOuts(net *Network) ([]*wire.TxOut, error) {
|
|
|
|
prevOuts := make([]*wire.TxOut, len(p.inputs))
|
|
|
|
|
|
|
|
for i, input := range p.inputs {
|
|
|
|
amount := input.OutPoint().Amount()
|
|
|
|
addr := input.Address().Address()
|
|
|
|
|
|
|
|
decodedAddr, err := btcutilw.DecodeAddress(addr, net.network)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to decode address %s in prevOut %d: %w", addr, i, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
script, err := txscriptw.PayToAddrScript(decodedAddr)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to craft output script for %s in prevOut %d: %w", addr, i, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
prevOuts[i] = &wire.TxOut{Value: amount, PkScript: script}
|
|
|
|
}
|
|
|
|
|
|
|
|
return prevOuts, nil
|
|
|
|
}
|
|
|
|
|
2020-11-09 10:05:29 -03:00
|
|
|
func (p *PartiallySignedTransaction) Sign(userKey *HDPrivateKey, muunKey *HDPublicKey) (*Transaction, error) {
|
2019-10-01 12:22:30 -03:00
|
|
|
|
2020-11-09 10:05:29 -03:00
|
|
|
coins, err := p.coins(userKey.Network)
|
|
|
|
if err != nil {
|
2021-01-29 18:51:08 -03:00
|
|
|
return nil, fmt.Errorf("could not convert input data to coin: %w", err)
|
2020-11-09 10:05:29 -03:00
|
|
|
}
|
2019-10-01 12:22:30 -03:00
|
|
|
|
2020-11-09 10:05:29 -03:00
|
|
|
for i, coin := range coins {
|
|
|
|
err = coin.SignInput(i, p.tx, userKey, muunKey)
|
2019-10-01 12:22:30 -03:00
|
|
|
if err != nil {
|
2021-01-29 18:51:08 -03:00
|
|
|
return nil, fmt.Errorf("failed to sign input: %w", err)
|
2019-10-01 12:22:30 -03:00
|
|
|
}
|
2020-11-09 10:05:29 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
return newTransaction(p.tx)
|
|
|
|
|
|
|
|
}
|
2019-10-01 12:22:30 -03:00
|
|
|
|
2020-11-09 10:05:29 -03:00
|
|
|
func (p *PartiallySignedTransaction) FullySign(userKey, muunKey *HDPrivateKey) (*Transaction, error) {
|
|
|
|
|
|
|
|
coins, err := p.coins(userKey.Network)
|
|
|
|
if err != nil {
|
2021-01-29 18:51:08 -03:00
|
|
|
return nil, fmt.Errorf("could not convert input data to coin: %w", err)
|
2020-11-09 10:05:29 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
for i, coin := range coins {
|
|
|
|
err = coin.FullySignInput(i, p.tx, userKey, muunKey)
|
2019-10-01 12:22:30 -03:00
|
|
|
if err != nil {
|
2021-01-29 18:51:08 -03:00
|
|
|
return nil, fmt.Errorf("failed to sign input: %w", err)
|
2019-10-01 12:22:30 -03:00
|
|
|
}
|
2020-11-09 10:05:29 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
return newTransaction(p.tx)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *PartiallySignedTransaction) Verify(expectations *SigningExpectations, userPublicKey *HDPublicKey, muunPublickKey *HDPublicKey) error {
|
|
|
|
|
|
|
|
// TODO: We don't have enough information (yet) to check the inputs are actually ours and they exist.
|
2019-10-01 12:22:30 -03:00
|
|
|
|
2020-11-09 10:05:29 -03:00
|
|
|
network := userPublicKey.Network
|
2019-10-01 12:22:30 -03:00
|
|
|
|
2020-11-09 10:05:29 -03:00
|
|
|
// We expect TX to be frugal in their ouputs: one to the destination and an optional change.
|
|
|
|
// If we were to receive more than that, we consider it invalid.
|
|
|
|
if expectations.change != nil {
|
|
|
|
if len(p.tx.TxOut) != 2 {
|
2021-01-29 18:51:08 -03:00
|
|
|
return fmt.Errorf("expected destination and change outputs but found %v", len(p.tx.TxOut))
|
2019-10-01 12:22:30 -03:00
|
|
|
}
|
2020-11-09 10:05:29 -03:00
|
|
|
} else {
|
|
|
|
if len(p.tx.TxOut) != 1 {
|
2021-01-29 18:51:08 -03:00
|
|
|
return fmt.Errorf("expected destination output only but found %v", len(p.tx.TxOut))
|
2020-11-09 10:05:29 -03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Build output script corresponding to the destination address.
|
|
|
|
toScript, err := addressToScript(expectations.destination, network)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
expectedAmount := expectations.amount
|
|
|
|
expectedFee := expectations.fee
|
|
|
|
expectedChange := expectations.change
|
2019-10-01 12:22:30 -03:00
|
|
|
|
2020-11-09 10:05:29 -03:00
|
|
|
// Build output script corresponding to the change address.
|
|
|
|
var changeScript []byte
|
|
|
|
if expectedChange != nil {
|
|
|
|
changeScript, err = addressToScript(expectations.change.Address(), network)
|
2019-10-01 12:22:30 -03:00
|
|
|
if err != nil {
|
2020-11-09 10:05:29 -03:00
|
|
|
return err
|
2019-10-01 12:22:30 -03:00
|
|
|
}
|
2020-11-09 10:05:29 -03:00
|
|
|
}
|
2019-10-01 12:22:30 -03:00
|
|
|
|
2020-11-09 10:05:29 -03:00
|
|
|
// Find destination and change outputs using the script we just built.
|
|
|
|
var toOutput, changeOutput *wire.TxOut
|
|
|
|
for _, output := range p.tx.TxOut {
|
|
|
|
if bytes.Equal(output.PkScript, toScript) {
|
|
|
|
toOutput = output
|
|
|
|
} else if changeScript != nil && bytes.Equal(output.PkScript, changeScript) {
|
|
|
|
changeOutput = output
|
|
|
|
}
|
2019-10-01 12:22:30 -03:00
|
|
|
}
|
|
|
|
|
2020-11-09 10:05:29 -03:00
|
|
|
// Fail if not destination output was found in the TX.
|
|
|
|
if toOutput == nil {
|
2021-01-29 18:51:08 -03:00
|
|
|
return errors.New("destination output is not present")
|
2019-10-01 12:22:30 -03:00
|
|
|
}
|
|
|
|
|
2020-11-09 10:05:29 -03:00
|
|
|
// Verify destination output value matches expected amount
|
|
|
|
if toOutput.Value != expectedAmount {
|
2021-01-29 18:51:08 -03:00
|
|
|
return fmt.Errorf("destination amount is mismatched. found %v expected %v", toOutput.Value, expectedAmount)
|
2020-11-09 10:05:29 -03:00
|
|
|
}
|
2019-10-01 12:22:30 -03:00
|
|
|
|
2020-11-09 10:05:29 -03:00
|
|
|
/*
|
|
|
|
NOT CHECKED: outputs smaller than dustThreshold.
|
|
|
|
We removed this check, which could be exploited by the crafter to invalidate the transaction. Since failing the
|
|
|
|
integrity check ourselves would have the same effect (preventing us from signing) it doesn't make much sense.
|
|
|
|
*/
|
|
|
|
|
|
|
|
var actualTotal int64
|
|
|
|
for _, input := range p.inputs {
|
|
|
|
actualTotal += input.OutPoint().Amount()
|
|
|
|
}
|
2019-10-01 12:22:30 -03:00
|
|
|
|
2020-11-09 10:05:29 -03:00
|
|
|
/*
|
|
|
|
NOT CHECKED: input amounts.
|
|
|
|
These are provided by the crafter, but for segwit inputs (scheme v3 and forward), the amount is part of
|
|
|
|
the data to sign. Thus, they can't be manipulated without invalidating the signature.
|
|
|
|
Client's using this code are all generating v3 or superior addresses. They could still have older UTXOs, but
|
|
|
|
they should be rare, only a handful of users ever used v1 and v2 addresses.
|
|
|
|
*/
|
|
|
|
|
|
|
|
// Verify change output is spendable by the wallet.
|
|
|
|
if expectedChange != nil {
|
|
|
|
if changeOutput == nil {
|
2021-01-29 18:51:08 -03:00
|
|
|
return errors.New("change is not present")
|
2020-11-09 10:05:29 -03:00
|
|
|
}
|
2019-10-01 12:22:30 -03:00
|
|
|
|
2020-11-09 10:05:29 -03:00
|
|
|
expectedChangeAmount := actualTotal - expectedAmount - expectedFee
|
|
|
|
if changeOutput.Value != expectedChangeAmount {
|
2021-01-29 18:51:08 -03:00
|
|
|
return fmt.Errorf("change amount is mismatched. found %v expected %v",
|
2020-11-09 10:05:29 -03:00
|
|
|
changeOutput.Value, expectedChangeAmount)
|
|
|
|
}
|
2019-10-01 12:22:30 -03:00
|
|
|
|
2020-11-09 10:05:29 -03:00
|
|
|
derivedUserKey, err := userPublicKey.DeriveTo(expectedChange.DerivationPath())
|
|
|
|
if err != nil {
|
2021-01-29 18:51:08 -03:00
|
|
|
return fmt.Errorf("failed to derive user key to change path %v: %w",
|
|
|
|
expectedChange.DerivationPath(), err)
|
2020-11-09 10:05:29 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
derivedMuunKey, err := muunPublickKey.DeriveTo(expectedChange.DerivationPath())
|
|
|
|
if err != nil {
|
2021-01-29 18:51:08 -03:00
|
|
|
return fmt.Errorf("failed to derive muun key to change path %v: %w",
|
|
|
|
expectedChange.DerivationPath(), err)
|
2020-11-09 10:05:29 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
expectedChangeAddress, err := addresses.Create(
|
|
|
|
expectedChange.Version(),
|
|
|
|
&derivedUserKey.key,
|
|
|
|
&derivedMuunKey.key,
|
|
|
|
expectedChange.DerivationPath(),
|
|
|
|
network.network,
|
|
|
|
)
|
|
|
|
if err != nil {
|
2021-01-29 18:51:08 -03:00
|
|
|
return fmt.Errorf("failed to build the change address with version %v: %w",
|
|
|
|
expectedChange.Version(), err)
|
2020-11-09 10:05:29 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
if expectedChangeAddress.Address() != expectedChange.Address() {
|
2021-01-29 18:51:08 -03:00
|
|
|
return fmt.Errorf("mismatched change address. found %v, expected %v",
|
2020-11-09 10:05:29 -03:00
|
|
|
expectedChange.Address(), expectedChangeAddress.Address())
|
|
|
|
}
|
|
|
|
|
|
|
|
actualFee := actualTotal - expectedAmount - expectedChangeAmount
|
|
|
|
if actualFee != expectedFee {
|
2021-01-29 18:51:08 -03:00
|
|
|
return fmt.Errorf("fee mismatched. found %v, expected %v", actualFee, expectedFee)
|
2020-11-09 10:05:29 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
actualFee := actualTotal - expectedAmount
|
|
|
|
if actualFee >= expectedFee+dustThreshold {
|
2021-01-29 18:51:08 -03:00
|
|
|
return errors.New("change output is too big to be burned as fee")
|
2020-11-09 10:05:29 -03:00
|
|
|
}
|
2019-10-01 12:22:30 -03:00
|
|
|
}
|
|
|
|
|
2020-11-09 10:05:29 -03:00
|
|
|
/*
|
|
|
|
NOT CHECKED: locktimes.
|
|
|
|
Using locktimes set in the future would invalidate the transaction, so the crafter could prevent us from spending
|
|
|
|
money. However, we would inflict the same denial on ourselves by rejecting it. Also, we'll eventually rely on
|
|
|
|
locktimes ourselves and would then need version checks to decide whether to send them to specific clients.
|
|
|
|
*/
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func addressToScript(address string, network *Network) ([]byte, error) {
|
2021-11-12 19:06:13 -03:00
|
|
|
parsedAddress, err := btcutilw.DecodeAddress(address, network.network)
|
2019-10-01 12:22:30 -03:00
|
|
|
if err != nil {
|
2021-01-29 18:51:08 -03:00
|
|
|
return nil, fmt.Errorf("failed to parse address %v: %w", address, err)
|
2019-10-01 12:22:30 -03:00
|
|
|
}
|
2021-11-12 19:06:13 -03:00
|
|
|
script, err := txscriptw.PayToAddrScript(parsedAddress)
|
2020-11-09 10:05:29 -03:00
|
|
|
if err != nil {
|
2021-01-29 18:51:08 -03:00
|
|
|
return nil, fmt.Errorf("failed to generate script for address %v: %w", address, err)
|
2020-11-09 10:05:29 -03:00
|
|
|
}
|
|
|
|
return script, nil
|
|
|
|
}
|
2019-10-01 12:22:30 -03:00
|
|
|
|
2020-11-09 10:05:29 -03:00
|
|
|
func newTransaction(tx *wire.MsgTx) (*Transaction, error) {
|
|
|
|
var buf bytes.Buffer
|
|
|
|
err := tx.Serialize(&buf)
|
|
|
|
if err != nil {
|
2021-01-29 18:51:08 -03:00
|
|
|
return nil, fmt.Errorf("failed to encode tx: %w", err)
|
2019-10-01 12:22:30 -03:00
|
|
|
}
|
|
|
|
|
2020-11-09 10:05:29 -03:00
|
|
|
return &Transaction{
|
|
|
|
Hash: tx.TxHash().String(),
|
|
|
|
Bytes: buf.Bytes(),
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type coin interface {
|
|
|
|
// TODO: these two methods can be collapsed into a single one once we move
|
|
|
|
// it to a submodule and use *hdkeychain.ExtendedKey's for the arguments.
|
|
|
|
SignInput(index int, tx *wire.MsgTx, userKey *HDPrivateKey, muunKey *HDPublicKey) error
|
|
|
|
FullySignInput(index int, tx *wire.MsgTx, userKey, muunKey *HDPrivateKey) error
|
|
|
|
}
|
|
|
|
|
2021-11-12 19:06:13 -03:00
|
|
|
func createCoin(index int, input Input, network *Network, sigHashes *txscriptw.TaprootSigHashes, nonces *MusigNonces) (coin, error) {
|
2020-11-09 10:05:29 -03:00
|
|
|
txID, err := chainhash.NewHash(input.OutPoint().TxId())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
outPoint := wire.OutPoint{
|
|
|
|
Hash: *txID,
|
|
|
|
Index: uint32(input.OutPoint().Index()),
|
|
|
|
}
|
|
|
|
keyPath := input.Address().DerivationPath()
|
|
|
|
amount := btcutil.Amount(input.OutPoint().Amount())
|
|
|
|
|
|
|
|
version := input.Address().Version()
|
|
|
|
|
|
|
|
switch version {
|
|
|
|
case addresses.V1:
|
|
|
|
return &coinV1{
|
|
|
|
Network: network.network,
|
|
|
|
OutPoint: outPoint,
|
|
|
|
KeyPath: keyPath,
|
|
|
|
}, nil
|
|
|
|
case addresses.V2:
|
|
|
|
return &coinV2{
|
|
|
|
Network: network.network,
|
|
|
|
OutPoint: outPoint,
|
|
|
|
KeyPath: keyPath,
|
|
|
|
MuunSignature: input.MuunSignature(),
|
|
|
|
}, nil
|
|
|
|
case addresses.V3:
|
|
|
|
return &coinV3{
|
|
|
|
Network: network.network,
|
|
|
|
OutPoint: outPoint,
|
|
|
|
KeyPath: keyPath,
|
|
|
|
Amount: amount,
|
|
|
|
MuunSignature: input.MuunSignature(),
|
|
|
|
}, nil
|
|
|
|
case addresses.V4:
|
|
|
|
return &coinV4{
|
|
|
|
Network: network.network,
|
|
|
|
OutPoint: outPoint,
|
|
|
|
KeyPath: keyPath,
|
|
|
|
Amount: amount,
|
|
|
|
MuunSignature: input.MuunSignature(),
|
|
|
|
}, nil
|
2021-11-12 19:06:13 -03:00
|
|
|
case addresses.V5:
|
|
|
|
var nonce [66]byte
|
|
|
|
copy(nonce[:], input.MuunPublicNonce())
|
|
|
|
var muunPartialSig [32]byte
|
|
|
|
copy(muunPartialSig[:], input.MuunSignature())
|
|
|
|
return &coinV5{
|
|
|
|
Network: network.network,
|
|
|
|
OutPoint: outPoint,
|
|
|
|
KeyPath: keyPath,
|
|
|
|
Amount: amount,
|
|
|
|
UserSessionId: nonces.sessionIds[index],
|
|
|
|
MuunPubNonce: nonce,
|
|
|
|
MuunPartialSig: muunPartialSig,
|
|
|
|
SigHashes: sigHashes,
|
|
|
|
}, nil
|
2020-11-09 10:05:29 -03:00
|
|
|
case addresses.SubmarineSwapV1:
|
|
|
|
swap := input.SubmarineSwapV1()
|
|
|
|
if swap == nil {
|
|
|
|
return nil, errors.New("submarine swap data is nil for swap input")
|
|
|
|
}
|
|
|
|
return &coinSubmarineSwapV1{
|
|
|
|
Network: network.network,
|
|
|
|
OutPoint: outPoint,
|
|
|
|
KeyPath: keyPath,
|
|
|
|
Amount: amount,
|
|
|
|
RefundAddress: swap.RefundAddress(),
|
|
|
|
PaymentHash256: swap.PaymentHash256(),
|
|
|
|
ServerPublicKey: swap.ServerPublicKey(),
|
|
|
|
LockTime: swap.LockTime(),
|
|
|
|
}, nil
|
|
|
|
case addresses.SubmarineSwapV2:
|
|
|
|
swap := input.SubmarineSwapV2()
|
|
|
|
if swap == nil {
|
|
|
|
return nil, errors.New("submarine swap data is nil for swap input")
|
|
|
|
}
|
|
|
|
return &coinSubmarineSwapV2{
|
|
|
|
Network: network.network,
|
|
|
|
OutPoint: outPoint,
|
|
|
|
KeyPath: keyPath,
|
|
|
|
Amount: amount,
|
|
|
|
PaymentHash256: swap.PaymentHash256(),
|
|
|
|
UserPublicKey: swap.UserPublicKey(),
|
|
|
|
MuunPublicKey: swap.MuunPublicKey(),
|
|
|
|
ServerPublicKey: swap.ServerPublicKey(),
|
|
|
|
BlocksForExpiration: swap.BlocksForExpiration(),
|
|
|
|
ServerSignature: swap.ServerSignature(),
|
|
|
|
}, nil
|
|
|
|
case addresses.IncomingSwap:
|
|
|
|
swap := input.IncomingSwap()
|
|
|
|
if swap == nil {
|
|
|
|
return nil, errors.New("incoming swap data is nil for incoming swap input")
|
|
|
|
}
|
|
|
|
swapServerPublicKey, err := hex.DecodeString(swap.SwapServerPublicKey())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &coinIncomingSwap{
|
|
|
|
Network: network.network,
|
|
|
|
MuunSignature: input.MuunSignature(),
|
|
|
|
Sphinx: swap.Sphinx(),
|
|
|
|
HtlcTx: swap.HtlcTx(),
|
|
|
|
PaymentHash256: swap.PaymentHash256(),
|
|
|
|
SwapServerPublicKey: swapServerPublicKey,
|
|
|
|
ExpirationHeight: swap.ExpirationHeight(),
|
2021-01-29 18:51:08 -03:00
|
|
|
Collect: btcutil.Amount(swap.CollectInSats()),
|
2020-11-09 10:05:29 -03:00
|
|
|
}, nil
|
|
|
|
default:
|
2021-01-29 18:51:08 -03:00
|
|
|
return nil, fmt.Errorf("can't create coin from input version %v", version)
|
2020-11-09 10:05:29 -03:00
|
|
|
}
|
2019-12-16 17:59:11 -03:00
|
|
|
}
|