From 2d9185a5ec2b039d6365b6489fefcaba64da1999 Mon Sep 17 00:00:00 2001 From: Trinity Date: Wed, 22 Oct 2025 08:44:24 +0700 Subject: [PATCH] fix: send tx with 2 notes --- crypto/master_key.go | 1 + wallet/nockhash.go | 215 +++++++++++++---------------------------- wallet/service.go | 84 ++++++++-------- wallet/service_test.go | 18 +--- wallet/ztree.go | 185 ++++++++++++++++++++++++++--------- 5 files changed, 247 insertions(+), 256 deletions(-) diff --git a/crypto/master_key.go b/crypto/master_key.go index 5606061..8319f70 100644 --- a/crypto/master_key.go +++ b/crypto/master_key.go @@ -18,6 +18,7 @@ var ( MagicDyckForPoint = []uint64{0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1} MagicDyckForT8 = []uint64{0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1} MagicDyckForSeed = []uint64{0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1} + MagicDyckForName = []uint64{0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1} Tip5Zero = [5]uint64{1730770831742798981, 2676322185709933211, 8329210750824781744, 16756092452590401876, 3547445316740171466} Tip5One = [5]uint64{6727110957294540849, 15606243244732609007, 11887284596344881785, 10646863421881571398, 8146872807338919620} Tip5ZeroZero = [5]uint64{4372149332062030091, 17876920912185183887, 13348798570422431948, 8872865212694716527, 3385176510443841516} diff --git a/wallet/nockhash.go b/wallet/nockhash.go index ac50671..6afa06a 100644 --- a/wallet/nockhash.go +++ b/wallet/nockhash.go @@ -61,6 +61,24 @@ func HashName(name *nockchain.NockchainName) [5]uint64 { return crypto.Tip5RehashTenCell(firstNameHash, crypto.Tip5RehashTenCell(lastNameHash, crypto.Tip5Zero)) } +func HashNameVarLen(name *nockchain.NockchainName) [5]uint64 { + belts := []crypto.Belt{} + for _, i := range crypto.Base58ToTip5Hash(name.First) { + belts = append(belts, crypto.Belt{Value: i}) + } + for _, i := range crypto.Base58ToTip5Hash(name.Last) { + belts = append(belts, crypto.Belt{Value: i}) + } + belts = append(belts, crypto.BELT_ZERO) + size := len(belts) + belts = append([]crypto.Belt{{Value: uint64(size)}}, belts...) + for _, i := range crypto.MagicDyckForName { + belts = append(belts, crypto.Belt{Value: i}) + } + res := crypto.Tip5HashBelts(belts) + return res +} + func HashNote(note *nockchain.NockchainNote) ([5]uint64, error) { versionHash := crypto.Tip5HashBelts([]crypto.Belt{{Value: 1}, {Value: uint64(note.Version)}}) blockHash := crypto.Tip5HashBelts([]crypto.Belt{{Value: 1}, {Value: note.BlockHeight}}) @@ -225,86 +243,36 @@ func HashSpend(spend *nockchain.NockchainSpend) ([5]uint64, error) { if err != nil { return [5]uint64{}, err } - seedsCount := len(spend.Seeds) - var finalSeedHash [5]uint64 - if seedsCount == 1 { - seedHash, err := HashSeedWithoutSource(spend.Seeds[0]) - if err != nil { - return [5]uint64{}, err - } - finalSeedHash = crypto.Tip5RehashTenCell(seedHash, crypto.Tip5ZeroZero) - } else { - seed1Hash, err := HashSeedWithoutSource(spend.Seeds[0]) - if err != nil { - return [5]uint64{}, err - } - seed1HashVarLen, err := HashSeedVarLen(spend.Seeds[0]) - if err != nil { - return [5]uint64{}, err - } - seed1DoubleHash := crypto.Tip5RehashTenCell(seed1HashVarLen, seed1HashVarLen) - seed2Hash, err := HashSeedWithoutSource(spend.Seeds[1]) - if err != nil { - return [5]uint64{}, err - } - seed2HashVarLen, err := HashSeedVarLen(spend.Seeds[1]) - if err != nil { - return [5]uint64{}, err - } - seed2DoubleHash := crypto.Tip5RehashTenCell(seed2HashVarLen, seed2HashVarLen) - - seedHash1BigInt := new(big.Int).SetBytes(base58.Decode(crypto.Tip5HashToBase58(seed1HashVarLen))) - seed1DoubleHashBigInt := new(big.Int).SetBytes(base58.Decode(crypto.Tip5HashToBase58(seed1DoubleHash))) - seedHash2BigInt := new(big.Int).SetBytes(base58.Decode(crypto.Tip5HashToBase58(seed2HashVarLen))) - seed2DoubleHashBigInt := new(big.Int).SetBytes(base58.Decode(crypto.Tip5HashToBase58(seed2DoubleHash))) - if seedHash1BigInt.Cmp(seedHash2BigInt) == -1 { - // seed 1 < seed 2 - - if seed1DoubleHashBigInt.Cmp(seed2DoubleHashBigInt) == -1 { - // seed1 - // / \ - // ~ seed2 - // / \ - // ~ ~ - finalSeedHash = crypto.Tip5RehashTenCell(seed2Hash, crypto.Tip5ZeroZero) - finalSeedHash = crypto.Tip5RehashTenCell(crypto.Tip5Zero, finalSeedHash) - finalSeedHash = crypto.Tip5RehashTenCell(seed1Hash, finalSeedHash) + seedsTree := NewZTree( + func(i interface{}) [5]uint64 { + if seed, ok := i.(*nockchain.NockchainSeed); ok { + seedHash, err := HashSeedVarLen(seed) + if err != nil { + return [5]uint64{} + } + return seedHash } else { - // seed2 - // / \ - // seed1 ~ - // / \ - // ~ ~ - finalSeedHash = crypto.Tip5RehashTenCell(seed1Hash, crypto.Tip5ZeroZero) - finalSeedHash = crypto.Tip5RehashTenCell(finalSeedHash, crypto.Tip5Zero) - finalSeedHash = crypto.Tip5RehashTenCell(seed2Hash, finalSeedHash) + return [5]uint64{} } - } else { - // seed 1 > seed 2 - - if seed1DoubleHashBigInt.Cmp(seed2DoubleHashBigInt) == -1 { - // seed1 - // / \ - // seed2 ~ - // / \ - // ~ ~ - finalSeedHash = crypto.Tip5RehashTenCell(seed2Hash, crypto.Tip5ZeroZero) - finalSeedHash = crypto.Tip5RehashTenCell(finalSeedHash, crypto.Tip5Zero) - finalSeedHash = crypto.Tip5RehashTenCell(seed1Hash, finalSeedHash) + }, + func(i interface{}) ([5]uint64, error) { + if seed, ok := i.(*nockchain.NockchainSeed); ok { + hash, err := HashSeedWithoutSource(seed) + return hash, err } else { - // seed2 - // / \ - // ~ seed1 - // / \ - // ~ ~ - finalSeedHash = crypto.Tip5RehashTenCell(seed1Hash, crypto.Tip5ZeroZero) - finalSeedHash = crypto.Tip5RehashTenCell(crypto.Tip5Zero, finalSeedHash) - finalSeedHash = crypto.Tip5RehashTenCell(seed2Hash, finalSeedHash) - + return [5]uint64{}, fmt.Errorf("invalid input type") } - } + }, + ) + for _, seed := range spend.Seeds { + seedsTree.Insert(seed, seed) } + finalSeedHash, err := seedsTree.Hash() + if err != nil { + return [5]uint64{}, err + } + feeHash := crypto.Tip5HashBelts([]crypto.Belt{{Value: 1}, {Value: spend.Fee}}) seedHashFee := crypto.Tip5RehashTenCell(finalSeedHash, feeHash) return crypto.Tip5RehashTenCell(sigHash, seedHashFee), nil @@ -312,84 +280,33 @@ func HashSpend(spend *nockchain.NockchainSpend) ([5]uint64, error) { } func HashMsg(spend *nockchain.NockchainSpend) ([5]uint64, error) { - seedsCount := len(spend.Seeds) - var finalSeedHash [5]uint64 - if seedsCount == 1 { - seedHash, err := HashSeed(spend.Seeds[0]) - if err != nil { - return [5]uint64{}, err - } - - finalSeedHash = crypto.Tip5RehashTenCell(seedHash, crypto.Tip5ZeroZero) - } else { - seed1HashVarLen, err := HashSeedVarLen(spend.Seeds[0]) - if err != nil { - return [5]uint64{}, err - } - seed1Hash, err := HashSeed(spend.Seeds[0]) - if err != nil { - return [5]uint64{}, err - } - seed1DoubleHash := crypto.Tip5RehashTenCell(seed1HashVarLen, seed1HashVarLen) - seed2HashVarLen, err := HashSeedVarLen(spend.Seeds[1]) - if err != nil { - return [5]uint64{}, err - } - seed2Hash, err := HashSeed(spend.Seeds[1]) - if err != nil { - return [5]uint64{}, err - } - seed2DoubleHash := crypto.Tip5RehashTenCell(seed2HashVarLen, seed2HashVarLen) - - seedHash1BigInt := new(big.Int).SetBytes(base58.Decode(crypto.Tip5HashToBase58(seed1HashVarLen))) - seed1DoubleHashBigInt := new(big.Int).SetBytes(base58.Decode(crypto.Tip5HashToBase58(seed1DoubleHash))) - seedHash2BigInt := new(big.Int).SetBytes(base58.Decode(crypto.Tip5HashToBase58(seed2HashVarLen))) - seed2DoubleHashBigInt := new(big.Int).SetBytes(base58.Decode(crypto.Tip5HashToBase58(seed2DoubleHash))) - if seedHash1BigInt.Cmp(seedHash2BigInt) == -1 { - // seed 1 < seed 2 - - if seed1DoubleHashBigInt.Cmp(seed2DoubleHashBigInt) == -1 { - // seed1 - // / \ - // ~ seed2 - // / \ - // ~ ~ - finalSeedHash = crypto.Tip5RehashTenCell(seed2Hash, crypto.Tip5ZeroZero) - finalSeedHash = crypto.Tip5RehashTenCell(crypto.Tip5Zero, finalSeedHash) - finalSeedHash = crypto.Tip5RehashTenCell(seed1Hash, finalSeedHash) + seedsTree := NewZTree( + func(i interface{}) [5]uint64 { + if seed, ok := i.(*nockchain.NockchainSeed); ok { + seedHash, err := HashSeedVarLen(seed) + if err != nil { + return [5]uint64{} + } + return seedHash } else { - // seed2 - // / \ - // seed1 ~ - // / \ - // ~ ~ - finalSeedHash = crypto.Tip5RehashTenCell(seed1Hash, crypto.Tip5ZeroZero) - finalSeedHash = crypto.Tip5RehashTenCell(finalSeedHash, crypto.Tip5Zero) - finalSeedHash = crypto.Tip5RehashTenCell(seed2Hash, finalSeedHash) + return [5]uint64{} } - } else { - // seed 1 > seed 2 - - if seed1DoubleHashBigInt.Cmp(seed2DoubleHashBigInt) == -1 { - // seed1 - // / \ - // seed2 ~ - // / \ - // ~ ~ - finalSeedHash = crypto.Tip5RehashTenCell(seed2Hash, crypto.Tip5ZeroZero) - finalSeedHash = crypto.Tip5RehashTenCell(finalSeedHash, crypto.Tip5Zero) - finalSeedHash = crypto.Tip5RehashTenCell(seed1Hash, finalSeedHash) + }, + func(i interface{}) ([5]uint64, error) { + if seed, ok := i.(*nockchain.NockchainSeed); ok { + hash, err := HashSeed(seed) + return hash, err } else { - // seed2 - // / \ - // ~ seed1 - // / \ - // ~ ~ - finalSeedHash = crypto.Tip5RehashTenCell(seed1Hash, crypto.Tip5ZeroZero) - finalSeedHash = crypto.Tip5RehashTenCell(crypto.Tip5Zero, finalSeedHash) - finalSeedHash = crypto.Tip5RehashTenCell(seed2Hash, finalSeedHash) + return [5]uint64{}, fmt.Errorf("invalid input type") } - } + }, + ) + for _, seed := range spend.Seeds { + seedsTree.Insert(seed, seed) + } + finalSeedHash, err := seedsTree.Hash() + if err != nil { + return [5]uint64{}, err } feeHash := crypto.Tip5HashBelts([]crypto.Belt{{Value: 1}, {Value: spend.Fee}}) return crypto.Tip5RehashTenCell(finalSeedHash, feeHash), nil @@ -417,7 +334,7 @@ func ComputeTxId(inputs []*nockchain.NockchainInput, timelockRange *nockchain.Ti inputTree := NewZTree( func(i interface{}) [5]uint64 { if name, ok := i.(*nockchain.NockchainName); ok { - return HashName(name) + return HashNameVarLen(name) } else { return [5]uint64{} } diff --git a/wallet/service.go b/wallet/service.go index 1c0dfc9..6d13916 100644 --- a/wallet/service.go +++ b/wallet/service.go @@ -287,7 +287,7 @@ func (h *GprcHandler) CreateTx(ctx context.Context, req *nockchain.CreateTxReque nameTree := NewZTree( func(i interface{}) [5]uint64 { if name, ok := i.(*nockchain.NockchainName); ok { - return HashName(name) + return HashNameVarLen(name) } else { return [5]uint64{} } @@ -297,15 +297,17 @@ func (h *GprcHandler) CreateTx(ctx context.Context, req *nockchain.CreateTxReque for _, name := range nnames { nameTree.Insert(name, nil) } - nameLeftMost := nameTree.KeyLeftMost().(*nockchain.NockchainName) - idxLeftMost := -1 - for i, name := range nnames { - if name.First == nameLeftMost.First && name.Last == nameLeftMost.Last { - idxLeftMost = i - } + nameList := nameTree.Tap() + nameKeys := []string{} + for _, nameKey := range nameList { + name := nameKey.Key.(*nockchain.NockchainName) + nameKeys = append(nameKeys, name.First+" "+name.Last) } - if idxLeftMost == -1 { - return nil, fmt.Errorf("unable to find left most node") + indices := make([]int, len(nnames)) + for i, name := range nnames { + if idx := slices.Index(nameKeys, name.First+" "+name.Last); idx != -1 { + indices[idx] = i + } } recipents := []*nockchain.NockchainLock{} @@ -411,40 +413,40 @@ func (h *GprcHandler) CreateTx(ctx context.Context, req *nockchain.CreateTxReque } } - inputs := []*nockchain.NockchainInput{} - for i := 0; i < len(names); i++ { - if notes[i] == nil { - return nil, fmt.Errorf("notes scanned is missing at name: %s", names[i]) + inputs := make([]*nockchain.NockchainInput, len(names)) + isSpent := false + for i := 0; i < len(nameKeys); i++ { + idx := indices[i] + if notes[idx] == nil { + return nil, fmt.Errorf("notes scanned is missing at name: %s", names[idx]) } - if notes[i].Asset < gifts[i]+req.Fee { - return nil, fmt.Errorf("note not enough balance") + if notes[idx].Asset < gifts[idx] { + return nil, fmt.Errorf("note %s not enough balance", names[idx]) } - parentHash, err := HashNote(notes[i]) + parentHash, err := HashNote(notes[idx]) if err != nil { return nil, err } seeds := []*nockchain.NockchainSeed{ { OutputSource: nil, - Recipient: recipents[i], + Recipient: recipents[idx], TimelockIntent: req.TimelockIntent, - Gift: gifts[i], + Gift: gifts[idx], ParentHash: crypto.Tip5HashToBase58(parentHash), }, } - if notes[i].Asset < gifts[i] { - return nil, fmt.Errorf("insufficient funds for notes %s", names[i]) - } - assetLeft := notes[i].Asset - gifts[i] - if i == idxLeftMost { - if assetLeft > req.Fee { - assetLeft -= req.Fee - } else { - return nil, fmt.Errorf("insufficient funds for notes %s", names[i]) - } + + assetLeft := notes[idx].Asset - gifts[idx] + fee := uint64(0) + if !isSpent && assetLeft >= req.Fee { + isSpent = true + fee = req.Fee + assetLeft -= req.Fee } + if assetLeft != 0 { seeds = append(seeds, &nockchain.NockchainSeed{ OutputSource: nil, @@ -457,19 +459,10 @@ func (h *GprcHandler) CreateTx(ctx context.Context, req *nockchain.CreateTxReque ParentHash: crypto.Tip5HashToBase58(parentHash), }) } - var spend nockchain.NockchainSpend - if i == idxLeftMost { - spend = nockchain.NockchainSpend{ - Signatures: nil, - Seeds: seeds, - Fee: req.Fee, - } - } else { - spend = nockchain.NockchainSpend{ - Signatures: nil, - Seeds: seeds, - Fee: 0, - } + spend := nockchain.NockchainSpend{ + Signatures: nil, + Seeds: seeds, + Fee: fee, } msg, err := HashMsg(&spend) @@ -492,13 +485,16 @@ func (h *GprcHandler) CreateTx(ctx context.Context, req *nockchain.CreateTxReque } input := nockchain.NockchainInput{ - Name: nnames[i], - Note: notes[i], + Name: nnames[idx], + Note: notes[idx], Spend: &spend, } - inputs = append(inputs, &input) + inputs[idx] = &input } + if !isSpent { + return nil, fmt.Errorf("insufficient funds") + } var timelockRange *nockchain.TimelockRange if req.TimelockIntent == nil { timelockRange = &nockchain.TimelockRange{ diff --git a/wallet/service_test.go b/wallet/service_test.go index 5cc6daf..40f254e 100644 --- a/wallet/service_test.go +++ b/wallet/service_test.go @@ -472,21 +472,5 @@ func TestCreateTx(t *testing.T) { assert.NoError(t, err) // the result is taken from create-tx scripts - //assert.Equal(t, res.RawTx.TxId, "8gjTa6H1MrKXPWgNJCF9fsYE7PUfqhYzxetUoTftz7zyHCv24ZYHDM3") - - _, err = nc.WalletSendTransaction(res.RawTx) - if err != nil { - panic(err) - } - - txAccepted, err := nc.TxAccepted(res.RawTx.TxId) - if err != nil { - panic(err) - } - - fmt.Println("tx pass: ", res.RawTx.TxId) - if !txAccepted.GetAccepted() { - panic("tx not accepted") - } - // expected: A8vSeRde61B4sZccSnNPEnkQgTe15EssoFwyhQXbkhtk4UNm5hyGSid + assert.Equal(t, res.RawTx.TxId, "A8vSeRde61B4sZccSnNPEnkQgTe15EssoFwyhQXbkhtk4UNm5hyGSid") } diff --git a/wallet/ztree.go b/wallet/ztree.go index ce8d3d6..87f0e4b 100644 --- a/wallet/ztree.go +++ b/wallet/ztree.go @@ -1,8 +1,10 @@ package wallet import ( + "math/big" "slices" + "github.com/btcsuite/btcd/btcutil/base58" "github.com/phamminh0811/private-grpc/crypto" ) @@ -20,6 +22,11 @@ type ZTree struct { HashValueFunc func(interface{}) ([5]uint64, error) } +type ZPair struct { + Key interface{} + Value interface{} +} + func NewZTree(hashKeyFunc func(interface{}) [5]uint64, hashValueFunc func(interface{}) ([5]uint64, error)) *ZTree { return &ZTree{ Root: nil, @@ -36,13 +43,13 @@ func (z *ZTree) Hash() ([5]uint64, error) { return z.Root.HashNode(z.HashValueFunc) } -func (z *ZTree) KeyLeftMost() interface{} { - node := z.Root - for node.Left != nil { - node = node.Left - } - return node.Key +func (t *ZTree) Tap() []ZPair { + tap := []ZPair{} + TapNode(t.Root, &tap) + slices.Reverse(tap) + return tap } + func (node *ZNode) InsertNode(hashFunc func(interface{}) [5]uint64, key, value interface{}) *ZNode { if node == nil { node = &ZNode{ @@ -53,42 +60,18 @@ func (node *ZNode) InsertNode(hashFunc func(interface{}) [5]uint64, key, value i } return node } - keyHash := hashFunc(key) keyDoubleHash := crypto.Tip5RehashTenCell(keyHash, keyHash) nodeKeyHash := hashFunc(node.Key) nodeKeyDoubleHash := crypto.Tip5RehashTenCell(nodeKeyHash, nodeKeyHash) - if slices.Compare(keyHash[:], nodeKeyHash[:]) == -1 { + keyHashBigInt := new(big.Int).SetBytes(base58.Decode(crypto.Tip5HashToBase58(keyHash))) + nodeKeyHashBigInt := new(big.Int).SetBytes(base58.Decode(crypto.Tip5HashToBase58(nodeKeyHash))) + keyDoubleHashBigInt := new(big.Int).SetBytes(base58.Decode(crypto.Tip5HashToBase58(keyDoubleHash))) + nodeKeyDoubleHashBigInt := new(big.Int).SetBytes(base58.Decode(crypto.Tip5HashToBase58(nodeKeyDoubleHash))) + if keyHashBigInt.Cmp(nodeKeyHashBigInt) == 1 { // key < node key - if slices.Compare(keyDoubleHash[:], nodeKeyDoubleHash[:]) == -1 { - // reinsert in left - node.Left = node.Left.InsertNode(hashFunc, key, value) - } else { - // new key - // / \ - // ~ old key - // / \ - // ... ... - nodeKey := node.Key - nodeValue := node.Value - leftNode := node.Left - rightNode := node.Right - node = &ZNode{ - Key: key, - Value: value, - Right: nil, - Left: &ZNode{ - Key: nodeKey, - Value: nodeValue, - Left: leftNode, - Right: rightNode, - }, - } - } - } else { - // key > node key - if slices.Compare(keyDoubleHash[:], nodeKeyDoubleHash[:]) == -1 { + if keyDoubleHashBigInt.Cmp(nodeKeyDoubleHashBigInt) == 1 { // reinsert in right node.Right = node.Right.InsertNode(hashFunc, key, value) } else { @@ -101,16 +84,104 @@ func (node *ZNode) InsertNode(hashFunc func(interface{}) [5]uint64, key, value i nodeValue := node.Value leftNode := node.Left rightNode := node.Right - node = &ZNode{ - Key: key, - Value: value, - Right: &ZNode{ - Key: nodeKey, - Value: nodeValue, - Left: leftNode, - Right: rightNode, - }, - Left: nil, + if rightNode == nil { + node = &ZNode{ + Key: key, + Value: value, + Right: nil, + Left: &ZNode{ + Key: nodeKey, + Value: nodeValue, + Left: leftNode, + Right: nil, + }, + } + } else { + rightNodeKeyHash := hashFunc(rightNode.Key) + rightNodeKeyHashBigInt := new(big.Int).SetBytes(base58.Decode(crypto.Tip5HashToBase58(rightNodeKeyHash))) + if rightNodeKeyHashBigInt.Cmp(keyHashBigInt) == -1 { + node = &ZNode{ + Key: key, + Value: value, + Right: nil, + Left: &ZNode{ + Key: nodeKey, + Value: nodeValue, + Left: leftNode, + Right: rightNode, + }, + } + } else { + node = &ZNode{ + Key: key, + Value: value, + Right: rightNode, + Left: &ZNode{ + Key: nodeKey, + Value: nodeValue, + Left: leftNode, + Right: nil, + }, + } + } + } + + } + } else { + // key > node key + if keyDoubleHashBigInt.Cmp(nodeKeyDoubleHashBigInt) == 1 { + // reinsert in left + node.Left = node.Left.InsertNode(hashFunc, key, value) + } else { + // new key + // / \ + // ~ old key + // / \ + // ... ... + nodeKey := node.Key + nodeValue := node.Value + leftNode := node.Left + rightNode := node.Right + if leftNode == nil { + node = &ZNode{ + Key: key, + Value: value, + Right: &ZNode{ + Key: nodeKey, + Value: nodeValue, + Left: nil, + Right: rightNode, + }, + Left: nil, + } + } else { + leftNodeKeyHash := hashFunc(leftNode.Key) + leftNodeKeyHashBigInt := new(big.Int).SetBytes(base58.Decode(crypto.Tip5HashToBase58(leftNodeKeyHash))) + if leftNodeKeyHashBigInt.Cmp(keyHashBigInt) == -1 { + node = &ZNode{ + Key: key, + Value: value, + Right: &ZNode{ + Key: nodeKey, + Value: nodeValue, + Left: nil, + Right: rightNode, + }, + Left: leftNode, + } + } else { + node = &ZNode{ + Key: key, + Value: value, + Right: &ZNode{ + Key: nodeKey, + Value: nodeValue, + Left: leftNode, + Right: rightNode, + }, + Left: nil, + } + } } } } @@ -138,3 +209,25 @@ func (node *ZNode) HashNode(hashFunc func(interface{}) ([5]uint64, error)) ([5]u } return crypto.Tip5RehashTenCell(valHash, hashLeftRight), nil } + +func TapNode(node *ZNode, acc *[]ZPair) { + if node == nil { + return + } + + stored := false + if node.Right != nil { + TapNode(node.Right, acc) + } else { + *acc = append(*acc, ZPair{Key: node.Key, Value: node.Value}) + stored = true + } + + if node.Left != nil { + TapNode(node.Left, acc) + } + + if !stored { + *acc = append(*acc, ZPair{Key: node.Key, Value: node.Value}) + } +}