mirror of
https://github.com/luxfi/node.git
synced 2026-10-30 15:24:05 +00:00
The module already called itself github.com/luxfi/node while containing no node in Go: cmd/luxd was twenty-one lines that printed the size of a few types, and the Go column of the matrix measured a VM plugin. A repo that fetches the thing it claims to be has not replaced it, so the Go sources move in — 1515 files, the daemon and everything under it — and the build reaches outside this module for nothing. Rust and C++ sit beside it unchanged and are still measured against it by the differential. That is the point of one repo: the reference implementation and the two ports are the same checkout, so parity is a thing you can run rather than a thing you assert. Verified: builds, and the closure carries no lux-private — checked at build time rather than believed. The daemon starts twelve chains on a local network (P X C Q Z A B G K M F, plus D through its plugin) and reports healthy.
99 lines
2.7 KiB
Go
99 lines
2.7 KiB
Go
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package merkledb
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/binary"
|
|
"slices"
|
|
|
|
"github.com/luxfi/ids"
|
|
)
|
|
|
|
const HashLength = 32
|
|
|
|
var (
|
|
SHA256Hasher Hasher = &sha256Hasher{}
|
|
|
|
// If a Hasher isn't specified, this package defaults to using the
|
|
// [SHA256Hasher].
|
|
DefaultHasher = SHA256Hasher
|
|
)
|
|
|
|
type Hasher interface {
|
|
// Returns the canonical hash of the non-nil [node].
|
|
HashNode(node *node) ids.ID
|
|
// Returns the canonical hash of [value].
|
|
HashValue(value []byte) ids.ID
|
|
}
|
|
|
|
type sha256Hasher struct{}
|
|
|
|
// This method is performance critical. It is not expected to perform any memory
|
|
// allocations.
|
|
func (*sha256Hasher) HashNode(n *node) ids.ID {
|
|
var (
|
|
// sha.Write always returns nil, so we ignore its return values.
|
|
sha = sha256.New()
|
|
hash ids.ID
|
|
// The hash length is larger than the maximum Uvarint length. This
|
|
// ensures binary.AppendUvarint doesn't perform any memory allocations.
|
|
emptyHashBuffer = hash[:0]
|
|
)
|
|
|
|
// By directly calling sha.Write rather than passing sha around as an
|
|
// io.Writer, the compiler can perform sufficient escape analysis to avoid
|
|
// allocating buffers on the heap.
|
|
numChildren := len(n.children)
|
|
_, _ = sha.Write(binary.AppendUvarint(emptyHashBuffer, uint64(numChildren)))
|
|
|
|
// Avoid allocating keys entirely if the node doesn't have any children.
|
|
if numChildren != 0 {
|
|
// By allocating BranchFactorLargest rather than [numChildren], this
|
|
// slice is allocated on the stack rather than the heap.
|
|
// BranchFactorLargest is at least [numChildren] which avoids memory
|
|
// allocations.
|
|
keys := make([]byte, numChildren, BranchFactorLargest)
|
|
i := 0
|
|
for k := range n.children {
|
|
keys[i] = k
|
|
i++
|
|
}
|
|
|
|
// Ensure that the order of entries is correct.
|
|
slices.Sort(keys)
|
|
for _, index := range keys {
|
|
entry := n.children[index]
|
|
_, _ = sha.Write(binary.AppendUvarint(emptyHashBuffer, uint64(index)))
|
|
_, _ = sha.Write(entry.id[:])
|
|
}
|
|
}
|
|
|
|
if n.valueDigest.HasValue() {
|
|
_, _ = sha.Write(trueBytes)
|
|
value := n.valueDigest.Value()
|
|
_, _ = sha.Write(binary.AppendUvarint(emptyHashBuffer, uint64(len(value))))
|
|
_, _ = sha.Write(value)
|
|
} else {
|
|
_, _ = sha.Write(falseBytes)
|
|
}
|
|
|
|
_, _ = sha.Write(binary.AppendUvarint(emptyHashBuffer, uint64(n.key.length)))
|
|
_, _ = sha.Write(n.key.Bytes())
|
|
sha.Sum(emptyHashBuffer)
|
|
return hash
|
|
}
|
|
|
|
// This method is performance critical. It is not expected to perform any memory
|
|
// allocations.
|
|
func (*sha256Hasher) HashValue(value []byte) ids.ID {
|
|
sha := sha256.New()
|
|
// sha.Write always returns nil, so we ignore its return values.
|
|
_, _ = sha.Write(value)
|
|
|
|
var hash ids.ID
|
|
sha.Sum(hash[:0])
|
|
return hash
|
|
}
|