feat(nu-proto): initial Faz 0 scaffold
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
commit
37862083c4
7 changed files with 321 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
target/
|
||||||
|
*.rs.bk
|
||||||
39
CLAUDE.md
Normal file
39
CLAUDE.md
Normal 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
89
rpc/api.proto
Normal 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
29
types/block.proto
Normal 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
20
types/nft.proto
Normal 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
39
types/story_node.proto
Normal 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
103
types/transaction.proto
Normal 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
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue