Skip to content

Commit e3319eb

Browse files
committed
Merge commit 'refs/pull/2532/head' of https://github.com/digitalbitbox/bitbox-wallet-app
2 parents 8be389c + b325da8 commit e3319eb

File tree

4 files changed

+97
-7
lines changed

4 files changed

+97
-7
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
## Unreleased
44
- Show QR scanner video in fullscreen on mobile for onchain transactions
55

6+
- Replace the existing BIP69 lexicographical sorting of tx inputs/outputs with a randomized sorting approach
7+
68
## 4.41.0
79
- New feature: insure your bitcoins through Bitsurance
810
- Bundle BitBox02 firmware version v9.16.0

backend/coins/btc/maketx/maketx.go

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,13 @@
1515
package maketx
1616

1717
import (
18+
"crypto/rand"
19+
"encoding/binary"
20+
mrand "math/rand"
1821
"sort"
22+
"time"
1923

2024
"github.com/btcsuite/btcd/btcutil"
21-
"github.com/btcsuite/btcd/btcutil/txsort"
2225
"github.com/btcsuite/btcd/chaincfg/chainhash"
2326
"github.com/btcsuite/btcd/wire"
2427
"github.com/digitalbitbox/bitbox-wallet-app/backend/accounts/errors"
@@ -168,7 +171,10 @@ func NewTxSpendAll(
168171
TxOut: []*wire.TxOut{output},
169172
LockTime: 0,
170173
}
171-
txsort.InPlaceSort(unsignedTransaction)
174+
175+
secureRand := mrand.New(mrand.NewSource(secureSeed()))
176+
shuffleTxInputsAndOutputs(unsignedTransaction, secureRand)
177+
172178
log.WithField("fee", maxRequiredFee).Debug("Preparing transaction to spend all outputs")
173179

174180
setRBF(coin, unsignedTransaction)
@@ -249,7 +255,10 @@ func NewTx(
249255
} else {
250256
changeAddress = nil
251257
}
252-
txsort.InPlaceSort(unsignedTransaction)
258+
259+
secureRand := mrand.New(mrand.NewSource(secureSeed()))
260+
shuffleTxInputsAndOutputs(unsignedTransaction, secureRand)
261+
253262
log.WithField("fee", finalFee).Debug("Preparing transaction")
254263

255264
setRBF(coin, unsignedTransaction)
@@ -263,3 +272,26 @@ func NewTx(
263272
}, nil
264273
}
265274
}
275+
276+
// shuffleTxInputsAndOutputs shuffles both the TxIn and TxOut slices of a wire.MsgTx.
277+
func shuffleTxInputsAndOutputs(tx *wire.MsgTx, secureRand *mrand.Rand) {
278+
// Shuffle inputs
279+
secureRand.Shuffle(len(tx.TxIn), func(i, j int) {
280+
tx.TxIn[i], tx.TxIn[j] = tx.TxIn[j], tx.TxIn[i]
281+
})
282+
283+
// Shuffle outputs
284+
secureRand.Shuffle(len(tx.TxOut), func(i, j int) {
285+
tx.TxOut[i], tx.TxOut[j] = tx.TxOut[j], tx.TxOut[i]
286+
})
287+
}
288+
289+
// secureSeed generates a secure seed value.
290+
func secureSeed() int64 {
291+
var b [8]byte
292+
_, err := rand.Read(b[:])
293+
if err != nil {
294+
return time.Now().UnixNano() // use timestamp as fallback seed
295+
}
296+
return int64(binary.LittleEndian.Uint64(b[:]))
297+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright 2024 Shift Crypto AG
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package maketx
16+
17+
import (
18+
"math/rand"
19+
"testing"
20+
21+
"github.com/btcsuite/btcd/wire"
22+
"github.com/stretchr/testify/require"
23+
)
24+
25+
func TestShuffleTxInputsAndOutputs(t *testing.T) {
26+
// Create transaction inputs and outputs
27+
txIns := []*wire.TxIn{
28+
wire.NewTxIn(&wire.OutPoint{Hash: [32]byte{0x01}, Index: 0}, nil, nil),
29+
wire.NewTxIn(&wire.OutPoint{Hash: [32]byte{0x02}, Index: 0}, nil, nil),
30+
wire.NewTxIn(&wire.OutPoint{Hash: [32]byte{0x03}, Index: 0}, nil, nil),
31+
}
32+
txOuts := []*wire.TxOut{
33+
{Value: 1000000, PkScript: []byte{}},
34+
{Value: 2000000, PkScript: []byte{}},
35+
{Value: 3000000, PkScript: []byte{}},
36+
}
37+
38+
// Create a new transaction and add inputs and outputs
39+
tx := wire.NewMsgTx(wire.TxVersion)
40+
tx.TxIn = txIns
41+
tx.TxOut = txOuts
42+
// Shuffle with constant seed
43+
testRand := rand.New(rand.NewSource(1000))
44+
shuffleTxInputsAndOutputs(tx, testRand)
45+
// Expected sorted inputs and outputs
46+
expectedSortedIns := []*wire.TxIn{
47+
wire.NewTxIn(&wire.OutPoint{Hash: [32]byte{0x02}, Index: 0}, nil, nil),
48+
wire.NewTxIn(&wire.OutPoint{Hash: [32]byte{0x01}, Index: 0}, nil, nil),
49+
wire.NewTxIn(&wire.OutPoint{Hash: [32]byte{0x03}, Index: 0}, nil, nil),
50+
}
51+
expectedSortedOuts := []*wire.TxOut{
52+
{Value: 3000000, PkScript: []byte{}},
53+
{Value: 1000000, PkScript: []byte{}},
54+
{Value: 2000000, PkScript: []byte{}},
55+
}
56+
57+
// Compare the shuffled inputs and outputs with the expected sorted ones
58+
require.Equal(t, expectedSortedIns, tx.TxIn, "The transaction inputs were not successfully shuffled.")
59+
require.Equal(t, expectedSortedOuts, tx.TxOut, "The transaction outputs were not successfully shuffled.")
60+
}

backend/coins/btc/sign.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
package btc
1616

1717
import (
18-
"github.com/btcsuite/btcd/btcutil/txsort"
1918
"github.com/btcsuite/btcd/chaincfg/chainhash"
2019
"github.com/btcsuite/btcd/txscript"
2120
"github.com/btcsuite/btcd/wire"
@@ -92,9 +91,6 @@ func (account *Account) signTransaction(
9291

9392
func txValidityCheck(transaction *wire.MsgTx, previousOutputs maketx.PreviousOutputs,
9493
sigHashes *txscript.TxSigHashes) error {
95-
if !txsort.IsSorted(transaction) {
96-
return errp.New("tx not bip69 conformant")
97-
}
9894
for index, txIn := range transaction.TxIn {
9995
spentOutput, ok := previousOutputs[txIn.PreviousOutPoint]
10096
if !ok {

0 commit comments

Comments
 (0)