diff --git a/blockchain/abci.go b/blockchain/abci.go index 4ad3558..1c4bb86 100644 --- a/blockchain/abci.go +++ b/blockchain/abci.go @@ -1,67 +1,24 @@ package blockchain import ( - "bytes" + "encoding/json" "fmt" + "strings" "github.com/dgraph-io/badger" abci "github.com/tendermint/tendermint/abci/types" ) -type KVStoreApplication struct { +type PromiseApp struct { db *badger.DB currentBatch *badger.Txn } -func NewKVStoreApplication(db *badger.DB) *KVStoreApplication { - return &KVStoreApplication{db: db} +func NewPromiseApp(db *badger.DB) *PromiseApp { + return &PromiseApp{db: db} } -// Проверка транзакции (CheckTx) -func (app *KVStoreApplication) isValid(tx []byte) uint32 { - parts := bytes.Split(tx, []byte("=")) - if len(parts) != 2 { - return 1 // неверный формат - } - key, value := parts[0], parts[1] - - // Проверяем, существует ли уже такая запись - 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 - } - - return item.Value(func(val []byte) error { - if bytes.Equal(val, value) { - return fmt.Errorf("duplicate key-value") - } - return nil - }) - }) - - if err != nil { - if err.Error() == "duplicate key-value" { - return 2 // дубликат найден - } - // любая другая ошибка - return 1 - } - - return 0 // все проверки пройдены -} - -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 +func (app *PromiseApp) BeginBlock(_ abci.RequestBeginBlock) abci.ResponseBeginBlock { if app.currentBatch != nil { app.currentBatch.Discard() } @@ -69,112 +26,124 @@ func (app *KVStoreApplication) BeginBlock(req abci.RequestBeginBlock) abci.Respo return abci.ResponseBeginBlock{} } -// Применение транзакции -func (app *KVStoreApplication) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx { - code := app.isValid(req.Tx) - if code != 0 { - return abci.ResponseDeliverTx{Code: code} +func (app *PromiseApp) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx { + var tx map[string]interface{} + if err := json.Unmarshal(req.Tx, &tx); err != nil { + return abci.ResponseDeliverTx{Code: 1, Log: "invalid JSON"} } - parts := bytes.Split(req.Tx, []byte("=")) + id, ok := tx["id"].(string) + if !ok || id == "" { + return abci.ResponseDeliverTx{Code: 2, Log: "missing id"} + } + + data, _ := json.Marshal(tx) + 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]) + err := app.currentBatch.Set([]byte(id), data) if err != nil { - return abci.ResponseDeliverTx{ - Code: 1, - Log: fmt.Sprintf("Failed to set key: %v", err), - } + return abci.ResponseDeliverTx{Code: 1, Log: fmt.Sprintf("failed to set: %v", err)} } return abci.ResponseDeliverTx{Code: 0} } -// Завершение блока и фиксация -func (app *KVStoreApplication) Commit() abci.ResponseCommit { +func (app *PromiseApp) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx { + var tx map[string]interface{} + if err := json.Unmarshal(req.Tx, &tx); err != nil { + return abci.ResponseCheckTx{Code: 1, Log: "invalid JSON"} + } + id, ok := tx["id"].(string) + if !ok || id == "" { + return abci.ResponseCheckTx{Code: 2, Log: "missing id"} + } + + // проверка уникальности id + err := app.db.View(func(txn *badger.Txn) error { + _, err := txn.Get([]byte(id)) + if err == badger.ErrKeyNotFound { + return nil + } + return fmt.Errorf("id exists") + }) + if err != nil { + return abci.ResponseCheckTx{Code: 3, Log: "duplicate id"} + } + + return abci.ResponseCheckTx{Code: 0} +} + +func (app *PromiseApp) Commit() abci.ResponseCommit { if app.currentBatch != nil { err := app.currentBatch.Commit() if err != nil { - // Log error but continue - in a real application, you might want - // to handle this more gracefully - fmt.Printf("Error committing batch: %v\n", err) + fmt.Printf("Commit error: %v\n", err) } app.currentBatch = nil } return abci.ResponseCommit{Data: []byte{}} } -// Обслуживание запросов Query -func (app *KVStoreApplication) Query(req abci.RequestQuery) abci.ResponseQuery { - resp := abci.ResponseQuery{Code: 0} +// SELECT * FROM