Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(BUX-290): updated calculation and verification of merkle roots #37

Merged
merged 1 commit into from
Nov 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 19 additions & 8 deletions bump.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package paymail

import (
"errors"

"github.com/libsv/go-bc"
"github.com/libsv/go-bt/v2"
)
Expand Down Expand Up @@ -30,17 +31,27 @@ const (
txIDFlag
)

func (b BUMP) calculateMerkleRoots() ([]string, error) {
merkleRoots := make([]string, 0)
func (b BUMP) calculateMerkleRoot() (string, error) {
merkleRoot := ""

for _, bumpPathElement := range b.path[0] {
merkleRoot, err := calculateMerkleRoot(bumpPathElement, b)
if err != nil {
return nil, err
if bumpPathElement.txId {
calcMerkleRoot, err := calculateMerkleRoot(bumpPathElement, b)
if err != nil {
return "", err
}

if merkleRoot == "" {
merkleRoot = calcMerkleRoot
continue
}

if calcMerkleRoot != merkleRoot {
return "", errors.New("different merkle roots for the same block")
}
}
merkleRoots = append(merkleRoots, merkleRoot)
}
return merkleRoots, nil
return merkleRoot, nil
}

func findLeafByOffset(offset uint64, bumpLeaves []BUMPLeaf) *BUMPLeaf {
Expand All @@ -52,7 +63,7 @@ func findLeafByOffset(offset uint64, bumpLeaves []BUMPLeaf) *BUMPLeaf {
return nil
}

// calculateMerkleRoots will calculate one merkle root for tx in the BUMPPath
// calculateMerkleRoots will calculate one merkle root for tx in the BUMPLeaf
func calculateMerkleRoot(baseLeaf BUMPLeaf, bump BUMP) (string, error) {
calculatedHash := baseLeaf.hash
offset := baseLeaf.offset
Expand Down
7 changes: 7 additions & 0 deletions definitions.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,10 @@ type AddressInformation struct {
PrivateKey string `json:"-"` // PrivateKey hex encoded
PubKey string `json:"pubkey"` // PublicKey hex encoded
}

// MerkleRootConfirmationRequestItem is a request type for verification
// of Merkle Roots inclusion in the longest chain.
type MerkleRootConfirmationRequestItem struct {
MerkleRoot string `json:"merkleRoot"`
BlockHeight int32 `json:"blockHeight"`
}
19 changes: 9 additions & 10 deletions examples/server/run_server/demo_interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,39 +14,38 @@ type demoServiceProvider struct {

// GetPaymailByAlias is a demo implementation of this interface
func (d *demoServiceProvider) GetPaymailByAlias(_ context.Context, alias, domain string,
_ *server.RequestMetadata) (*paymail.AddressInformation, error) {

_ *server.RequestMetadata,
) (*paymail.AddressInformation, error) {
// Get the data from the demo database
return DemoGetPaymailByAlias(alias, domain)
}

// CreateAddressResolutionResponse is a demo implementation of this interface
func (d *demoServiceProvider) CreateAddressResolutionResponse(ctx context.Context, alias, domain string,
senderValidation bool, _ *server.RequestMetadata) (*paymail.ResolutionPayload, error) {

senderValidation bool, _ *server.RequestMetadata,
) (*paymail.ResolutionPayload, error) {
// Generate a new destination / output for the basic address resolution
return DemoCreateAddressResolutionResponse(ctx, alias, domain, senderValidation)
}

// CreateP2PDestinationResponse is a demo implementation of this interface
func (d *demoServiceProvider) CreateP2PDestinationResponse(ctx context.Context, alias, domain string,
satoshis uint64, _ *server.RequestMetadata) (*paymail.PaymentDestinationPayload, error) {

satoshis uint64, _ *server.RequestMetadata,
) (*paymail.PaymentDestinationPayload, error) {
// Generate a new destination for the p2p request
return DemoCreateP2PDestinationResponse(ctx, alias, domain, satoshis)
}

// RecordTransaction is a demo implementation of this interface
func (d *demoServiceProvider) RecordTransaction(ctx context.Context,
p2pTx *paymail.P2PTransaction, _ *server.RequestMetadata) (*paymail.P2PTransactionPayload, error) {

p2pTx *paymail.P2PTransaction, _ *server.RequestMetadata,
) (*paymail.P2PTransactionPayload, error) {
// Record the tx into your datastore layer
return DemoRecordTransaction(ctx, p2pTx)
}

// VerifyMerkleRoots is a demo implementation of this interface
func (d *demoServiceProvider) VerifyMerkleRoots(ctx context.Context, merkleProofs []string) error {

func (d *demoServiceProvider) VerifyMerkleRoots(ctx context.Context, merkleProofs []paymail.MerkleRootConfirmationRequestItem) error {
// Verify the Merkle roots
return nil
}
18 changes: 13 additions & 5 deletions p2p_beef_tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/hex"
"errors"
"fmt"

"github.com/libsv/go-bt/v2"
)

Expand Down Expand Up @@ -35,16 +36,23 @@ type DecodedBEEF struct {
}

// GetMerkleRoots will calculate the merkle roots for the BUMPs in the BEEF transaction
func (dBeef *DecodedBEEF) GetMerkleRoots() ([]string, error) {
var merkleRoots []string
func (dBeef *DecodedBEEF) GetMerkleRootsRequest() ([]MerkleRootConfirmationRequestItem, error) {
var merkleRootsRequest []MerkleRootConfirmationRequestItem

for _, bump := range dBeef.BUMPs {
partialMerkleRoots, err := bump.calculateMerkleRoots()
merkleRoot, err := bump.calculateMerkleRoot()
if err != nil {
return nil, err
}
merkleRoots = append(merkleRoots, partialMerkleRoots...)

request := MerkleRootConfirmationRequestItem{
BlockHeight: int32(bump.blockHeight),
MerkleRoot: merkleRoot,
}
merkleRootsRequest = append(merkleRootsRequest, request)
}
return merkleRoots, nil

return merkleRootsRequest, nil
}

func DecodeBEEF(beefHex string) (*DecodedBEEF, error) {
Expand Down
10 changes: 6 additions & 4 deletions p2p_beef_tx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ package paymail
import (
"context"
"errors"
"github.com/libsv/go-bt/v2/bscript"
"testing"

"github.com/libsv/go-bt/v2"
"github.com/libsv/go-bt/v2/bscript"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
Expand All @@ -15,7 +15,7 @@ import (
type mockServiceProvider struct{}

// VerifyMerkleRoots is a mock implementation of this interface
func (m *mockServiceProvider) VerifyMerkleRoots(_ context.Context, _ []string) error {
func (m *mockServiceProvider) VerifyMerkleRoots(_ context.Context, _ []MerkleRootConfirmationRequestItem) error {
// Verify the merkle roots
return nil
}
Expand Down Expand Up @@ -79,7 +79,8 @@ func TestDecodeBEEF_DecodeBEEF_HappyPaths(t *testing.T) {
Satoshis: 26174,
LockingScript: bscript.NewFromBytes([]byte("76a9146bfd5c7fbe21529d45803dbcf0c87dd3c71efbc288ac")),
},
}},
},
},
PathIndex: func(v bt.VarInt) *bt.VarInt { return &v }(0x0),
},
},
Expand All @@ -100,7 +101,8 @@ func TestDecodeBEEF_DecodeBEEF_HappyPaths(t *testing.T) {
Satoshis: 26172,
LockingScript: bscript.NewFromBytes([]byte("76a9146bfd5c7fbe21529d45803dbcf0c87dd3c71efbc288ac")),
},
}},
},
},
PathIndex: nil,
},
},
Expand Down
5 changes: 3 additions & 2 deletions p2p_spv.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ import (
"context"
"errors"
"fmt"

"github.com/libsv/go-bt/v2"
"github.com/libsv/go-bt/v2/bscript/interpreter"
)

