diff --git a/blockchain/abci.go b/blockchain/abci.go index e90f342..4ad3558 100644 --- a/blockchain/abci.go +++ b/blockchain/abci.go @@ -1,260 +1,67 @@ package blockchain import ( - "crypto/ed25519" - "encoding/base64" - "encoding/json" - "errors" + "bytes" "fmt" - "strings" - - types "github.com/gregorybednov/lbc_sdk" "github.com/dgraph-io/badger" abci "github.com/tendermint/tendermint/abci/types" ) -type PromiseApp struct { +type KVStoreApplication struct { db *badger.DB currentBatch *badger.Txn } -type compoundTxRaw struct { - Body json.RawMessage `json:"body"` - Signature string `json:"signature"` +func NewKVStoreApplication(db *badger.DB) *KVStoreApplication { + return &KVStoreApplication{db: db} } -type compoundBody struct { - Promise *types.PromiseTxBody `json:"promise"` - Commitment *types.CommitmentTxBody `json:"commitment"` -} - -func NewPromiseApp(db *badger.DB) *PromiseApp { - return &PromiseApp{db: db} -} - -func hasPrefix(id, pref string) bool { return strings.HasPrefix(id, pref+":") } - -func requireIDPrefix(id, pref string) error { - if strings.TrimSpace(id) == "" { - return fmt.Errorf("missing %s id", pref) +// Проверка транзакции (CheckTx) +func (app *KVStoreApplication) isValid(tx []byte) uint32 { + parts := bytes.Split(tx, []byte("=")) + if len(parts) != 2 { + return 1 // неверный формат } - if !hasPrefix(id, pref) { - return fmt.Errorf("invalid %s id prefix", pref) - } - return nil -} + key, value := parts[0], parts[1] -func verifyAndExtractBody(db *badger.DB, tx []byte) (map[string]interface{}, error) { - var outer struct { - Body types.CommiterTxBody `json:"body"` - Signature string `json:"signature"` - } - - if err := json.Unmarshal(tx, &outer); err != nil { - return nil, errors.New("invalid JSON wrapper") - } - - msg, err := json.Marshal(outer.Body) - if err != nil { - return nil, err - } - - sig, err := base64.StdEncoding.DecodeString(outer.Signature) - if err != nil { - return nil, errors.New("invalid signature base64") - } - if len(sig) != ed25519.SignatureSize { - return nil, fmt.Errorf("invalid signature length: got %d, want %d", len(sig), ed25519.SignatureSize) - } - - pubkeyB64 := strings.TrimSpace(outer.Body.CommiterPubKey) - if pubkeyB64 == "" { - return nil, errors.New("missing commiter pubkey") - } - pubkey, err := base64.StdEncoding.DecodeString(pubkeyB64) - if err != nil { - return nil, errors.New("invalid pubkey base64") - } - if len(pubkey) != ed25519.PublicKeySize { - return nil, fmt.Errorf("invalid pubkey length: got %d, want %d", len(pubkey), ed25519.PublicKeySize) - } - - if !ed25519.Verify(pubkey, msg, sig) { - return nil, errors.New("signature verification failed") - } - - var result map[string]interface{} - if err := json.Unmarshal(msg, &result); err != nil { - return nil, err - } - - return result, nil -} - -func (app *PromiseApp) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx { - compound, err := verifyCompoundTx(app.db, req.Tx) - if err == nil { - // ---- Валидация содержимого композита по ER ---- - p := compound.Body.Promise - c := compound.Body.Commitment - - // Оба тела должны присутствовать - if p == nil || c == nil { - return abci.ResponseCheckTx{Code: 1, Log: "compound must include promise and commitment"} - } - - // ID-предикаты и префиксы - if err := requireIDPrefix(p.ID, "promise"); err != nil { - return abci.ResponseCheckTx{Code: 2, Log: err.Error()} - } - if err := requireIDPrefix(c.ID, "commitment"); err != nil { - return abci.ResponseCheckTx{Code: 2, Log: err.Error()} - } - if err := requireIDPrefix(c.CommiterID, "commiter"); err != nil { - return abci.ResponseCheckTx{Code: 2, Log: err.Error()} - } - //if err := requireIDPrefix(p.BeneficiaryID, "beneficiary"); err != nil { - // return abci.ResponseCheckTx{Code: 2, Log: err.Error()} - //} - if p.ParentPromiseID != nil { - if err := requireIDPrefix(*p.ParentPromiseID, "promise"); err != nil { - return abci.ResponseCheckTx{Code: 2, Log: err.Error()} - } - if *p.ParentPromiseID == p.ID { - return abci.ResponseCheckTx{Code: 2, Log: "parent_promise_id must not equal promise id"} + // Проверяем, существует ли уже такая запись + err := app.db.View(func(txn *badger.Txn) error { + item, err := txn.Get(key) + if err != nil { + if err == badger.ErrKeyNotFound { + return nil // ключ не найден, все ок } + return err } - // Базовые обязательные поля Promise - if strings.TrimSpace(p.Text) == "" { - return abci.ResponseCheckTx{Code: 2, Log: "promise.text is required"} - } - //if p.Due == 0 { - // return abci.ResponseCheckTx{Code: 2, Log: "promise.due is required"} - //} - // Commitment due - //if c.Due == 0 { - // return abci.ResponseCheckTx{Code: 2, Log: "commitment.due is required"} - //} - - // Связность по ER - if c.PromiseID != p.ID { - return abci.ResponseCheckTx{Code: 2, Log: "commitment.promise_id must equal promise.id"} - } - - // Уникальность Promise.ID - if err := app.db.View(func(txn *badger.Txn) error { - _, e := txn.Get([]byte(p.ID)) - if e == badger.ErrKeyNotFound { - return nil - } - return errors.New("duplicate promise ID") - }); err != nil { - return abci.ResponseCheckTx{Code: 3, Log: err.Error()} - } - // Уникальность Commitment.ID - if err := app.db.View(func(txn *badger.Txn) error { - _, e := txn.Get([]byte(c.ID)) - if e == badger.ErrKeyNotFound { - return nil - } - return errors.New("duplicate commitment ID") - }); err != nil { - return abci.ResponseCheckTx{Code: 3, Log: err.Error()} - } - - // Существование коммитера - if err := app.db.View(func(txn *badger.Txn) error { - _, e := txn.Get([]byte(c.CommiterID)) - if e == badger.ErrKeyNotFound { - return errors.New("unknown commiter") + return item.Value(func(val []byte) error { + if bytes.Equal(val, value) { + return fmt.Errorf("duplicate key-value") } return nil - }); err != nil { - return abci.ResponseCheckTx{Code: 4, Log: err.Error()} - } + }) + }) - // Существование бенефициара - if err := app.db.View(func(txn *badger.Txn) error { - _, e := txn.Get([]byte(p.BeneficiaryID)) - if e == badger.ErrKeyNotFound { - return errors.New("unknown beneficiary") - } - return nil - }); err != nil { - return abci.ResponseCheckTx{Code: 5, Log: err.Error()} + if err != nil { + if err.Error() == "duplicate key-value" { + return 2 // дубликат найден } - - // Существование parent (если задан) - if p.ParentPromiseID != nil { - if err := app.db.View(func(txn *badger.Txn) error { - _, e := txn.Get([]byte(*p.ParentPromiseID)) - if e == badger.ErrKeyNotFound { - return errors.New("unknown parent promise") - } - return nil - }); err != nil { - return abci.ResponseCheckTx{Code: 6, Log: err.Error()} - } - } - - return abci.ResponseCheckTx{Code: 0} + // любая другая ошибка + return 1 } - // ---- Попытка ОДИНОЧНЫХ транзакций ---- - - // 3.1) Совместимость: одиночный commiter (твоя старая логика) - if body, oldErr := verifyAndExtractBody(app.db, req.Tx); oldErr == nil { - id, ok := body["id"].(string) - if !ok || id == "" { - return abci.ResponseCheckTx{Code: 2, Log: "missing id"} - } - if err := requireIDPrefix(id, "commiter"); err != nil { - return abci.ResponseCheckTx{Code: 2, Log: err.Error()} - } - // Дубликат - if err := app.db.View(func(txn *badger.Txn) error { - _, e := txn.Get([]byte(id)) - if e == badger.ErrKeyNotFound { - return nil - } - return errors.New("duplicate id") - }); err != nil { - return abci.ResponseCheckTx{Code: 3, Log: err.Error()} - } - return abci.ResponseCheckTx{Code: 0} - } - - var single struct { - Body types.BeneficiaryTxBody `json:"body"` - Signature string `json:"signature"` - } - if err2 := json.Unmarshal(req.Tx, &single); err2 == nil && single.Body.Type == "beneficiary" { - if err := requireIDPrefix(single.Body.ID, "beneficiary"); err != nil { - return abci.ResponseCheckTx{Code: 2, Log: err.Error()} - } - if strings.TrimSpace(single.Body.Name) == "" { - return abci.ResponseCheckTx{Code: 2, Log: "beneficiary.name is required"} - } - // уникальность - if err := app.db.View(func(txn *badger.Txn) error { - _, e := txn.Get([]byte(single.Body.ID)) - if e == badger.ErrKeyNotFound { - return nil - } - return errors.New("duplicate beneficiary ID") - }); err != nil { - return abci.ResponseCheckTx{Code: 3, Log: err.Error()} - } - return abci.ResponseCheckTx{Code: 0} - } - - // Если дошли сюда — составной формат не прошёл; вернём его причину. - return abci.ResponseCheckTx{Code: 1, Log: err.Error()} + return 0 // все проверки пройдены } -func (app *PromiseApp) BeginBlock(_ abci.RequestBeginBlock) abci.ResponseBeginBlock { +func (app *KVStoreApplication) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx { + code := app.isValid(req.Tx) + return abci.ResponseCheckTx{Code: code, GasWanted: 1} +} + +// Начало блока +func (app *KVStoreApplication) BeginBlock(req abci.RequestBeginBlock) abci.ResponseBeginBlock { + // If there's an existing batch for some reason, discard it first if app.currentBatch != nil { app.currentBatch.Discard() } @@ -262,222 +69,112 @@ func (app *PromiseApp) BeginBlock(_ abci.RequestBeginBlock) abci.ResponseBeginBl return abci.ResponseBeginBlock{} } -func (app *PromiseApp) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx { - // Попытка композита - if compound, err := verifyCompoundTx(app.db, req.Tx); err == nil { - if app.currentBatch == nil { - app.currentBatch = app.db.NewTransaction(true) - } - if compound.Body.Promise != nil { - data, _ := json.Marshal(compound.Body.Promise) - if err := app.currentBatch.Set([]byte(compound.Body.Promise.ID), data); err != nil { - return abci.ResponseDeliverTx{Code: 1, Log: "failed to save promise"} - } - } - if compound.Body.Commitment != nil { - data, _ := json.Marshal(compound.Body.Commitment) - if err := app.currentBatch.Set([]byte(compound.Body.Commitment.ID), data); err != nil { - return abci.ResponseDeliverTx{Code: 1, Log: "failed to save commitment"} - } - } - return abci.ResponseDeliverTx{Code: 0} +// Применение транзакции +func (app *KVStoreApplication) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx { + code := app.isValid(req.Tx) + if code != 0 { + return abci.ResponseDeliverTx{Code: code} } - // Одиночный commiter (как раньше) - { - var outer struct { - Body types.CommiterTxBody `json:"body"` - Signature string `json:"signature"` - } - if err := json.Unmarshal(req.Tx, &outer); err == nil && outer.Body.Type == "commiter" { - // сигнатуру проверяем прежней функцией - if _, vErr := verifyAndExtractBody(app.db, req.Tx); vErr != nil { - return abci.ResponseDeliverTx{Code: 1, Log: vErr.Error()} - } - if app.currentBatch == nil { - app.currentBatch = app.db.NewTransaction(true) - } - data, _ := json.Marshal(outer.Body) - if err := app.currentBatch.Set([]byte(outer.Body.ID), data); err != nil { - return abci.ResponseDeliverTx{Code: 1, Log: err.Error()} - } - return abci.ResponseDeliverTx{Code: 0} + parts := bytes.Split(req.Tx, []byte("=")) + if app.currentBatch == nil { + // In case BeginBlock wasn't called or batch was discarded + app.currentBatch = app.db.NewTransaction(true) + } + + err := app.currentBatch.Set(parts[0], parts[1]) + if err != nil { + return abci.ResponseDeliverTx{ + Code: 1, + Log: fmt.Sprintf("Failed to set key: %v", err), } } - // Одиночный beneficiary - { - var outer struct { - Body types.BeneficiaryTxBody `json:"body"` - Signature string `json:"signature"` - } - if err := json.Unmarshal(req.Tx, &outer); err == nil && outer.Body.Type == "beneficiary" { - // (пока без проверки подписи — можно добавить политику позже) - if app.currentBatch == nil { - app.currentBatch = app.db.NewTransaction(true) - } - data, _ := json.Marshal(outer.Body) - if err := app.currentBatch.Set([]byte(outer.Body.ID), data); err != nil { - return abci.ResponseDeliverTx{Code: 1, Log: err.Error()} - } - return abci.ResponseDeliverTx{Code: 0} - } - } - - return abci.ResponseDeliverTx{Code: 1, Log: "invalid tx format"} + return abci.ResponseDeliverTx{Code: 0} } -func verifyCompoundTx(db *badger.DB, tx []byte) (*types.CompoundTx, error) { - // 1) Разобрать внешний конверт, body оставить сырым - var outerRaw compoundTxRaw - if err := json.Unmarshal(tx, &outerRaw); err != nil { - return nil, errors.New("invalid compound tx JSON") - } - if len(outerRaw.Body) == 0 { - return nil, errors.New("missing body") - } - - // 2) Вынуть commiter_id из body, не парся всё - var tiny struct { - Commitment *struct { - CommiterID string `json:"commiter_id"` - } `json:"commitment"` - } - if err := json.Unmarshal(outerRaw.Body, &tiny); err != nil { - return nil, errors.New("invalid body JSON") - } - if tiny.Commitment == nil || strings.TrimSpace(tiny.Commitment.CommiterID) == "" { - return nil, errors.New("missing commitment") - } - commiterID := tiny.Commitment.CommiterID - - // 3) Достать коммитера из БД и получить публичный ключ - var commiterData []byte - err := db.View(func(txn *badger.Txn) error { - item, err := txn.Get([]byte(commiterID)) - if err != nil { - return errors.New("unknown commiter") - } - return item.Value(func(v []byte) error { - commiterData = append([]byte{}, v...) - return nil - }) - }) - if err != nil { - return nil, err - } - var commiter types.CommiterTxBody - if err := json.Unmarshal(commiterData, &commiter); err != nil { - return nil, errors.New("corrupted commiter record") - } - pubkeyB64 := strings.TrimSpace(commiter.CommiterPubKey) - if pubkeyB64 == "" { - return nil, errors.New("missing commiter pubkey") - } - pubkey, err := base64.StdEncoding.DecodeString(pubkeyB64) - if err != nil { - return nil, errors.New("invalid pubkey base64") - } - if len(pubkey) != ed25519.PublicKeySize { - return nil, fmt.Errorf("invalid pubkey length: got %d, want %d", len(pubkey), ed25519.PublicKeySize) - } - - // 4) Проверка подписи над СЫРЫМИ БАЙТАМИ body (как клиент подписывал) - sig, err := base64.StdEncoding.DecodeString(outerRaw.Signature) - if err != nil { - return nil, errors.New("invalid signature base64") - } - if len(sig) != ed25519.SignatureSize { - return nil, fmt.Errorf("invalid signature length: got %d, want %d", len(sig), ed25519.SignatureSize) - } - if !ed25519.Verify(pubkey, outerRaw.Body, sig) { - return nil, errors.New("signature verification failed") - } - - // 5) Только после успешной проверки — распарсить body в наши структуры - var body compoundBody - if err := json.Unmarshal(outerRaw.Body, &body); err != nil { - return nil, errors.New("invalid body JSON") - } - - // 6) Вернуть в привычной форме - return &types.CompoundTx{ - Body: struct { - Promise *types.PromiseTxBody `json:"promise"` - Commitment *types.CommitmentTxBody `json:"commitment"` - }{ - Promise: body.Promise, - Commitment: body.Commitment, - }, - Signature: outerRaw.Signature, - }, nil -} - -func (app *PromiseApp) Commit() abci.ResponseCommit { +// Завершение блока и фиксация +func (app *KVStoreApplication) Commit() abci.ResponseCommit { if app.currentBatch != nil { err := app.currentBatch.Commit() if err != nil { - fmt.Printf("Commit error: %v\n", err) + // Log error but continue - in a real application, you might want + // to handle this more gracefully + fmt.Printf("Error committing batch: %v\n", err) } app.currentBatch = nil } return abci.ResponseCommit{Data: []byte{}} } -func (app *PromiseApp) Query(req abci.RequestQuery) abci.ResponseQuery { - parts := strings.Split(strings.Trim(req.Path, "/"), "/") - if len(parts) != 2 || parts[0] != "list" { - return abci.ResponseQuery{Code: 1, Log: "unsupported query"} - } - prefix := []byte(parts[1] + ":") +// Обслуживание запросов Query +func (app *KVStoreApplication) Query(req abci.RequestQuery) abci.ResponseQuery { + resp := abci.ResponseQuery{Code: 0} - var result []json.RawMessage err := app.db.View(func(txn *badger.Txn) error { - it := txn.NewIterator(badger.DefaultIteratorOptions) - defer it.Close() - for it.Seek(prefix); it.ValidForPrefix(prefix); it.Next() { - item := it.Item() - err := item.Value(func(v []byte) error { - var raw json.RawMessage = make([]byte, len(v)) - copy(raw, v) - result = append(result, raw) - return nil - }) - if err != nil { - return err - } + item, err := txn.Get(req.Data) + if err != nil { + return err } - return nil + + return item.Value(func(val []byte) error { + resp.Value = val + return nil + }) }) if err != nil { - return abci.ResponseQuery{Code: 1, Log: err.Error()} + resp.Code = 1 + resp.Log = err.Error() } - all, _ := json.Marshal(result) - return abci.ResponseQuery{Code: 0, Value: all} + + return resp } -func (app *PromiseApp) Info(req abci.RequestInfo) abci.ResponseInfo { - return abci.ResponseInfo{Data: "promises", Version: "0.1"} +// Добавляем недостающие методы ABCI интерфейса + +// Info возвращает информацию о приложении +func (app *KVStoreApplication) Info(req abci.RequestInfo) abci.ResponseInfo { + return abci.ResponseInfo{ + Data: "kvstore", + Version: "1.0.0", + AppVersion: 1, + LastBlockHeight: 0, + LastBlockAppHash: []byte{}, + } } -func (app *PromiseApp) SetOption(req abci.RequestSetOption) abci.ResponseSetOption { - return abci.ResponseSetOption{} + +// SetOption устанавливает опцию приложения +func (app *KVStoreApplication) SetOption(req abci.RequestSetOption) abci.ResponseSetOption { + return abci.ResponseSetOption{Code: 0} } -func (app *PromiseApp) InitChain(req abci.RequestInitChain) abci.ResponseInitChain { + +// InitChain инициализирует блокчейн +func (app *KVStoreApplication) InitChain(req abci.RequestInitChain) abci.ResponseInitChain { return abci.ResponseInitChain{} } -func (app *PromiseApp) EndBlock(req abci.RequestEndBlock) abci.ResponseEndBlock { + +// EndBlock сигнализирует о конце блока +func (app *KVStoreApplication) EndBlock(req abci.RequestEndBlock) abci.ResponseEndBlock { return abci.ResponseEndBlock{} } -func (app *PromiseApp) ListSnapshots(req abci.RequestListSnapshots) abci.ResponseListSnapshots { + +// ListSnapshots возвращает список доступных снапшотов +func (app *KVStoreApplication) ListSnapshots(req abci.RequestListSnapshots) abci.ResponseListSnapshots { return abci.ResponseListSnapshots{} } -func (app *PromiseApp) OfferSnapshot(req abci.RequestOfferSnapshot) abci.ResponseOfferSnapshot { + +// OfferSnapshot предлагает снапшот приложению +func (app *KVStoreApplication) OfferSnapshot(req abci.RequestOfferSnapshot) abci.ResponseOfferSnapshot { return abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_REJECT} } -func (app *PromiseApp) LoadSnapshotChunk(req abci.RequestLoadSnapshotChunk) abci.ResponseLoadSnapshotChunk { + +// LoadSnapshotChunk загружает часть снапшота +func (app *KVStoreApplication) LoadSnapshotChunk(req abci.RequestLoadSnapshotChunk) abci.ResponseLoadSnapshotChunk { return abci.ResponseLoadSnapshotChunk{} } -func (app *PromiseApp) ApplySnapshotChunk(req abci.RequestApplySnapshotChunk) abci.ResponseApplySnapshotChunk { + +// ApplySnapshotChunk применяет часть снапшота +func (app *KVStoreApplication) ApplySnapshotChunk(req abci.RequestApplySnapshotChunk) abci.ResponseApplySnapshotChunk { return abci.ResponseApplySnapshotChunk{Result: abci.ResponseApplySnapshotChunk_ACCEPT} } diff --git a/blockchain/main.go b/blockchain/main.go index e80d8a4..092d159 100644 --- a/blockchain/main.go +++ b/blockchain/main.go @@ -4,10 +4,9 @@ import ( "context" "fmt" + "lbc/cfg" "os" - "github.com/gregorybednov/lbc/cfg" - "github.com/dgraph-io/badger" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/log" @@ -24,7 +23,6 @@ func openBadger(path string) (*badger.DB, error) { func newTendermint(app abci.Application, config *cfg.Config, laddrReturner chan string) (*nm.Node, error) { config.P2P.ListenAddress = "tcp://" + <-laddrReturner - config.P2P.ExternalAddress = <-laddrReturner config.P2P.PersistentPeers = <-laddrReturner var pv tmTypes.PrivValidator @@ -65,7 +63,7 @@ func GetNodeInfo(config *cfg.Config, dbPath string) (p2p.NodeInfo, error) { } defer db.Close() - app := NewPromiseApp(db) + app := NewKVStoreApplication(db) nodeKey, err := p2p.LoadNodeKey(config.NodeKeyFile()) if err != nil { @@ -112,7 +110,7 @@ func Run(ctx context.Context, dbPath string, config *cfg.Config, laddrReturner c } defer db.Close() - app := NewPromiseApp(db) + app := NewKVStoreApplication(db) node, err := newTendermint(app, config, laddrReturner) if err != nil { return fmt.Errorf("build node: %w", err) diff --git a/cfg/configfunctions.go b/cfg/configfunctions.go index befb37d..b4705d9 100644 --- a/cfg/configfunctions.go +++ b/cfg/configfunctions.go @@ -6,13 +6,12 @@ import ( "flag" "fmt" "io" + "lbc/yggdrasil" "os" "path/filepath" "strconv" "time" - "github.com/gregorybednov/lbc/yggdrasil" - "github.com/spf13/viper" cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/p2p" @@ -164,17 +163,16 @@ func WriteConfig(config *cfg.Config, configPath *string, nodeInfo p2p.NodeInfo) config.P2P.PersistentPeers = a } - v.Set("p2p", map[string]any{ - "use_legacy": false, - "queue_type": "priority", - "laddr": strconv.Itoa(yggListenPort) + ":127.0.0.1:8000", - "external_address": "", - "upnp": false, - "bootstrap_peers": "", - "persistent_peers": config.P2P.PersistentPeers, - "allow_duplicate_ip": true, // needed because of Yggdrasil proxy - "addr_book_file": "config/addrbook.json", - "addr_book_strict": false, + v.Set("p2p", map[string]interface{}{ + "use_legacy": false, + "queue_type": "priority", + "laddr": strconv.Itoa(yggListenPort) + ":127.0.0.1:8000", + "external_address": "", // will be set automatically by Tendermint if needed + "upnp": false, + "bootstrap_peers": "", + "persistent_peers": config.P2P.PersistentPeers, + "addr_book_file": "config/addrbook.json", + "addr_book_strict": false, }) err = v.WriteConfigAs(*configPath) @@ -252,25 +250,18 @@ func UpdateGenesisJson(nodeInfo p2p.NodeInfo, v *viper.Viper, defaultConfigDirec } } -func InitGenesis(chainName, defaultConfigPath string) (*cfg.Config, *viper.Viper, error) { +func InitGenesis(chainName, defaultConfigPath string) (*cfg.Config, *viper.Viper) { config := cfg.DefaultConfig() config.RootDir = filepath.Dir(filepath.Dir(defaultConfigPath)) - if err := os.MkdirAll(config.RootDir, 0o755); err != nil { - return nil, nil, fmt.Errorf("failed to create config directory %s: %w", config.RootDir, err) - } - if err := os.MkdirAll(filepath.Dir(defaultConfigPath), 0o755); err != nil { - return nil, nil, fmt.Errorf("failed to create config directory %s: %w", filepath.Dir(defaultConfigPath), err) - } - nodeinfo := p2p.DefaultNodeInfo{} viper := WriteConfig(config, &defaultConfigPath, nodeinfo) - if err := InitTendermintFiles(config, true, chainName); err != nil { - return nil, nil, fmt.Errorf("failed to init tendermint files: %w", err) + fmt.Fprintf(os.Stderr, "Failed to init files: %v\n", err) + panic(err) } - return config, viper, nil + return config, viper } func InitJoiner(chainName, defaultConfigPath, path string) error { diff --git a/cli/init.go b/cli/init.go index 03c6747..77fab6d 100644 --- a/cli/init.go +++ b/cli/init.go @@ -2,12 +2,11 @@ package cli import ( "fmt" + "lbc/blockchain" + "lbc/cfg" "os" "path/filepath" - "github.com/gregorybednov/lbc/blockchain" - "github.com/gregorybednov/lbc/cfg" - "github.com/spf13/cobra" ) @@ -18,11 +17,7 @@ var initCmd = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { switch args[0] { case "genesis": - config, viper, err := cfg.InitGenesis(chainName, defaultConfigPath) - if err != nil { - fmt.Fprintf(os.Stderr, "Error: %v", err) - panic(err) - } + config, viper := cfg.InitGenesis(chainName, defaultConfigPath) nodeinfo, err := blockchain.GetNodeInfo(config, dbPath) if err != nil { fmt.Fprintf(os.Stderr, "Error: %v", err) @@ -34,14 +29,14 @@ var initCmd = &cobra.Command{ case "join": if len(args) < 2 { fmt.Fprintln(os.Stderr, "Укажите путь к genesis.json") - return + os.Exit(1) } cfg.InitJoiner(chainName, defaultConfigPath, args[1]) fmt.Println("Joiner node initialized.") default: fmt.Fprintf(os.Stderr, "Неизвестный режим init: %s\n", args[0]) - return + os.Exit(1) } }, } diff --git a/cli/root.go b/cli/root.go index 303956c..e582de6 100644 --- a/cli/root.go +++ b/cli/root.go @@ -3,16 +3,13 @@ package cli import ( "context" "fmt" - "io" + "lbc/blockchain" + "lbc/cfg" + "lbc/yggdrasil" "os" "os/signal" - "path/filepath" "syscall" - "github.com/gregorybednov/lbc/blockchain" - "github.com/gregorybednov/lbc/cfg" - "github.com/gregorybednov/lbc/yggdrasil" - "github.com/spf13/cobra" ) @@ -44,7 +41,7 @@ var rootCmd = &cobra.Command{ По умолчанию файл конфигурации ищется по пути: %s `, err, defaultConfigPath) - return + os.Exit(1) } config, err := cfg.ReadConfig(defaultConfigPath) @@ -53,7 +50,7 @@ var rootCmd = &cobra.Command{ } ctx, cancel := context.WithCancel(context.Background()) - laddrReturner := make(chan string, 3) + laddrReturner := make(chan string, 2) go yggdrasil.Yggdrasil(v, laddrReturner) go blockchain.Run(ctx, dbPath, config, laddrReturner) @@ -70,38 +67,6 @@ var rootCmd = &cobra.Command{ func Execute() { if err := rootCmd.Execute(); err != nil { fmt.Fprintf(os.Stderr, "ошибка: %v\n", err) - return + os.Exit(1) } } - -func ExecuteWithArgs(args []string, home string, stdout, stderr io.Writer) error { - // Перенаправляем вывод Cobra - if stdout != nil { - rootCmd.SetOut(stdout) - } - if stderr != nil { - rootCmd.SetErr(stderr) - } - - // Если задан home, подставим дефолты, на которые завязаны флаги - // (флаги привязаны к переменным через StringVar, поэтому смена переменных до Execute — норм.) - origCfg := defaultConfigPath - origDB := dbPath - - if home != "" { - defaultConfigPath = filepath.Join(home, "config", "config.toml") - // Примем convention: BADGER в (home)/data/badger, но если у тебя другое — поменяй строку ниже. - dbPath = filepath.Join(home, "data", "badger") - } - - // Важное: подаём именно те аргументы, которые хотел вызвать вызывающий код. - rootCmd.SetArgs(args) - - // Выполняем и аккуратно восстанавливаем глобальные дефолты. - err := rootCmd.Execute() - - defaultConfigPath = origCfg - dbPath = origDB - - return err -} diff --git a/cli/testyggdrasil.go b/cli/testyggdrasil.go index eca78be..58f8b01 100644 --- a/cli/testyggdrasil.go +++ b/cli/testyggdrasil.go @@ -4,6 +4,8 @@ import ( "context" "fmt" "io" + "lbc/cfg" + "lbc/yggdrasil" "log" "net" "net/url" @@ -13,9 +15,6 @@ import ( "syscall" "time" - "github.com/gregorybednov/lbc/cfg" - "github.com/gregorybednov/lbc/yggdrasil" - "github.com/spf13/cobra" ) @@ -26,7 +25,7 @@ var testYggdrasilCmd = &cobra.Command{ v, err := cfg.LoadViperConfig(defaultConfigPath) if err != nil { fmt.Fprintf(os.Stderr, "не удалось прочитать конфигурацию viper: %v", err) - return err + os.Exit(1) } config, err := cfg.ReadConfig(defaultConfigPath) if err != nil { diff --git a/docs/ER.svg b/docs/ER.svg deleted file mode 100644 index 5bec34d..0000000 --- a/docs/ER.svg +++ /dev/null @@ -1 +0,0 @@ -PromiseID: uuidtext: textdue: datetimeBeneficiaryID: uuidParentPromiseID: uuidBeneficiaryID: uuidname: stringCommitmentID: uuidPromiseID: intCommiterID: intdue: datetimeCommiterID: intname: stringbelongs tomade byhasparent of \ No newline at end of file diff --git a/docs/database_schema.md b/docs/database_schema.md deleted file mode 100644 index 00ba10c..0000000 --- a/docs/database_schema.md +++ /dev/null @@ -1,43 +0,0 @@ -# Логическая модель данных - -![[ER.svg]] - -
- @startuml - - entity Promise { - * ID: uuid - -- - * text: string - due: datetime - BeneficiaryID: uuid - ParentPromiseID: uuid - } - - entity Beneficiary { - * ID: uuid - -- - * name: string - } - - entity Commitment { - * ID: uuid - -- - PromiseID: uuid - CommiterID: uuid - due: datetime - } - - entity Commiter { - * ID: uuid - -- - * name: string - } - - Commitment }|--|| Promise : belongs to - Commitment }|--|| Commiter : made by - Promise }o--|| Beneficiary : has - Promise }--o Promise : parent of - - @enduml -
diff --git a/go.mod b/go.mod index 7b3f7b1..e9944ee 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/gregorybednov/lbc +module lbc go 1.24.3 @@ -68,7 +68,6 @@ require ( github.com/google/orderedcode v0.0.1 // indirect github.com/google/pprof v0.0.0-20241017200806-017d972448fc // indirect github.com/gorilla/websocket v1.5.3 // indirect - github.com/gregorybednov/lbc_sdk v0.0.0-20250810123844-a90b874431fa // indirect github.com/gtank/merlin v0.1.1 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/hjson/hjson-go/v4 v4.4.0 // indirect diff --git a/go.sum b/go.sum index fca1357..fbd0cf5 100644 --- a/go.sum +++ b/go.sum @@ -195,10 +195,6 @@ 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/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -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/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is= github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= diff --git a/main.go b/main.go index 5aeb0d8..2e6c6fb 100644 --- a/main.go +++ b/main.go @@ -1,7 +1,7 @@ package main import ( - "github.com/gregorybednov/lbc/cli" + "lbc/cli" ) func main() { diff --git a/yggdrasil/main.go b/yggdrasil/main.go index 4e86711..6d81bbb 100644 --- a/yggdrasil/main.go +++ b/yggdrasil/main.go @@ -62,7 +62,7 @@ func Yggdrasil(config *viper.Viper, ch chan string) { parsed, err := ParseEntries(peers) if err != nil { parsed = []ParsedEntry{} - // ch <- "" + ch <- "" log.Warnln("Warning: persistent peers has an error") } @@ -141,18 +141,11 @@ func Yggdrasil(config *viper.Viper, ch chan string) { panic(err) } address, subnet := n.core.Address(), n.core.Subnet() - yggPort := 26656 - if len(remoteTcp) > 0 && remoteTcp[0].Listen.Port != 0 { - yggPort = remoteTcp[0].Listen.Port - } - ipStr := address.String() // ожидается чистый IPv6 без /префикса - yggExternal := fmt.Sprintf("[%s]:%d", ipStr, yggPort) - ch <- yggExternal - - //logger.Printf("Your public key is %s", publicstr) + publicstr := hex.EncodeToString(n.core.PublicKey()) + logger.Printf("Your public key is %s", publicstr) logger.Printf("Your IPv6 address is %s", address.String()) logger.Printf("Your IPv6 subnet is %s", subnet.String()) - //logger.Printf("Your Yggstack resolver name is %s%s", publicstr, types.NameMappingSuffix) + logger.Printf("Your Yggstack resolver name is %s%s", publicstr, types.NameMappingSuffix) } // Setup the admin socket.