Skip to content

Commit aaf0e94

Browse files
committed
Merge branch 'needs-prevtxs'
2 parents b6cefdf + b630754 commit aaf0e94

File tree

2 files changed

+165
-2
lines changed

2 files changed

+165
-2
lines changed

api/firmware/psbt.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,3 +655,23 @@ func (device *Device) BTCSignPSBT(
655655

656656
return nil
657657
}
658+
659+
// BTCSignNeedsNonWitnessUTXOs returns true if the BitBox requires the NON_WITNESS_UTXO fields of
660+
// each PSBT input to be present. They are the previous transactions of the inputs, and the BitBox
661+
// needs them to validate the input amount, unless all inputs are Taproot. This helper function
662+
// exists because different BitBox firmware versions have slightly different requirements about when
663+
// the prevtxs are needed.
664+
//
665+
// This is meant to be called by a PSBT updater to decide whether to retrieve and add the previous
666+
// transactions. Call this before `BTCSignPSBT()` with the same arguments.
667+
func (device *Device) BTCSignNeedsNonWitnessUTXOs(psbt_ *psbt.Packet, options *PSBTSignOptions) (bool, error) {
668+
ourRootFingerprint, err := device.RootFingerprint()
669+
if err != nil {
670+
return false, err
671+
}
672+
result, err := newBTCTxFromPSBT(device.version, psbt_, ourRootFingerprint, options)
673+
if err != nil {
674+
return false, err
675+
}
676+
return BTCSignNeedsPrevTxs(result.scriptConfigs), nil
677+
}

api/firmware/psbt_test.go

Lines changed: 145 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,131 @@ func TestSimulatorBTCPSBTTaprootKeySpend(t *testing.T) {
335335
Bip32Path: changePath,
336336
}}
337337