type MerkleRootVerifier interface {
VerifyMerkleRoots(
ctx context.Context,
merkleRoots []string,
merkleRoots []MerkleRootConfirmationRequestItem,
) error
}

Expand Down Expand Up @@ -41,7 +42,7 @@ func ExecuteSimplifiedPaymentVerification(dBeef *DecodedBEEF, provider MerkleRoo
}

func verifyMerkleRoots(dBeef *DecodedBEEF, provider MerkleRootVerifier) error {
merkleRoots, err := dBeef.GetMerkleRoots()
merkleRoots, err := dBeef.GetMerkleRootsRequest()
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion server/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,6 @@ type PaymailServiceProvider interface {

VerifyMerkleRoots(
ctx context.Context,
merkleProofs []string,
merkleProofs []paymail.MerkleRootConfirmationRequestItem,
) error
}
2 changes: 1 addition & 1 deletion server/mock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func (m *mockServiceProvider) RecordTransaction(_ context.Context,
}

// VerifyMerkleRoots is a mock implementation of this interface
func (m *mockServiceProvider) VerifyMerkleRoots(_ context.Context, _ []string) error {
func (m *mockServiceProvider) VerifyMerkleRoots(_ context.Context, _ []paymail.MerkleRootConfirmationRequestItem) error {

// Verify the merkle roots
return nil
Expand Down
Loading