Skip to content

Commit 7e65bfe

Browse files
authored
Merge pull request #205 from kcalvinalvin/2024-10-22-add-msgutreexotx
wire: add msgutreexotx
2 parents 8ea6726 + a76f89e commit 7e65bfe

File tree

7 files changed

+242
-0
lines changed

7 files changed

+242
-0
lines changed

wire/leaf.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,23 @@ type LeafData struct {
4949
PkScript []byte
5050
}
5151

52+
// Copy creates a deep copy of the leafdata so the original does not get modified
53+
// when the copy is manipulated.
54+
func (l *LeafData) Copy() *LeafData {
55+
newL := LeafData{
56+
BlockHash: l.BlockHash,
57+
OutPoint: l.OutPoint,
58+
Height: l.Height,
59+
IsCoinBase: l.IsCoinBase,
60+
Amount: l.Amount,
61+
ReconstructablePkType: l.ReconstructablePkType,
62+
PkScript: make([]byte, len(l.PkScript)),
63+
}
64+
65+
copy(newL.PkScript, l.PkScript)
66+
return &newL
67+
}
68+
5269
func (l LeafData) MarshalJSON() ([]byte, error) {
5370
s := struct {
5471
BlockHash string `json:"blockhash"`

wire/leaf_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,3 +633,42 @@ func TestLeafHash(t *testing.T) {
633633
}
634634
}
635635
}
636+
637+
// TestLeafDataCopy tests that modifying the leafdata copy does not modify the original.
638+
func TestLeafDataCopy(t *testing.T) {
639+
ld := LeafData{
640+
BlockHash: *newHashFromStr("00000000000172ff8a4e14441512072bacaf8d38b995a3fcd2f8435efc61717d"),
641+
OutPoint: OutPoint{
642+
Hash: *newHashFromStr("061bb0bf3a1b9df13773da06bf92920394887a9c2b8b8772ac06be4e077df5eb"),
643+
Index: 10,
644+
},
645+
Amount: 200000,
646+
PkScript: hexToBytes("a914e8d74935cfa223f9750a32b18d609cba17a5c3fe87"),
647+
Height: 1599255,
648+
IsCoinBase: false,
649+
}
650+
651+
ldOrig := LeafData{
652+
BlockHash: *newHashFromStr("00000000000172ff8a4e14441512072bacaf8d38b995a3fcd2f8435efc61717d"),
653+
OutPoint: OutPoint{
654+
Hash: *newHashFromStr("061bb0bf3a1b9df13773da06bf92920394887a9c2b8b8772ac06be4e077df5eb"),
655+
Index: 10,
656+
},
657+
Amount: 200000,
658+
PkScript: hexToBytes("a914e8d74935cfa223f9750a32b18d609cba17a5c3fe87"),
659+
Height: 1599255,
660+
IsCoinBase: false,
661+
}
662+
663+
ldCopy := ld.Copy()
664+
ldCopy.OutPoint.Index = 7777
665+
ldCopy.OutPoint.Hash[31] = 0x17
666+
ldCopy.PkScript[0] = 0x77
667+
if reflect.DeepEqual(ldCopy, ld) {
668+
t.Fatalf("ldCopy and ld are same")
669+
}
670+
671+
if !reflect.DeepEqual(ld, ldOrig) {
672+
t.Fatalf("ld and ldOrig are different")
673+
}
674+
}

wire/message.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ const (
3838
CmdNotFound = "notfound"
3939
CmdBlock = "block"
4040
CmdTx = "tx"
41+
CmdUtreexoTx = "utreexotx"
4142
CmdGetHeaders = "getheaders"
4243
CmdHeaders = "headers"
4344
CmdPing = "ping"
@@ -131,6 +132,9 @@ func makeEmptyMessage(command string) (Message, error) {
131132
case CmdTx:
132133
msg = &MsgTx{}
133134

135+
case CmdUtreexoTx:
136+
msg = &MsgUtreexoTx{}
137+
134138
case CmdPing:
135139
msg = &MsgPing{}
136140

wire/message_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ func TestMessage(t *testing.T) {
5757
msgGetData := NewMsgGetData()
5858
msgNotFound := NewMsgNotFound()
5959
msgTx := NewMsgTx(1)
60+
msgUtreexoTx := NewMsgUtreexoTx(1)
6061
msgPing := NewMsgPing(123123)
6162
msgPong := NewMsgPong(123123)
6263
msgGetHeaders := NewMsgGetHeaders()
@@ -94,6 +95,7 @@ func TestMessage(t *testing.T) {
9495
{msgGetData, msgGetData, pver, MainNet, 25},
9596
{msgNotFound, msgNotFound, pver, MainNet, 25},
9697
{msgTx, msgTx, pver, MainNet, 34},
98+
{msgUtreexoTx, msgUtreexoTx, pver, MainNet, 37},
9799
{msgPing, msgPing, pver, MainNet, 32},
98100
{msgPong, msgPong, pver, MainNet, 32},
99101
{msgGetHeaders, msgGetHeaders, pver, MainNet, 61},

wire/msgutreexotx.go

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
// Copyright (c) 2024 The btcsuite developers
2+
// Use of this source code is governed by an ISC
3+
// license that can be found in the LICENSE file.
4+
5+
package wire
6+
7+
import (
8+
"io"
9+
)
10+
11+
// MsgUtreexoTx implements the Message interface and represents a bitcoin utreexo
12+
// tx message. It is used to deliver transaction information in response to a getdata
13+
// message (MsgGetData) for a given transaction with the utreexo proof to verify the
14+
// transaction.
15+
//
16+
// Use the AddTxIn and AddTxOut functions to build up the list of transaction
17+
// inputs and outputs.
18+
type MsgUtreexoTx struct {
19+
// MsgTx is the underlying Bitcoin transaction message.
20+
MsgTx
21+
22+
// UData is the underlying utreexo data.
23+
UData
24+
}
25+
26+
// Copy creates a deep copy of a transaction so that the original does not get
27+
// modified when the copy is manipulated.
28+
func (msg *MsgUtreexoTx) Copy() *MsgUtreexoTx {
29+
msgTx := msg.MsgTx.Copy()
30+
newTx := MsgUtreexoTx{
31+
MsgTx: *msgTx,
32+
UData: *msg.UData.Copy(),
33+
}
34+
35+
return &newTx
36+
}
37+
38+
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
39+
// This is part of the Message interface implementation.
40+
// See Deserialize for decoding transactions stored to disk, such as in a
41+
// database, as opposed to decoding transactions from the wire.
42+
func (msg *MsgUtreexoTx) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
43+
// Decode the MsgTx.
44+
var msgTx MsgTx
45+
err := msgTx.BtcDecode(r, pver, enc)
46+
if err != nil {
47+
return err
48+
}
49+
msg.MsgTx = msgTx
50+
51+
// Decode the utreexo data.
52+
ud := new(UData)
53+
ud.LeafDatas = nil
54+
err = ud.Deserialize(r)
55+
if err != nil {
56+
return err
57+
}
58+
msg.UData = *ud
59+
60+
return nil
61+
}
62+
63+
// Deserialize decodes a transaction from r into the receiver using a format
64+
// that is suitable for long-term storage such as a database while respecting
65+
// the Version field in the transaction. This function differs from BtcDecode
66+
// in that BtcDecode decodes from the bitcoin wire protocol as it was sent
67+
// across the network. The wire encoding can technically differ depending on
68+
// the protocol version and doesn't even really need to match the format of a
69+
// stored transaction at all. As of the time this comment was written, the
70+
// encoded transaction is the same in both instances, but there is a distinct
71+
// difference and separating the two allows the API to be flexible enough to
72+
// deal with changes.
73+
func (msg *MsgUtreexoTx) Deserialize(r io.Reader) error {
74+
// At the current time, there is no difference between the wire encoding
75+
// at protocol version 0 and the stable long-term storage format. As
76+
// a result, make use of BtcDecode.
77+
return msg.BtcDecode(r, 0, WitnessEncoding)
78+
}
79+
80+
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
81+
// This is part of the Message interface implementation.
82+
// See Serialize for encoding transactions to be stored to disk, such as in a
83+
// database, as opposed to encoding transactions for the wire.
84+
func (msg *MsgUtreexoTx) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
85+
// Encode the msgTx.
86+
err := msg.MsgTx.BtcEncode(w, pver, enc)
87+
if err != nil {
88+
return err
89+
}
90+
91+
// Encode the utreexo data.
92+
return msg.UData.Serialize(w)
93+
}
94+
95+
// Command returns the protocol command string for the message. This is part
96+
// of the Message interface implementation.
97+
func (msg *MsgUtreexoTx) Command() string {
98+
return CmdUtreexoTx
99+
}
100+
101+
// MaxPayloadLength returns the maximum length the payload can be for the
102+
// receiver. This is part of the Message interface implementation.
103+
func (msg *MsgUtreexoTx) MaxPayloadLength(pver uint32) uint32 {
104+
return MaxBlockPayload
105+
}
106+
107+
// NewMsgUtreexoTx returns a new bitcoin utreexotx message that conforms to the
108+
// Message interface. The return instance has a default tx message and the udata
109+
// is initialized to the default values.
110+
func NewMsgUtreexoTx(version int32) *MsgUtreexoTx {
111+
return &MsgUtreexoTx{
112+
MsgTx: *NewMsgTx(1),
113+
}
114+
}

wire/udata.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,28 @@ type UData struct {
2323
LeafDatas []LeafData
2424
}
2525

26+
// Copy creates a deep copy of the utreexo data so the original does not get modified
27+
// when the copy is manipulated.
28+
func (ud *UData) Copy() *UData {
29+
proofCopy := utreexo.Proof{
30+
Targets: make([]uint64, len(ud.AccProof.Targets)),
31+
Proof: make([]utreexo.Hash, len(ud.AccProof.Proof)),
32+
}
33+
copy(proofCopy.Targets, ud.AccProof.Targets)
34+
copy(proofCopy.Proof, ud.AccProof.Proof)
35+
36+
newUD := UData{
37+
AccProof: proofCopy,
38+
LeafDatas: make([]LeafData, len(ud.LeafDatas)),
39+
}
40+
41+
for i := range newUD.LeafDatas {
42+
newUD.LeafDatas[i] = *ud.LeafDatas[i].Copy()
43+
}
44+
45+
return &newUD
46+
}
47+
2648
// StxosHashes returns the hash of all stxos in this UData. The hashes returned
2749
// here represent the hash commitments of the stxos.
2850
func (ud *UData) StxoHashes() []utreexo.Hash {

wire/udata_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -631,3 +631,47 @@ func TestGenerateUData(t *testing.T) {
631631
t.Fatal(err)
632632
}
633633
}
634+
635+
// TestUDataCopy tests that modifying the leafdata copy does not modify the original.
636+
func TestUDataCopy(t *testing.T) {
637+
// New forest object.
638+
p := utreexo.NewAccumulator()
639+
640+
// Create hashes to add from the stxo data.
641+
testDatas := getTestDatas()
642+
addHashes := make([]utreexo.Leaf, 0, len(testDatas[0].leavesPerBlock))
643+
for i, ld := range testDatas[0].leavesPerBlock {
644+
addHashes = append(addHashes, utreexo.Leaf{
645+
Hash: ld.LeafHash(),
646+
// Just half and half.
647+
Remember: i%2 == 0,
648+
})
649+
}
650+
// Add to the accumulator.
651+
err := p.Modify(addHashes, nil, utreexo.Proof{})
652+
if err != nil {
653+
t.Fatal(err)
654+
}
655+
656+
// Generate Proof.
657+
ud, err := GenerateUData(testDatas[0].leavesPerBlock, &p)
658+
if err != nil {
659+
t.Fatal(err)
660+
}
661+
udOrig, err := GenerateUData(testDatas[0].leavesPerBlock, &p)
662+
if err != nil {
663+
t.Fatal(err)
664+
}
665+
666+
udCopy := ud.Copy()
667+
udCopy.AccProof.Targets[0] = 1 << 17
668+
udCopy.LeafDatas[0].Amount = 55
669+
670+
if reflect.DeepEqual(udCopy, ud) {
671+
t.Fatalf("udCopy and ud are same")
672+
}
673+
674+
if !reflect.DeepEqual(ud, udOrig) {
675+
t.Fatalf("ud and udOrig are different")
676+
}
677+
}

0 commit comments

Comments
 (0)