Skip to content

Commit

Permalink
feature: merkletree to bump equivalent function (#74)
Browse files Browse the repository at this point in the history
* work in progress
* tests passing
* don’t console log in tests
* go vendor
* Use chainhash Hash within all the functions to avoid reversing all the bytes.
* linter

Signed-off-by: Darren Kellenschwiler <d.kellenschwiler@bsvblockchain.org>
  • Loading branch information
sirdeggen authored Nov 2, 2023
1 parent 5d05fb3 commit 58361c8
Show file tree
Hide file tree
Showing 18 changed files with 465 additions and 65 deletions.
99 changes: 80 additions & 19 deletions bump.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,27 @@ import (
"encoding/json"
"errors"
"fmt"
"math"
"sort"

"github.com/libsv/go-bt/v2"
"github.com/libsv/go-p2p/chaincfg/chainhash"
)

// BUMP data model json format according to BRC-74.
type BUMP struct {
BlockHeight uint32 `json:"blockHeight"`
BlockHeight uint64 `json:"blockHeight"`
Path [][]leaf `json:"path"`
}

// It should be written such that the internal bytes are kept for calculations.
// and the JSON is generated from the internal struct to an external format.
// leaf represents a leaf in the Merkle tree.
type leaf struct {
Offset uint64 `json:"offset,omitempty"`
Hash string `json:"hash,omitempty"`
Txid *bool `json:"txid,omitempty"`
Duplicate *bool `json:"duplicate,omitempty"`
Offset *uint64 `json:"offset,omitempty"`
Hash *string `json:"hash,omitempty"`
Txid *bool `json:"txid,omitempty"`
Duplicate *bool `json:"duplicate,omitempty"`
}

// NewBUMPFromBytes creates a new BUMP from a byte slice.
Expand All @@ -34,7 +36,7 @@ func NewBUMPFromBytes(bytes []byte) (*BUMP, error) {
var skip int
index, size := bt.NewVarIntFromBytes(bytes[skip:])
skip += size
bump.BlockHeight = uint32(index)
bump.BlockHeight = uint64(index)

// Next byte is the tree height.
treeHeight := uint(bytes[skip])
Expand All @@ -54,20 +56,20 @@ func NewBUMPFromBytes(bytes []byte) (*BUMP, error) {
offset, size := bt.NewVarIntFromBytes(bytes[skip:])
skip += size
var l leaf
l.Offset = uint64(offset)
o := uint64(offset)
l.Offset = &o
flags := bytes[skip]
skip++
var dup bool
var txid bool
dup = flags&1 > 0
txid = flags&2 > 0
dup := flags&1 > 0
txid := flags&2 > 0
if dup {
l.Duplicate = &dup
}
if txid {
l.Txid = &txid
}
l.Hash = StringFromBytesReverse(bytes[skip : skip+32])
h := StringFromBytesReverse(bytes[skip : skip+32])
l.Hash = &h
skip += 32
bump.Path[lv][lf] = l
}
Expand All @@ -76,7 +78,7 @@ func NewBUMPFromBytes(bytes []byte) (*BUMP, error) {
// Sort each of the levels by the offset for consistency.
for _, level := range bump.Path {
sort.Slice(level, func(i, j int) bool {
return level[i].Offset < level[j].Offset
return *level[i].Offset < *level[j].Offset
})
}

Expand Down Expand Up @@ -112,7 +114,7 @@ func (bump *BUMP) Bytes() ([]byte, error) {
nLeaves := len(bump.Path[level])
bytes = append(bytes, bt.VarInt(nLeaves).Bytes()...)
for _, leaf := range bump.Path[level] {
bytes = append(bytes, bt.VarInt(leaf.Offset).Bytes()...)
bytes = append(bytes, bt.VarInt(*leaf.Offset).Bytes()...)
flags := byte(0)
if leaf.Duplicate != nil {
flags |= 1
Expand All @@ -122,7 +124,7 @@ func (bump *BUMP) Bytes() ([]byte, error) {
}
bytes = append(bytes, flags)
if (flags & 1) == 0 {
bytes = append(bytes, BytesFromStringReverse(leaf.Hash)...)
bytes = append(bytes, BytesFromStringReverse(*leaf.Hash)...)
}
}
}
Expand All @@ -143,9 +145,9 @@ func (bump *BUMP) CalculateRootGivenTxid(txid string) (string, error) {
var index uint64
txidFound := false
for _, l := range bump.Path[0] {
if l.Hash == txid {
if *l.Hash == txid {
txidFound = true
index = l.Offset
index = *l.Offset
break
}
}
Expand All @@ -160,7 +162,7 @@ func (bump *BUMP) CalculateRootGivenTxid(txid string) (string, error) {
var leafAtThisLevel leaf
offsetFound := false
for _, l := range leaves {
if l.Offset == offset {
if *l.Offset == offset {
offsetFound = true
leafAtThisLevel = l
break
Expand All @@ -174,7 +176,7 @@ func (bump *BUMP) CalculateRootGivenTxid(txid string) (string, error) {
if leafAtThisLevel.Duplicate != nil {
digest = append(workingHash, workingHash...)
} else {
leafBytes := BytesFromStringReverse(leafAtThisLevel.Hash)
leafBytes := BytesFromStringReverse(*leafAtThisLevel.Hash)
if (offset % 2) != 0 {
digest = append(workingHash, leafBytes...)
} else {
Expand All @@ -185,3 +187,62 @@ func (bump *BUMP) CalculateRootGivenTxid(txid string) (string, error) {
}
return StringFromBytesReverse(workingHash), nil
}

// NewBUMPFromMerkleTreeAndIndex with merkle tree we calculate the merkle path for a given transaction.
func NewBUMPFromMerkleTreeAndIndex(blockHeight uint64, merkleTree []*chainhash.Hash, txIndex uint64) (*BUMP, error) {
bump := &BUMP{
BlockHeight: blockHeight,
Path: [][]leaf{},
}
t := true

numOfTxids := (len(merkleTree) + 1) / 2
treeHeight := int(math.Log2(float64(numOfTxids)))
numOfHashes := numOfTxids

if len(merkleTree) == 0 {
return nil, errors.New("merkle tree is empty")
}

offsets := make([]uint64, treeHeight)
for i := 0; i < treeHeight; i++ {
if txIndex>>uint64(i)&1 == 0 {
offsets[i] = txIndex>>uint64(i) + 1
} else {
offsets[i] = txIndex>>uint64(i) - 1
}
}

// if we have only one transaction in the block there is no merkle path to calculate
if len(merkleTree) != 1 {
// if our hash index is odd the next hash of the path is the previous element in the array otherwise the next element.
levelOffset := 0
for height := 0; height < treeHeight; height++ {
leaves := []leaf{}
bump.Path = append(bump.Path, leaves)
for offset := 0; offset < numOfHashes; offset++ {
o := uint64(offset)
thisLeaf := leaf{Offset: &o}
hash := merkleTree[levelOffset+offset]
if hash.IsEqual(&chainhash.Hash{}) {
thisLeaf.Duplicate = &t
} else {
sh := hash.String()
thisLeaf.Hash = &sh
if height == 0 {
thisLeaf.Txid = &t
}
}
bump.Path[height] = append(bump.Path[height], thisLeaf)
}
levelOffset += numOfHashes
numOfHashes >>= 1
}
} else {
sh := merkleTree[0].String()
o := uint64(0)
bump.Path[0][0] = leaf{Hash: &sh, Offset: &o, Txid: &t}
}

return bump, nil
}
38 changes: 35 additions & 3 deletions bump_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,49 @@ package bc
import (
"testing"

"github.com/libsv/go-p2p/chaincfg/chainhash"
"github.com/stretchr/testify/require"
)

const (
jsonExample = `{"blockHeight":814435,"path":[[{"offset":20,"hash":"0dc75b4efeeddb95d8ee98ded75d781fcf95d35f9d88f7f1ce54a77a0c7c50fe"},{"offset":21,"txid":true,"hash":"3ecead27a44d013ad1aae40038acbb1883ac9242406808bb4667c15b4f164eac"}],[{"offset":11,"hash":"5745cf28cd3a31703f611fb80b5a080da55acefa4c6977b21917d1ef95f34fbc"}],[{"offset":4,"hash":"522a096a1a6d3b64a4289ab456134158d8443f2c3b8ed8618bd2b842912d4b57"}],[{"offset":3,"hash":"191c70d2ecb477f90716d602f4e39f2f81f686f8f4230c255d1b534dc85fa051"}],[{"offset":0,"hash":"1f487b8cd3b11472c56617227e7e8509b44054f2a796f33c52c28fd5291578fd"}],[{"offset":1,"hash":"5ecc0ad4f24b5d8c7e6ec5669dc1d45fcb3405d8ce13c0860f66a35ef442f562"}],[{"offset":1,"hash":"31631241c8124bc5a9531c160bfddb6fcff3729f4e652b10d57cfd3618e921b1"}]]}`

hexExample = `fe636d0c0007021400fe507c0c7aa754cef1f7889d5fd395cf1f785dd7de98eed895dbedfe4e5bc70d1502ac4e164f5bc16746bb0868404292ac8318bbac3800e4aad13a014da427adce3e010b00bc4ff395efd11719b277694cface5aa50d085a0bb81f613f70313acd28cf4557010400574b2d9142b8d28b61d88e3b2c3f44d858411356b49a28a4643b6d1a6a092a5201030051a05fc84d531b5d250c23f4f886f6812f9fe3f402d61607f977b4ecd2701c19010000fd781529d58fc2523cf396a7f25440b409857e7e221766c57214b1d38c7b481f01010062f542f45ea3660f86c013ced80534cb5fd4c19d66c56e7e8c5d4bf2d40acc5e010100b121e91836fd7cd5102b654e9f72f3cf6fdbfd0b161c53a9c54b12c841126331`
rootExample = `bb6f640cc4ee56bf38eb5a1969ac0c16caa2d3d202b22bf3735d10eec0ca6e00`
txidExample = `3ecead27a44d013ad1aae40038acbb1883ac9242406808bb4667c15b4f164eac`
hexExample = `fe636d0c0007021400fe507c0c7aa754cef1f7889d5fd395cf1f785dd7de98eed895dbedfe4e5bc70d1502ac4e164f5bc16746bb0868404292ac8318bbac3800e4aad13a014da427adce3e010b00bc4ff395efd11719b277694cface5aa50d085a0bb81f613f70313acd28cf4557010400574b2d9142b8d28b61d88e3b2c3f44d858411356b49a28a4643b6d1a6a092a5201030051a05fc84d531b5d250c23f4f886f6812f9fe3f402d61607f977b4ecd2701c19010000fd781529d58fc2523cf396a7f25440b409857e7e221766c57214b1d38c7b481f01010062f542f45ea3660f86c013ced80534cb5fd4c19d66c56e7e8c5d4bf2d40acc5e010100b121e91836fd7cd5102b654e9f72f3cf6fdbfd0b161c53a9c54b12c841126331`
rootExample = `bb6f640cc4ee56bf38eb5a1969ac0c16caa2d3d202b22bf3735d10eec0ca6e00`
txidExample = `3ecead27a44d013ad1aae40038acbb1883ac9242406808bb4667c15b4f164eac`
rootOfBlockTxExample = `1a1e779cd7dfc59f603b4e88842121001af822b2dc5d3b167ae66152e586a6b0`
fakeMadeUpNum = 814435
)

var blockTxExample = []string{
"b6d4d13aa08bb4b6cdb3b329cef29b5a5d55d85a85c330d56fddbce78d99c7d6",
"426f65f6a6ce79c909e54d8959c874a767db3076e76031be70942b896cc64052",
"adc23d36cc457d5847968c2e4d5f017a6f12a2f165102d10d2843f5276cfe68e",
"728714bbbddd81a54cae473835ae99eb92ed78191327eb11a9d7494273dcad2a",
"e3aa0230aa81abd483023886ad12790acf070e2a9f92d7f0ae3bebd90a904361",
"4848b9e94dd0e4f3173ebd6982ae7eb6b793de305d8450624b1d86c02a5c61d9",
"912f77eefdd311e24f96850ed8e701381fc4943327f9cf73f9c4dec0d93a056d",
"397fe2ae4d1d24efcc868a02daae42d1b419289d9a1ded3a5fe771efcc1219d9",
}

func TestNewBUMPFromMerkleTree(t *testing.T) {
chainHashBlock := make([]*chainhash.Hash, 0)
for _, txid := range blockTxExample {
hash, err := chainhash.NewHashFromStr(txid)
require.NoError(t, err)
chainHashBlock = append(chainHashBlock, hash)
}
merkles, err := BuildMerkleTreeStoreChainHash(chainHashBlock)
require.NoError(t, err)
for txIndex, txid := range blockTxExample {
bump, err := NewBUMPFromMerkleTreeAndIndex(fakeMadeUpNum, merkles, uint64(txIndex))
require.NoError(t, err)
root, err := bump.CalculateRootGivenTxid(txid)
require.NoError(t, err)
require.Equal(t, rootOfBlockTxExample, root)
}
}

func TestNewBUMPFromStr(t *testing.T) {
bump, err := NewBUMPFromStr(hexExample)
require.NoError(t, err)
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ require (
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/libsv/go-p2p v0.1.3
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rogpeppe/go-internal v1.9.0 // indirect
github.com/stretchr/objx v0.5.0 // indirect
golang.org/x/crypto v0.13.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
16 changes: 15 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
github.com/cbeuw/connutil v0.0.0-20200411215123-966bfaa51ee3/go.mod h1:6jR2SzckGv8hIIS9zWJ160mzGVVOYp4AXZMDtacL6LE=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
Expand All @@ -13,6 +14,15 @@ github.com/libsv/go-bk v0.1.6 h1:c9CiT5+64HRDbzxPl1v/oiFmbvWZTuUYqywCf+MBs/c=
github.com/libsv/go-bk v0.1.6/go.mod h1:khJboDoH18FPUaZlzRFKzlVN84d4YfdmlDtdX4LAjQA=
github.com/libsv/go-bt/v2 v2.2.5 h1:VoggBLMRW9NYoFujqe5bSYKqnw5y+fYfufgERSoubog=
github.com/libsv/go-bt/v2 v2.2.5/go.mod h1:cV45+jDlPOLfhJLfpLmpQoWzrIvVth9Ao2ZO1f6CcqU=
github.com/libsv/go-p2p v0.1.3 h1:70v/k7d6mtFPRP8tXYpAMGZNTxqSZAVTbYeTPuTbjTA=
github.com/libsv/go-p2p v0.1.3/go.mod h1:5+VqOblMYadFH7pmm55PcfbbWcXib8cTh9CHIrrxtZg=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/ordishs/go-utils v1.0.24/go.mod h1:k9G7Bbv2GwoOn9fwZx70yM5jwwIymkv+90FUKLudtyc=
github.com/ordishs/gocore v1.0.33/go.mod h1:Nm48yxIUBuKvVXwLC8bB8aQDNUsBpaoVRTtcmjKlhrQ=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
Expand All @@ -22,11 +32,11 @@ github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZV
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
Expand All @@ -48,10 +58,13 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
Expand All @@ -63,6 +76,7 @@ golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
Expand Down
38 changes: 37 additions & 1 deletion merkleroot.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/libsv/go-bk/crypto"
"github.com/libsv/go-bt/v2"
"github.com/libsv/go-p2p/chaincfg/chainhash"
)

// TxsToTxIDs takes an array of transactions
Expand Down Expand Up @@ -67,7 +68,7 @@ func BuildMerkleRoot(txids []string) (string, error) {
//
// The above stored as a linear array is as follows:
//
// [h1 h2 h3 h4 h12 h34 root]
// [h1 h2 h3 h4 h12 h34 root]
//
// As the above shows, the merkle root is always the last element in the array.
//
Expand Down Expand Up @@ -128,6 +129,41 @@ func BuildMerkleTreeStore(txids []string) ([]string, error) {
return merkles, nil
}

// BuildMerkleTreeStoreChainHash has the same functionality as BuildMerkleTreeStore but uses chainhash as a type to avoid string conversions.
func BuildMerkleTreeStoreChainHash(txids []*chainhash.Hash) ([]*chainhash.Hash, error) {
// // Calculate how many entries are re?n array of that size.
nextPoT := nextPowerOfTwo(len(txids))
arraySize := nextPoT*2 - 1
merkles := make([]*chainhash.Hash, arraySize)

// Create the base transaction hashes and populate the array with them.
copy(merkles, txids)

// Start the array offset after the last transaction and adjusted to the
// next power of two.
offset := nextPoT
for i := 0; i < arraySize-1; i += 2 {
switch {
// When there is no left child node, the parent is nil ("") too.
case merkles[i].IsEqual(&chainhash.Hash{}):
merkles[offset] = &chainhash.Hash{}

// When there is no right child, the parent is generated by
// hashing the concatenation of the left child with itself.
case merkles[i+1].IsEqual(&chainhash.Hash{}):
merkles[offset] = MerkleTreeParentBytes(merkles[i], merkles[i])

// The normal case sets the parent node to the double sha256
// of the concatenation of the left and right children.
default:
merkles[offset] = MerkleTreeParentBytes(merkles[i], merkles[i+1])
}
offset++
}

return merkles, nil
}

// nextPowerOfTwo returns the next highest power of two from a given number if
// it is not already a power of two. This is a helper function used during the
// calculation of a merkle tree.
Expand Down
Loading

0 comments on commit 58361c8

Please sign in to comment.