package libwallet import ( "bytes" "encoding/hex" "github.com/btcsuite/btcd/wire" "github.com/pkg/errors" ) type MuunAddress interface { Version() int DerivationPath() string Address() string } // TODO: Change name type RedeemableAddress interface { RedeemScript() []byte } type Outpoint interface { TxId() []byte Index() int Amount() int64 } type InputSubmarineSwap interface { RefundAddress() string PaymentHash256() []byte ServerPublicKey() []byte LockTime() int64 } type Input interface { OutPoint() Outpoint Address() MuunAddress UserSignature() []byte MuunSignature() []byte SubmarineSwap() InputSubmarineSwap } type PartiallySignedTransaction struct { tx *wire.MsgTx inputs []Input } type Transaction struct { Hash string Bytes []byte } func NewPartiallySignedTransaction(hexTx string) (*PartiallySignedTransaction, error) { rawTx, err := hex.DecodeString(hexTx) if err != nil { return nil, errors.Wrapf(err, "failed to decode hex tx") } tx := wire.NewMsgTx(0) err = tx.BtcDecode(bytes.NewBuffer(rawTx), 0, wire.WitnessEncoding) if err != nil { return nil, errors.Wrapf(err, "failed to decode tx") } return &PartiallySignedTransaction{tx: tx, inputs: []Input{}}, nil } func (p *PartiallySignedTransaction) AddInput(input Input) { p.inputs = append(p.inputs, input) } func (p *PartiallySignedTransaction) Sign(key *HDPrivateKey, muunKey *HDPublicKey) (*Transaction, error) { for i, input := range p.inputs { derivedKey, err := key.DeriveTo(input.Address().DerivationPath()) if err != nil { return nil, errors.Wrapf(err, "failed to derive user key") } derivedMuunKey, err := muunKey.DeriveTo(input.Address().DerivationPath()) if err != nil { return nil, errors.Wrapf(err, "failed to derive muun key") } var txIn *wire.TxIn switch AddressVersion(input.Address().Version()) { case addressV1: txIn, err = addUserSignatureInputV1(input, i, p.tx, derivedKey) case addressV2: txIn, err = addUserSignatureInputV2(input, i, p.tx, derivedKey, derivedMuunKey) case addressV3: txIn, err = addUserSignatureInputV3(input, i, p.tx, derivedKey, derivedMuunKey) case addressSubmarineSwap: txIn, err = addUserSignatureInputSubmarineSwap(input, i, p.tx, derivedKey, derivedMuunKey) default: return nil, errors.Errorf("cant sign transaction of version %v", input.Address().Version()) } if err != nil { return nil, errors.Wrapf(err, "failed to sign input using version %v", input.Address().Version()) } p.tx.TxIn[i] = txIn } var writer bytes.Buffer err := p.tx.BtcEncode(&writer, 0, wire.WitnessEncoding) if err != nil { return nil, errors.Wrapf(err, "failed to encode tx") } return &Transaction{ Hash: p.tx.TxHash().String(), Bytes: writer.Bytes(), }, nil } func (p *PartiallySignedTransaction) MuunSignatureForInput(index int, userKey *HDPublicKey, muunKey *HDPrivateKey) ([]byte, error) { input := p.inputs[index] derivedUserKey, err := userKey.DeriveTo(input.Address().DerivationPath()) if err != nil { return nil, errors.Wrapf(err, "failed to derive user key") } derivedMuunKey, err := muunKey.DeriveTo(input.Address().DerivationPath()) if err != nil { return nil, errors.Wrapf(err, "failed to derive muun key") } switch AddressVersion(input.Address().Version()) { case addressV1: return []byte{}, nil case addressV2: return signInputV2(input, index, p.tx, derivedUserKey, derivedMuunKey.PublicKey(), derivedMuunKey) case addressV3: return signInputV3(input, index, p.tx, derivedUserKey, derivedMuunKey.PublicKey(), derivedMuunKey) case addressSubmarineSwap: return nil, errors.New("cant sign arbitrary submarine swap inputs") } return nil, errors.New("unknown address scheme") }