// cmd/wallet — wallet management CLI. // // Commands: // // wallet create --type node|user --label --out [--pass ] // wallet info --wallet [--pass ] // wallet balance --wallet [--pass ] --db // wallet bind --wallet [--pass ] --node-key // Build a BIND_WALLET transaction to link a node to this wallet. // Print the tx JSON (broadcast separately). // wallet address --pub-key Derive DC address from any pub key package main import ( "encoding/json" "flag" "fmt" "log" "os" "time" "go-blockchain/blockchain" "go-blockchain/economy" "go-blockchain/identity" "go-blockchain/wallet" ) func main() { if len(os.Args) < 2 { usage() os.Exit(1) } switch os.Args[1] { case "create": cmdCreate(os.Args[2:]) case "info": cmdInfo(os.Args[2:]) case "balance": cmdBalance(os.Args[2:]) case "bind": cmdBind(os.Args[2:]) case "address": cmdAddress(os.Args[2:]) default: usage() os.Exit(1) } } func usage() { fmt.Print(`wallet — manage DC wallets Commands: create --type node|user --label --out [--pass ] info --wallet [--pass ] balance --wallet [--pass ] --db bind --wallet [--pass ] --node-key address --pub-key `) } func cmdCreate(args []string) { fs := flag.NewFlagSet("create", flag.ExitOnError) wtype := fs.String("type", "user", "wallet type: node or user") label := fs.String("label", "My Wallet", "wallet label") out := fs.String("out", "wallet.json", "output file") pass := fs.String("pass", "", "encryption passphrase (empty = no encryption)") if err := fs.Parse(args); err != nil { log.Fatal(err) } wt := wallet.UserWallet if *wtype == "node" { wt = wallet.NodeWallet } w, err := wallet.New(wt, *label) if err != nil { log.Fatalf("create wallet: %v", err) } if err := w.Save(*out, *pass); err != nil { log.Fatalf("save wallet: %v", err) } fmt.Printf("Wallet created:\n") fmt.Printf(" type: %s\n", w.Type) fmt.Printf(" label: %s\n", w.Label) fmt.Printf(" address: %s\n", w.Address) fmt.Printf(" pub_key: %s\n", w.ID.PubKeyHex()) fmt.Printf(" saved: %s\n", *out) if *pass == "" { fmt.Println(" warning: no passphrase set — private key is unencrypted!") } } func cmdInfo(args []string) { fs := flag.NewFlagSet("info", flag.ExitOnError) file := fs.String("wallet", "wallet.json", "wallet file") pass := fs.String("pass", "", "passphrase") if err := fs.Parse(args); err != nil { log.Fatal(err) } w, err := wallet.Load(*file, *pass) if err != nil { log.Fatalf("load wallet: %v", err) } data, _ := json.MarshalIndent(w.Info(), "", " ") fmt.Println(string(data)) } func cmdBalance(args []string) { fs := flag.NewFlagSet("balance", flag.ExitOnError) file := fs.String("wallet", "wallet.json", "wallet file") pass := fs.String("pass", "", "passphrase") dbPath := fs.String("db", "./chaindata", "chain DB path") if err := fs.Parse(args); err != nil { log.Fatal(err) } w, err := wallet.Load(*file, *pass) if err != nil { log.Fatalf("load wallet: %v", err) } chain, err := blockchain.NewChain(*dbPath) if err != nil { log.Fatalf("open chain: %v", err) } defer chain.Close() bal, err := chain.Balance(w.ID.PubKeyHex()) if err != nil { log.Fatalf("query balance: %v", err) } rep, err := chain.Reputation(w.ID.PubKeyHex()) if err != nil { log.Printf("reputation unavailable: %v", err) } binding, _ := chain.WalletBinding(w.ID.PubKeyHex()) fmt.Printf("Wallet: %s\n", w.Short()) fmt.Printf(" Address: %s\n", w.Address) fmt.Printf(" Pub key: %s\n", w.ID.PubKeyHex()) fmt.Printf(" Balance: %s (%d µT)\n", economy.FormatTokens(bal), bal) fmt.Printf(" Reputation: score=%d rank=%s (blocks=%d relay=%d slashes=%d)\n", rep.Score, rep.Rank(), rep.BlocksProduced, rep.RelayProofs, rep.SlashCount) if binding != "" { fmt.Printf(" Wallet binding: → %s\n", wallet.PubKeyToAddress(binding)) } else if w.Type == wallet.NodeWallet { fmt.Printf(" Wallet binding: (none — rewards go to node key itself)\n") } } func cmdBind(args []string) { fs := flag.NewFlagSet("bind", flag.ExitOnError) file := fs.String("wallet", "wallet.json", "payout wallet file") pass := fs.String("pass", "", "wallet passphrase") nodeKeyFile := fs.String("node-key", "node.json", "node identity JSON file") if err := fs.Parse(args); err != nil { log.Fatal(err) } // Load payout wallet (where rewards should go) w, err := wallet.Load(*file, *pass) if err != nil { log.Fatalf("load wallet: %v", err) } // Load node identity (the one that signs blocks) type rawKey struct { PubKey string `json:"pub_key"` PrivKey string `json:"priv_key"` } raw, err := os.ReadFile(*nodeKeyFile) if err != nil { log.Fatalf("read node key: %v", err) } var rk rawKey if err := json.Unmarshal(raw, &rk); err != nil { log.Fatalf("parse node key: %v", err) } nodeID, err := identity.FromHex(rk.PubKey, rk.PrivKey) if err != nil { log.Fatalf("load node identity: %v", err) } // Build BIND_WALLET transaction signed by the node key payload := blockchain.BindWalletPayload{ WalletPubKey: w.ID.PubKeyHex(), WalletAddr: w.Address, } payloadBytes, _ := json.Marshal(payload) tx := &blockchain.Transaction{ ID: fmt.Sprintf("bind-%d", time.Now().UnixNano()), Type: blockchain.EventBindWallet, From: nodeID.PubKeyHex(), To: w.ID.PubKeyHex(), Payload: payloadBytes, Fee: blockchain.MinFee, Timestamp: time.Now().UTC(), } // Sign with the node key (node authorises the binding) signBytes, _ := json.Marshal(struct { ID string `json:"id"` Type blockchain.EventType `json:"type"` From string `json:"from"` To string `json:"to"` Amount uint64 `json:"amount"` Fee uint64 `json:"fee"` Payload []byte `json:"payload"` Timestamp time.Time `json:"timestamp"` }{tx.ID, tx.Type, tx.From, tx.To, tx.Amount, tx.Fee, tx.Payload, tx.Timestamp}) tx.Signature = nodeID.Sign(signBytes) data, _ := json.MarshalIndent(tx, "", " ") fmt.Printf("BIND_WALLET transaction (broadcast to a node to commit):\n\n%s\n\n", string(data)) fmt.Printf("Effect: node %s...%s will pay rewards to wallet %s\n", nodeID.PubKeyHex()[:8], nodeID.PubKeyHex()[len(nodeID.PubKeyHex())-4:], w.Address) } func cmdAddress(args []string) { fs := flag.NewFlagSet("address", flag.ExitOnError) pubKey := fs.String("pub-key", "", "hex-encoded Ed25519 public key") if err := fs.Parse(args); err != nil { log.Fatal(err) } if *pubKey == "" { log.Fatal("--pub-key is required") } addr := wallet.PubKeyToAddress(*pubKey) fmt.Printf("pub_key: %s\naddress: %s\n", *pubKey, addr) }