338+
needsPrevTxs, err := device.BTCSignNeedsNonWitnessUTXOs(psbt_, nil)
339+
require.NoError(t, err)
340+
require.False(t, needsPrevTxs)
341+
342+
// Sign & validate.
343+
require.NoError(t, device.BTCSignPSBT(messages.BTCCoin_TBTC, psbt_, nil))
344+
require.NoError(t, psbt.MaybeFinalizeAll(psbt_))
345+
require.NoError(t, txValidityCheck(psbt_))
346+
})
347+
}
348+
349+
// All inputs are Taproot, but the change output is not Taproot. Some BitBox firmwares erroneously
350+
// also request the previous transactions in this case.
351+
func TestSimulatorBTCPSBTTaprootSpendsToNonTaproot(t *testing.T) {
352+
testInitializedSimulators(t, func(t *testing.T, device *Device, stdOut *bytes.Buffer) {
353+
t.Helper()
354+
355+
fingerprint, err := device.RootFingerprint()
356+
require.NoError(t, err)
357+
358+
changePath := []uint32{84 + HARDENED, 1 + HARDENED, 0 + HARDENED, 1, 0}
359+
changePubKey := simulatorPub(t, device, changePath...)
360+
changePkScript := p2wpkhPkScript(changePubKey)
361+
362+
input0Path := []uint32{86 + HARDENED, 1 + HARDENED, 0 + HARDENED, 0, 0}
363+
input0Pubkey := simulatorPub(t, device, input0Path...)
364+
_, input0PkScript := makeTaprootOutput(t, input0Pubkey)
365+
366+
input1Path := []uint32{86 + HARDENED, 1 + HARDENED, 0 + HARDENED, 0, 1}
367+
input1PubKey := simulatorPub(t, device, input1Path...)
368+
_, input1PkScript := makeTaprootOutput(t, input1PubKey)
369+
370+
// A previous tx which creates some UTXOs we can reference later.
371+
prevTx := &wire.MsgTx{
372+
Version: 2,
373+
LockTime: 0,
374+
TxIn: []*wire.TxIn{
375+
{
376+
PreviousOutPoint: *mustOutpoint("3131313131313131313131313131313131313131313131313131313131313131:0"),
377+
SignatureScript: nil,
378+
Sequence: 0xFFFFFFFF,
379+
Witness: nil,
380+
},
381+
},
382+
TxOut: []*wire.TxOut{
383+
{
384+
Value: 100_000_000,
385+
PkScript: input0PkScript,
386+
},
387+
{
388+
Value: 100_000_000,
389+
PkScript: input1PkScript,
390+
},
391+
},
392+
}
393+
394+
tx := &wire.MsgTx{
395+
Version: 2,
396+
LockTime: 0,
397+
TxIn: []*wire.TxIn{
398+
{
399+
PreviousOutPoint: wire.OutPoint{
400+
Hash: prevTx.TxHash(),
401+
Index: 0,
402+
},
403+
SignatureScript: nil,
404+
Sequence: 0xFFFFFFFF,
405+
Witness: nil,
406+
},
407+
{
408+
PreviousOutPoint: wire.OutPoint{
409+
Hash: prevTx.TxHash(),
410+
Index: 1,
411+
},
412+
SignatureScript: nil,
413+
Sequence: 0xFFFFFFFF,
414+
Witness: nil,
415+
},
416+
},
417+
TxOut: []*wire.TxOut{
418+
{
419+
Value: 100_000_000,
420+
PkScript: changePkScript,
421+
},
422+
{
423+
Value: 20_000_000,
424+
// random private key:
425+
// 9dbb534622a6100a39b73dece43c6d4db14b9a612eb46a6c64c2bb849e283ce8
426+
PkScript: p2trPkScript(unhex("e4adbb12c3426ec71ebb10688d8ae69d531ca822a2b790acee216a7f1b95b576")),
427+
},
428+
},
429+
}
430+
psbt_, err := psbt.NewFromUnsignedTx(tx)
431+
require.NoError(t, err)
432+
433+
// Add input and change infos.
434+
psbt_.Inputs[0].WitnessUtxo = prevTx.TxOut[0]
435+
psbt_.Inputs[0].TaprootInternalKey = schnorr.SerializePubKey(input0Pubkey)
436+
psbt_.Inputs[0].TaprootBip32Derivation = []*psbt.TaprootBip32Derivation{{
437+
XOnlyPubKey: psbt_.Inputs[0].TaprootInternalKey,
438+
MasterKeyFingerprint: binary.LittleEndian.Uint32(fingerprint),
439+
Bip32Path: input0Path,
440+
}}
441+
442+
psbt_.Inputs[1].WitnessUtxo = prevTx.TxOut[1]
443+
psbt_.Inputs[1].TaprootInternalKey = schnorr.SerializePubKey(input1PubKey)
444+
psbt_.Inputs[1].TaprootBip32Derivation = []*psbt.TaprootBip32Derivation{{
445+
XOnlyPubKey: psbt_.Inputs[1].TaprootInternalKey,
446+
MasterKeyFingerprint: binary.LittleEndian.Uint32(fingerprint),
447+
Bip32Path: input1Path,
448+
}}
449+
450+
psbt_.Outputs[0].TaprootInternalKey = schnorr.SerializePubKey(changePubKey)
451+
psbt_.Outputs[0].TaprootBip32Derivation = []*psbt.TaprootBip32Derivation{{
452+
XOnlyPubKey: psbt_.Outputs[0].TaprootInternalKey,
453+
MasterKeyFingerprint: binary.LittleEndian.Uint32(fingerprint),
454+
Bip32Path: changePath,
455+
}}
456+
457+
needsPrevTxs, err := device.BTCSignNeedsNonWitnessUTXOs(psbt_, nil)
458+
require.NoError(t, err)
459+
require.True(t, needsPrevTxs)
460+
psbt_.Inputs[0].NonWitnessUtxo = prevTx
461+
psbt_.Inputs[1].NonWitnessUtxo = prevTx
462+
338463
// Sign & validate.
339464
require.NoError(t, device.BTCSignPSBT(messages.BTCCoin_TBTC, psbt_, nil))
340465
require.NoError(t, psbt.MaybeFinalizeAll(psbt_))
@@ -457,6 +582,10 @@ func TestSimulatorBTCPSBTMixedSpend(t *testing.T) {
457582
Bip32Path: changePath,
458583
}}
459584

585+
needsPrevTxs, err := device.BTCSignNeedsNonWitnessUTXOs(psbt_, nil)
586+
require.NoError(t, err)
587+
require.True(t, needsPrevTxs)
588+
460589
// Sign & validate
461590
require.NoError(t, device.BTCSignPSBT(messages.BTCCoin_TBTC, psbt_, nil))
462591
require.NoError(t, psbt.MaybeFinalizeAll(psbt_))
@@ -578,14 +707,19 @@ func TestSimulatorBTCPSBTSilentPayment(t *testing.T) {
578707
Bip32Path: changePath,
579708
}}
580709

581-
// Sign & validate
582710
signOptions := &PSBTSignOptions{
583711
Outputs: map[int]*PSBTSignOutputOptions{
584712
1: {
585713
SilentPaymentAddress: "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv",
586714
},
587715
},
588716
}
717+
718+
needsPrevTxs, err := device.BTCSignNeedsNonWitnessUTXOs(psbt_, signOptions)
719+
require.NoError(t, err)
720+
require.True(t, needsPrevTxs)
721+
722+
// Sign & validate
589723
err = device.BTCSignPSBT(messages.BTCCoin_BTC, psbt_, signOptions)
590724
if !device.version.AtLeast(semver.NewSemVer(9, 21, 0)) {
591725
require.EqualError(t, err, UnsupportedError("9.21.0").Error())
@@ -709,6 +843,10 @@ func TestSimulatorBTCPSBTSendSelfSameAccount(t *testing.T) {
709843
Bip32Path: sendSelfPath,
710844
}}
711845

846+
needsPrevTxs, err := device.BTCSignNeedsNonWitnessUTXOs(psbt_, nil)
847+
require.NoError(t, err)
848+
require.True(t, needsPrevTxs)
849+
712850
// Sign & validate
713851
require.NoError(t, device.BTCSignPSBT(messages.BTCCoin_TBTC, psbt_, nil))
714852
require.NoError(t, psbt.MaybeFinalizeAll(psbt_))
@@ -844,7 +982,6 @@ func TestSimulatorBTCPSBTPaymentRequest(t *testing.T) {
844982
}}
845983
psbt_.Outputs[0].RedeemScript = changeRedeemScript
846984

847-
// Sign & validate
848985
paymentRequestIndex := uint32(0)
849986
signOptions := &PSBTSignOptions{
850987
PaymentRequests: []*messages.BTCPaymentRequestRequest{paymentRequest},
@@ -854,6 +991,12 @@ func TestSimulatorBTCPSBTPaymentRequest(t *testing.T) {
854991
},
855992
},
856993
}
994+
995+
needsPrevTxs, err := device.BTCSignNeedsNonWitnessUTXOs(psbt_, signOptions)
996+
require.NoError(t, err)
997+
require.False(t, needsPrevTxs)
998+
999+
// Sign & validate
8571000
require.NoError(t, device.BTCSignPSBT(messages.BTCCoin_TBTC, psbt_, signOptions))
8581001
require.NoError(t, psbt.MaybeFinalizeAll(psbt_))
8591002
require.NoError(t, txValidityCheck(psbt_))

0 commit comments

Comments
 (0)