Skip to content

Commit

Permalink
Merge pull request #205 from kcalvinalvin/2024-10-22-add-msgutreexotx
Browse files Browse the repository at this point in the history
wire: add msgutreexotx
  • Loading branch information
kcalvinalvin authored Nov 1, 2024
2 parents 8ea6726 + a76f89e commit 7e65bfe
Show file tree
Hide file tree
Showing 7 changed files with 242 additions and 0 deletions.
17 changes: 17 additions & 0 deletions wire/leaf.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,23 @@ type LeafData struct {
PkScript []byte
}

// Copy creates a deep copy of the leafdata so the original does not get modified
// when the copy is manipulated.
func (l *LeafData) Copy() *LeafData {
newL := LeafData{
BlockHash: l.BlockHash,
OutPoint: l.OutPoint,
Height: l.Height,
IsCoinBase: l.IsCoinBase,
Amount: l.Amount,
ReconstructablePkType: l.ReconstructablePkType,
PkScript: make([]byte, len(l.PkScript)),
}

copy(newL.PkScript, l.PkScript)
return &newL
}

func (l LeafData) MarshalJSON() ([]byte, error) {
s := struct {
BlockHash string `json:"blockhash"`
Expand Down
39 changes: 39 additions & 0 deletions wire/leaf_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -633,3 +633,42 @@ func TestLeafHash(t *testing.T) {
}
}
}

