Skip to content
This repository has been archived by the owner on Apr 2, 2024. It is now read-only.

Commit

Permalink
feat(BUX-250): fixing BUMP merging according to BRC - matching roots
Browse files Browse the repository at this point in the history
  • Loading branch information
kuba-4chain authored and arkadiuszos4chain committed Nov 13, 2023
1 parent 9f25e24 commit 61fd134
Show file tree
Hide file tree
Showing 3 changed files with 410 additions and 243 deletions.
134 changes: 123 additions & 11 deletions model_bump.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"reflect"
"sort"

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

Expand All @@ -35,36 +36,56 @@ type BUMPLeaf struct {
}

// CalculateMergedBUMP calculates Merged BUMP from a slice of Merkle Proofs
func CalculateMergedBUMP(bh uint64, bumps []BUMP) (*BUMP, error) {
bump := BUMP{
BlockHeight: bh,
}

func CalculateMergedBUMP(bumps []BUMP) (*BUMP, error) {
if len(bumps) == 0 || bumps == nil {
return nil, nil
}

height := len(bumps[0].Path)
if height > maxBumpHeight {
blockHeight := bumps[0].BlockHeight
bumpHeight := len(bumps[0].Path)
if bumpHeight > maxBumpHeight {
return nil,
fmt.Errorf("BUMP cannot be higher than %d", maxBumpHeight)
}

for _, b := range bumps {
if height != len(b.Path) {
if bumpHeight != len(b.Path) {
return nil,
errors.New("Merged BUMP cannot be obtained from Merkle Proofs of different heights")
}
if b.BlockHeight != blockHeight {
return nil,
errors.New("BUMPs have different block heights. Cannot merge BUMPs from different blocks")
}
if len(b.Path) == 0 {
return nil,
errors.New("Empty BUMP given")
}
}

bump.Path = make([][]BUMPLeaf, height)
bump.allNodes = make([]map[uint64]bool, height)
bump := BUMP{BlockHeight: blockHeight}
bump.Path = make([][]BUMPLeaf, bumpHeight)
bump.allNodes = make([]map[uint64]bool, bumpHeight)
for i := range bump.allNodes {
bump.allNodes[i] = make(map[uint64]bool, 0)
}

merkleRoot, err := bumps[0].calculateMerkleRoot()
if err != nil {
return nil, err
}

for _, b := range bumps {
err := bump.add(b)
mr, err := b.calculateMerkleRoot()
if err != nil {
return nil, err
}

if merkleRoot != mr {
return nil, errors.New("BUMPs have different merkle roots")
}

err = bump.add(b)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -105,6 +126,97 @@ func (bump *BUMP) add(b BUMP) error {
return nil
}

func (b *BUMP) calculateMerkleRoot() (string, error) {
merkleRoot := ""

for _, bumpPathElement := range b.Path[0] {
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")
}
}
}
return merkleRoot, nil
}

// 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

for _, bLevel := range bump.Path {
newOffset := getOffsetPair(offset)
leafInPair := findLeafByOffset(newOffset, bLevel)
if leafInPair == nil {
return "", errors.New("could not find pair")
}

leftNode, rightNode := prepareNodes(baseLeaf, offset, *leafInPair, newOffset)

str, err := bc.MerkleTreeParentStr(leftNode, rightNode)
if err != nil {
return "", err
}
calculatedHash = str

offset = offset / 2

baseLeaf = BUMPLeaf{
Hash: calculatedHash,
Offset: offset,
}
}

return calculatedHash, nil
}

func findLeafByOffset(offset uint64, bumpLeaves []BUMPLeaf) *BUMPLeaf {
for _, bumpTx := range bumpLeaves {
if bumpTx.Offset == offset {
return &bumpTx
}
}
return nil
}

func getOffsetPair(offset uint64) uint64 {
if offset%2 == 0 {
return offset + 1
}
return offset - 1
}

func prepareNodes(baseLeaf BUMPLeaf, offset uint64, leafInPair BUMPLeaf, newOffset uint64) (string, string) {
var baseLeafHash, pairLeafHash string

if baseLeaf.Duplicate {
baseLeafHash = leafInPair.Hash
} else {
baseLeafHash = baseLeaf.Hash
}

if leafInPair.Duplicate {
pairLeafHash = baseLeaf.Hash
} else {
pairLeafHash = leafInPair.Hash
}

if newOffset > offset {
return baseLeafHash, pairLeafHash
}
return pairLeafHash, baseLeafHash
}

// Bytes returns BUMPs bytes
func (bumps *BUMPs) Bytes() []byte {
var buff bytes.Buffer
Expand Down
Loading

0 comments on commit 61fd134

Please sign in to comment.