Skip to content
Draft
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
43 changes: 43 additions & 0 deletions rvgo/fast/keccakfast.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package fast

import (
"reflect"
_ "unsafe" // we use go:linkname

"golang.org/x/crypto/sha3"
)

type keccakState struct {
a [25]uint64 // main state of the hash
// and other fields, unimportant
}

//go:noescape
//go:linkname keccakReset golang.org/x/crypto/sha3.(*state).Reset
func keccakReset(st *keccakState)

//go:noescape
//go:linkname keccakWrite golang.org/x/crypto/sha3.(*state).Write
func keccakWrite(st *keccakState, p []byte) (n int, err error)

//go:noescape
//go:linkname keccakRead golang.org/x/crypto/sha3.(*state).Read
func keccakRead(st *keccakState, out []byte) (n int, err error)

// example of how to get access to a hasher where the call arguments do not escape to the heap
var hasher = (*keccakState)(reflect.ValueOf(sha3.NewLegacyKeccak256()).UnsafePointer())

func hashPair(left, right [32]byte) (out [32]byte) {
keccakReset(hasher)
_, _ = keccakWrite(hasher, left[:])
_, _ = keccakWrite(hasher, right[:])
_, _ = keccakRead(hasher, out[:])
return
}

func hash(data [64]byte) (out [32]byte) {
keccakReset(hasher)
_, _ = keccakWrite(hasher, data[:])
_, _ = keccakRead(hasher, out[:])
return
}
11 changes: 2 additions & 9 deletions rvgo/fast/memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import (
"fmt"
"io"
"sort"

"github.com/ethereum/go-ethereum/crypto"
_ "unsafe"
)

// Note: 2**12 = 4 KiB, the minimum page-size in Unicorn for mmap
Expand All @@ -22,17 +21,11 @@ const (
ProofLen = 64 - 4
)

func HashPair(left, right [32]byte) [32]byte {
out := crypto.Keccak256Hash(left[:], right[:])
//fmt.Printf("0x%x 0x%x -> 0x%x\n", left, right, out)
return out
}

