mirror of
https://github.com/QuilibriumNetwork/ceremonyclient.git
synced 2026-02-25 20:37:27 +08:00
* experiment: reject bad peer info messages * v2.1.0.18 preview * add tagged sync * Add missing hypergraph changes * small tweaks to sync * allow local sync, use it for provers with workers * missing file * resolve build error * resolve sync issue, remove raw sync * resolve deletion promotion bug * resolve sync abstraction leak from tree deletion changes * rearrange prover sync * remove pruning from sync * restore removed sync flag * fix: sync, event stream deadlock, heuristic scoring of better shards * resolve hanging shutdown + pubsub proxy issue * further bugfixes: sync (restore old leaf sync), pubsub shutdown, merge events * fix: clean up rust ffi, background coverage events, and sync tweaks * fix: linking issue for channel, connectivity test aggression, sync regression, join tests * fix: disjoint sync, improper application of filter * resolve sync/reel/validation deadlock * adjust sync to handle no leaf edge cases, multi-path segment traversal * use simpler sync * faster, simpler sync with some debug extras * migration to recalculate * don't use batch * square up the roots * fix nil pointer * fix: seniority calculation, sync race condition, migration * make sync dumber * fix: tree deletion issue * fix: missing seniority merge request canonical serialization * address issues from previous commit test * stale workers should be cleared * remove missing gap check * rearrange collect, reduce sync logging noise * fix: the disjoint leaf/branch sync case * nuclear option on sync failures * v2.1.0.18, finalized
261 lines
7.0 KiB
Go
261 lines
7.0 KiB
Go
package global
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"math/big"
|
|
"slices"
|
|
|
|
"github.com/iden3/go-iden3-crypto/poseidon"
|
|
"github.com/pkg/errors"
|
|
"source.quilibrium.com/quilibrium/monorepo/types/crypto"
|
|
"source.quilibrium.com/quilibrium/monorepo/types/execution/intrinsics"
|
|
"source.quilibrium.com/quilibrium/monorepo/types/execution/state"
|
|
"source.quilibrium.com/quilibrium/monorepo/types/hypergraph"
|
|
"source.quilibrium.com/quilibrium/monorepo/types/keys"
|
|
)
|
|
|
|
// AltShardUpdate represents an update to an alternative shard's roots.
|
|
// The shard address is derived from the poseidon hash of the BLS48-581 public key.
|
|
// This allows external entities to maintain their own state trees with provable
|
|
// ownership through signature verification.
|
|
type AltShardUpdate struct {
|
|
// The BLS48-581 public key that owns this shard
|
|
// The shard address is poseidon(PublicKey)
|
|
PublicKey []byte
|
|
|
|
// The frame number when this update was signed
|
|
// Must be within 2 frames of the verification frame number
|
|
FrameNumber uint64
|
|
|
|
// The root hash for vertex adds tree
|
|
VertexAddsRoot []byte
|
|
|
|
// The root hash for vertex removes tree
|
|
VertexRemovesRoot []byte
|
|
|
|
// The root hash for hyperedge adds tree
|
|
HyperedgeAddsRoot []byte
|
|
|
|
// The root hash for hyperedge removes tree
|
|
HyperedgeRemovesRoot []byte
|
|
|
|
// The BLS48-581 signature over (FrameNumber || VertexAddsRoot ||
|
|
// VertexRemovesRoot || HyperedgeAddsRoot || HyperedgeRemovesRoot)
|
|
Signature []byte
|
|
|
|
// Private dependencies
|
|
hypergraph hypergraph.Hypergraph
|
|
keyManager keys.KeyManager
|
|
signer crypto.Signer
|
|
}
|
|
|
|
// NewAltShardUpdate creates a new AltShardUpdate instance
|
|
func NewAltShardUpdate(
|
|
frameNumber uint64,
|
|
vertexAddsRoot []byte,
|
|
vertexRemovesRoot []byte,
|
|
hyperedgeAddsRoot []byte,
|
|
hyperedgeRemovesRoot []byte,
|
|
hypergraph hypergraph.Hypergraph,
|
|
keyManager keys.KeyManager,
|
|
signer crypto.Signer,
|
|
) (*AltShardUpdate, error) {
|
|
return &AltShardUpdate{
|
|
FrameNumber: frameNumber,
|
|
VertexAddsRoot: vertexAddsRoot,
|
|
VertexRemovesRoot: vertexRemovesRoot,
|
|
HyperedgeAddsRoot: hyperedgeAddsRoot,
|
|
HyperedgeRemovesRoot: hyperedgeRemovesRoot,
|
|
hypergraph: hypergraph,
|
|
keyManager: keyManager,
|
|
signer: signer,
|
|
}, nil
|
|
}
|
|
|
|
// GetCost returns the cost of this operation (zero for shard updates)
|
|
func (a *AltShardUpdate) GetCost() (*big.Int, error) {
|
|
return big.NewInt(0), nil
|
|
}
|
|
|
|
// getSignedMessage constructs the message that is signed
|
|
func (a *AltShardUpdate) getSignedMessage() []byte {
|
|
frameBytes := make([]byte, 8)
|
|
binary.BigEndian.PutUint64(frameBytes, a.FrameNumber)
|
|
|
|
return slices.Concat(
|
|
frameBytes,
|
|
a.VertexAddsRoot,
|
|
a.VertexRemovesRoot,
|
|
a.HyperedgeAddsRoot,
|
|
a.HyperedgeRemovesRoot,
|
|
)
|
|
}
|
|
|
|
// getShardAddress derives the shard address from the public key
|
|
func (a *AltShardUpdate) getShardAddress() ([]byte, error) {
|
|
if len(a.PublicKey) == 0 {
|
|
return nil, errors.New("public key is empty")
|
|
}
|
|
|
|
addrBI, err := poseidon.HashBytes(a.PublicKey)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "hash public key")
|
|
}
|
|
|
|
return addrBI.FillBytes(make([]byte, 32)), nil
|
|
}
|
|
|
|
// Prove signs the update with the signer's BLS48-581 key
|
|
func (a *AltShardUpdate) Prove(frameNumber uint64) error {
|
|
if a.signer == nil {
|
|
return errors.New("signer is nil")
|
|
}
|
|
|
|
a.PublicKey = a.signer.Public().([]byte)
|
|
|
|
// Create domain for signature
|
|
domainPreimage := slices.Concat(
|
|
intrinsics.GLOBAL_INTRINSIC_ADDRESS[:],
|
|
[]byte("ALT_SHARD_UPDATE"),
|
|
)
|
|
domain, err := poseidon.HashBytes(domainPreimage)
|
|
if err != nil {
|
|
return errors.Wrap(err, "prove")
|
|
}
|
|
|
|
message := a.getSignedMessage()
|
|
signature, err := a.signer.SignWithDomain(
|
|
message,
|
|
domain.FillBytes(make([]byte, 32)),
|
|
)
|
|
if err != nil {
|
|
return errors.Wrap(err, "prove")
|
|
}
|
|
|
|
a.Signature = signature
|
|
return nil
|
|
}
|
|
|
|
// Verify validates the signature and frame number constraints
|
|
func (a *AltShardUpdate) Verify(frameNumber uint64) (bool, error) {
|
|
if a.keyManager == nil {
|
|
return false, errors.New("key manager is nil")
|
|
}
|
|
|
|
// Validate public key length (BLS48-581 public key is 585 bytes)
|
|
if len(a.PublicKey) != 585 {
|
|
return false, errors.Errorf(
|
|
"invalid public key length: expected 585, got %d",
|
|
len(a.PublicKey),
|
|
)
|
|
}
|
|
|
|
// Validate signature length (BLS48-581 signature is 74 bytes)
|
|
if len(a.Signature) != 74 {
|
|
return false, errors.Errorf(
|
|
"invalid signature length: expected 74, got %d",
|
|
len(a.Signature),
|
|
)
|
|
}
|
|
|
|
// Validate root lengths (must be 64 or 74 bytes)
|
|
isValidRootLen := func(length int) bool {
|
|
return length == 64 || length == 74
|
|
}
|
|
if !isValidRootLen(len(a.VertexAddsRoot)) {
|
|
return false, errors.Errorf(
|
|
"vertex adds root must be 64 or 74 bytes, got %d",
|
|
len(a.VertexAddsRoot),
|
|
)
|
|
}
|
|
if !isValidRootLen(len(a.VertexRemovesRoot)) {
|
|
return false, errors.Errorf(
|
|
"vertex removes root must be 64 or 74 bytes, got %d",
|
|
len(a.VertexRemovesRoot),
|
|
)
|
|
}
|
|
if !isValidRootLen(len(a.HyperedgeAddsRoot)) {
|
|
return false, errors.Errorf(
|
|
"hyperedge adds root must be 64 or 74 bytes, got %d",
|
|
len(a.HyperedgeAddsRoot),
|
|
)
|
|
}
|
|
if !isValidRootLen(len(a.HyperedgeRemovesRoot)) {
|
|
return false, errors.Errorf(
|
|
"hyperedge removes root must be 64 or 74 bytes, got %d",
|
|
len(a.HyperedgeRemovesRoot),
|
|
)
|
|
}
|
|
|
|
// Frame number must be within 2 frames of the verification frame
|
|
// and not in the future
|
|
if a.FrameNumber > frameNumber {
|
|
return false, errors.New("frame number is in the future")
|
|
}
|
|
if frameNumber-a.FrameNumber > 2 {
|
|
return false, errors.New("frame number is too old (more than 2 frames)")
|
|
}
|
|
|
|
// Create domain for signature verification
|
|
domainPreimage := slices.Concat(
|
|
intrinsics.GLOBAL_INTRINSIC_ADDRESS[:],
|
|
[]byte("ALT_SHARD_UPDATE"),
|
|
)
|
|
domain, err := poseidon.HashBytes(domainPreimage)
|
|
if err != nil {
|
|
return false, errors.Wrap(err, "verify")
|
|
}
|
|
|
|
message := a.getSignedMessage()
|
|
valid, err := a.keyManager.ValidateSignature(
|
|
crypto.KeyTypeBLS48581G1,
|
|
a.PublicKey,
|
|
message,
|
|
a.Signature,
|
|
domain.FillBytes(make([]byte, 32)),
|
|
)
|
|
if err != nil {
|
|
return false, errors.Wrap(err, "verify")
|
|
}
|
|
if !valid {
|
|
return false, errors.New("invalid signature")
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
// GetReadAddresses returns the addresses this operation reads from
|
|
func (a *AltShardUpdate) GetReadAddresses(
|
|
frameNumber uint64,
|
|
) ([][]byte, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
// GetWriteAddresses returns the addresses this operation writes to
|
|
func (a *AltShardUpdate) GetWriteAddresses(
|
|
frameNumber uint64,
|
|
) ([][]byte, error) {
|
|
shardAddress, err := a.getShardAddress()
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "get write addresses")
|
|
}
|
|
|
|
// We write to four trees under this shard address, all at the zero key
|
|
// The full address is shardAddress (app) + 00...00 (data)
|
|
zeroKey := bytes.Repeat([]byte{0x00}, 32)
|
|
fullAddress := slices.Concat(shardAddress, zeroKey)
|
|
|
|
return [][]byte{fullAddress}, nil
|
|
}
|
|
|
|
// Materialize applies the shard update to the state
|
|
func (a *AltShardUpdate) Materialize(
|
|
frameNumber uint64,
|
|
state state.State,
|
|
) (state.State, error) {
|
|
return state, nil
|
|
}
|
|
|
|
var _ intrinsics.IntrinsicOperation = (*AltShardUpdate)(nil)
|