Split and cleanup merkle tree for transactions
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
951b1105ff
commit
3b268558d8
|
@ -10,7 +10,6 @@ import (
|
|||
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/randomx"
|
||||
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/transaction"
|
||||
"git.gammaspectra.live/P2Pool/p2pool-observer/types"
|
||||
"git.gammaspectra.live/P2Pool/sha3"
|
||||
"io"
|
||||
)
|
||||
|
||||
|
@ -244,7 +243,10 @@ func (b *Block) HashingBlobBufferLength() int {
|
|||
func (b *Block) HashingBlob(preAllocatedBuf []byte) []byte {
|
||||
buf := b.HeaderBlob(preAllocatedBuf)
|
||||
|
||||
txTreeHash := b.TxTreeHash()
|
||||
merkleTree := make(crypto.BinaryTreeHash, len(b.Transactions)+1)
|
||||
merkleTree[0] = b.Coinbase.Id()
|
||||
copy(merkleTree[1:], b.Transactions)
|
||||
txTreeHash := merkleTree.RootHash()
|
||||
buf = append(buf, txTreeHash[:]...)
|
||||
|
||||
buf = binary.AppendUvarint(buf, uint64(len(b.Transactions)+1))
|
||||
|
@ -252,69 +254,6 @@ func (b *Block) HashingBlob(preAllocatedBuf []byte) []byte {
|
|||
return buf
|
||||
}
|
||||
|
||||
func (b *Block) TxTreeHash() (rootHash types.Hash) {
|
||||
//TODO: cache
|
||||
//transaction hashes
|
||||
h := make([]byte, 0, types.HashSize*len(b.Transactions)+types.HashSize)
|
||||
coinbaseTxId := b.Coinbase.Id()
|
||||
|
||||
h = append(h, coinbaseTxId[:]...)
|
||||
for _, txId := range b.Transactions {
|
||||
h = append(h, txId[:]...)
|
||||
}
|
||||
|
||||
count := len(b.Transactions) + 1
|
||||
if count == 1 {
|
||||
rootHash = types.HashFromBytes(h)
|
||||
} else if count == 2 {
|
||||
rootHash = crypto.PooledKeccak256(h)
|
||||
} else {
|
||||
hashInstance := crypto.GetKeccak256Hasher()
|
||||
defer crypto.PutKeccak256Hasher(hashInstance)
|
||||
var cnt int
|
||||
|
||||
{
|
||||
//TODO: expand this loop properly
|
||||
//find closest low power of two
|
||||
for cnt = 1; cnt <= count; cnt <<= 1 {
|
||||
}
|
||||
cnt >>= 1
|
||||
}
|
||||
|
||||
ints := make([]byte, cnt*types.HashSize)
|
||||
copy(ints, h[:(cnt*2-count)*types.HashSize])
|
||||
|
||||
{
|
||||
i := cnt*2 - count
|
||||
j := cnt*2 - count
|
||||
for j < cnt {
|
||||
keccakl(hashInstance, ints[j*types.HashSize:], h[i*types.HashSize:], types.HashSize*2)
|
||||
i += 2
|
||||
j++
|
||||
}
|
||||
}
|
||||
|
||||
for cnt > 2 {
|
||||
cnt >>= 1
|
||||
{
|
||||
i := 0
|
||||
j := 0
|
||||
|
||||
for j < cnt {
|
||||
keccakl(hashInstance, ints[j*types.HashSize:], ints[i*types.HashSize:], types.HashSize*2)
|
||||
|
||||
i += 2
|
||||
j++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
keccakl(hashInstance, rootHash[:], ints, types.HashSize*2)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (b *Block) Difficulty(f GetDifficultyByHeightFunc) types.Difficulty {
|
||||
//cached by sidechain.Share
|
||||
return f(b.Coinbase.GenHeight)
|
||||
|
@ -335,9 +274,3 @@ func (b *Block) Id() types.Hash {
|
|||
buf := b.HashingBlob(make([]byte, 0, b.HashingBlobBufferLength()))
|
||||
return crypto.PooledKeccak256(varIntBuf[:binary.PutUvarint(varIntBuf[:], uint64(len(buf)))], buf)
|
||||
}
|
||||
|
||||
func keccakl(hasher *sha3.HasherState, dst []byte, data []byte, len int) {
|
||||
hasher.Reset()
|
||||
_, _ = hasher.Write(data[:len])
|
||||
crypto.HashFastSum(hasher, dst)
|
||||
}
|
||||
|
|
57
monero/crypto/merkle.go
Normal file
57
monero/crypto/merkle.go
Normal file
|
@ -0,0 +1,57 @@
|
|||
package crypto
|
||||
|
||||
import (
|
||||
"git.gammaspectra.live/P2Pool/p2pool-observer/types"
|
||||
"git.gammaspectra.live/P2Pool/p2pool-observer/utils"
|
||||
"git.gammaspectra.live/P2Pool/sha3"
|
||||
)
|
||||
|
||||
type BinaryTreeHash []types.Hash
|
||||
|
||||
func (t BinaryTreeHash) leafHash(hasher *sha3.HasherState) (rootHash types.Hash) {
|
||||
switch len(t) {
|
||||
case 0:
|
||||
panic("unsupported length")
|
||||
case 1:
|
||||
return t[0]
|
||||
case 2:
|
||||
hasher.Reset()
|
||||
_, _ = hasher.Write(t[0][:])
|
||||
_, _ = hasher.Write(t[1][:])
|
||||
HashFastSum(hasher, rootHash[:])
|
||||
return rootHash
|
||||
default:
|
||||
panic("unsupported length")
|
||||
}
|
||||
}
|
||||
|
||||
func (t BinaryTreeHash) RootHash() (rootHash types.Hash) {
|
||||
|
||||
hasher := GetKeccak256Hasher()
|
||||
defer PutKeccak256Hasher(hasher)
|
||||
count := len(t)
|
||||
if count <= 2 {
|
||||
return t.leafHash(hasher)
|
||||
}
|
||||
|
||||
cnt := utils.PreviousPowerOfTwo(uint64(len(t)))
|
||||
|
||||
temporaryTree := make(BinaryTreeHash, cnt)
|
||||
copy(temporaryTree, t[:cnt*2-count])
|
||||
|
||||
offset := cnt*2 - count
|
||||
for i := 0; (i + offset) < cnt; i++ {
|
||||
temporaryTree[offset+i] = t[offset+i*2 : offset+i*2+2].leafHash(hasher)
|
||||
}
|
||||
|
||||
for cnt > 2 {
|
||||
cnt >>= 1
|
||||
for i := 0; i < cnt; i++ {
|
||||
temporaryTree[i] = temporaryTree[i*2 : i*2+2].leafHash(hasher)
|
||||
}
|
||||
}
|
||||
|
||||
rootHash = temporaryTree[:2].leafHash(hasher)
|
||||
|
||||
return
|
||||
}
|
|
@ -4,6 +4,7 @@ import (
|
|||
"encoding/hex"
|
||||
"github.com/jxskiss/base62"
|
||||
"golang.org/x/exp/constraints"
|
||||
"math/bits"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
@ -83,3 +84,10 @@ func Max[T constraints.Ordered](v0 T, values ...T) (result T) {
|
|||
}
|
||||
return
|
||||
}
|
||||
|
||||
func PreviousPowerOfTwo(x uint64) int {
|
||||
if x == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1 << (64 - bits.LeadingZeros64(x) - 1)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package utils
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNumber(t *testing.T) {
|
||||
s := "S"
|
||||
|
@ -10,3 +12,19 @@ func TestNumber(t *testing.T) {
|
|||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestPreviousPowerOfTwo(t *testing.T) {
|
||||
loopPath := func(x uint64) int {
|
||||
//find closest low power of two
|
||||
var cnt uint64
|
||||
for cnt = 1; cnt <= x; cnt <<= 1 {
|
||||
}
|
||||
cnt >>= 1
|
||||
return int(cnt)
|
||||
}
|
||||
for i := uint64(1); i < 65536; i++ {
|
||||
if PreviousPowerOfTwo(i) != loopPath(i) {
|
||||
t.Fatalf("expected %d, got %d for iteration %d", loopPath(i), PreviousPowerOfTwo(i), i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue