feat(nu-proto): initial Faz 0 scaffold

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Mukan Erkin TÖRÜK 2026-04-24 00:00:26 +03:00
commit 37862083c4
7 changed files with 321 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
target/
*.rs.bk

39
CLAUDE.md Normal file
View file

@ -0,0 +1,39 @@
# nu-proto — CLAUDE.md
Tüm ekosistemin canonical tip ve RPC API tanımları. **Değişince diğer repolar kırılır.**
## Kritik Kural
`v0.1` tag'i atıldıktan sonra field silemezsin, field tipi değiştiremezsin. Yeni field eklemek non-breaking — sıra numarası bir kez atandıktan sonra değişmez.
```protobuf
// BREAKING: field silindi/tipi değişti — bunu yapma v0.1 sonrası
// Yeni field ekle, eski field'ı deprecated bırak
```
## Dosya Yapısı
```
types/
transaction.proto ← TxPayload enum; tüm sistemi kilitler
block.proto ← BlockHeader, Block, BlockReceipt
story_node.proto ← StoryNode, Story, NodeStatus
nft.proto ← Nft, Collection
rpc/
api.proto ← Platform ↔ Node JSON-RPC sözleşmesi
```
## Derleme
```bash
# Rust crate üret
protoc --rust_out=src/ types/*.proto rpc/*.proto
# TypeScript (platform için)
protoc --ts_out=../nu-platform/src/proto/ types/*.proto rpc/*.proto
```
## Versiyon
Şu an: **v0.1-dev** (henüz dondurulmadı)
v0.1 lock sonrası paralel geliştirme başlar.

89
rpc/api.proto Normal file
View file

@ -0,0 +1,89 @@
syntax = "proto3";
package nu.rpc;
import "types/block.proto";
import "types/transaction.proto";
import "types/story_node.proto";
import "types/nft.proto";
// JSON-RPC v2 method catalog v0.1
// All methods over HTTP POST /rpc and WS /ws
// Queries
message GetBlockRequest { uint64 height = 1; }
message GetBlockResponse { nu.types.Block block = 1; }
message GetTxRequest { string tx_id = 1; }
message GetTxResponse {
nu.types.Transaction tx = 1;
nu.types.BlockReceipt receipt = 2;
}
message GetAccountRequest { string address = 1; }
message GetAccountResponse {
string address = 1;
uint64 balance = 2; // NUT micro-units
uint64 staked = 3;
uint64 locked = 4;
uint64 locked_until = 5; // Unix epoch ms
string pon_score = 6; // decimal string
repeated string nft_ids = 7;
}
message GetStoryRequest { string story_id = 1; }
message GetStoryResponse {
nu.types.Story story = 1;
repeated nu.types.StoryNode nodes = 2;
}
message GetNodeRequest { string node_id = 1; } // canonical or temp_id
message GetNodeResponse { nu.types.StoryNode node = 1; }
message GetNftRequest { string nft_id = 1; }
message GetNftResponse { nu.types.Nft nft = 1; }
message ListStoriesRequest {
uint32 page = 1;
uint32 per_page = 2;
}
message ListStoriesResponse {
repeated nu.types.Story stories = 1;
uint32 total = 2;
}
message ListPendingVotesRequest { string voter = 1; } // Address; empty = all
message ListPendingVotesResponse { repeated nu.types.StoryNode nodes = 1; }
// Mutations
message SendRawTxRequest { bytes raw_tx = 1; } // protobuf-encoded signed Transaction
message SendRawTxResponse {
string tx_id = 1;
bool queued = 2;
string error = 3;
}
// Chain Info
message ChainInfoRequest {}
message ChainInfoResponse {
uint64 latest_height = 1;
string latest_hash = 2;
uint32 validator_count = 3;
string chain_id = 4; // e.g. "nu-devnet-1"
}
// WebSocket Subscriptions (event stream)
message SubscribeRequest {
repeated string topics = 1;
// topics: "blocks", "node:<story_id>", "vote:<node_id>", "nft:<address>"
}
message Event {
string topic = 1;
string kind = 2; // "new_block" | "node_approved" | "vote_cast" | "nft_minted"
bytes payload = 3; // protobuf-encoded type depending on kind
}

29
types/block.proto Normal file
View file

@ -0,0 +1,29 @@
syntax = "proto3";
package nu.types;
import "types/transaction.proto";
message BlockHeader {
uint64 height = 1;
string prev_block_hash = 2; // Hash256
int64 timestamp = 3; // Unix epoch milliseconds
string validator_addr = 4; // Address
bytes validator_sig = 5; // secp256k1
string tx_root = 6; // Merkle root of transactions
string state_root = 7; // Merkle Patricia Trie root
string receipts_root = 8;
uint32 slot = 9;
}
message Block {
BlockHeader header = 1;
repeated Transaction transactions = 2;
}
message BlockReceipt {
string tx_id = 1;
bool success = 2;
string error = 3; // empty if success
uint64 gas_used = 4;
}

20
types/nft.proto Normal file
View file

@ -0,0 +1,20 @@
syntax = "proto3";
package nu.types;
message Nft {
string nft_id = 1; // same as canonical node_id
string node_id = 2;
string owner = 3; // Address
string collection_id = 4; // empty until CollectionClaim
uint32 depth = 5; // path length from root
repeated string lineage = 6; // ancestor node_ids root this
int64 minted_at = 7;
}
message Collection {
string collection_id = 1; // leaf node_id of the path
string owner = 2; // Address
repeated string nft_ids = 3; // ordered root leaf
int64 claimed_at = 4;
}

39
types/story_node.proto Normal file
View file

@ -0,0 +1,39 @@
syntax = "proto3";
package nu.types;
enum NodeStatus {
PENDING = 0;
VOTING_OPEN = 1;
APPROVED = 2;
REJECTED = 3;
}
message WeightedVote {
string voter = 1; // Address
bool approve = 2;
string weight = 3; // decimal string; computed at finalization
}
message StoryNode {
string node_id = 1; // canonical; empty until approved
string temp_id = 2; // client UUID during pending/voting
string story_id = 3;
string parent_id = 4;
string author = 5; // Address
string content_hash = 6; // IpfsHash
NodeStatus status = 7;
int64 submitted_at = 8; // Unix epoch ms
int64 vote_open_at = 9; // day 7
int64 vote_end_at = 10; // day 10
repeated WeightedVote votes = 11;
string nft_id = 12; // set on approval
}
message Story {
string story_id = 1;
string root_node_id = 2; // canonical
string creator = 3; // Address
int64 created_at = 4;
bool is_genesis = 5; // genesis dev log no voting, no entry fee
}

103
types/transaction.proto Normal file
View file

@ -0,0 +1,103 @@
syntax = "proto3";
package nu.types;
option java_package = "tr.cw.narrativeunion.types";
option java_outer_classname = "TransactionProto";
// Hash256: 32-byte SHA-256 digest, hex-encoded
// Address: 20-byte account address, hex-encoded
// IpfsHash: CIDv1 string
message Transaction {
string tx_id = 1; // SHA-256(payload bytes)
string sender = 2; // Address
uint64 nonce = 3;
uint64 fee = 4; // NUT micro-units (1 NUT = 1_000_000 units)
bytes signature = 5; // secp256k1 compact (64 bytes)
TxPayload payload = 6;
}
message TxPayload {
oneof kind {
TokenTransfer token_transfer = 1;
NodeSubmit node_submit = 2;
VoteRegister vote_register = 3;
VoteCast vote_cast = 4;
NftTransfer nft_transfer = 5;
CollectionClaim collection_claim = 6;
StakeOp stake_op = 7;
ValidatorRegister validator_register = 8;
// Auto-generated by validator never submitted by users directly:
NodeApprove node_approve = 9;
NftMint nft_mint = 10;
NodeReject node_reject = 11;
VotingOpen voting_open = 12;
}
}
message TokenTransfer {
string to = 1; // Address
uint64 amount = 2;
}
message NodeSubmit {
string story_id = 1;
string parent_node_id = 2; // empty if root node
string content_hash = 3; // IpfsHash
uint64 entry_fee = 4; // must equal reward * 0.25
string temp_id = 5; // client-generated UUID; replaced by canonical NodeId on approval
}
message VoteRegister {
string node_id = 1; // temp_id at this stage
uint64 stake_lock = 2; // must equal node_reward * 0.10; locked 10 days
}
message VoteCast {
string node_id = 1;
bool approve = 2; // true = approve, false = reject
}
message NftTransfer {
string nft_id = 1;
string to = 2; // Address
}
message CollectionClaim {
repeated string nft_ids = 1; // must form a valid lineage path from root
}
message StakeOp {
enum Kind {
STAKE = 0;
UNSTAKE = 1;
}
Kind op = 1;
uint64 amount = 2;
}
message ValidatorRegister {
uint64 stake = 1; // minimum 1_000_000_000 units (1000 NUT)
}
// --- Auto-generated scheduler/validator transactions ---
message NodeApprove {
string node_id = 1; // canonical NodeId assigned at approval
string temp_id = 2; // maps back to NodeSubmit.temp_id
string canonical_id = 3; // numeric path-encoded id e.g. "1159"
}
message NftMint {
string node_id = 1; // canonical
string recipient = 2; // Address node author
}
message NodeReject {
string temp_id = 1;
}
message VotingOpen {
string node_id = 1; // temp_id; triggered at day 7
}