mirror of
https://github.com/luxfi/sdk.git
synced 2026-10-30 15:19:23 +00:00
Lux Partners Limited is not the entity behind this work. Every place the old name stood — copyright lines, site footers, brand config, legal and compliance text — now names the company that actually holds it. lux.fund is the exception and is not renamed to match: that site belongs to Lux Assets Management LLC, a different company, and it says so. Two kinds of line were left for a person rather than rewritten. A dated news item recording that Lux Partners Limited was established in the Isle of Man is a historical fact, and renaming it would falsify the record. And a sentence asserting that the company is domiciled in the Isle of Man is a claim about a jurisdiction, not a name — an Inc or an LLC is not an Isle of Man company, so swapping the name there would assert something untrue. Co-authored-by: Hanzo Dev <[email protected]>
122 lines
4.9 KiB
Go
122 lines
4.9 KiB
Go
// Copyright (C) 2026, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package aichain
|
|
|
|
import (
|
|
"fmt"
|
|
"math/big"
|
|
|
|
"github.com/luxfi/crypto"
|
|
"github.com/luxfi/geth/common"
|
|
)
|
|
|
|
// InferenceReceipt is the cross-chain settlement receipt produced by the A-Chain
|
|
// when a Tier-2 inference task settles. Its canonical encoding is PINNED
|
|
// byte-for-byte (Encode / ReceiptEncodedLen = 355) and identical to
|
|
// chains/aivm/receipts.go AInferenceReceipt; its hash is
|
|
// keccak(DomainReceipt || Encode()), the leaf the receipt_root commits to.
|
|
//
|
|
// FeePaid is a non-negative *big.Int (nil == zero on the wire). Field order here
|
|
// is presentation; the WIRE order is fixed by Encode and MUST NOT be reordered.
|
|
type InferenceReceipt struct {
|
|
Version uint16 `json:"version"`
|
|
IntentID common.Hash `json:"intentId"` // source C-chain intent id
|
|
TaskID common.Hash `json:"taskId"` // A-chain task id
|
|
CChainID common.Hash `json:"cChainId"`
|
|
AChainID common.Hash `json:"aChainId"`
|
|
Requester common.Address `json:"requester"`
|
|
ModelSpecHash common.Hash `json:"modelSpecHash"`
|
|
PromptHash common.Hash `json:"promptHash"`
|
|
CanonicalOutputHash common.Hash `json:"canonicalOutputHash"` // zero if Failed
|
|
Status uint8 `json:"status"`
|
|
N uint16 `json:"n"`
|
|
Threshold uint16 `json:"threshold"`
|
|
WinnersRoot common.Hash `json:"winnersRoot"`
|
|
OperatorsRoot common.Hash `json:"operatorsRoot"`
|
|
FeePaid *big.Int `json:"feePaid"`
|
|
SettledAtHeight uint64 `json:"settledAtHeight"`
|
|
}
|
|
|
|
// Encode produces the canonical fixed-width encoding (exactly ReceiptEncodedLen =
|
|
// 355 bytes) in the PINNED order, byte-identical to chains/aivm:
|
|
//
|
|
// u16be(Version) || IntentID(32) || TaskID(32) || CChainID(32) || AChainID(32) ||
|
|
// Requester(20) || ModelSpecHash(32) || PromptHash(32) || CanonicalOutputHash(32) ||
|
|
// u8(Status) || u16be(N) || u16be(Threshold) || WinnersRoot(32) ||
|
|
// OperatorsRoot(32) || u256be(FeePaid,32) || u64be(SettledAtHeight)
|
|
func (r InferenceReceipt) Encode() []byte {
|
|
buf := make([]byte, 0, ReceiptEncodedLen)
|
|
buf = append(buf, u16be(r.Version)...)
|
|
buf = append(buf, r.IntentID.Bytes()...)
|
|
buf = append(buf, r.TaskID.Bytes()...)
|
|
buf = append(buf, r.CChainID.Bytes()...)
|
|
buf = append(buf, r.AChainID.Bytes()...)
|
|
buf = append(buf, r.Requester.Bytes()...)
|
|
buf = append(buf, r.ModelSpecHash.Bytes()...)
|
|
buf = append(buf, r.PromptHash.Bytes()...)
|
|
buf = append(buf, r.CanonicalOutputHash.Bytes()...)
|
|
buf = append(buf, r.Status)
|
|
buf = append(buf, u16be(r.N)...)
|
|
buf = append(buf, u16be(r.Threshold)...)
|
|
buf = append(buf, r.WinnersRoot.Bytes()...)
|
|
buf = append(buf, r.OperatorsRoot.Bytes()...)
|
|
buf = append(buf, u256be(r.FeePaid)...)
|
|
buf = append(buf, u64be(r.SettledAtHeight)...)
|
|
return buf
|
|
}
|
|
|
|
// Hash is the receipt commitment: keccak256(DomainReceipt || Encode()). This is
|
|
// the leaf value (pre-leaf-hash) that goes into the receipt_root and that an
|
|
// exported proof proves membership of. Identical to chains/aivm
|
|
// AInferenceReceipt.Hash.
|
|
func (r InferenceReceipt) Hash() common.Hash {
|
|
return common.BytesToHash(crypto.Keccak256(append([]byte(DomainReceipt), r.Encode()...)))
|
|
}
|
|
|
|
// Completed reports whether the receipt is the only actionable shape C-side:
|
|
// Status == StatusCompleted with a non-zero canonical output. A Pending,
|
|
// Failed, or Challenged receipt — or a zero output — is never credited.
|
|
func (r InferenceReceipt) Completed() bool {
|
|
return r.Status == StatusCompleted && r.CanonicalOutputHash != (common.Hash{})
|
|
}
|
|
|
|
// DecodeReceipt parses a canonical 355-byte receipt encoding back into an
|
|
// InferenceReceipt. It is the inverse of Encode and rejects any input that is
|
|
// not EXACTLY ReceiptEncodedLen bytes (a corrupt/short/over-long frame is never
|
|
// reinterpreted into a credit — the same fail-secure discipline the precompile's
|
|
// DecodeReceipt applies).
|
|
func DecodeReceipt(b []byte) (InferenceReceipt, error) {
|
|
var r InferenceReceipt
|
|
if len(b) != ReceiptEncodedLen {
|
|
return r, fmt.Errorf("aichain: receipt length %d, want %d", len(b), ReceiptEncodedLen)
|
|
}
|
|
o := 0
|
|
rd16 := func() uint16 { v := uint16(b[o])<<8 | uint16(b[o+1]); o += 2; return v }
|
|
rd32 := func() common.Hash { h := common.BytesToHash(b[o : o+32]); o += 32; return h }
|
|
r.Version = rd16()
|
|
r.IntentID = rd32()
|
|
r.TaskID = rd32()
|
|
r.CChainID = rd32()
|
|
r.AChainID = rd32()
|
|
r.Requester = common.BytesToAddress(b[o : o+20])
|
|
o += 20
|
|
r.ModelSpecHash = rd32()
|
|
r.PromptHash = rd32()
|
|
r.CanonicalOutputHash = rd32()
|
|
r.Status = b[o]
|
|
o++
|
|
r.N = rd16()
|
|
r.Threshold = rd16()
|
|
r.WinnersRoot = rd32()
|
|
r.OperatorsRoot = rd32()
|
|
r.FeePaid = new(big.Int).SetBytes(b[o : o+32])
|
|
o += 32
|
|
var h uint64
|
|
for i := 0; i < 8; i++ {
|
|
h = h<<8 | uint64(b[o+i])
|
|
}
|
|
r.SettledAtHeight = h
|
|
return r, nil
|
|
}
|