Skip to content

Commit e5162c8

Browse files
committed
Merge branch 'psbt'
2 parents bda09e7 + 2ea4ded commit e5162c8

File tree

19 files changed

+297
-425
lines changed

19 files changed

+297
-425
lines changed

.golangci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ linters:
120120
- predeclared
121121
- tenv
122122
- recvcheck
123+
- godot
123124
disable-all: false
124125

125126
issues:

backend/accounts.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1063,14 +1063,14 @@ func (backend *Backend) createAndAddAccount(coin coinpkg.Coin, persistedConfig *
10631063

10641064
// This function is passed as a callback to the BTC account constructor. It is called when the
10651065
// keystore needs to determine whether an address belongs to an account on its same keystore.
1066-
getAddressCallback := func(askingAccount *btc.Account, scriptHashHex blockchain.ScriptHashHex) (*addresses.AccountAddress, bool, error) {
1066+
getAddressCallback := func(coinCode coinpkg.Code, scriptHashHex blockchain.ScriptHashHex) (*addresses.AccountAddress, error) {
10671067
accountsByKeystore, err := backend.AccountsByKeystore()
10681068
if err != nil {
1069-
return nil, false, err
1069+
return nil, err
10701070
}
10711071
rootFingerprint, err := backend.keystore.RootFingerprint()
10721072
if err != nil {
1073-
return nil, false, err
1073+
return nil, err
10741074
}
10751075
for _, account := range accountsByKeystore[hex.EncodeToString(rootFingerprint)] {
10761076
// This only makes sense for BTC accounts.
@@ -1079,14 +1079,14 @@ func (backend *Backend) createAndAddAccount(coin coinpkg.Coin, persistedConfig *
10791079
continue
10801080
}
10811081
// Only return an address if the coin codes match.
1082-
if btcAccount.Coin().Code() != askingAccount.Coin().Code() {
1082+
if btcAccount.Coin().Code() != coinCode {
10831083
continue
10841084
}
10851085
if address := btcAccount.GetAddress(scriptHashHex); address != nil {
1086-
return address, askingAccount == btcAccount, nil
1086+
return address, nil
10871087
}
10881088
}
1089-
return nil, false, nil
1089+
return nil, nil
10901090
}
10911091

10921092
switch specificCoin := coin.(type) {

backend/accounts_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1491,7 +1491,7 @@ func TestKeystoresBalance(t *testing.T) {
14911491
b := newBackend(t, testnetDisabled, regtestDisabled)
14921492
defer b.Close()
14931493

1494-
b.makeBtcAccount = func(config *accounts.AccountConfig, coin *btc.Coin, gapLimits *types.GapLimits, getAddress func(*btc.Account, blockchain.ScriptHashHex) (*addresses.AccountAddress, bool, error), log *logrus.Entry) accounts.Interface {
1494+
b.makeBtcAccount = func(config *accounts.AccountConfig, coin *btc.Coin, gapLimits *types.GapLimits, getAddress func(coinpkg.Code, blockchain.ScriptHashHex) (*addresses.AccountAddress, error), log *logrus.Entry) accounts.Interface {
14951495
accountMock := MockBtcAccount(t, config, coin, gapLimits, log)
14961496
accountMock.BalanceFunc = func() (*accounts.Balance, error) {
14971497
return accounts.NewBalance(coinpkg.NewAmountFromInt64(1e8), coinpkg.NewAmountFromInt64(0)), nil
@@ -1561,7 +1561,7 @@ func TestCoinsTotalBalance(t *testing.T) {
15611561
b := newBackend(t, testnetDisabled, regtestDisabled)
15621562
defer b.Close()
15631563

1564-
b.makeBtcAccount = func(config *accounts.AccountConfig, coin *btc.Coin, gapLimits *types.GapLimits, getAddress func(*btc.Account, blockchain.ScriptHashHex) (*addresses.AccountAddress, bool, error), log *logrus.Entry) accounts.Interface {
1564+
b.makeBtcAccount = func(config *accounts.AccountConfig, coin *btc.Coin, gapLimits *types.GapLimits, getAddress func(coinpkg.Code, blockchain.ScriptHashHex) (*addresses.AccountAddress, error), log *logrus.Entry) accounts.Interface {
15651565
accountMock := MockBtcAccount(t, config, coin, gapLimits, log)
15661566
accountMock.BalanceFunc = func() (*accounts.Balance, error) {
15671567
return accounts.NewBalance(coinpkg.NewAmountFromInt64(1e8), coinpkg.NewAmountFromInt64(0)), nil
@@ -1622,7 +1622,7 @@ func TestAccountsFiatAndCoinBalance(t *testing.T) {
16221622
b := newBackend(t, testnetDisabled, regtestDisabled)
16231623
defer b.Close()
16241624

1625-
b.makeBtcAccount = func(config *accounts.AccountConfig, coin *btc.Coin, gapLimits *types.GapLimits, getAddress func(*btc.Account, blockchain.ScriptHashHex) (*addresses.AccountAddress, bool, error), log *logrus.Entry) accounts.Interface {
1625+
b.makeBtcAccount = func(config *accounts.AccountConfig, coin *btc.Coin, gapLimits *types.GapLimits, getAddress func(coinpkg.Code, blockchain.ScriptHashHex) (*addresses.AccountAddress, error), log *logrus.Entry) accounts.Interface {
16261626
accountMock := MockBtcAccount(t, config, coin, gapLimits, log)
16271627
accountMock.BalanceFunc = func() (*accounts.Balance, error) {
16281628
return accounts.NewBalance(coinpkg.NewAmountFromInt64(1e8), coinpkg.NewAmountFromInt64(0)), nil

backend/backend.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ type Backend struct {
229229

230230
// makeBtcAccount creates a BTC account. In production this is `btc.NewAccount`, but can be
231231
// overridden in unit tests for mocking.
232-
makeBtcAccount func(*accounts.AccountConfig, *btc.Coin, *types.GapLimits, func(*btc.Account, blockchain.ScriptHashHex) (*addresses.AccountAddress, bool, error), *logrus.Entry) accounts.Interface
232+
makeBtcAccount func(*accounts.AccountConfig, *btc.Coin, *types.GapLimits, func(coinpkg.Code, blockchain.ScriptHashHex) (*addresses.AccountAddress, error), *logrus.Entry) accounts.Interface
233233
// makeEthAccount creates an ETH account. In production this is `eth.NewAccount`, but can be
234234
// overridden in unit tests for mocking.
235235
makeEthAccount func(*accounts.AccountConfig, *eth.Coin, *http.Client, *logrus.Entry) accounts.Interface
@@ -298,7 +298,7 @@ func NewBackend(arguments *arguments.Arguments, environment Environment) (*Backe
298298
coins: map[coinpkg.Code]coinpkg.Coin{},
299299
accounts: []accounts.Interface{},
300300
aopp: AOPP{State: aoppStateInactive},
301-
makeBtcAccount: func(config *accounts.AccountConfig, coin *btc.Coin, gapLimits *types.GapLimits, getAddress func(*btc.Account, blockchain.ScriptHashHex) (*addresses.AccountAddress, bool, error), log *logrus.Entry) accounts.Interface {
301+
makeBtcAccount: func(config *accounts.AccountConfig, coin *btc.Coin, gapLimits *types.GapLimits, getAddress func(coinpkg.Code, blockchain.ScriptHashHex) (*addresses.AccountAddress, error), log *logrus.Entry) accounts.Interface {
302302
return btc.NewAccount(config, coin, gapLimits, getAddress, log, hclient)
303303
},
304304
makeEthAccount: func(config *accounts.AccountConfig, coin *eth.Coin, httpClient *http.Client, log *logrus.Entry) accounts.Interface {

backend/backend_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ func newBackend(t *testing.T, testing, regtest bool) *Backend {
281281
}
282282
b.ratesUpdater.SetCoingeckoURL("unused") // avoid hitting real API
283283

284-
b.makeBtcAccount = func(config *accounts.AccountConfig, coin *btc.Coin, gapLimits *types.GapLimits, getAddress func(*btc.Account, blockchain.ScriptHashHex) (*addresses.AccountAddress, bool, error), log *logrus.Entry) accounts.Interface {
284+
b.makeBtcAccount = func(config *accounts.AccountConfig, coin *btc.Coin, gapLimits *types.GapLimits, getAddress func(coinpkg.Code, blockchain.ScriptHashHex) (*addresses.AccountAddress, error), log *logrus.Entry) accounts.Interface {
285285
return MockBtcAccount(t, config, coin, gapLimits, log)
286286
}
287287
b.makeEthAccount = func(config *accounts.AccountConfig, coin *eth.Coin, httpClient *http.Client, log *logrus.Entry) accounts.Interface {

backend/coins/btc/account.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ type Account struct {
122122
httpClient *http.Client
123123

124124
// getAddressFromSameKeystore is a function that retrieves an address from any account on the same keystore as this one.
125-
getAddressFromSameKeystore func(*Account, blockchain.ScriptHashHex) (*addresses.AccountAddress, bool, error)
125+
getAddressFromSameKeystore func(coin.Code, blockchain.ScriptHashHex) (*addresses.AccountAddress, error)
126126
}
127127

128128
// NewAccount creates a new account.
@@ -133,7 +133,7 @@ func NewAccount(
133133
config *accounts.AccountConfig,
134134
coin *Coin,
135135
forceGapLimits *types.GapLimits,
136-
getAddressFromSameKeystore func(*Account, blockchain.ScriptHashHex) (*addresses.AccountAddress, bool, error),
136+
getAddressFromSameKeystore func(coin.Code, blockchain.ScriptHashHex) (*addresses.AccountAddress, error),
137137
log *logrus.Entry,
138138
httpClient *http.Client,
139139
) *Account {

backend/coins/btc/addresses/address.go

Lines changed: 8 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,11 @@ import (
2121
"github.com/BitBoxSwiss/bitbox-wallet-app/backend/coins/btc/types"
2222
ourbtcutil "github.com/BitBoxSwiss/bitbox-wallet-app/backend/coins/btc/util"
2323
"github.com/BitBoxSwiss/bitbox-wallet-app/backend/signing"
24-
"github.com/BitBoxSwiss/bitbox-wallet-app/util/errp"
2524
"github.com/btcsuite/btcd/btcec/v2"
2625
"github.com/btcsuite/btcd/btcec/v2/schnorr"
2726
"github.com/btcsuite/btcd/btcutil"
2827
"github.com/btcsuite/btcd/chaincfg"
2928
"github.com/btcsuite/btcd/txscript"
30-
"github.com/btcsuite/btcd/wire"
3129
"github.com/sirupsen/logrus"
3230
)
3331

@@ -38,12 +36,13 @@ type AccountAddress struct {
3836

3937
// AccountConfiguration is the account level configuration from which this address was derived.
4038
AccountConfiguration *signing.Configuration
41-
// publicKey is the public key of a single-sig address.
42-
publicKey *btcec.PublicKey
39+
// PublicKey is the public key of a single-sig address.
40+
PublicKey *btcec.PublicKey
4341
Derivation types.Derivation
4442

45-
// redeemScript stores the redeem script of a BIP16 P2SH output or nil if address type is P2PKH.
46-
redeemScript []byte
43+
// redeemScript stores the redeem script of a BIP16 P2SH output or nil if address type is not
44+
// P2SH.
45+
RedeemScript []byte
4746

4847
log *logrus.Entry
4948
}
@@ -116,9 +115,9 @@ func NewAccountAddress(
116115
return &AccountAddress{
117116
Address: address,
118117
AccountConfiguration: accountConfiguration,
119-
publicKey: publicKey,
118+
PublicKey: publicKey,
120119
Derivation: derivation,
121-
redeemScript: redeemScript,
120+
RedeemScript: redeemScript,
122121
log: log,
123122
}
124123
}
@@ -128,23 +127,6 @@ func (address *AccountAddress) ID() string {
128127
return string(address.PubkeyScriptHashHex())
129128
}
130129

131-
// BIP352Pubkey returns the pubkey used for silent payments:
132-
// - 33 byte compressed public key for p2pkh, p2wpkh, p2wpkh-p2sh.
133-
// - 32 byte x-only public key for p2tr
134-
// See https://github.com/bitcoin/bips/blob/master/bip-0352.mediawiki#user-content-Inputs_For_Shared_Secret_Derivation.
135-
func (address *AccountAddress) BIP352Pubkey() ([]byte, error) {
136-
publicKey := address.publicKey
137-
switch address.AccountConfiguration.ScriptType() {
138-
case signing.ScriptTypeP2PKH, signing.ScriptTypeP2WPKHP2SH, signing.ScriptTypeP2WPKH:
139-
return publicKey.SerializeCompressed(), nil
140-
case signing.ScriptTypeP2TR:
141-
outputKey := txscript.ComputeTaprootKeyNoScript(publicKey)
142-
return schnorr.SerializePubKey(outputKey), nil
143-
default:
144-
return nil, errp.New("unsupported script type for silent payments")
145-
}
146-
}
147-
148130
// EncodeForHumans implements accounts.Address.
149131
func (address *AccountAddress) EncodeForHumans() string {
150132
return address.EncodeAddress()
@@ -180,59 +162,11 @@ func (address *AccountAddress) ScriptForHashToSign() (bool, []byte) {
180162
case signing.ScriptTypeP2PKH:
181163
return false, address.PubkeyScript()
182164
case signing.ScriptTypeP2WPKHP2SH:
183-
return true, address.redeemScript
165+
return true, address.RedeemScript
184166
case signing.ScriptTypeP2WPKH:
185167
return true, address.PubkeyScript()
186168
default:
187169
address.log.Panic("Unrecognized address type.")
188170
}
189171
panic("The end of the function cannot be reached.")
190172
}
191-
192-
// SignatureScript returns the signature script (and witness) needed to spend from this address.
193-
// The signatures have to be provided in the order of the configuration (and some can be nil).
194-
func (address *AccountAddress) SignatureScript(
195-
signature types.Signature,
196-
) ([]byte, wire.TxWitness) {
197-
publicKey := address.publicKey
198-
switch address.AccountConfiguration.ScriptType() {
199-
case signing.ScriptTypeP2PKH:
200-
signatureScript, err := txscript.NewScriptBuilder().
201-
AddData(append(signature.SerializeDER(), byte(txscript.SigHashAll))).
202-
AddData(publicKey.SerializeCompressed()).
203-
Script()
204-
if err != nil {
205-
address.log.WithError(err).Panic("Failed to build signature script for P2PKH.")
206-
}
207-
return signatureScript, nil
208-
case signing.ScriptTypeP2WPKHP2SH:
209-
signatureScript, err := txscript.NewScriptBuilder().
210-
AddData(address.redeemScript).
211-
Script()
212-
if err != nil {
213-
address.log.WithError(err).Panic("Failed to build segwit signature script.")
214-
}
215-
txWitness := wire.TxWitness{
216-
append(signature.SerializeDER(), byte(txscript.SigHashAll)),
217-
publicKey.SerializeCompressed(),
218-
}
219-
return signatureScript, txWitness
220-
case signing.ScriptTypeP2WPKH:
221-
txWitness := wire.TxWitness{
222-
append(signature.SerializeDER(), byte(txscript.SigHashAll)),
223-
publicKey.SerializeCompressed(),
224-
}
225-
return []byte{}, txWitness
226-
case signing.ScriptTypeP2TR:
227-
// We assume SIGHASH_DEFAULT, which defaults to SIGHASH_ALL without needing to explicitly
228-
// append it to the signature. See:
229-
// https://github.com/bitcoin/bips/blob/97e02b2223b21753acefa813a4e59dbb6e849e77/bip-0341.mediawiki#taproot-key-path-spending-signature-validation
230-
txWitness := wire.TxWitness{
231-
signature.SerializeCompact(),
232-
}
233-
return []byte{}, txWitness
234-
default:
235-
address.log.Panic("Unrecognized address type.")
236-
}
237-
panic("The end of the function cannot be reached.")
238-
}

backend/coins/btc/addresses/address_export_test.go

Lines changed: 0 additions & 22 deletions
This file was deleted.

backend/coins/btc/addresses/addresschain_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ func (s *addressChainTestSuite) TestEnsureAddresses() {
143143
s.Require().Len(newAddresses, s.gapLimit)
144144
for index, address := range newAddresses {
145145
s.Require().Equal(uint32(index), address.AbsoluteKeypath().ToUInt32()[1])
146-
s.Require().Equal(getPubKey(index), address.TstPublicKey())
146+
s.Require().Equal(getPubKey(index), address.PublicKey)
147147
}
148148
// Address statuses are still the same, so calling it again won't produce more addresses.
149149
addrs, err := s.addresses.EnsureAddresses()

backend/coins/btc/maketx/maketx.go

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"github.com/BitBoxSwiss/bitbox-wallet-app/backend/signing"
2929
"github.com/BitBoxSwiss/bitbox-wallet-app/util/errp"
3030
"github.com/btcsuite/btcd/btcutil"
31+
"github.com/btcsuite/btcd/btcutil/psbt"
3132
"github.com/btcsuite/btcd/chaincfg/chainhash"
3233
"github.com/btcsuite/btcd/txscript"
3334
"github.com/btcsuite/btcd/wire"
@@ -49,8 +50,7 @@ type TxProposal struct {
4950
// Amount is the amount that is sent out. The fee is not included and is deducted on top.
5051
Amount btcutil.Amount
5152
// Fee is the mining fee used.
52-
Fee btcutil.Amount
53-
Transaction *wire.MsgTx
53+
Fee btcutil.Amount
5454
// ChangeAddress is the address of the wallet to which the change of the transaction is sent.
5555
ChangeAddress *addresses.AccountAddress
5656
PreviousOutputs PreviousOutputs
@@ -60,11 +60,12 @@ type TxProposal struct {
6060
SilentPaymentAddress string
6161
// OutIndex is the index of the output we send to.
6262
OutIndex int
63+
Psbt *psbt.Packet
6364
}
6465

6566
// SigHashes computes the hashes cache to speed up per-input sighash computations.
6667
func (txProposal *TxProposal) SigHashes() *txscript.TxSigHashes {
67-
return txscript.NewTxSigHashes(txProposal.Transaction, txProposal.PreviousOutputs)
68+
return txscript.NewTxSigHashes(txProposal.Psbt.UnsignedTx, txProposal.PreviousOutputs)
6869
}
6970

7071
// Total is amount+fee.
@@ -209,15 +210,19 @@ func NewTxSpendAll(
209210
log.WithField("fee", maxRequiredFee).Debug("Preparing transaction to spend all outputs")
210211

211212
setRBF(coin, unsignedTransaction)
213+
psbt, err := psbt.NewFromUnsignedTx(unsignedTransaction)
214+
if err != nil {
215+
return nil, err
216+
}
212217
return &TxProposal{
213218
Coin: coin,
214219
Amount: btcutil.Amount(output.Value),
215220
Fee: maxRequiredFee,
216-
Transaction: unsignedTransaction,
217221
PreviousOutputs: spendableOutputs,
218222
SilentPaymentAddress: outputInfo.silentPaymentAddress,
219223
// Only one output in send-all
220224
OutIndex: 0,
225+
Psbt: psbt,
221226
}, nil
222227
}
223228

@@ -307,15 +312,20 @@ func NewTx(
307312
}
308313

309314
setRBF(coin, unsignedTransaction)
315+
psbt, err := psbt.NewFromUnsignedTx(unsignedTransaction)
316+
if err != nil {
317+
return nil, err
318+
}
319+
310320
return &TxProposal{
311321
Coin: coin,
312322
Amount: targetAmount,
313323
Fee: finalFee,
314-
Transaction: unsignedTransaction,
315324
ChangeAddress: changeAddress,
316325
PreviousOutputs: previousOutputs,
317326
SilentPaymentAddress: outputInfo.silentPaymentAddress,
318327
OutIndex: outIndex,
328+
Psbt: psbt,
319329
}, nil
320330
}
321331
}

0 commit comments

Comments
 (0)