major update
This commit is contained in:
parent
9c2d1aa992
commit
a8f460388a
3 changed files with 425 additions and 126 deletions
2
go.mod
2
go.mod
|
|
@ -4,6 +4,6 @@ go 1.24.3
|
|||
|
||||
require (
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gregorybednov/lbc_sdk v0.0.0-20250810102513-432a51e65f76
|
||||
github.com/gregorybednov/lbc_sdk v0.0.0-20250810123844-a90b874431fa
|
||||
github.com/spf13/pflag v1.0.7
|
||||
)
|
||||
|
|
|
|||
2
go.sum
2
go.sum
|
|
@ -2,5 +2,7 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
|||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gregorybednov/lbc_sdk v0.0.0-20250810102513-432a51e65f76 h1:e3A+1v+Mjt8nuJcVnHuhHZuh4052KRLCUBpH4g74rVs=
|
||||
github.com/gregorybednov/lbc_sdk v0.0.0-20250810102513-432a51e65f76/go.mod h1:DBE00+SaYBtD4qw+nOtSTLuF6h9Ia4TkuBMJB+6krik=
|
||||
github.com/gregorybednov/lbc_sdk v0.0.0-20250810123844-a90b874431fa h1:8EuqAmsS94ju83o4aEIV8e2fdocdAJ9xVerKRSI+nDA=
|
||||
github.com/gregorybednov/lbc_sdk v0.0.0-20250810123844-a90b874431fa/go.mod h1:DBE00+SaYBtD4qw+nOtSTLuF6h9Ia4TkuBMJB+6krik=
|
||||
github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M=
|
||||
github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
|
|
|
|||
507
main.go
507
main.go
|
|
@ -6,18 +6,21 @@ import (
|
|||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
types "github.com/gregorybednov/lbc_sdk"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
// ===== RPC plumbing =====
|
||||
|
||||
type rpcResp struct {
|
||||
JSONRPC string `json:"jsonrpc"`
|
||||
ID any `json:"id"`
|
||||
|
|
@ -38,10 +41,6 @@ type rpcResp struct {
|
|||
} `json:"result"`
|
||||
}
|
||||
|
||||
const configDir = "./config"
|
||||
const privKeyPath = configDir + "/ed25519.key"
|
||||
const pubKeyPath = configDir + "/ed25519.pub"
|
||||
|
||||
func parseRPCResult(data []byte) error {
|
||||
var r rpcResp
|
||||
_ = json.Unmarshal(data, &r)
|
||||
|
|
@ -60,6 +59,31 @@ func parseRPCResult(data []byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func postRPC(txB64, rpcURL string) error {
|
||||
final := map[string]any{
|
||||
"jsonrpc": "2.0",
|
||||
"id": uuid.NewString(),
|
||||
"method": "broadcast_tx_commit",
|
||||
"params": map[string]string{
|
||||
"tx": txB64,
|
||||
},
|
||||
}
|
||||
finalBytes, _ := json.Marshal(final)
|
||||
resp, err := http.Post(rpcURL, "application/json", bytes.NewReader(finalBytes))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
data, _ := io.ReadAll(resp.Body)
|
||||
return parseRPCResult(data)
|
||||
}
|
||||
|
||||
// ===== Keys =====
|
||||
|
||||
const configDir = "./config"
|
||||
const privKeyPath = configDir + "/ed25519.key"
|
||||
const pubKeyPath = configDir + "/ed25519.pub"
|
||||
|
||||
func ensureKeypair() (ed25519.PublicKey, ed25519.PrivateKey, error) {
|
||||
if _, err := os.Stat(privKeyPath); os.IsNotExist(err) {
|
||||
fmt.Println("🔐 Generating new ed25519 keypair...")
|
||||
|
|
@ -67,163 +91,436 @@ func ensureKeypair() (ed25519.PublicKey, ed25519.PrivateKey, error) {
|
|||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
os.MkdirAll(configDir, 0700)
|
||||
os.WriteFile(privKeyPath, priv, 0600)
|
||||
os.WriteFile(pubKeyPath, pub, 0644)
|
||||
_ = os.MkdirAll(configDir, 0700)
|
||||
_ = os.WriteFile(privKeyPath, priv, 0600)
|
||||
_ = os.WriteFile(pubKeyPath, pub, 0644)
|
||||
return pub, priv, nil
|
||||
}
|
||||
priv, _ := os.ReadFile(privKeyPath)
|
||||
pub, _ := os.ReadFile(pubKeyPath)
|
||||
priv, err := os.ReadFile(privKeyPath)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
pub, err := os.ReadFile(pubKeyPath)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return ed25519.PublicKey(pub), ed25519.PrivateKey(priv), nil
|
||||
}
|
||||
|
||||
func sendTx(tx types.SignedTx, rpcURL string) error {
|
||||
txBytes, _ := json.Marshal(tx)
|
||||
txB64 := base64.StdEncoding.EncodeToString(txBytes)
|
||||
final := map[string]any{
|
||||
"jsonrpc": "2.0",
|
||||
"id": uuid.NewString(),
|
||||
"method": "broadcast_tx_commit",
|
||||
"params": map[string]string{
|
||||
"tx": txB64,
|
||||
},
|
||||
// ===== Tx bodies per ER =====
|
||||
|
||||
type CommiterTxBody struct {
|
||||
Type string `json:"type"` // "commiter"
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
CommiterPubKey string `json:"commiter_pubkey"`
|
||||
}
|
||||
finalBytes, _ := json.Marshal(final)
|
||||
fmt.Println("🚧 Raw tx JSON:", string(txBytes))
|
||||
fmt.Printf("🚧 Final RPC body:\n%s\n", string(finalBytes))
|
||||
resp, err := http.Post(rpcURL, "application/json", bytes.NewReader(finalBytes))
|
||||
|
||||
type BeneficiaryTxBody struct {
|
||||
Type string `json:"type"` // "beneficiary"
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type PromiseTxBody struct {
|
||||
Type string `json:"type"` // "promise"
|
||||
ID string `json:"id"`
|
||||
Text string `json:"text"`
|
||||
Due int64 `json:"due"`
|
||||
BeneficiaryID string `json:"beneficiary_id"`
|
||||
ParentPromiseID *string `json:"parent_promise_id"`
|
||||
}
|
||||
|
||||
type CommitmentTxBody struct {
|
||||
Type string `json:"type"` // "commitment"
|
||||
ID string `json:"id"`
|
||||
PromiseID string `json:"promise_id"`
|
||||
CommiterID string `json:"commiter_id"`
|
||||
Due int64 `json:"due"`
|
||||
}
|
||||
|
||||
type SignedTx struct {
|
||||
Body any `json:"body"`
|
||||
Signature string `json:"signature"`
|
||||
}
|
||||
|
||||
type CompositeSignedTx struct {
|
||||
Body struct {
|
||||
Promise *PromiseTxBody `json:"promise"`
|
||||
Commitment *CommitmentTxBody `json:"commitment"`
|
||||
} `json:"body"`
|
||||
Signature string `json:"signature"`
|
||||
}
|
||||
|
||||
// ===== Helpers =====
|
||||
|
||||
func mustUUID(prefix string) string { return prefix + ":" + uuid.NewString() }
|
||||
|
||||
func parseWhen(s string) (int64, error) {
|
||||
if s == "" {
|
||||
return 0, errors.New("missing datetime")
|
||||
}
|
||||
if t, err := time.Parse(time.RFC3339, s); err == nil {
|
||||
return t.Unix(), nil
|
||||
}
|
||||
if t, err := time.Parse("2006-01-02", s); err == nil {
|
||||
return time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, time.UTC).Unix(), nil
|
||||
}
|
||||
return 0, fmt.Errorf("cannot parse time: %q (use 2006-01-02 or RFC3339)", s)
|
||||
}
|
||||
|
||||
func sign(priv ed25519.PrivateKey, body any) (sigB64 string, raw []byte, err error) {
|
||||
raw, err = json.Marshal(body)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
sig := ed25519.Sign(priv, raw)
|
||||
return base64.StdEncoding.EncodeToString(sig), raw, nil
|
||||
}
|
||||
|
||||
// ===== High-level ops: send =====
|
||||
|
||||
func registerCommiter(name, rpcURL string) error {
|
||||
pub, priv, err := ensureKeypair()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
data, _ := io.ReadAll(resp.Body)
|
||||
fmt.Println("📡 Response:")
|
||||
fmt.Println(string(data))
|
||||
if err := parseRPCResult(data); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func registerCommiter(name, rpcURL string) error {
|
||||
pub, priv, _ := ensureKeypair()
|
||||
pubB64 := base64.StdEncoding.EncodeToString(pub)
|
||||
id := "commiter:" + pubB64
|
||||
body := types.CommiterTxBody{
|
||||
body := CommiterTxBody{
|
||||
Type: "commiter",
|
||||
ID: id,
|
||||
Name: name,
|
||||
CommiterPubKey: pubB64,
|
||||
}
|
||||
bodyBytes, _ := json.Marshal(body)
|
||||
sig := ed25519.Sign(priv, bodyBytes)
|
||||
tx := types.SignedTx{Body: body, Signature: base64.StdEncoding.EncodeToString(sig)}
|
||||
return sendTx(tx, rpcURL)
|
||||
sigB64, _, err := sign(priv, body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tx := SignedTx{Body: body, Signature: sigB64}
|
||||
txBytes, _ := json.Marshal(tx)
|
||||
return postRPC(base64.StdEncoding.EncodeToString(txBytes), rpcURL)
|
||||
}
|
||||
|
||||
func createPromise(desc, rpcURL string) error {
|
||||
pub, priv, _ := ensureKeypair()
|
||||
func createBeneficiary(name, rpcURL string) (string, error) {
|
||||
_, priv, err := ensureKeypair()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
id := mustUUID("beneficiary")
|
||||
body := BeneficiaryTxBody{
|
||||
Type: "beneficiary",
|
||||
ID: id,
|
||||
Name: name,
|
||||
}
|
||||
sigB64, _, err := sign(priv, body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
tx := SignedTx{Body: body, Signature: sigB64}
|
||||
txBytes, _ := json.Marshal(tx)
|
||||
if err := postRPC(base64.StdEncoding.EncodeToString(txBytes), rpcURL); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
type CreatePromiseArgs struct {
|
||||
Text string
|
||||
DueISO string
|
||||
BeneficiaryID string
|
||||
ParentPromiseID string
|
||||
CommitmentDueISO string
|
||||
}
|
||||
|
||||
func createPromiseAndCommit(args CreatePromiseArgs, rpcURL string) error {
|
||||
if args.Text == "" {
|
||||
return errors.New("--text is required")
|
||||
}
|
||||
if args.BeneficiaryID == "" {
|
||||
return errors.New("--beneficiary-id is required")
|
||||
}
|
||||
promiseDue, err := parseWhen(args.DueISO)
|
||||
if err != nil {
|
||||
return fmt.Errorf("promise --due: %w", err)
|
||||
}
|
||||
commitDue, err := parseWhen(args.CommitmentDueISO)
|
||||
if err != nil {
|
||||
return fmt.Errorf("commitment --commitment-due: %w", err)
|
||||
}
|
||||
|
||||
pub, priv, err := ensureKeypair()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pubB64 := base64.StdEncoding.EncodeToString(pub)
|
||||
|
||||
commiterID := "commiter:" + pubB64
|
||||
promiseID := "promise:" + uuid.NewString()
|
||||
commitmentID := "commitment:" + uuid.NewString()
|
||||
|
||||
// Объекты тела
|
||||
promise := &types.PromiseTxBody{
|
||||
promiseID := mustUUID("promise")
|
||||
commitmentID := mustUUID("commitment")
|
||||
|
||||
var parentPtr *string
|
||||
if args.ParentPromiseID != "" {
|
||||
p := args.ParentPromiseID
|
||||
parentPtr = &p
|
||||
}
|
||||
|
||||
promise := &PromiseTxBody{
|
||||
Type: "promise",
|
||||
ID: promiseID,
|
||||
Description: desc,
|
||||
Timestamp: time.Now().Unix(),
|
||||
Text: args.Text,
|
||||
Due: promiseDue,
|
||||
BeneficiaryID: args.BeneficiaryID,
|
||||
ParentPromiseID: parentPtr,
|
||||
}
|
||||
commitment := &types.CommitmentTxBody{
|
||||
commitment := &CommitmentTxBody{
|
||||
Type: "commitment",
|
||||
ID: commitmentID,
|
||||
PromiseID: promiseID,
|
||||
CommiterID: commiterID,
|
||||
Due: commitDue,
|
||||
}
|
||||
|
||||
// Сборка тела для подписи
|
||||
bodyStruct := struct {
|
||||
Promise *types.PromiseTxBody `json:"promise"`
|
||||
Commitment *types.CommitmentTxBody `json:"commitment"`
|
||||
}{
|
||||
Promise: promise,
|
||||
Commitment: commitment,
|
||||
}
|
||||
var compound CompositeSignedTx
|
||||
compound.Body.Promise = promise
|
||||
compound.Body.Commitment = commitment
|
||||
|
||||
// Сериализация и подпись
|
||||
bodyBytes, err := json.Marshal(bodyStruct)
|
||||
sigB64, _, err := sign(priv, compound.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal compound body: %w", err)
|
||||
return err
|
||||
}
|
||||
sig := ed25519.Sign(priv, bodyBytes)
|
||||
sigB64 := base64.StdEncoding.EncodeToString(sig)
|
||||
compound.Signature = sigB64
|
||||
|
||||
// Формирование транзакции
|
||||
compound := types.CompoundTx{
|
||||
Body: bodyStruct,
|
||||
Signature: sigB64,
|
||||
}
|
||||
|
||||
// Отправка
|
||||
txBytes, err := json.Marshal(compound)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal compound tx: %w", err)
|
||||
return err
|
||||
}
|
||||
txB64 := base64.StdEncoding.EncodeToString(txBytes)
|
||||
final := map[string]any{
|
||||
"jsonrpc": "2.0",
|
||||
"id": uuid.NewString(),
|
||||
"method": "broadcast_tx_commit",
|
||||
"params": map[string]string{
|
||||
"tx": txB64,
|
||||
},
|
||||
return postRPC(base64.StdEncoding.EncodeToString(txBytes), rpcURL)
|
||||
}
|
||||
|
||||
finalBytes, _ := json.Marshal(final)
|
||||
fmt.Printf("🚧 Final RPC body:\n%s\n", string(finalBytes))
|
||||
resp, err := http.Post(rpcURL, "application/json", bytes.NewReader(finalBytes))
|
||||
// ===== High-level ops: get (abci_query) =====
|
||||
|
||||
type abciQueryResp struct {
|
||||
JSONRPC string `json:"jsonrpc"`
|
||||
ID any `json:"id"`
|
||||
Result struct {
|
||||
Response struct {
|
||||
Code int `json:"code"`
|
||||
Log string `json:"log"`
|
||||
Info string `json:"info"`
|
||||
Index string `json:"index"`
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"` // base64
|
||||
ProofOps any `json:"proofOps"`
|
||||
Height string `json:"height"`
|
||||
Codespace string `json:"codespace"`
|
||||
} `json:"response"`
|
||||
} `json:"result"`
|
||||
Error *struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Data string `json:"data"`
|
||||
} `json:"error"`
|
||||
}
|
||||
|
||||
func abciQuery(rpcURL, path, dataB64 string, height string) (*abciQueryResp, error) {
|
||||
v := url.Values{}
|
||||
// Tendermint любит path в кавычках, как в твоём примере
|
||||
v.Set("path", fmt.Sprintf("%q", path))
|
||||
if dataB64 != "" {
|
||||
v.Set("data", dataB64)
|
||||
}
|
||||
if height != "" {
|
||||
v.Set("height", height)
|
||||
}
|
||||
u := strings.TrimRight(rpcURL, "/") + "/abci_query?" + v.Encode()
|
||||
resp, err := http.Get(u)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
data, _ := io.ReadAll(resp.Body)
|
||||
fmt.Println("📡 Response:")
|
||||
fmt.Println(string(data))
|
||||
if err := parseRPCResult(data); err != nil {
|
||||
return err
|
||||
b, _ := io.ReadAll(resp.Body)
|
||||
var q abciQueryResp
|
||||
if err := json.Unmarshal(b, &q); err != nil {
|
||||
return nil, fmt.Errorf("decode json: %w", err)
|
||||
}
|
||||
return nil
|
||||
if q.Error != nil {
|
||||
return nil, fmt.Errorf("RPC error: %d %s (%s)", q.Error.Code, q.Error.Message, q.Error.Data)
|
||||
}
|
||||
return &q, nil
|
||||
}
|
||||
|
||||
func printRawJSON(obj any) {
|
||||
out, _ := json.MarshalIndent(obj, "", " ")
|
||||
fmt.Println(string(out))
|
||||
}
|
||||
|
||||
func tryPrintValueAsJSONOrText(b []byte) {
|
||||
// попытка как JSON
|
||||
var anyJSON any
|
||||
if json.Unmarshal(b, &anyJSON) == nil {
|
||||
printRawJSON(anyJSON)
|
||||
return
|
||||
}
|
||||
// если выглядит как текст — печатаем строкой
|
||||
s := string(b)
|
||||
// грубая эвристика: если есть нулевые байты — покажем как base64
|
||||
if strings.IndexByte(s, 0x00) >= 0 {
|
||||
fmt.Println(base64.StdEncoding.EncodeToString(b))
|
||||
return
|
||||
}
|
||||
fmt.Println(s)
|
||||
}
|
||||
|
||||
// ===== CLI =====
|
||||
|
||||
func main() {
|
||||
var name, desc, rpc string
|
||||
pflag.StringVar(&name, "name", "", "register commiter name")
|
||||
pflag.StringVar(&desc, "desc", "", "create promise description")
|
||||
pflag.StringVar(&rpc, "rpc", "http://localhost:26657", "Tendermint RPC URL")
|
||||
pflag.Parse()
|
||||
if len(os.Args) < 2 {
|
||||
usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
cmd := os.Args[1]
|
||||
switch cmd {
|
||||
case "send":
|
||||
sendMain(os.Args[2:])
|
||||
case "get":
|
||||
getMain(os.Args[2:])
|
||||
default:
|
||||
usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if name != "" {
|
||||
err := registerCommiter(name, rpc)
|
||||
func usage() {
|
||||
fmt.Println("Usage:")
|
||||
fmt.Println(" lbc_client send [--rpc URL] (--name NAME | --beneficiary-name NAME | --text TXT --due DATE --beneficiary-id ID [--parent-id ID] --commitment-due DATE)")
|
||||
fmt.Println(" lbc_client get [--rpc URL] --path PATH [--data BYTES] [--height H] [--raw-json | --value]")
|
||||
}
|
||||
|
||||
func sendMain(args []string) {
|
||||
fs := pflag.NewFlagSet("send", pflag.ExitOnError)
|
||||
var rpc string
|
||||
var name string
|
||||
var beneficiaryName string
|
||||
var text, due, beneficiaryID, parentID, commitmentDue string
|
||||
|
||||
fs.StringVar(&rpc, "rpc", "http://localhost:26657", "Tendermint RPC URL")
|
||||
fs.StringVar(&name, "name", "", "register commiter name")
|
||||
fs.StringVar(&beneficiaryName, "beneficiary-name", "", "create beneficiary with a given name")
|
||||
fs.StringVar(&text, "text", "", "promise text (required for promise)")
|
||||
fs.StringVar(&due, "due", "", "promise due (YYYY-MM-DD or RFC3339)")
|
||||
fs.StringVar(&beneficiaryID, "beneficiary-id", "", "beneficiary ID (required for promise)")
|
||||
fs.StringVar(&parentID, "parent-id", "", "optional parent promise ID")
|
||||
fs.StringVar(&commitmentDue, "commitment-due", "", "commitment due (YYYY-MM-DD or RFC3339)")
|
||||
|
||||
_ = fs.Parse(args)
|
||||
|
||||
switch {
|
||||
case name != "":
|
||||
if err := registerCommiter(name, rpc); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "❌ Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println("✅ Commiter registered")
|
||||
case beneficiaryName != "":
|
||||
id, err := createBeneficiary(beneficiaryName, rpc)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "❌ Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println("✅ Commiter registered successfully")
|
||||
return
|
||||
fmt.Printf("✅ Beneficiary created: %s\n", id)
|
||||
default:
|
||||
// promise+commitment
|
||||
if text == "" || due == "" || beneficiaryID == "" || commitmentDue == "" {
|
||||
fmt.Fprintln(os.Stderr, "⛔ For promise+commitment you must pass --text, --due, --beneficiary-id, --commitment-due")
|
||||
os.Exit(1)
|
||||
}
|
||||
args := CreatePromiseArgs{
|
||||
Text: text,
|
||||
DueISO: due,
|
||||
BeneficiaryID: beneficiaryID,
|
||||
ParentPromiseID: parentID,
|
||||
CommitmentDueISO: commitmentDue,
|
||||
}
|
||||
if err := createPromiseAndCommit(args, rpc); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "❌ Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println("✅ Promise+Commitment created atomically")
|
||||
}
|
||||
}
|
||||
|
||||
if desc != "" {
|
||||
err := createPromise(desc, rpc)
|
||||
func getMain(args []string) {
|
||||
fs := pflag.NewFlagSet("get", pflag.ExitOnError)
|
||||
var rpc string
|
||||
var path string
|
||||
var listAlias string
|
||||
var dataArg string
|
||||
var height string
|
||||
var rawJSON bool
|
||||
var value bool
|
||||
|
||||
fs.StringVar(&rpc, "rpc", "http://localhost:26657", "Tendermint RPC URL")
|
||||
fs.StringVar(&path, "path", "", "ABCI path (e.g. /list/promise)")
|
||||
fs.StringVar(&listAlias, "list", "", "entity alias: promise | commitment | commiter | beneficiary")
|
||||
fs.StringVar(&dataArg, "data", "", "optional key/arg (sent as base64)")
|
||||
fs.StringVar(&height, "height", "", "block height")
|
||||
fs.BoolVar(&rawJSON, "raw-json", false, "print raw abci_query JSON")
|
||||
fs.BoolVar(&value, "value", false, "decode response.value (base64) and try to parse JSON")
|
||||
|
||||
_ = fs.Parse(args)
|
||||
|
||||
// алиасы -> path
|
||||
if path == "" && listAlias != "" {
|
||||
switch listAlias {
|
||||
case "promise", "commitment", "commiter", "beneficiary":
|
||||
path = "/list/" + listAlias
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "⛔ unknown alias for --list: %q\n", listAlias)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if path == "" {
|
||||
fmt.Fprintln(os.Stderr, "⛔ Either --path or --list is required")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// поведение по умолчанию:
|
||||
// - при --list, если ни --raw-json, ни --value не заданы → включаем --value
|
||||
// - при --path — остаётся прежняя логика (тоже включает --value по умолчанию)
|
||||
if !rawJSON && !value {
|
||||
value = true
|
||||
}
|
||||
|
||||
// data → base64
|
||||
var dataB64 string
|
||||
if dataArg != "" {
|
||||
dataB64 = base64.StdEncoding.EncodeToString([]byte(dataArg))
|
||||
}
|
||||
|
||||
q, err := abciQuery(rpc, path, dataB64, height)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "❌ Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println("✅ Promise and Commitment created successfully")
|
||||
|
||||
if rawJSON {
|
||||
printRawJSON(q)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("⛔ Please provide either --name or --desc")
|
||||
os.Exit(1)
|
||||
// value mode
|
||||
valB64 := q.Result.Response.Value
|
||||
if valB64 == "" {
|
||||
fmt.Println("")
|
||||
return
|
||||
}
|
||||
raw, err := base64.StdEncoding.DecodeString(valB64)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "⚠️ cannot base64-decode value: %v\n", err)
|
||||
fmt.Println(valB64)
|
||||
return
|
||||
}
|
||||
tryPrintValueAsJSONOrText(raw)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue