mirror of
https://github.com/luxfi/node.git
synced 2026-07-27 03:39:39 +00:00
CommitteeCert.Verify(committee) now verifies each endorsement as a BLS signature over the certificate's canonical signing digest by a distinct, known committee member's registered public key. Rejects (fail-closed) nil, unknown, duplicate, or invalid endorsements, parameter mismatches, and sub-threshold sets; requires >= Threshold valid distinct signatures. The engine holds a committee registry (RegisterCommittee) and VerifyResult fails closed on an unknown committee. Replaces the count-only stub. Tests: valid quorum + sub-threshold, duplicate-signer, forged-signature, unknown-signer, parameter-mismatch, and engine fail-closed cases.
652 lines
19 KiB
Go
652 lines
19 KiB
Go
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package chainadapter
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"crypto/sha256"
|
|
"encoding/binary"
|
|
"errors"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/luxfi/crypto/bls"
|
|
"github.com/luxfi/ids"
|
|
)
|
|
|
|
// Confidential compute errors
|
|
var (
|
|
ErrTEENotAvailable = errors.New("TEE not available")
|
|
ErrAttestationFailed = errors.New("attestation verification failed")
|
|
ErrComputeTimeout = errors.New("compute operation timed out")
|
|
ErrInvalidQuote = errors.New("invalid TEE quote")
|
|
ErrCommitteeCertInvalid = errors.New("committee certificate invalid")
|
|
)
|
|
|
|
// TEEType defines the type of Trusted Execution Environment
|
|
type TEEType uint8
|
|
|
|
const (
|
|
// TEEIntelSGX is Intel SGX
|
|
TEEIntelSGX TEEType = iota
|
|
// TEEAMDSev is AMD SEV
|
|
TEEAMDSev
|
|
// TEENvidiaCC is NVIDIA Confidential Computing
|
|
TEENvidiaCC
|
|
// TEEArmTrustZone is ARM TrustZone
|
|
TEEArmTrustZone
|
|
// TEEAWSNitro is AWS Nitro Enclaves
|
|
TEEAWSNitro
|
|
// TEEAzureSGX is Azure Confidential Computing (SGX)
|
|
TEEAzureSGX
|
|
)
|
|
|
|
// String returns the string representation of TEEType
|
|
func (t TEEType) String() string {
|
|
switch t {
|
|
case TEEIntelSGX:
|
|
return "intel_sgx"
|
|
case TEEAMDSev:
|
|
return "amd_sev"
|
|
case TEENvidiaCC:
|
|
return "nvidia_cc"
|
|
case TEEArmTrustZone:
|
|
return "arm_trustzone"
|
|
case TEEAWSNitro:
|
|
return "aws_nitro"
|
|
case TEEAzureSGX:
|
|
return "azure_sgx"
|
|
default:
|
|
return "unknown"
|
|
}
|
|
}
|
|
|
|
// ComputeMode defines how confidential compute is performed
|
|
type ComputeMode uint8
|
|
|
|
const (
|
|
// ComputeModeTEE uses Trusted Execution Environment
|
|
ComputeModeTEE ComputeMode = iota
|
|
// ComputeModeFHE uses Fully Homomorphic Encryption
|
|
ComputeModeFHE
|
|
// ComputeModeZK uses Zero-Knowledge Proofs
|
|
ComputeModeZK
|
|
// ComputeModeHybrid combines TEE + FHE or TEE + ZK
|
|
ComputeModeHybrid
|
|
)
|
|
|
|
// ComputeRequest represents a request for confidential computation
|
|
type ComputeRequest struct {
|
|
ID ids.ID `json:"id"`
|
|
AppChainID ids.ID `json:"appChainId"`
|
|
Requester []byte `json:"requester"`
|
|
|
|
// Input specification
|
|
InputRefs []DataRef `json:"inputRefs"` // References to encrypted input data
|
|
InputCommitment [32]byte `json:"inputCommitment"`
|
|
|
|
// Computation specification
|
|
ComputeMode ComputeMode `json:"computeMode"`
|
|
Program []byte `json:"program"` // WASM, bytecode, or program hash
|
|
ProgramHash [32]byte `json:"programHash"`
|
|
Parameters []byte `json:"parameters"`
|
|
|
|
// Output specification
|
|
OutputDomain EncryptionDomain `json:"outputDomain"`
|
|
OutputRecipients [][]byte `json:"outputRecipients"`
|
|
|
|
// Attestation requirements
|
|
RequireTEE bool `json:"requireTee"`
|
|
AcceptedTEEs []TEEType `json:"acceptedTees"`
|
|
RequireProof bool `json:"requireProof"`
|
|
|
|
// Timing
|
|
Deadline time.Time `json:"deadline"`
|
|
MaxGas uint64 `json:"maxGas"`
|
|
}
|
|
|
|
// DataRef references encrypted data for compute
|
|
type DataRef struct {
|
|
DocumentID DocumentID `json:"documentId"`
|
|
DAPointer *DAPointer `json:"daPointer"`
|
|
Commitment [32]byte `json:"commitment"`
|
|
Domain EncryptionDomain `json:"domain"`
|
|
}
|
|
|
|
// ComputeResult represents the result of confidential computation
|
|
type ComputeResult struct {
|
|
RequestID ids.ID `json:"requestId"`
|
|
Status ComputeStatus `json:"status"`
|
|
|
|
// Output
|
|
OutputData []byte `json:"outputData"` // Encrypted
|
|
OutputCommitment [32]byte `json:"outputCommitment"`
|
|
OutputDAPointer *DAPointer `json:"outputDaPointer,omitempty"`
|
|
|
|
// Attestation
|
|
Attestation *TEEAttestation `json:"attestation,omitempty"`
|
|
Proof *ComputeProof `json:"proof,omitempty"`
|
|
CommitteeCert *CommitteeCert `json:"committeeCert,omitempty"`
|
|
|
|
// Timing
|
|
ComputedAt time.Time `json:"computedAt"`
|
|
GasUsed uint64 `json:"gasUsed"`
|
|
}
|
|
|
|
// ComputeStatus represents the status of a compute request
|
|
type ComputeStatus uint8
|
|
|
|
const (
|
|
ComputeStatusPending ComputeStatus = iota
|
|
ComputeStatusRunning
|
|
ComputeStatusCompleted
|
|
ComputeStatusFailed
|
|
ComputeStatusTimeout
|
|
)
|
|
|
|
// TEEAttestation contains attestation evidence from a TEE
|
|
type TEEAttestation struct {
|
|
TEEType TEEType `json:"teeType"`
|
|
Quote []byte `json:"quote"` // Platform-specific quote
|
|
QuoteVersion uint32 `json:"quoteVersion"`
|
|
|
|
// Measurements
|
|
MRENCLAVE [32]byte `json:"mrenclave,omitempty"` // SGX enclave measurement
|
|
MRSIGNER [32]byte `json:"mrsigner,omitempty"` // SGX signer measurement
|
|
ProductID uint16 `json:"productId,omitempty"`
|
|
SecurityVersion uint16 `json:"securityVersion,omitempty"`
|
|
|
|
// Report data (binds to computation)
|
|
ReportData [64]byte `json:"reportData"`
|
|
|
|
// Input/Output commitments in report
|
|
InputCommitment [32]byte `json:"inputCommitment"`
|
|
OutputCommitment [32]byte `json:"outputCommitment"`
|
|
ProgramHash [32]byte `json:"programHash"`
|
|
|
|
// Certification chain
|
|
CertChain [][]byte `json:"certChain"`
|
|
|
|
// Timestamp
|
|
Timestamp time.Time `json:"timestamp"`
|
|
Expiry time.Time `json:"expiry"`
|
|
}
|
|
|
|
// Verify verifies the TEE attestation
|
|
func (a *TEEAttestation) Verify() error {
|
|
// In production, verify:
|
|
// 1. Quote signature using Intel/AMD/NVIDIA attestation service
|
|
// 2. Certificate chain validity
|
|
// 3. Security version is acceptable
|
|
// 4. Report data matches expected commitments
|
|
|
|
// Verify report data contains our commitments
|
|
expectedReportData := computeReportData(a.InputCommitment, a.OutputCommitment, a.ProgramHash)
|
|
if a.ReportData != expectedReportData {
|
|
return ErrInvalidQuote
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// computeReportData computes expected report data from commitments
|
|
func computeReportData(input, output, program [32]byte) [64]byte {
|
|
h := sha256.New()
|
|
h.Write(input[:])
|
|
h.Write(output[:])
|
|
h.Write(program[:])
|
|
hash := h.Sum(nil)
|
|
|
|
var reportData [64]byte
|
|
copy(reportData[:32], hash)
|
|
return reportData
|
|
}
|
|
|
|
// ComputeProof represents a ZK proof of correct computation
|
|
type ComputeProof struct {
|
|
ProofSystem string `json:"proofSystem"` // "groth16", "plonk", "stark"
|
|
Proof []byte `json:"proof"`
|
|
PublicInputs [][]byte `json:"publicInputs"`
|
|
VerifyingKey []byte `json:"verifyingKey"`
|
|
|
|
// Commitments
|
|
InputCommitment [32]byte `json:"inputCommitment"`
|
|
OutputCommitment [32]byte `json:"outputCommitment"`
|
|
ProgramHash [32]byte `json:"programHash"`
|
|
}
|
|
|
|
// Verify verifies the ZK proof
|
|
func (p *ComputeProof) Verify() error {
|
|
// Basic validation - full ZK verification is performed by ZKVM
|
|
if len(p.Proof) == 0 {
|
|
return ErrAttestationFailed
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// CommitteeCert represents threshold endorsement from a compute committee
|
|
type CommitteeCert struct {
|
|
CommitteeID ids.ID `json:"committeeId"`
|
|
Threshold int `json:"threshold"`
|
|
TotalMembers int `json:"totalMembers"`
|
|
|
|
// Endorsements from committee members
|
|
Endorsements []*Endorsement `json:"endorsements"`
|
|
|
|
// Aggregate signature (if using BLS)
|
|
AggregateSignature []byte `json:"aggregateSignature,omitempty"`
|
|
|
|
// What is being certified
|
|
RequestID ids.ID `json:"requestId"`
|
|
OutputCommitment [32]byte `json:"outputCommitment"`
|
|
Timestamp time.Time `json:"timestamp"`
|
|
}
|
|
|
|
// Endorsement is a single committee member's endorsement
|
|
type Endorsement struct {
|
|
MemberID []byte `json:"memberId"`
|
|
MemberIndex int `json:"memberIndex"`
|
|
Signature []byte `json:"signature"`
|
|
TEEAttestation *TEEAttestation `json:"teeAttestation,omitempty"`
|
|
}
|
|
|
|
// signingDigest is the canonical, domain-separated message that every
|
|
// committee member signs when endorsing this certificate. It binds the
|
|
// committee, the request, the certified output commitment and the
|
|
// timestamp so that an endorsement for one (request, output) pair can
|
|
// never be replayed for another.
|
|
func (c *CommitteeCert) signingDigest() [32]byte {
|
|
h := sha256.New()
|
|
h.Write([]byte("lux/chainadapter/committee-cert/v1"))
|
|
h.Write(c.CommitteeID[:])
|
|
h.Write(c.RequestID[:])
|
|
h.Write(c.OutputCommitment[:])
|
|
var ts [8]byte
|
|
binary.BigEndian.PutUint64(ts[:], uint64(c.Timestamp.UTC().UnixNano()))
|
|
h.Write(ts[:])
|
|
var out [32]byte
|
|
copy(out[:], h.Sum(nil))
|
|
return out
|
|
}
|
|
|
|
// Verify checks that the certificate carries at least Threshold valid,
|
|
// distinct endorsement signatures from members of the supplied committee.
|
|
//
|
|
// Each endorsement must:
|
|
// - reference a member that exists in the committee roster (known signer);
|
|
// - carry a MemberID matching the roster entry at that index;
|
|
// - be a BLS signature over the certificate's canonical signing digest
|
|
// that verifies under that member's registered public key;
|
|
// - be distinct — no member may endorse twice.
|
|
//
|
|
// The certificate is rejected (fail-closed) on any nil/malformed, unknown,
|
|
// duplicate, or cryptographically invalid endorsement, on a committee/cert
|
|
// parameter mismatch, or when fewer than Threshold distinct valid
|
|
// endorsements are present. There is no count-only path: every accepted
|
|
// endorsement has had its signature verified against a registered key.
|
|
func (c *CommitteeCert) Verify(committee *ComputeCommittee) error {
|
|
if committee == nil {
|
|
return ErrCommitteeCertInvalid
|
|
}
|
|
// Threshold and committee identity must agree, and the roster must be
|
|
// internally consistent (one public key per member).
|
|
if c.Threshold <= 0 || c.Threshold != committee.Threshold {
|
|
return ErrCommitteeCertInvalid
|
|
}
|
|
if c.CommitteeID != committee.ID {
|
|
return ErrCommitteeCertInvalid
|
|
}
|
|
if len(committee.PublicKeys) != len(committee.Members) || len(committee.PublicKeys) == 0 {
|
|
return ErrCommitteeCertInvalid
|
|
}
|
|
if len(c.Endorsements) < c.Threshold {
|
|
return ErrCommitteeCertInvalid
|
|
}
|
|
|
|
msg := c.signingDigest()
|
|
seen := make(map[int]struct{}, len(c.Endorsements))
|
|
|
|
for _, e := range c.Endorsements {
|
|
if e == nil {
|
|
return ErrCommitteeCertInvalid
|
|
}
|
|
idx := e.MemberIndex
|
|
if idx < 0 || idx >= len(committee.PublicKeys) {
|
|
return ErrCommitteeCertInvalid // unknown signer
|
|
}
|
|
if _, dup := seen[idx]; dup {
|
|
return ErrCommitteeCertInvalid // duplicate signer
|
|
}
|
|
if !bytes.Equal(e.MemberID, committee.Members[idx]) {
|
|
return ErrCommitteeCertInvalid // identity does not match roster index
|
|
}
|
|
pk, err := bls.PublicKeyFromCompressedBytes(committee.PublicKeys[idx])
|
|
if err != nil {
|
|
return ErrCommitteeCertInvalid
|
|
}
|
|
sig, err := bls.SignatureFromBytes(e.Signature)
|
|
if err != nil {
|
|
return ErrCommitteeCertInvalid
|
|
}
|
|
if !bls.Verify(pk, sig, msg[:]) {
|
|
return ErrCommitteeCertInvalid // forged or invalid signature
|
|
}
|
|
seen[idx] = struct{}{}
|
|
}
|
|
|
|
if len(seen) < c.Threshold {
|
|
return ErrCommitteeCertInvalid
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ConfidentialComputeEngine handles confidential computation
|
|
type ConfidentialComputeEngine struct {
|
|
mu sync.RWMutex
|
|
|
|
// TEE configuration
|
|
teeType TEEType
|
|
teeAvailable bool
|
|
|
|
// Active compute sessions
|
|
sessions map[ids.ID]*ComputeSession
|
|
|
|
// Result cache
|
|
results map[ids.ID]*ComputeResult
|
|
|
|
// Registered committee rosters, keyed by committee ID. A committee
|
|
// certificate can only be verified against a roster registered here;
|
|
// an unknown committee fails closed.
|
|
committees map[ids.ID]*ComputeCommittee
|
|
}
|
|
|
|
// ComputeSession tracks an active computation
|
|
type ComputeSession struct {
|
|
Request *ComputeRequest
|
|
Status ComputeStatus
|
|
StartedAt time.Time
|
|
Endorsements []*Endorsement
|
|
}
|
|
|
|
// ComputeCommittee is a group that collectively attests to computation
|
|
type ComputeCommittee struct {
|
|
ID ids.ID
|
|
Members [][]byte
|
|
Threshold int
|
|
PublicKeys [][]byte
|
|
}
|
|
|
|
// NewConfidentialComputeEngine creates a new compute engine
|
|
func NewConfidentialComputeEngine(teeType TEEType) *ConfidentialComputeEngine {
|
|
return &ConfidentialComputeEngine{
|
|
teeType: teeType,
|
|
teeAvailable: true, // Check actual TEE availability
|
|
sessions: make(map[ids.ID]*ComputeSession),
|
|
results: make(map[ids.ID]*ComputeResult),
|
|
committees: make(map[ids.ID]*ComputeCommittee),
|
|
}
|
|
}
|
|
|
|
// RegisterCommittee registers a committee roster so that certificates the
|
|
// committee produces can be verified against its members' public keys.
|
|
// PublicKeys and Members must be parallel arrays (one compressed BLS public
|
|
// key per member).
|
|
func (e *ConfidentialComputeEngine) RegisterCommittee(committee *ComputeCommittee) error {
|
|
if committee == nil || len(committee.PublicKeys) != len(committee.Members) || len(committee.PublicKeys) == 0 {
|
|
return ErrCommitteeCertInvalid
|
|
}
|
|
e.mu.Lock()
|
|
defer e.mu.Unlock()
|
|
e.committees[committee.ID] = committee
|
|
return nil
|
|
}
|
|
|
|
// SubmitRequest submits a compute request
|
|
func (e *ConfidentialComputeEngine) SubmitRequest(ctx context.Context, req *ComputeRequest) error {
|
|
e.mu.Lock()
|
|
defer e.mu.Unlock()
|
|
|
|
if req.RequireTEE && !e.teeAvailable {
|
|
return ErrTEENotAvailable
|
|
}
|
|
|
|
session := &ComputeSession{
|
|
Request: req,
|
|
Status: ComputeStatusPending,
|
|
StartedAt: time.Now(),
|
|
}
|
|
|
|
e.sessions[req.ID] = session
|
|
return nil
|
|
}
|
|
|
|
// Execute executes a computation inside TEE
|
|
func (e *ConfidentialComputeEngine) Execute(ctx context.Context, reqID ids.ID) (*ComputeResult, error) {
|
|
e.mu.Lock()
|
|
session, exists := e.sessions[reqID]
|
|
if !exists {
|
|
e.mu.Unlock()
|
|
return nil, errors.New("session not found")
|
|
}
|
|
session.Status = ComputeStatusRunning
|
|
e.mu.Unlock()
|
|
|
|
req := session.Request
|
|
|
|
// Execute computation based on mode
|
|
var result *ComputeResult
|
|
var err error
|
|
|
|
switch req.ComputeMode {
|
|
case ComputeModeTEE:
|
|
result, err = e.executeTEE(ctx, req)
|
|
case ComputeModeFHE:
|
|
result, err = e.executeFHE(ctx, req)
|
|
case ComputeModeZK:
|
|
result, err = e.executeZK(ctx, req)
|
|
case ComputeModeHybrid:
|
|
result, err = e.executeHybrid(ctx, req)
|
|
default:
|
|
err = errors.New("unsupported compute mode")
|
|
}
|
|
|
|
e.mu.Lock()
|
|
if err != nil {
|
|
session.Status = ComputeStatusFailed
|
|
} else {
|
|
session.Status = ComputeStatusCompleted
|
|
e.results[reqID] = result
|
|
}
|
|
e.mu.Unlock()
|
|
|
|
return result, err
|
|
}
|
|
|
|
// executeTEE executes computation inside a TEE
|
|
func (e *ConfidentialComputeEngine) executeTEE(ctx context.Context, req *ComputeRequest) (*ComputeResult, error) {
|
|
// In production:
|
|
// 1. Load encrypted inputs into TEE
|
|
// 2. Decrypt inside TEE using sealed keys
|
|
// 3. Execute program
|
|
// 4. Encrypt outputs
|
|
// 5. Generate attestation quote
|
|
|
|
// Simulate TEE execution
|
|
outputCommitment := sha256.Sum256(req.InputCommitment[:])
|
|
|
|
attestation := &TEEAttestation{
|
|
TEEType: e.teeType,
|
|
Quote: make([]byte, 256), // Simulated quote
|
|
QuoteVersion: 3,
|
|
InputCommitment: req.InputCommitment,
|
|
OutputCommitment: outputCommitment,
|
|
ProgramHash: req.ProgramHash,
|
|
Timestamp: time.Now(),
|
|
Expiry: time.Now().Add(24 * time.Hour),
|
|
}
|
|
attestation.ReportData = computeReportData(attestation.InputCommitment, attestation.OutputCommitment, attestation.ProgramHash)
|
|
|
|
return &ComputeResult{
|
|
RequestID: req.ID,
|
|
Status: ComputeStatusCompleted,
|
|
OutputData: []byte{}, // Encrypted output
|
|
OutputCommitment: outputCommitment,
|
|
Attestation: attestation,
|
|
ComputedAt: time.Now(),
|
|
}, nil
|
|
}
|
|
|
|
// executeFHE executes FHE computation
|
|
func (e *ConfidentialComputeEngine) executeFHE(ctx context.Context, req *ComputeRequest) (*ComputeResult, error) {
|
|
// In production:
|
|
// 1. Perform homomorphic operations on encrypted data
|
|
// 2. Return encrypted result (no decryption needed)
|
|
|
|
outputCommitment := sha256.Sum256(req.InputCommitment[:])
|
|
|
|
return &ComputeResult{
|
|
RequestID: req.ID,
|
|
Status: ComputeStatusCompleted,
|
|
OutputData: []byte{}, // FHE encrypted output
|
|
OutputCommitment: outputCommitment,
|
|
ComputedAt: time.Now(),
|
|
}, nil
|
|
}
|
|
|
|
// executeZK executes computation with ZK proof
|
|
func (e *ConfidentialComputeEngine) executeZK(ctx context.Context, req *ComputeRequest) (*ComputeResult, error) {
|
|
// In production:
|
|
// 1. Execute computation
|
|
// 2. Generate ZK proof of correct execution
|
|
|
|
outputCommitment := sha256.Sum256(req.InputCommitment[:])
|
|
|
|
proof := &ComputeProof{
|
|
ProofSystem: "plonk",
|
|
Proof: make([]byte, 128), // Simulated proof
|
|
InputCommitment: req.InputCommitment,
|
|
OutputCommitment: outputCommitment,
|
|
ProgramHash: req.ProgramHash,
|
|
}
|
|
|
|
return &ComputeResult{
|
|
RequestID: req.ID,
|
|
Status: ComputeStatusCompleted,
|
|
OutputData: []byte{},
|
|
OutputCommitment: outputCommitment,
|
|
Proof: proof,
|
|
ComputedAt: time.Now(),
|
|
}, nil
|
|
}
|
|
|
|
// executeHybrid executes with hybrid TEE+ZK or TEE+FHE
|
|
func (e *ConfidentialComputeEngine) executeHybrid(ctx context.Context, req *ComputeRequest) (*ComputeResult, error) {
|
|
// Execute in TEE first
|
|
result, err := e.executeTEE(ctx, req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Optionally add ZK proof
|
|
if req.RequireProof {
|
|
proof := &ComputeProof{
|
|
ProofSystem: "plonk",
|
|
Proof: make([]byte, 128),
|
|
InputCommitment: req.InputCommitment,
|
|
OutputCommitment: result.OutputCommitment,
|
|
ProgramHash: req.ProgramHash,
|
|
}
|
|
result.Proof = proof
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// GetResult retrieves a compute result
|
|
func (e *ConfidentialComputeEngine) GetResult(reqID ids.ID) (*ComputeResult, bool) {
|
|
e.mu.RLock()
|
|
defer e.mu.RUnlock()
|
|
|
|
result, exists := e.results[reqID]
|
|
return result, exists
|
|
}
|
|
|
|
// VerifyResult verifies a compute result
|
|
func (e *ConfidentialComputeEngine) VerifyResult(result *ComputeResult) error {
|
|
// Verify attestation if present
|
|
if result.Attestation != nil {
|
|
if err := result.Attestation.Verify(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Verify proof if present
|
|
if result.Proof != nil {
|
|
if err := result.Proof.Verify(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Verify committee cert if present. The roster must have been
|
|
// registered; an unknown committee fails closed.
|
|
if result.CommitteeCert != nil {
|
|
e.mu.RLock()
|
|
committee := e.committees[result.CommitteeCert.CommitteeID]
|
|
e.mu.RUnlock()
|
|
if committee == nil {
|
|
return ErrCommitteeCertInvalid
|
|
}
|
|
if err := result.CommitteeCert.Verify(committee); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ComputeAnchor anchors compute result to chain
|
|
type ComputeAnchor struct {
|
|
ResultID ids.ID `json:"resultId"`
|
|
RequestID ids.ID `json:"requestId"`
|
|
AppChainID ids.ID `json:"appChainId"`
|
|
|
|
// Commitments
|
|
InputCommitment [32]byte `json:"inputCommitment"`
|
|
OutputCommitment [32]byte `json:"outputCommitment"`
|
|
ProgramHash [32]byte `json:"programHash"`
|
|
|
|
// Attestation summary
|
|
TEEType TEEType `json:"teeType,omitempty"`
|
|
HasTEEAttest bool `json:"hasTeeAttest"`
|
|
HasZKProof bool `json:"hasZkProof"`
|
|
HasCommitteeCert bool `json:"hasCommitteeCert"`
|
|
|
|
// DA reference for full attestation
|
|
AttestationDAPointer *DAPointer `json:"attestationDaPointer"`
|
|
|
|
// Block anchoring
|
|
BlockHeight uint64 `json:"blockHeight"`
|
|
BlockHash [32]byte `json:"blockHash"`
|
|
TxIndex uint32 `json:"txIndex"`
|
|
Timestamp time.Time `json:"timestamp"`
|
|
}
|
|
|
|
// Hash returns the hash of the compute anchor
|
|
func (a *ComputeAnchor) Hash() [32]byte {
|
|
h := sha256.New()
|
|
h.Write(a.ResultID[:])
|
|
h.Write(a.RequestID[:])
|
|
h.Write(a.AppChainID[:])
|
|
h.Write(a.InputCommitment[:])
|
|
h.Write(a.OutputCommitment[:])
|
|
h.Write(a.ProgramHash[:])
|
|
binary.Write(h, binary.BigEndian, a.BlockHeight)
|
|
h.Write(a.BlockHash[:])
|
|
|
|
var hash [32]byte
|
|
copy(hash[:], h.Sum(nil))
|
|
return hash
|
|
}
|