mirror of
https://github.com/muun/recovery.git
synced 2025-11-11 22:40:16 -05:00
Update project structure and build process
This commit is contained in:
26
libwallet/btcsuitew/txscriptw/hashcache.go
Normal file
26
libwallet/btcsuitew/txscriptw/hashcache.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package txscriptw
|
||||
|
||||
import (
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
)
|
||||
|
||||
// TaprootSigHashes contains the sigHash parts for a PayToTaproot signature
|
||||
type TaprootSigHashes struct {
|
||||
HashPrevOuts chainhash.Hash
|
||||
HashSequence chainhash.Hash
|
||||
HashOutputs chainhash.Hash
|
||||
HashAmounts chainhash.Hash
|
||||
HashScriptPubKeys chainhash.Hash
|
||||
}
|
||||
|
||||
// NewTaprootSigHashes calculates and returns the TaprootSigHashes
|
||||
func NewTaprootSigHashes(tx *wire.MsgTx, prevOuts []*wire.TxOut) *TaprootSigHashes {
|
||||
return &TaprootSigHashes{
|
||||
HashPrevOuts: calcHashPrevOuts(tx),
|
||||
HashSequence: calcHashSequences(tx),
|
||||
HashOutputs: calcHashOutputs(tx),
|
||||
HashAmounts: calcHashAmounts(prevOuts),
|
||||
HashScriptPubKeys: calcHashScriptPubKeys(prevOuts),
|
||||
}
|
||||
}
|
||||
149
libwallet/btcsuitew/txscriptw/script.go
Normal file
149
libwallet/btcsuitew/txscriptw/script.go
Normal file
@@ -0,0 +1,149 @@
|
||||
package txscriptw
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/muun/libwallet/btcsuitew/chainhashw"
|
||||
)
|
||||
|
||||
// CalcTaprootSigHash crafts signature digest.
|
||||
// It only supports SIGHASH_ALL without ANYONECANPAY, and no annex or script paths.
|
||||
func CalcTaprootSigHash(
|
||||
tx *wire.MsgTx,
|
||||
sigHashes *TaprootSigHashes,
|
||||
index int,
|
||||
hashType txscript.SigHashType,
|
||||
) ([]byte, error) {
|
||||
|
||||
if index >= len(tx.TxIn) {
|
||||
return nil, fmt.Errorf("wanted index %d but found only %d inputs", index, len(tx.TxIn))
|
||||
}
|
||||
|
||||
anyoneCanPay := hashType&txscript.SigHashAnyOneCanPay != 0
|
||||
hashType = hashType & 0x1f
|
||||
|
||||
if hashType != txscript.SigHashAll {
|
||||
return nil, fmt.Errorf("only SIGHASH_ALL is supported")
|
||||
}
|
||||
|
||||
if anyoneCanPay {
|
||||
return nil, fmt.Errorf("anyoneCanPay is not supported")
|
||||
}
|
||||
|
||||
b := new(bytes.Buffer)
|
||||
|
||||
// Epoch [1] (not technically part of the message, but every use-case adds this prefix later)
|
||||
b.WriteByte(0x00)
|
||||
|
||||
// SigHash type [1]
|
||||
b.WriteByte(byte(hashType))
|
||||
|
||||
// nVersion [4]
|
||||
b.Write(uInt32Le(uint32(tx.Version)))
|
||||
|
||||
// nLockTime [4]
|
||||
b.Write(uInt32Le(tx.LockTime))
|
||||
|
||||
// input data [128 per input] always included since we failed for anyoneCanPay
|
||||
if !anyoneCanPay {
|
||||
b.Write(sigHashes.HashPrevOuts[:])
|
||||
b.Write(sigHashes.HashAmounts[:])
|
||||
b.Write(sigHashes.HashScriptPubKeys[:])
|
||||
b.Write(sigHashes.HashSequence[:])
|
||||
}
|
||||
|
||||
// output data [?] always included since we checked for SigHashAll
|
||||
if hashType != txscript.SigHashNone && hashType != txscript.SigHashSingle {
|
||||
b.Write(sigHashes.HashOutputs[:])
|
||||
}
|
||||
|
||||
// Spend type [1] always 0x00 since we don't support annex or script path
|
||||
b.WriteByte(0x00)
|
||||
|
||||
if anyoneCanPay {
|
||||
// MISSING: commit to the spent output and sequence (never since we failed for anyoneCanPay)
|
||||
} else {
|
||||
// Input index [4]
|
||||
b.Write(uInt32Le(uint32(index)))
|
||||
}
|
||||
|
||||
// MISSING: do some more hashing and commit to the annex (not supported)
|
||||
|
||||
if hashType == txscript.SigHashSingle {
|
||||
return nil, fmt.Errorf("SIGHASH_SINGLE is not supported")
|
||||
}
|
||||
|
||||
// MISSING: encode extensions, such as the script path commitment from BIP-342 (not supported)
|
||||
// As with the epoch byte above, not technically part of the message, but used in all cases
|
||||
|
||||
return chainhashw.TaggedHashB(chainhashw.TagTapSighash, b.Bytes()), nil
|
||||
}
|
||||
|
||||
func uInt32Le(n uint32) []byte {
|
||||
var nBytes [4]byte
|
||||
binary.LittleEndian.PutUint32(nBytes[:], n)
|
||||
return nBytes[:]
|
||||
}
|
||||
|
||||
func uInt64Le(n uint64) []byte {
|
||||
var nBytes [8]byte
|
||||
binary.LittleEndian.PutUint64(nBytes[:], n)
|
||||
return nBytes[:]
|
||||
}
|
||||
|
||||
func calcHashPrevOuts(tx *wire.MsgTx) chainhash.Hash {
|
||||
b := new(bytes.Buffer)
|
||||
|
||||
for _, txIn := range tx.TxIn {
|
||||
b.Write(txIn.PreviousOutPoint.Hash[:])
|
||||
b.Write(uInt32Le(txIn.PreviousOutPoint.Index))
|
||||
}
|
||||
|
||||
return chainhash.HashH(b.Bytes())
|
||||
}
|
||||
|
||||
func calcHashSequences(tx *wire.MsgTx) chainhash.Hash {
|
||||
b := new(bytes.Buffer)
|
||||
|
||||
for _, txIn := range tx.TxIn {
|
||||
b.Write(uInt32Le(txIn.Sequence))
|
||||
}
|
||||
|
||||
return chainhash.HashH(b.Bytes())
|
||||
}
|
||||
|
||||
func calcHashOutputs(tx *wire.MsgTx) chainhash.Hash {
|
||||
b := new(bytes.Buffer)
|
||||
|
||||
for _, txOut := range tx.TxOut {
|
||||
wire.WriteTxOut(b, 0, 0, txOut)
|
||||
}
|
||||
|
||||
return chainhash.HashH(b.Bytes())
|
||||
}
|
||||
|
||||
func calcHashScriptPubKeys(txOuts []*wire.TxOut) chainhash.Hash {
|
||||
b := new(bytes.Buffer)
|
||||
|
||||
for _, txOut := range txOuts {
|
||||
wire.WriteVarInt(b, 0, uint64(len(txOut.PkScript)))
|
||||
b.Write(txOut.PkScript)
|
||||
}
|
||||
|
||||
return chainhash.HashH(b.Bytes())
|
||||
}
|
||||
|
||||
func calcHashAmounts(txOuts []*wire.TxOut) chainhash.Hash {
|
||||
b := new(bytes.Buffer)
|
||||
|
||||
for _, txOut := range txOuts {
|
||||
b.Write(uInt64Le(uint64(txOut.Value)))
|
||||
}
|
||||
|
||||
return chainhash.HashH(b.Bytes())
|
||||
}
|
||||
139
libwallet/btcsuitew/txscriptw/script_test.go
Normal file
139
libwallet/btcsuitew/txscriptw/script_test.go
Normal file
@@ -0,0 +1,139 @@
|
||||
package txscriptw
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
_ "unsafe"
|
||||
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
)
|
||||
|
||||
// These test cases were taken from rust-bitcoin, which in turn took them from Bitcoin Core:
|
||||
var sigHashTestCases = []sigHashTestCase{
|
||||
{
|
||||
tx: "020000000164eb050a5e3da0c2a65e4786f26d753b7bc69691fabccafb11f7acef36641f1846010000003101b2b404392a22000000000017a9147f2bde86fe78bf68a0544a4f290e12f0b7e0a08c87580200000000000017a91425d11723074ecfb96a0a83c3956bfaf362ae0c908758020000000000001600147e20f938993641de67bb0cdd71682aa34c4d29ad5802000000000000160014c64984dc8761acfa99418bd6bedc79b9287d652d72000000",
|
||||
prevOuts: "01365724000000000023542156b39dab4f8f3508e0432cfb41fab110170acaa2d4c42539cb90a4dc7c093bc500",
|
||||
index: 0,
|
||||
hashType: txscript.SigHashOld,
|
||||
// expectSigHash: "33ca0ebfb4a945eeee9569fc0f5040221275f88690b7f8592ada88ce3bdf6703",
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
tx: "0200000002fff49be59befe7566050737910f6ccdc5e749c7f8860ddc140386463d88c5ad0f3000000002cf68eb4a3d67f9d4c079249f7e4f27b8854815cb1ed13842d4fbf395f9e217fd605ee24090100000065235d9203f458520000000000160014b6d48333bb13b4c644e57c43a9a26df3a44b785e58020000000000001976a914eea9461a9e1e3f765d3af3e726162e0229fe3eb688ac58020000000000001976a9143a8869c9f2b5ea1d4ff3aeeb6a8fb2fffb1ad5fe88ac0ad7125c",
|
||||
prevOuts: "02591f220000000000225120f25ad35583ea31998d968871d7de1abd2a52f6fe4178b54ea158274806ff4ece48fb310000000000225120f25ad35583ea31998d968871d7de1abd2a52f6fe4178b54ea158274806ff4ece",
|
||||
index: 1,
|
||||
hashType: txscript.SigHashAll,
|
||||
expectSigHash: "626ab955d58c9a8a600a0c580549d06dc7da4e802eb2a531f62a588e430967a8",
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
tx: "0200000001350005f65aa830ced2079df348e2d8c2bdb4f10e2dde6a161d8a07b40d1ad87dae000000001611d0d603d9dc0e000000000017a914459b6d7d6bbb4d8837b4bf7e9a4556f952da2f5c8758020000000000001976a9141dd70e1299ffc2d5b51f6f87de9dfe9398c33cbb88ac58020000000000001976a9141dd70e1299ffc2d5b51f6f87de9dfe9398c33cbb88aca71c1f4f",
|
||||
prevOuts: "01c4811000000000002251201bf9297d0a2968ae6693aadd0fa514717afefd218087a239afb7418e2d22e65c",
|
||||
index: 0,
|
||||
hashType: txscript.SigHashAll | txscript.SigHashAnyOneCanPay,
|
||||
// expectSigHash: "dfa9437f9c9a1d1f9af271f79f2f5482f287cdb0d2e03fa92c8a9b216cc6061c",
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
tx: "020000000185bed1a6da2bffbd60ec681a1bfb71c5111d6395b99b3f8b2bf90167111bcb18f5010000007c83ace802ded24a00000000001600142c4698f9f7a773866879755aa78c516fb332af8e5802000000000000160014d38639dfbac4259323b98a472405db0c461b31fa61073747",
|
||||
prevOuts: "0144c84d0000000000225120e3f2107989c88e67296ab2faca930efa2e3a5bd3ff0904835a11c9e807458621",
|
||||
index: 0,
|
||||
hashType: txscript.SigHashNone,
|
||||
// expectSigHash: "3129de36a5d05fff97ffca31eb75fcccbbbc27b3147a7a36a9e4b45d8b625067",
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
tx: "eb93dbb901028c8515589dac980b6e7f8e4088b77ed866ca0d6d210a7218b6fd0f6b22dd6d7300000000eb4740a9047efc0e0000000000160014913da2128d8fcf292b3691db0e187414aa1783825802000000000000160014913da2128d8fcf292b3691db0e187414aa178382580200000000000017a9143dd27f01c6f7ef9bb9159937b17f17065ed01a0c875802000000000000160014d7630e19df70ada9905ede1722b800c0005f246641000000",
|
||||
prevOuts: "013fed110000000000225120eb536ae8c33580290630fc495046e998086a64f8f33b93b07967d9029b265c55",
|
||||
index: 0,
|
||||
hashType: txscript.SigHashNone | txscript.SigHashAnyOneCanPay,
|
||||
// expectSigHash: "2441e8b0e063a2083ee790f14f2045022f07258ddde5ee01de543c9e789d80ae",
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
tx: "02000000017836b409a5fed32211407e44b971591f2032053f14701fb5b3a30c0ff382f2cc9c0100000061ac55f60288fb5600000000001976a9144ea02f6f182b082fb6ce47e36bbde390b6a41b5088ac58020000000000001976a9144ea02f6f182b082fb6ce47e36bbde390b6a41b5088ace4000000",
|
||||
prevOuts: "01efa558000000000022512007071ea3dc7e331b0687d0193d1e6d6ed10e645ef36f10ef8831d5e522ac9e80",
|
||||
index: 0,
|
||||
hashType: txscript.SigHashSingle,
|
||||
// expectSigHash: "30239345177cadd0e3ea413d49803580abb6cb27971b481b7788a78d35117a88",
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
tx: "0100000001aa6deae89d5e0aaca58714fc76ef6f3c8284224888089232d4e663843ed3ab3eae010000008b6657a60450cb4c0000000000160014a3d42b5413ef0c0701c4702f3cd7d4df222c147058020000000000001976a91430b4ed8723a4ee8992aa2c8814cfe5c3ad0ab9d988ac5802000000000000160014365b1166a6ed0a5e8e9dff17a6d00bbb43454bc758020000000000001976a914bc98c51a84fe7fad5dc380eb8b39586eff47241688ac4f313247",
|
||||
prevOuts: "0107af4e00000000002251202c36d243dfc06cb56a248e62df27ecba7417307511a81ae61aa41c597a929c69",
|
||||
index: 0,
|
||||
hashType: txscript.SigHashSingle | txscript.SigHashAnyOneCanPay,
|
||||
// expectSigHash: "bf9c83f26c6dd16449e4921f813f551c4218e86f2ec906ca8611175b41b566df",
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
|
||||
func TestTaprootSigHash(t *testing.T) {
|
||||
for i, testCase := range sigHashTestCases {
|
||||
tx := testCase.ParseTx()
|
||||
prevOuts := testCase.ParsePrevOuts()
|
||||
|
||||
sigHashes := NewTaprootSigHashes(tx, prevOuts)
|
||||
|
||||
sigHash, err := CalcTaprootSigHash(tx, sigHashes, testCase.index, testCase.hashType)
|
||||
if (err != nil) != testCase.expectError {
|
||||
t.Fatalf("case %d: expect error %v, actual error: %v", i, testCase.expectError, err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(sigHash, testCase.ParseExpectedSigHash()) {
|
||||
t.Fatalf("case %d: sigHash does not match expected value", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type sigHashTestCase struct {
|
||||
tx string
|
||||
prevOuts string
|
||||
index int
|
||||
hashType txscript.SigHashType
|
||||
expectSigHash string
|
||||
expectError bool
|
||||
}
|
||||
|
||||
func (c *sigHashTestCase) ParseTx() *wire.MsgTx {
|
||||
b, _ := hex.DecodeString(c.tx)
|
||||
r := bytes.NewReader(b)
|
||||
|
||||
tx := wire.NewMsgTx(0)
|
||||
tx.BtcDecode(r, 0, wire.WitnessEncoding)
|
||||
|
||||
return tx
|
||||
}
|
||||
|
||||
func (c *sigHashTestCase) ParsePrevOuts() []*wire.TxOut {
|
||||
b, _ := hex.DecodeString(c.prevOuts)
|
||||
r := bytes.NewReader(b)
|
||||
|
||||
prevOutCount, _ := wire.ReadVarInt(r, 0)
|
||||
prevOuts := make([]*wire.TxOut, prevOutCount)
|
||||
|
||||
for i := 0; i < int(prevOutCount); i++ {
|
||||
valueLe := make([]byte, 8)
|
||||
r.Read(valueLe[:])
|
||||
value := binary.LittleEndian.Uint64(valueLe)
|
||||
|
||||
pkScriptSize, _ := wire.ReadVarInt(r, 0)
|
||||
pkScript := make([]byte, pkScriptSize)
|
||||
r.Read(pkScript)
|
||||
|
||||
prevOuts[i] = &wire.TxOut{
|
||||
Value: int64(value),
|
||||
PkScript: pkScript,
|
||||
}
|
||||
}
|
||||
|
||||
return prevOuts
|
||||
}
|
||||
|
||||
func (c *sigHashTestCase) ParseExpectedSigHash() []byte {
|
||||
b, _ := hex.DecodeString(c.expectSigHash)
|
||||
return b
|
||||
}
|
||||
23
libwallet/btcsuitew/txscriptw/standard.go
Normal file
23
libwallet/btcsuitew/txscriptw/standard.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package txscriptw
|
||||
|
||||
import (
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/muun/libwallet/btcsuitew/btcutilw"
|
||||
)
|
||||
|
||||
// PayToAddrScript uses txscript.PayToAddrScript for all cases except AddressTaprootKey, which is
|
||||
// by this wrapper.
|
||||
func PayToAddrScript(address btcutil.Address) ([]byte, error) {
|
||||
// Detect the only additional case we support, delegate otherwise:
|
||||
trkAddr, ok := address.(*btcutilw.AddressTaprootKey)
|
||||
if !ok {
|
||||
return txscript.PayToAddrScript(address)
|
||||
}
|
||||
|
||||
return payToTaprootKeyScript(trkAddr.ScriptAddress())
|
||||
}
|
||||
|
||||
func payToTaprootKeyScript(key []byte) ([]byte, error) {
|
||||
return txscript.NewScriptBuilder().AddOp(txscript.OP_1).AddData(key).Script()
|
||||
}
|
||||
Reference in New Issue
Block a user