Files
zeekay 7cf87b230b node-unified is the node
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.
2026-09-07 16:15:25 -07:00

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
}