diff --git a/abci/example/code/code.go b/abci/example/code/code.go index 988b2a93e..6d011ed9d 100644 --- a/abci/example/code/code.go +++ b/abci/example/code/code.go @@ -7,4 +7,5 @@ const ( CodeTypeBadNonce uint32 = 2 CodeTypeUnauthorized uint32 = 3 CodeTypeUnknownError uint32 = 4 + CodeTypeExecuted uint32 = 5 ) diff --git a/abci/example/kvstore/kvstore.go b/abci/example/kvstore/kvstore.go index 8f28e2852..31a9f8742 100644 --- a/abci/example/kvstore/kvstore.go +++ b/abci/example/kvstore/kvstore.go @@ -68,6 +68,7 @@ type Application struct { state State RetainBlocks int64 // blocks to retain after commit (via ResponseCommit.RetainHeight) + txToRemove map[string]struct{} } func NewApplication() *Application { @@ -87,6 +88,9 @@ func (app *Application) Info(req types.RequestInfo) (resInfo types.ResponseInfo) // tx is either "key=value" or just arbitrary bytes func (app *Application) DeliverTx(req types.RequestDeliverTx) types.ResponseDeliverTx { + if isReplacedTx(req.Tx) { + app.txToRemove[string(req.Tx)] = struct{}{} + } var key, value []byte parts := bytes.Split(req.Tx, []byte("=")) if len(parts) == 2 { @@ -117,6 +121,11 @@ func (app *Application) DeliverTx(req types.RequestDeliverTx) types.ResponseDeli } func (app *Application) CheckTx(req types.RequestCheckTx) types.ResponseCheckTx { + if req.Type == types.CheckTxType_Recheck { + if _, ok := app.txToRemove[string(req.Tx)]; ok { + return types.ResponseCheckTx{Code: code.CodeTypeExecuted, GasWanted: 1} + } + } return types.ResponseCheckTx{Code: code.CodeTypeOK, GasWanted: 1} } @@ -126,6 +135,9 @@ func (app *Application) Commit() types.ResponseCommit { binary.PutVarint(appHash, app.state.Size) app.state.AppHash = appHash app.state.Height++ + + // empty out the set of transactions to remove via rechecktx + app.txToRemove = map[string]struct{}{} saveState(app.state) resp := types.ResponseCommit{Data: appHash} diff --git a/abci/example/kvstore/persistent_kvstore.go b/abci/example/kvstore/persistent_kvstore.go index 04ad14814..fa5db10db 100644 --- a/abci/example/kvstore/persistent_kvstore.go +++ b/abci/example/kvstore/persistent_kvstore.go @@ -45,7 +45,10 @@ func NewPersistentKVStoreApplication(dbDir string) *PersistentKVStoreApplication state := loadState(db) return &PersistentKVStoreApplication{ - app: &Application{state: state}, + app: &Application{ + state: state, + txToRemove: map[string]struct{}{}, + }, valAddrToPubKeyMap: make(map[string]pc.PublicKey), logger: log.NewNopLogger(), } @@ -302,12 +305,19 @@ func (app *PersistentKVStoreApplication) updateValidator(v types.ValidatorUpdate // ----------------------------- -const PreparePrefix = "prepare" +const ( + PreparePrefix = "prepare" + ReplacePrefix = "replace" +) func isPrepareTx(tx []byte) bool { return bytes.HasPrefix(tx, []byte(PreparePrefix)) } +func isReplacedTx(tx []byte) bool { + return bytes.HasPrefix(tx, []byte(ReplacePrefix)) +} + // execPrepareTx is noop. tx data is considered as placeholder // and is substitute at the PrepareProposal. func (app *PersistentKVStoreApplication) execPrepareTx(tx []byte) types.ResponseDeliverTx { @@ -323,7 +333,7 @@ func (app *PersistentKVStoreApplication) substPrepareTx(blockData [][]byte, maxT for _, tx := range blockData { txMod := tx if isPrepareTx(tx) { - txMod = bytes.TrimPrefix(tx, []byte(PreparePrefix)) + txMod = bytes.Replace(tx, []byte(PreparePrefix), []byte(ReplacePrefix), 1) } totalBytes += int64(len(txMod)) if totalBytes > maxTxBytes {