DEV Community

NOVAInetwork
NOVAInetwork

Posted on

Shipped 7 AI Infrastructure Features in One Weekend. Here's What I Built.

This weekend I shipped seven protocol features for NOVAI, the AI-native Layer 1 blockchain I am building. The code is on a 4-validator testnet. All tests pass. The codec went from V3 to V5. The signal type set went from 7 to 16. Here is what each piece does and why it matters.

What NOVAI is

NOVAI is an L1 blockchain written from scratch in Rust. Consensus is HotStuff-style BFT with a 3-chain commit rule. There is no virtual machine. AI entities are first-class protocol primitives, not contracts. The chain has a fixed transaction set, and entity registration is one of those transactions. I am 18, in my first year of university, and I work on this alone.

Feature 1: On-chain AI reputation

The reputation system lets oracle entities adjust other entities' on-chain scores. An oracle is an AI entity that holds the submit_reputation_updates capability. It emits a ReputationUpdate signal (type 7). The execution handler applies the score change atomically.

The entity codec went from V3 (236 bytes) to V4 (246 bytes) to add reputation fields. The signal payload tail is 35 bytes: target id (32), event type (1), points delta (2). I added two memory types for the audit trail: ReputationEvent (memory type 5) and Rating (memory type 6).

Reputation events are typed. Job completed, dispute won, fraud detected, auto-release penalty, decay, stake slash, composition failure, proof verified. The handler rejects updates with unknown event types. The protocol controls what can move a reputation, not application code.

Why it matters: AI agents need a reputation the chain itself can read. Fee floors and slashing rules want to gate on trustworthiness. Reputation that lives in an indexer is reputation the protocol cannot use.

Tests live in crates/execution/tests/reputation_system.rs.

Feature 2: Signal marketplace

The signal marketplace lets one entity buy a priced signal from another. The seller publishes a SignalCatalog memory object (type 7). It lists signal types and prices. The buyer emits a SignalPurchase signal (type 8) with a 41-byte tail: seller id, purchased signal type, max price.

The handler matches the requested signal type against the seller's catalog. It checks the buyer can pay. It transfers from buyer to seller minus a 200-basis-point protocol fee. The fee accrues to the marketplace treasury.

pub const KEY_MARKETPLACE_TREASURY: &[u8] = b"treasury/marketplace";
pub const MARKETPLACE_FEE_BPS: u128 = 200;
pub const BPS_DENOMINATOR: u128 = 10_000;
Enter fullscreen mode Exit fullscreen mode

Why it matters: AI signals have economic value. An anomaly detector that sees something tradable should be able to charge for the signal. Without a protocol-level marketplace, you end up with off-chain backchannels. Trust assumptions move outside the chain, where the chain cannot enforce them.

The handler returns specific error variants on the unhappy paths: BuyerInsufficientBalance, SellerCatalogNotFound, SignalTypeNotInCatalog, MaxPriceExceeded. Tests live in crates/execution/tests/marketplace_system.rs.

Feature 3: Entity staking and slashing

The codec went from V4 to V5 (270 bytes). I added two fields: stake_balance: u128 and stake_locked_until: u64. Three new signals manage stake.

StakeDeposit (type 9) moves funds from economic_balance to stake_balance. It sets stake_locked_until = current_height + 1000. StakeWithdraw (type 10) moves funds back. The handler rejects the move unless the lock has expired. StakeSlash (type 11) is oracle-only.

The slash handler is the most careful piece in this batch. It deducts from the target's stake with saturating subtraction. It credits the slashed amount to the slash treasury. It applies a REP_EVENT_STAKE_SLASH reputation event. All writes go in one atomic batch. If any step fails, none apply.

pub const STAKE_LOCK_PERIOD: u64 = 1000;
pub const KEY_SLASH_TREASURY: &[u8] = b"treasury/slash";
pub const REP_EVENT_STAKE_SLASH: u8 = 6;
Enter fullscreen mode Exit fullscreen mode

Why it matters: an entity with no stake has nothing to lose. Reputation alone is gameable by sock puppets. Stake plus a slash path lets oracles actually punish bad behavior. The lock period blocks the obvious "deposit, misbehave, withdraw fast" attack.

Tests live in crates/execution/tests/staking_system.rs.

Feature 4: Cross-entity composition

The composition protocol lets entities declare which other entities they depend on. The owner publishes a CompositionGraph memory object (type 8). The graph lists source entities, the signal types consumed, optional minimum reputation, optional minimum stake, and a required flag.