var zeroHashes = func() [256][32]byte {
// empty parts of the tree are all zero. Precompute the hash of each full-zero range sub-tree level.
var out [256][32]byte
for i := 1; i < 256; i++ {
out[i] = HashPair(out[i-1], out[i-1])
out[i] = hashPair(out[i-1], out[i-1])
}
return out
}()
Expand Down
22 changes: 11 additions & 11 deletions rvgo/fast/memory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ func TestMemoryMerkleProof(t *testing.T) {
for i := 32; i < len(proof); i += 32 {
sib := *(*[32]byte)(proof[i : i+32])
if path&1 != 0 {
node = HashPair(sib, node)
node = hashPair(sib, node)
} else {
node = HashPair(node, sib)
node = hashPair(node, sib)
}
path >>= 1
}
Expand Down Expand Up @@ -253,9 +253,9 @@ func verifyProof(t *testing.T, expectedRoot [32]byte, proof [ProofLen * 32]byte,
for i := 32; i < len(proof); i += 32 {
sib := *(*[32]byte)(proof[i : i+32])
if path&1 != 0 {
node = HashPair(sib, node)
node = hashPair(sib, node)
} else {
node = HashPair(node, sib)
node = hashPair(node, sib)
}
path >>= 1
}
Expand Down Expand Up @@ -310,14 +310,14 @@ func TestMemoryMerkleRoot(t *testing.T) {
p6 := m.radix.MerkleizeNode(0, 14)
p7 := m.radix.MerkleizeNode(0, 15)

r1 := HashPair(
HashPair(
HashPair(p0, p1), // 0,1
HashPair(p2, p3), // 2,3
r1 := hashPair(
hashPair(
hashPair(p0, p1), // 0,1
hashPair(p2, p3), // 2,3
),
HashPair(
HashPair(p4, p5), // 4,5
HashPair(p6, p7), // 6,7
hashPair(
hashPair(p4, p5), // 4,5
hashPair(p6, p7), // 6,7
),
)
r2 := m.MerkleRoot()
Expand Down
11 changes: 3 additions & 8 deletions rvgo/fast/page.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ package fast
import (
"encoding/hex"
"fmt"

"github.com/ethereum/go-ethereum/crypto"
)

type Page [PageSize]byte
Expand Down Expand Up @@ -55,7 +53,7 @@ func (p *CachedPage) MerkleRoot() [32]byte {
if p.Ok[j] {
continue
}
p.Cache[j] = crypto.Keccak256Hash(p.Data[i : i+64])
p.Cache[j] = hash([64]byte(p.Data[i : i+64]))
//fmt.Printf("0x%x 0x%x -> 0x%x\n", p.Data[i:i+32], p.Data[i+32:i+64], p.Cache[j])
p.Ok[j] = true
}
Expand All @@ -66,7 +64,7 @@ func (p *CachedPage) MerkleRoot() [32]byte {
if p.Ok[j] {
continue
}
p.Cache[j] = HashPair(p.Cache[i], p.Cache[i+1])
p.Cache[j] = hashPair(p.Cache[i], p.Cache[i+1])
p.Ok[j] = true
}

Expand Down Expand Up @@ -100,11 +98,10 @@ func (p *CachedPage) MerkleizeNode(addr, gindex uint64) [32]byte {
return p.Cache[gindex]
}

func (p *CachedPage) GenerateProof(addr uint64) [][32]byte {
func (p *CachedPage) GenerateProof(addr uint64, proofs *[8][32]byte) {
// Page-level proof
pageGindex := PageSize>>5 + (addr&PageAddrMask)>>5

proofs := make([][32]byte, 8)
proofIndex := 0

proofs[proofIndex] = p.MerkleizeSubtree(pageGindex)
Expand All @@ -114,6 +111,4 @@ func (p *CachedPage) GenerateProof(addr uint64) [][32]byte {
proofIndex++
proofs[proofIndex] = p.MerkleizeSubtree(uint64(sibling))
}

return proofs
}
4 changes: 2 additions & 2 deletions rvgo/fast/page_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ func TestCachedPage(t *testing.T) {
require.Equal(t, expectedLeaf, node, "leaf nodes should not be hashed")

node = p.MerkleizeSubtree(gindex >> 1)
expectedParent := common.Hash(HashPair(zeroHashes[0], expectedLeaf))
expectedParent := common.Hash(hashPair(zeroHashes[0], expectedLeaf))
require.Equal(t, expectedParent, node, "can get the parent node")

node = p.MerkleizeSubtree(gindex >> 2)
expectedParentParent := common.Hash(HashPair(expectedParent, zeroHashes[1]))
expectedParentParent := common.Hash(hashPair(expectedParent, zeroHashes[1]))
require.Equal(t, expectedParentParent, node, "and the parent of the parent")

pre := p.MerkleRoot()
Expand Down
13 changes: 6 additions & 7 deletions rvgo/fast/radix.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,7 @@ func (m *Memory) GenerateProof(addr uint64, proofs [][32]byte) {
// number of proof for a page is 8
// 0: leaf page data, 7: page's root
if p, ok := m.pages[pageIndex]; ok {
pageProofs := p.GenerateProof(addr) // Generate proof from the page.
copy(proofs[:8], pageProofs)
p.GenerateProof(addr, (*[8][32]byte)(proofs[:8])) // Generate proof from the page.
} else {
fillZeroHashRange(proofs, 0, 8) // Return zero hashes if the page does not exist.
}
Expand Down Expand Up @@ -203,8 +202,8 @@ func (n *SmallRadixNode[C]) MerkleizeNode(addr, gindex uint64) [32]byte {
left := n.MerkleizeNode(addr, gindex<<1)
right := n.MerkleizeNode(addr, (gindex<<1)|1)

// Hash the pair and cache the result.
r := HashPair(left, right)
// hash the pair and cache the result.
r := hashPair(left, right)
n.Hashes[gindex] = r
n.HashValid |= 1 << hashBit
return r
Expand Down Expand Up @@ -249,8 +248,8 @@ func (n *MediumRadixNode[C]) MerkleizeNode(addr, gindex uint64) [32]byte {
left := n.MerkleizeNode(addr, gindex<<1)
right := n.MerkleizeNode(addr, (gindex<<1)|1)

// Hash the pair and cache the result.
r := HashPair(left, right)
// hash the pair and cache the result.
r := hashPair(left, right)
n.Hashes[gindex] = r
n.HashValid |= 1 << hashBit
return r
Expand Down Expand Up @@ -290,7 +289,7 @@ func (n *LargeRadixNode[C]) MerkleizeNode(addr, gindex uint64) [32]byte {
left := n.MerkleizeNode(addr, gindex<<1)
right := n.MerkleizeNode(addr, (gindex<<1)|1)

r := HashPair(left, right)
r := hashPair(left, right)
n.Hashes[gindex] = r
n.HashValid[hashIndex] |= 1 << hashBit
return r
Expand Down