// TestLeafDataCopy tests that modifying the leafdata copy does not modify the original.
func TestLeafDataCopy(t *testing.T) {
ld := LeafData{
BlockHash: *newHashFromStr("00000000000172ff8a4e14441512072bacaf8d38b995a3fcd2f8435efc61717d"),
OutPoint: OutPoint{
Hash: *newHashFromStr("061bb0bf3a1b9df13773da06bf92920394887a9c2b8b8772ac06be4e077df5eb"),
Index: 10,
},
Amount: 200000,
PkScript: hexToBytes("a914e8d74935cfa223f9750a32b18d609cba17a5c3fe87"),
Height: 1599255,
IsCoinBase: false,
}

ldOrig := LeafData{
BlockHash: *newHashFromStr("00000000000172ff8a4e14441512072bacaf8d38b995a3fcd2f8435efc61717d"),
OutPoint: OutPoint{
Hash: *newHashFromStr("061bb0bf3a1b9df13773da06bf92920394887a9c2b8b8772ac06be4e077df5eb"),
Index: 10,
},
Amount: 200000,
PkScript: hexToBytes("a914e8d74935cfa223f9750a32b18d609cba17a5c3fe87"),
Height: 1599255,
IsCoinBase: false,
}

ldCopy := ld.Copy()
ldCopy.OutPoint.Index = 7777
ldCopy.OutPoint.Hash[31] = 0x17
ldCopy.PkScript[0] = 0x77
if reflect.DeepEqual(ldCopy, ld) {
t.Fatalf("ldCopy and ld are same")
}

if !reflect.DeepEqual(ld, ldOrig) {
t.Fatalf("ld and ldOrig are different")
}
}
4 changes: 4 additions & 0 deletions wire/message.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const (
CmdNotFound = "notfound"
CmdBlock = "block"
CmdTx = "tx"
CmdUtreexoTx = "utreexotx"
CmdGetHeaders = "getheaders"
CmdHeaders = "headers"
CmdPing = "ping"
Expand Down Expand Up @@ -131,6 +132,9 @@ func makeEmptyMessage(command string) (Message, error) {
case CmdTx:
msg = &MsgTx{}

case CmdUtreexoTx:
msg = &MsgUtreexoTx{}

case CmdPing:
msg = &MsgPing{}

Expand Down
2 changes: 2 additions & 0 deletions wire/message_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ func TestMessage(t *testing.T) {
msgGetData := NewMsgGetData()
msgNotFound := NewMsgNotFound()
msgTx := NewMsgTx(1)
msgUtreexoTx := NewMsgUtreexoTx(1)
msgPing := NewMsgPing(123123)
msgPong := NewMsgPong(123123)
msgGetHeaders := NewMsgGetHeaders()
Expand Down Expand Up @@ -94,6 +95,7 @@ func TestMessage(t *testing.T) {
{msgGetData, msgGetData, pver, MainNet, 25},
{msgNotFound, msgNotFound, pver, MainNet, 25},
{msgTx, msgTx, pver, MainNet, 34},
{msgUtreexoTx, msgUtreexoTx, pver, MainNet, 37},
{msgPing, msgPing, pver, MainNet, 32},
{msgPong, msgPong, pver, MainNet, 32},
{msgGetHeaders, msgGetHeaders, pver, MainNet, 61},
Expand Down
114 changes: 114 additions & 0 deletions wire/msgutreexotx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// Copyright (c) 2024 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package wire

import (
"io"
)

// MsgUtreexoTx implements the Message interface and represents a bitcoin utreexo
// tx message. It is used to deliver transaction information in response to a getdata
// message (MsgGetData) for a given transaction with the utreexo proof to verify the
// transaction.
//
// Use the AddTxIn and AddTxOut functions to build up the list of transaction
// inputs and outputs.
type MsgUtreexoTx struct {
// MsgTx is the underlying Bitcoin transaction message.
MsgTx

// UData is the underlying utreexo data.
UData
}

// Copy creates a deep copy of a transaction so that the original does not get
// modified when the copy is manipulated.
func (msg *MsgUtreexoTx) Copy() *MsgUtreexoTx {
msgTx := msg.MsgTx.Copy()
newTx := MsgUtreexoTx{
MsgTx: *msgTx,
UData: *msg.UData.Copy(),
}

return &newTx
}

// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
// See Deserialize for decoding transactions stored to disk, such as in a
// database, as opposed to decoding transactions from the wire.
func (msg *MsgUtreexoTx) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
// Decode the MsgTx.
var msgTx MsgTx
err := msgTx.BtcDecode(r, pver, enc)
if err != nil {
return err
}
msg.MsgTx = msgTx

// Decode the utreexo data.
ud := new(UData)
ud.LeafDatas = nil
err = ud.Deserialize(r)
if err != nil {
return err
}
msg.UData = *ud

return nil
}

// Deserialize decodes a transaction from r into the receiver using a format
// that is suitable for long-term storage such as a database while respecting
// the Version field in the transaction. This function differs from BtcDecode
// in that BtcDecode decodes from the bitcoin wire protocol as it was sent
// across the network. The wire encoding can technically differ depending on
// the protocol version and doesn't even really need to match the format of a
// stored transaction at all. As of the time this comment was written, the
// encoded transaction is the same in both instances, but there is a distinct
// difference and separating the two allows the API to be flexible enough to
// deal with changes.
func (msg *MsgUtreexoTx) Deserialize(r io.Reader) error {
// At the current time, there is no difference between the wire encoding
// at protocol version 0 and the stable long-term storage format. As
// a result, make use of BtcDecode.
return msg.BtcDecode(r, 0, WitnessEncoding)
}

// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation.
// See Serialize for encoding transactions to be stored to disk, such as in a
// database, as opposed to encoding transactions for the wire.
func (msg *MsgUtreexoTx) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
// Encode the msgTx.
err := msg.MsgTx.BtcEncode(w, pver, enc)
if err != nil {
return err
}

// Encode the utreexo data.
return msg.UData.Serialize(w)
}

// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgUtreexoTx) Command() string {
return CmdUtreexoTx
}

// MaxPayloadLength returns the maximum length the payload can be for the
// receiver. This is part of the Message interface implementation.
func (msg *MsgUtreexoTx) MaxPayloadLength(pver uint32) uint32 {
return MaxBlockPayload
}

// NewMsgUtreexoTx returns a new bitcoin utreexotx message that conforms to the
// Message interface. The return instance has a default tx message and the udata
// is initialized to the default values.
func NewMsgUtreexoTx(version int32) *MsgUtreexoTx {
return &MsgUtreexoTx{
MsgTx: *NewMsgTx(1),
}
}
22 changes: 22 additions & 0 deletions wire/udata.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,28 @@ type UData struct {
LeafDatas []LeafData
}

// Copy creates a deep copy of the utreexo data so the original does not get modified
// when the copy is manipulated.
func (ud *UData) Copy() *UData {
proofCopy := utreexo.Proof{
Targets: make([]uint64, len(ud.AccProof.Targets)),
Proof: make([]utreexo.Hash, len(ud.AccProof.Proof)),
}
copy(proofCopy.Targets, ud.AccProof.Targets)
copy(proofCopy.Proof, ud.AccProof.Proof)

newUD := UData{
AccProof: proofCopy,
LeafDatas: make([]LeafData, len(ud.LeafDatas)),
}

for i := range newUD.LeafDatas {
newUD.LeafDatas[i] = *ud.LeafDatas[i].Copy()
}

return &newUD
}

// StxosHashes returns the hash of all stxos in this UData. The hashes returned
// here represent the hash commitments of the stxos.
func (ud *UData) StxoHashes() []utreexo.Hash {
Expand Down
44 changes: 44 additions & 0 deletions wire/udata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -631,3 +631,47 @@ func TestGenerateUData(t *testing.T) {
t.Fatal(err)
}
}

// TestUDataCopy tests that modifying the leafdata copy does not modify the original.
func TestUDataCopy(t *testing.T) {
// New forest object.
p := utreexo.NewAccumulator()

// Create hashes to add from the stxo data.
testDatas := getTestDatas()
addHashes := make([]utreexo.Leaf, 0, len(testDatas[0].leavesPerBlock))
for i, ld := range testDatas[0].leavesPerBlock {
addHashes = append(addHashes, utreexo.Leaf{
Hash: ld.LeafHash(),
// Just half and half.
Remember: i%2 == 0,
})
}
// Add to the accumulator.
err := p.Modify(addHashes, nil, utreexo.Proof{})
if err != nil {
t.Fatal(err)
}

// Generate Proof.
ud, err := GenerateUData(testDatas[0].leavesPerBlock, &p)
if err != nil {
t.Fatal(err)
}
udOrig, err := GenerateUData(testDatas[0].leavesPerBlock, &p)
if err != nil {
t.Fatal(err)
}

udCopy := ud.Copy()
udCopy.AccProof.Targets[0] = 1 << 17
udCopy.LeafDatas[0].Amount = 55

if reflect.DeepEqual(udCopy, ud) {
t.Fatalf("udCopy and ud are same")
}

if !reflect.DeepEqual(ud, udOrig) {
t.Fatalf("ud and udOrig are different")
}
}

0 comments on commit 7e65bfe

Please sign in to comment.