An oracle can emit a CompositionCheck signal (type 12) when a dependency has failed. The handler walks the target's composition graph. It finds the named dependency. It verifies the failure reason against current chain state. If the dependency is required, the handler sets is_active = false on the owner. The handler always emits a REP_EVENT_COMPOSITION_FAILURE event with delta -1.

pub const COMPOSITION_FAILURE_SOURCE_INACTIVE: u8 = 0;
pub const COMPOSITION_FAILURE_REPUTATION_BELOW_MIN: u8 = 1;
pub const COMPOSITION_FAILURE_STAKE_BELOW_MIN: u8 = 2;
pub const COMPOSITION_FAILURE_SOURCE_NOT_FOUND: u8 = 3;
Enter fullscreen mode Exit fullscreen mode

Each failure reason is verified against state. If an oracle claims "source is inactive" but the source is in fact active, the handler rejects with DependencyFailureNotVerified. The protocol does not trust the oracle. It trusts the oracle's signature plus the on-chain state.

Why it matters: AI services compose. A trading bot might consume signals from a market-maker, an oracle, and an anomaly detector. If the upstream anomaly detector goes offline, the bot keeps deciding on stale data. The composition graph and the auto-pause mechanism let the protocol stop downstream entities when their inputs go bad.

I rejected self-dependency at create time and at update time. Tests live in crates/execution/tests/composition_system.rs.

Feature 5: ZK proof verification

The ProofSubmission signal (type 13) lets an entity attest to off-chain computation. The signal carries a 65-byte tail: proof_type (1), code_hash (32), computation_hash (32). The actual proof bytes live in the off-chain payload referenced by signal_hash. The handler calls a ZkVerifier trait.

The v1 verifier is a stub that always accepts. Production verifiers will replace it. The trait signature includes proof_type and code_hash. A future Groth16 or PLONK verifier can dispatch on the proof system. It can also bind the verification key to the entity's code hash.

When a proof verifies, the handler does two things. It creates a VerificationRecord memory object (type 9) owned by the issuer. It emits a REP_EVENT_PROOF_VERIFIED event with delta +3. The record has a fixed 105-byte payload: proof_type, code_hash, computation_hash, proof_hash, height_be.

pub const PROOF_TYPE_STUB: u8 = 0;
pub const PROOF_TYPE_GROTH16: u8 = 1;  // reserved
pub const PROOF_TYPE_PLONK: u8 = 2;    // reserved
pub const PROOF_TYPE_MAX: u8 = PROOF_TYPE_STUB;
Enter fullscreen mode Exit fullscreen mode

Only PROOF_TYPE_STUB works in v1. The handler rejects Groth16 and PLONK with UnsupportedProofType. Bumping PROOF_TYPE_MAX is the gate to activate a real verifier.

Why it matters: AI computation cannot be repeated on chain. Re-running a model is too expensive, and the result depends on hardware nondeterminism anyway. ZK proofs let an entity claim "I ran this code on this input and got this output". The chain can verify the claim without re-running anything.

The v1 stub is honest about not being a real verifier. The trait shape, the verification record format, and the reputation pathway are what shipped. The verifier itself is the next thing to activate.

I added 12 integration tests in crates/execution/tests/verification_system.rs.

Feature 6: Entity delegation (OAuth for AI)

The delegation system lets entity A grant a subset of its capabilities to entity B. The grant is bounded in time. It is auditable on chain. Revocation is a single DELETE transaction.

The delegator publishes a DelegationGrant memory object (type 10). The 42-byte payload holds version (1), delegate entity id (32), granted capabilities (1), and expires-at (8). An expires_at of 0 means no expiry. Updates of an existing grant are rejected.

Each delegator can hold at most 20 active grants. The granted_capabilities bits must be a subset of the delegator's static capabilities. You cannot delegate what you do not have yourself.

pub const MAX_DELEGATION_GRANTS: u32 = 20;
pub const DELEGATION_GRANT_SIZE: usize = 1 + 32 + 1 + 8;
Enter fullscreen mode Exit fullscreen mode

Capability resolution has a fast path. If the entity has the requested capability statically, the handler returns immediately. Only if the static check fails does the resolver scan the delegation index. The slow path reads any active grants where the entity is the delegate. It ORs the granted bits into an effective capability set. The selector is re-evaluated against the merged set.

The fast path matters because most calls do not use delegation. Adding delegation should not slow down the common case.

Why it matters: AI services need to delegate. Imagine a trading firm running ten sub-strategies. The parent entity holds capabilities like emit_proposals. It wants to authorize each strategy to act on its behalf. Without on-chain delegation, you either share keys (bad) or build a custom relay contract (slow). With delegation, one DELETE revokes a misbehaving sub-strategy in a single transaction.

I added 14 integration tests in crates/execution/tests/delegation_e2e.rs.

Feature 7: Signal subscriptions

The subscription protocol lets a buyer pay a producer per-block for a fixed duration. The buyer locks the full amount upfront. The producer is paid lazily, when the buyer cancels.

The subscriber emits a SubscriptionCreate signal (type 14). The 49-byte tail carries: producer id (32), covered signal type (1), rate per block (8), duration in blocks (8). The handler locks rate_per_block * duration_blocks from the subscriber's economic balance. It creates a Subscription memory object (type 11) owned by the subscriber.

The subscription record is 114 bytes. The fields are subscriber id, producer id, covered signal type, rate per block, start height. Also end height, last settled height, total locked, and an is_active flag.

pub const SUBSCRIPTION_SIZE: usize = 32 + 32 + 1 + 8 + 8 + 8 + 8 + 16 + 1;
pub const MAX_SUBSCRIPTIONS_PER_ENTITY: u32 = 10;
pub const SUBSCRIPTION_CANCEL_FEE_BPS: u128 = 500;
Enter fullscreen mode Exit fullscreen mode

To settle, the subscriber emits a SubscriptionCancel signal (type 15) with the subscription id. The handler computes accrued blocks: min(current_height, end_height) - last_settled_height. It credits the producer with accrued_blocks * rate_per_block minus a 2% marketplace fee.

It then computes the unaccrued remainder: total_locked - accrued_blocks * rate_per_block. It pays the producer a 5% cancel fee on the remainder, with no marketplace cut. The rest is refunded to the subscriber. The record is marked inactive.

Only the original subscriber may cancel. Cancelled records remain in state for audit. The subscriber reclaims the slot by issuing a DELETE transaction.

Why it matters: many AI services want recurring revenue. A DeFi protocol may need oracle predictions for ten thousand blocks. Buying each signal one at a time is wasteful. Locking funds upfront gives the producer assurance the buyer can pay. Lazy settlement on cancel avoids on-chain bookkeeping every block.

I added 18 integration tests in crates/execution/tests/subscription_system.rs.

The numbers

Before this weekend the chain had 7 signal types and 5 memory object types. The entity codec was V3 at 236 bytes.

After:

  • 16 signal types
  • 12 memory object types
  • V5 entity codec, 270 bytes
  • 1,327 passing tests
  • 60 commits on main
  • Zero clippy warnings, zero failing tests

Three treasuries hold the relevant funds. The AI treasury collects fees from AI-related transactions. The marketplace treasury collects the 200-basis-point fee on signal purchases. The slash treasury accumulates funds from misbehaving entities.

Transaction payload version bytes now cover ten distinct types. One transfer. One signal commitment. Three memory object operations: create, update, delete. Two governance operations: submit and execute. Three entity operations: register, register-with-key, and credit.

The documentation

I also wrote four docs to make the new surface usable.

docs/QUICKSTART.md walks through a 5-minute local devnet bring-up. docs/AI_ENTITY_COOKBOOK.md is seven end-to-end recipes covering reputation, marketplace, staking, composition, ZK, subscriptions, and delegation. docs/BUILDER_OVERVIEW.md is a mental model for developers who want to build on NOVAI without reading the consensus paper. docs/RPC_REFERENCE.md is the full RPC surface, audited against the V5 codec and the new signal types.

The CLI now supports all 16 signal types, including subscription create and cancel. The memory CRUD commands cover the original 10 memory object types. The getAiEntity RPC exposes V4 reputation and V5 stake fields.

What's next

Next on the list: service-level agreements as on-chain contracts between buyer and seller entities. Payment channels for high-frequency AI-to-AI economic activity. A real ZK verifier to replace the v1 stub. A cross-chain bridge so AI signals can settle on other L1s.

The thesis is simple. AI is going to do economic work. The chain it runs on should know what an AI is. It should know what the AI has done. It should know what its outputs are worth. NOVAI is the layer that makes AI economic activity legible to the protocol itself, not to a side indexer.

Try it

The repo is public. The quickstart gets a 4-node devnet up locally in five minutes. Read the cookbook to see the seven new features end to end. The tests pass. Open an issue if something breaks.

The repo is public: https://github.com/0x-devc/NOVAI-node

Top comments (0)