From e6b5c2368ff7c3e3af807d48d17e8a9f2bf5fd01 Mon Sep 17 00:00:00 2001
From: Philip Offtermatt
Date: Wed, 10 May 2023 10:05:47 +0200
Subject: [PATCH 01/26] Add functionality to automatically run blocks
---
cometmock/abci_client/client.go | 11 +++++++++++
cometmock/main.go | 9 ++++++++-
2 files changed, 19 insertions(+), 1 deletion(-)
diff --git a/cometmock/abci_client/client.go b/cometmock/abci_client/client.go
index ea6414c..cc9ff15 100644
--- a/cometmock/abci_client/client.go
+++ b/cometmock/abci_client/client.go
@@ -3,6 +3,7 @@ package abci_client
import (
"fmt"
"reflect"
+ "sync"
"time"
abciclient "github.com/cometbft/cometbft/abci/client"
@@ -19,6 +20,9 @@ import (
var GlobalClient *AbciClient
+// store a mutex that allows only running one block at a time
+var blockMutex = sync.Mutex{}
+
// AbciClient facilitates calls to the ABCI interface of multiple nodes.
// It also tracks the current state and a common logger.
type AbciClient struct {
@@ -256,7 +260,11 @@ func (a *AbciClient) SendAbciQuery(data []byte, path string, height int64, prove
// RunBlock runs a block with a specified transaction through the ABCI application.
// It calls BeginBlock, DeliverTx, EndBlock, Commit and then
// updates the state.
+// RunBlock is safe for use by multiple goroutines simultaneously.
func (a *AbciClient) RunBlock(tx *[]byte) (*abcitypes.ResponseBeginBlock, *abcitypes.ResponseDeliverTx, *abcitypes.ResponseEndBlock, *abcitypes.ResponseCommit, error) {
+ // lock mutex to avoid running two blocks at the same time
+ blockMutex.Lock()
+
a.Logger.Info("Running block")
resBeginBlock, err := a.SendBeginBlock()
@@ -290,6 +298,9 @@ func (a *AbciClient) RunBlock(tx *[]byte) (*abcitypes.ResponseBeginBlock, *abcit
return nil, nil, nil, nil, err
}
+ // unlock mutex
+ blockMutex.Unlock()
+
return resBeginBlock, resDeliverTx, resEndBlock, resCommit, nil
}
diff --git a/cometmock/main.go b/cometmock/main.go
index 314c86f..ca1bb2c 100644
--- a/cometmock/main.go
+++ b/cometmock/main.go
@@ -3,6 +3,7 @@ package main
import (
"os"
"strings"
+ "time"
comet_abciclient "github.com/cometbft/cometbft/abci/client"
cometlog "github.com/cometbft/cometbft/libs/log"
@@ -61,5 +62,11 @@ func main() {
// run an empty block
abci_client.GlobalClient.RunBlock(nil)
- rpc_server.StartRPCServerWithDefaultConfig(cometMockListenAddress, logger)
+ go rpc_server.StartRPCServerWithDefaultConfig(cometMockListenAddress, logger)
+
+ // produce a block every 5 seconds
+ for {
+ abci_client.GlobalClient.RunBlock(nil)
+ time.Sleep(5 * time.Second)
+ }
}
From 54d42cf359c8aa126e3ada49a0e5bbe5c41f8a62 Mon Sep 17 00:00:00 2001
From: Philip Offtermatt
Date: Wed, 10 May 2023 10:08:04 +0200
Subject: [PATCH 02/26] Decrease block time
---
cometmock/main.go | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/cometmock/main.go b/cometmock/main.go
index ca1bb2c..7022196 100644
--- a/cometmock/main.go
+++ b/cometmock/main.go
@@ -64,9 +64,9 @@ func main() {
go rpc_server.StartRPCServerWithDefaultConfig(cometMockListenAddress, logger)
- // produce a block every 5 seconds
+ // produce a block every second
for {
abci_client.GlobalClient.RunBlock(nil)
- time.Sleep(5 * time.Second)
+ time.Sleep(1 * time.Second)
}
}
From 6d7add1503093efb9f74629a346518095c436a7c Mon Sep 17 00:00:00 2001
From: Philip Offtermatt
Date: Wed, 10 May 2023 11:41:53 +0200
Subject: [PATCH 03/26] Add logging and make block show the block height
---
cometmock/abci_client/client.go | 1 +
cometmock/rpc_server/routes.go | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/cometmock/abci_client/client.go b/cometmock/abci_client/client.go
index cc9ff15..01ebd62 100644
--- a/cometmock/abci_client/client.go
+++ b/cometmock/abci_client/client.go
@@ -266,6 +266,7 @@ func (a *AbciClient) RunBlock(tx *[]byte) (*abcitypes.ResponseBeginBlock, *abcit
blockMutex.Lock()
a.Logger.Info("Running block")
+ a.Logger.Info("State at start of block", "state", a.CurState)
resBeginBlock, err := a.SendBeginBlock()
if err != nil {
diff --git a/cometmock/rpc_server/routes.go b/cometmock/rpc_server/routes.go
index c24afa1..872dd35 100644
--- a/cometmock/rpc_server/routes.go
+++ b/cometmock/rpc_server/routes.go
@@ -187,7 +187,7 @@ func Block(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlock, error)
blockID := abci_client.GlobalClient.CurState.LastBlockID
// TODO: return an actual block if it is needed, for now return en empty block
- block := &types.Block{}
+ block := &types.Block{Header: types.Header{Height: abci_client.GlobalClient.CurState.LastBlockHeight}}
return &ctypes.ResultBlock{BlockID: blockID, Block: block}, nil
}
From 060ac3db6bd49a76a68d5ee6779dd52e8806e7c7 Mon Sep 17 00:00:00 2001
From: Philip Offtermatt
Date: Wed, 10 May 2023 17:24:45 +0200
Subject: [PATCH 04/26] Fix typo Commut to Commit
---
cometmock/abci_client/client.go | 12 +++++++++---
cometmock/rpc_server/routes.go | 2 +-
2 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/cometmock/abci_client/client.go b/cometmock/abci_client/client.go
index 01ebd62..53befe0 100644
--- a/cometmock/abci_client/client.go
+++ b/cometmock/abci_client/client.go
@@ -39,7 +39,7 @@ type AbciClient struct {
func (a *AbciClient) SendBeginBlock() (*abcitypes.ResponseBeginBlock, error) {
a.Logger.Info("Sending BeginBlock to clients")
// build the BeginBlock request
- beginBlockRequest := CreateBeginBlockRequest(a.CurState, time.Now(), *a.CurState.LastValidators.Proposer)
+ beginBlockRequest := CreateBeginBlockRequest(a.CurState, time.Now(), a.CurState.LastValidators.Proposer)
// send BeginBlock to all clients and collect the responses
responses := []*abcitypes.ResponseBeginBlock{}
@@ -63,7 +63,13 @@ func (a *AbciClient) SendBeginBlock() (*abcitypes.ResponseBeginBlock, error) {
return responses[0], nil
}
-func CreateBeginBlockRequest(curState state.State, curTime time.Time, proposer types.Validator) *abcitypes.RequestBeginBlock {
+func CreateBeginBlockRequest(curState state.State, curTime time.Time, proposer *types.Validator) *abcitypes.RequestBeginBlock {
+ // special behaviour when proposer is nil
+ var proposerAddress types.Address
+ if proposer != nil {
+ proposerAddress = proposer.Address
+ }
+
return &abcitypes.RequestBeginBlock{
LastCommitInfo: abcitypes.CommitInfo{
Round: 0,
@@ -76,7 +82,7 @@ func CreateBeginBlockRequest(curState state.State, curTime time.Time, proposer t
Time: curTime,
LastBlockId: curState.LastBlockID.ToProto(),
LastCommitHash: curState.LastResultsHash,
- ProposerAddress: proposer.Address,
+ ProposerAddress: proposerAddress,
},
}
}
diff --git a/cometmock/rpc_server/routes.go b/cometmock/rpc_server/routes.go
index 872dd35..111a5b2 100644
--- a/cometmock/rpc_server/routes.go
+++ b/cometmock/rpc_server/routes.go
@@ -37,7 +37,7 @@ var Routes = map[string]*rpc.RPCFunc{
// then return.
func BroadcastTxCommit(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
abci_client.GlobalClient.Logger.Info(
- "BroadcastTxCommut called", "tx", tx)
+ "BroadcastTxCommit called", "tx", tx)
return BroadcastTx(&tx)
}
From f6b9c85f0d8935fd0f7142895a216d3cf2b45027 Mon Sep 17 00:00:00 2001
From: Philip Offtermatt
Date: Thu, 11 May 2023 09:45:29 +0200
Subject: [PATCH 05/26] Remove unused func and add handler wrapper
---
cometmock/rpc_server/rpc_server.go | 18 ++----------------
1 file changed, 2 insertions(+), 16 deletions(-)
diff --git a/cometmock/rpc_server/rpc_server.go b/cometmock/rpc_server/rpc_server.go
index c161706..286aa3f 100644
--- a/cometmock/rpc_server/rpc_server.go
+++ b/cometmock/rpc_server/rpc_server.go
@@ -4,7 +4,6 @@ import (
"encoding/json"
"errors"
"fmt"
- "net"
"net/http"
"os"
"runtime/debug"
@@ -37,7 +36,7 @@ func StartRPCServer(listenAddr string, logger log.Logger, config *rpcserver.Conf
var rootHandler http.Handler = mux
if err := rpcserver.Serve(
listener,
- rootHandler,
+ RecoverAndLogHandler(rootHandler, rpcLogger),
rpcLogger,
config,
); err != nil {
@@ -50,20 +49,6 @@ func StartRPCServerWithDefaultConfig(listenAddr string, logger log.Logger) {
StartRPCServer(listenAddr, logger, rpcserver.DefaultConfig())
}
-func ServeRPC(listener net.Listener, handler http.Handler, logger log.Logger, config *rpcserver.Config) error {
- logger.Info("serve", "msg", log.NewLazySprintf("Starting RPC HTTP server on %s", listener.Addr()))
- s := &http.Server{
- Handler: RecoverAndLogHandler(maxBytesHandler{h: handler, n: config.MaxBodyBytes}, logger),
- ReadTimeout: config.ReadTimeout,
- ReadHeaderTimeout: config.ReadTimeout,
- WriteTimeout: config.WriteTimeout,
- MaxHeaderBytes: config.MaxHeaderBytes,
- }
- err := s.Serve(listener)
- logger.Info("RPC HTTP server stopped", "err", err)
- return err
-}
-
// RecoverAndLogHandler wraps an HTTP handler, adding error logging.
// If the inner function panics, the outer function recovers, logs, sends an
// HTTP 500 error response.
@@ -132,6 +117,7 @@ func RecoverAndLogHandler(handler http.Handler, logger log.Logger) http.Handler
"status", rww.Status,
"duration", durationMS,
"remoteAddr", r.RemoteAddr,
+ "requestURI", r.RequestURI,
)
}()
From b1453c7dd05a184087fc8244fa3981891a434ae1 Mon Sep 17 00:00:00 2001
From: Philip Offtermatt
Date: Thu, 11 May 2023 09:49:28 +0200
Subject: [PATCH 06/26] Remove wrapped handler
---
cometmock/rpc_server/rpc_server.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cometmock/rpc_server/rpc_server.go b/cometmock/rpc_server/rpc_server.go
index 286aa3f..fada2ed 100644
--- a/cometmock/rpc_server/rpc_server.go
+++ b/cometmock/rpc_server/rpc_server.go
@@ -36,7 +36,7 @@ func StartRPCServer(listenAddr string, logger log.Logger, config *rpcserver.Conf
var rootHandler http.Handler = mux
if err := rpcserver.Serve(
listener,
- RecoverAndLogHandler(rootHandler, rpcLogger),
+ rootHandler,
rpcLogger,
config,
); err != nil {
From ed158137dc19c3b04eca293f78cfd105464533e8 Mon Sep 17 00:00:00 2001
From: Philip Offtermatt
Date: Thu, 11 May 2023 10:12:55 +0200
Subject: [PATCH 07/26] Add logging for request body to write jsonrpc method
---
cometmock/rpc_server/rpc_server.go | 89 ++++++------------------------
1 file changed, 17 insertions(+), 72 deletions(-)
diff --git a/cometmock/rpc_server/rpc_server.go b/cometmock/rpc_server/rpc_server.go
index fada2ed..a8c5114 100644
--- a/cometmock/rpc_server/rpc_server.go
+++ b/cometmock/rpc_server/rpc_server.go
@@ -1,13 +1,12 @@
package rpc_server
import (
+ "bytes"
"encoding/json"
- "errors"
"fmt"
+ "io"
+ "io/ioutil"
"net/http"
- "os"
- "runtime/debug"
- "time"
"github.com/cometbft/cometbft/libs/log"
rpcserver "github.com/cometbft/cometbft/rpc/jsonrpc/server"
@@ -36,7 +35,7 @@ func StartRPCServer(listenAddr string, logger log.Logger, config *rpcserver.Conf
var rootHandler http.Handler = mux
if err := rpcserver.Serve(
listener,
- rootHandler,
+ ExtraLogHandler(rootHandler, rpcLogger),
rpcLogger,
config,
); err != nil {
@@ -52,76 +51,22 @@ func StartRPCServerWithDefaultConfig(listenAddr string, logger log.Logger) {
// RecoverAndLogHandler wraps an HTTP handler, adding error logging.
// If the inner function panics, the outer function recovers, logs, sends an
// HTTP 500 error response.
-func RecoverAndLogHandler(handler http.Handler, logger log.Logger) http.Handler {
+func ExtraLogHandler(handler http.Handler, logger log.Logger) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- // Wrap the ResponseWriter to remember the status
- rww := &responseWriterWrapper{-1, w}
- begin := time.Now()
-
- rww.Header().Set("X-Server-Time", fmt.Sprintf("%v", begin.Unix()))
-
- defer func() {
- // Handle any panics in the panic handler below. Does not use the logger, since we want
- // to avoid any further panics. However, we try to return a 500, since it otherwise
- // defaults to 200 and there is no other way to terminate the connection. If that
- // should panic for whatever reason then the Go HTTP server will handle it and
- // terminate the connection - panicing is the de-facto and only way to get the Go HTTP
- // server to terminate the request and close the connection/stream:
- // https://github.com/golang/go/issues/17790#issuecomment-258481416
- if e := recover(); e != nil {
- fmt.Fprintf(os.Stderr, "Panic during RPC panic recovery: %v\n%v\n", e, string(debug.Stack()))
- w.WriteHeader(500)
- }
- }()
-
- defer func() {
- // Send a 500 error if a panic happens during a handler.
- // Without this, Chrome & Firefox were retrying aborted ajax requests,
- // at least to my localhost.
- if e := recover(); e != nil {
- // If RPCResponse
- if res, ok := e.(types.RPCResponse); ok {
- if wErr := WriteRPCResponseHTTP(rww, res); wErr != nil {
- logger.Error("failed to write response", "res", res, "err", wErr)
- }
- } else {
- // Panics can contain anything, attempt to normalize it as an error.
- var err error
- switch e := e.(type) {
- case error:
- err = e
- case string:
- err = errors.New(e)
- case fmt.Stringer:
- err = errors.New(e.String())
- default:
- }
-
- logger.Error("panic in RPC HTTP handler", "err", e, "stack", string(debug.Stack()))
-
- res := types.RPCInternalError(types.JSONRPCIntID(-1), err)
- if wErr := WriteRPCResponseHTTPError(rww, http.StatusInternalServerError, res); wErr != nil {
- logger.Error("failed to write response", "res", res, "err", wErr)
- }
- }
+ if r.Body != nil {
+ body, err := io.ReadAll(r.Body)
+ if err != nil {
+ logger.Error("failed to read request body", "err", err)
+ } else {
+ logger.Debug("served RPC HTTP request",
+ "body", string(body),
+ )
}
- // Finally, log.
- durationMS := time.Since(begin).Nanoseconds() / 1000000
- if rww.Status == -1 {
- rww.Status = 200
- }
- logger.Debug("served RPC HTTP response",
- "method", r.Method,
- "url", r.URL,
- "status", rww.Status,
- "duration", durationMS,
- "remoteAddr", r.RemoteAddr,
- "requestURI", r.RequestURI,
- )
- }()
-
- handler.ServeHTTP(rww, r)
+ r.Body = ioutil.NopCloser(bytes.NewBuffer(body))
+ }
+
+ handler.ServeHTTP(w, r)
})
}
From fbd519e97925e9445b233d52694b0740ae2c4769 Mon Sep 17 00:00:00 2001
From: Philip Offtermatt
Date: Thu, 11 May 2023 11:00:47 +0200
Subject: [PATCH 08/26] Add status and health endpoints
---
cometmock/rpc_server/routes.go | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/cometmock/rpc_server/routes.go b/cometmock/rpc_server/routes.go
index 111a5b2..dcb0a5a 100644
--- a/cometmock/rpc_server/routes.go
+++ b/cometmock/rpc_server/routes.go
@@ -5,6 +5,7 @@ import (
"github.com/cometbft/cometbft/libs/bytes"
cmtmath "github.com/cometbft/cometbft/libs/math"
+ cometp2p "github.com/cometbft/cometbft/p2p"
ctypes "github.com/cometbft/cometbft/rpc/core/types"
rpc "github.com/cometbft/cometbft/rpc/jsonrpc/server"
rpctypes "github.com/cometbft/cometbft/rpc/jsonrpc/types"
@@ -19,6 +20,8 @@ const (
var Routes = map[string]*rpc.RPCFunc{
// info API
+ "health": rpc.NewRPCFunc(Health, ""),
+ "status": rpc.NewRPCFunc(Status, ""),
"validators": rpc.NewRPCFunc(Validators, "height,page,per_page"),
"block": rpc.NewRPCFunc(Block, "height", rpc.Cacheable("height")),
@@ -31,6 +34,28 @@ var Routes = map[string]*rpc.RPCFunc{
"abci_query": rpc.NewRPCFunc(ABCIQuery, "path,data,height,prove"),
}
+// Status returns CometBFT status including node info, pubkey, latest block
+// hash, app hash, block height and time.
+// More: https://docs.cometbft.com/v0.37/rpc/#/Info/status
+func Status(ctx *rpctypes.Context) (*ctypes.ResultStatus, error) {
+ nodeInfo := cometp2p.DefaultNodeInfo{}
+ syncInfo := ctypes.SyncInfo{}
+ validatorInfo := ctypes.ValidatorInfo{}
+ result := &ctypes.ResultStatus{
+ NodeInfo: nodeInfo,
+ SyncInfo: syncInfo,
+ ValidatorInfo: validatorInfo,
+ }
+
+ return result, nil
+}
+
+// Health gets node health. Returns empty result (200 OK) on success, no
+// response - in case of an error.
+func Health(ctx *rpctypes.Context) (*ctypes.ResultHealth, error) {
+ return &ctypes.ResultHealth{}, nil
+}
+
// BroadcastTxCommit broadcasts a transaction,
// and wait until it is included in a block and and comitted.
// In our case, this means running a block with just the the transition,
From f9b93db687ab76962095da3f5e4d1bef1ee58149 Mon Sep 17 00:00:00 2001
From: Philip Offtermatt
Date: Fri, 12 May 2023 13:13:00 +0200
Subject: [PATCH 09/26] Utilize MakeBlock to handle much more realistic blocks
---
cometmock/abci_client/client.go | 176 +++++++++++++++++++-----------
cometmock/main.go | 21 +++-
cometmock/rpc_server/routes.go | 66 +++++++++--
cometmock/rpc_server/websocket.go | 125 +++++++++++++++++++++
4 files changed, 318 insertions(+), 70 deletions(-)
create mode 100644 cometmock/rpc_server/websocket.go
diff --git a/cometmock/abci_client/client.go b/cometmock/abci_client/client.go
index 53befe0..a7c34e2 100644
--- a/cometmock/abci_client/client.go
+++ b/cometmock/abci_client/client.go
@@ -10,10 +10,8 @@ import (
abcitypes "github.com/cometbft/cometbft/abci/types"
cryptoenc "github.com/cometbft/cometbft/crypto/encoding"
"github.com/cometbft/cometbft/crypto/merkle"
- "github.com/cometbft/cometbft/libs/bytes"
cometlog "github.com/cometbft/cometbft/libs/log"
cmtstate "github.com/cometbft/cometbft/proto/tendermint/state"
- ttypes "github.com/cometbft/cometbft/proto/tendermint/types"
"github.com/cometbft/cometbft/state"
"github.com/cometbft/cometbft/types"
)
@@ -29,6 +27,7 @@ type AbciClient struct {
Clients []abciclient.Client
Logger cometlog.Logger
CurState state.State
+ EventBus types.EventBus
// if this is true, then an error will be returned if the responses from the clients are not all equal.
// can be used to check for nondeterminism in apps, but also slows down execution a bit,
@@ -36,10 +35,10 @@ type AbciClient struct {
ErrorOnUnequalResponses bool
}
-func (a *AbciClient) SendBeginBlock() (*abcitypes.ResponseBeginBlock, error) {
+func (a *AbciClient) SendBeginBlock(block *types.Block) (*abcitypes.ResponseBeginBlock, error) {
a.Logger.Info("Sending BeginBlock to clients")
// build the BeginBlock request
- beginBlockRequest := CreateBeginBlockRequest(a.CurState, time.Now(), a.CurState.LastValidators.Proposer)
+ beginBlockRequest := CreateBeginBlockRequest(&block.Header, block.LastCommit)
// send BeginBlock to all clients and collect the responses
responses := []*abcitypes.ResponseBeginBlock{}
@@ -63,27 +62,11 @@ func (a *AbciClient) SendBeginBlock() (*abcitypes.ResponseBeginBlock, error) {
return responses[0], nil
}
-func CreateBeginBlockRequest(curState state.State, curTime time.Time, proposer *types.Validator) *abcitypes.RequestBeginBlock {
- // special behaviour when proposer is nil
- var proposerAddress types.Address
- if proposer != nil {
- proposerAddress = proposer.Address
- }
-
+func CreateBeginBlockRequest(header *types.Header, lastCommit *types.Commit) *abcitypes.RequestBeginBlock {
return &abcitypes.RequestBeginBlock{
- LastCommitInfo: abcitypes.CommitInfo{
- Round: 0,
- Votes: []abcitypes.VoteInfo{},
- },
- Header: ttypes.Header{
- ChainID: curState.ChainID,
- Version: curState.Version.Consensus,
- Height: curState.LastBlockHeight + 1,
- Time: curTime,
- LastBlockId: curState.LastBlockID.ToProto(),
- LastCommitHash: curState.LastResultsHash,
- ProposerAddress: proposerAddress,
- },
+ // TODO: fill in Votes
+ LastCommitInfo: abcitypes.CommitInfo{Round: lastCommit.Round, Votes: []abcitypes.VoteInfo{}},
+ Header: *header.ToProto(),
}
}
@@ -263,18 +246,51 @@ func (a *AbciClient) SendAbciQuery(data []byte, path string, height int64, prove
return client.QuerySync(request)
}
+func GetBlockIdFromBlock(block *types.Block) (*types.BlockID, error) {
+ partSet, error := block.MakePartSet(2)
+ if error != nil {
+ return nil, error
+ }
+
+ partSetHeader := partSet.Header()
+ blockID := types.BlockID{
+ Hash: block.Hash(),
+ PartSetHeader: partSetHeader,
+ }
+ return &blockID, nil
+}
+
// RunBlock runs a block with a specified transaction through the ABCI application.
// It calls BeginBlock, DeliverTx, EndBlock, Commit and then
// updates the state.
// RunBlock is safe for use by multiple goroutines simultaneously.
-func (a *AbciClient) RunBlock(tx *[]byte) (*abcitypes.ResponseBeginBlock, *abcitypes.ResponseDeliverTx, *abcitypes.ResponseEndBlock, *abcitypes.ResponseCommit, error) {
+func (a *AbciClient) RunBlock(tx *[]byte, blockTime time.Time, proposer *types.Validator) (*abcitypes.ResponseBeginBlock, *abcitypes.ResponseDeliverTx, *abcitypes.ResponseEndBlock, *abcitypes.ResponseCommit, error) {
// lock mutex to avoid running two blocks at the same time
blockMutex.Lock()
a.Logger.Info("Running block")
a.Logger.Info("State at start of block", "state", a.CurState)
- resBeginBlock, err := a.SendBeginBlock()
+ txs := make([]types.Tx, 0)
+ if tx != nil {
+ txs = append(txs, *tx)
+ }
+
+ // TODO: handle special case where proposer is nil
+ var proposerAddress types.Address
+ if proposer != nil {
+ proposerAddress = proposer.Address
+ }
+
+ block := a.CurState.MakeBlock(a.CurState.LastBlockHeight+1, txs, &types.Commit{}, []types.Evidence{}, proposerAddress)
+ // override the block time, since we do not actually get votes from peers to median the time out of
+ block.Time = blockTime
+ blockId, err := GetBlockIdFromBlock(block)
+ if err != nil {
+ return nil, nil, nil, nil, err
+ }
+
+ resBeginBlock, err := a.SendBeginBlock(block)
if err != nil {
return nil, nil, nil, nil, err
}
@@ -300,7 +316,7 @@ func (a *AbciClient) RunBlock(tx *[]byte) (*abcitypes.ResponseBeginBlock, *abcit
}
// updates state as a side effect. returns an error if the state update fails
- err = a.UpdateStateFromBlock(resBeginBlock, resEndBlock, resCommit)
+ err = a.UpdateStateFromBlock(blockId, block, resBeginBlock, resEndBlock, resCommit)
if err != nil {
return nil, nil, nil, nil, err
}
@@ -317,33 +333,13 @@ func (a *AbciClient) RunBlock(tx *[]byte) (*abcitypes.ResponseBeginBlock, *abcit
// block results hash, validators, consensus
// params, and app hash.
func (a *AbciClient) UpdateStateFromBlock(
+ blockId *types.BlockID,
+ block *types.Block,
beginBlockResponse *abcitypes.ResponseBeginBlock,
endBlockResponse *abcitypes.ResponseEndBlock,
commitResponse *abcitypes.ResponseCommit,
) error {
// build components of the state update, then call the update function
-
- // TODO: if necessary, construct actual block id
- blockID := types.BlockID{}
-
- // TODO: if necessary, refine header to be more true to CometBFT behaviour
- header := types.Header{
- Version: a.CurState.Version.Consensus,
- ChainID: a.CurState.ChainID,
- Height: a.CurState.LastBlockHeight + 1,
- Time: time.Now(),
- LastBlockID: a.CurState.LastBlockID,
- LastCommitHash: a.CurState.LastResultsHash,
- DataHash: bytes.HexBytes{},
- ValidatorsHash: a.CurState.Validators.Hash(),
- NextValidatorsHash: a.CurState.NextValidators.Hash(),
- ConsensusHash: a.CurState.ConsensusParams.Hash(),
- AppHash: a.CurState.AppHash,
- LastResultsHash: commitResponse.Data,
- EvidenceHash: bytes.HexBytes{},
- ProposerAddress: a.CurState.Validators.Proposer.Address,
- }
-
abciResponses := cmtstate.ABCIResponses{
DeliverTxs: []*abcitypes.ResponseDeliverTx{},
EndBlock: endBlockResponse,
@@ -363,9 +359,10 @@ func (a *AbciClient) UpdateStateFromBlock(
newState, err := UpdateState(
a.CurState,
- blockID,
- &header,
+ blockId,
+ &block.Header,
&abciResponses,
+ commitResponse,
validatorUpdates,
)
if err != nil {
@@ -373,6 +370,10 @@ func (a *AbciClient) UpdateStateFromBlock(
}
a.CurState = newState
+
+ // Events are fired after everything else.
+ // NOTE: if we crash between Commit and Save, events wont be fired during replay
+ fireEvents(a.Logger, &a.EventBus, block, &abciResponses, validatorUpdates)
return nil
}
@@ -380,9 +381,10 @@ func (a *AbciClient) UpdateStateFromBlock(
// updateState returns a new State updated according to the header and responses.
func UpdateState(
curState state.State,
- blockID types.BlockID,
- header *types.Header,
+ blockId *types.BlockID,
+ blockHeader *types.Header,
abciResponses *cmtstate.ABCIResponses,
+ commitResponse *abcitypes.ResponseCommit,
validatorUpdates []*types.Validator,
) (state.State, error) {
// Copy the valset so we can apply changes from EndBlock
@@ -397,7 +399,7 @@ func UpdateState(
return curState, fmt.Errorf("error changing validator set: %v", err)
}
// Change results from this height but only applies to the next next height.
- lastHeightValsChanged = header.Height + 1 + 1
+ lastHeightValsChanged = blockHeader.Height + 1 + 1
}
// Update validator proposer priority and set state variables.
@@ -417,20 +419,18 @@ func UpdateState(
curState.Version.Consensus.App = nextParams.Version.App
// Change results from this height but only applies to the next height.
- lastHeightParamsChanged = header.Height + 1
+ lastHeightParamsChanged = blockHeader.Height + 1
}
nextVersion := curState.Version
- // NOTE: the AppHash has not been populated.
- // It will be filled on state.Save.
return state.State{
Version: nextVersion,
ChainID: curState.ChainID,
InitialHeight: curState.InitialHeight,
- LastBlockHeight: header.Height,
- LastBlockID: blockID,
- LastBlockTime: header.Time,
+ LastBlockHeight: blockHeader.Height,
+ LastBlockID: *blockId,
+ LastBlockTime: blockHeader.Time,
NextValidators: nValSet,
Validators: curState.NextValidators.Copy(),
LastValidators: curState.Validators.Copy(),
@@ -438,7 +438,7 @@ func UpdateState(
ConsensusParams: nextParams,
LastHeightConsensusParamsChanged: lastHeightParamsChanged,
LastResultsHash: state.ABCIResponsesResultsHash(abciResponses),
- AppHash: nil,
+ AppHash: commitResponse.Data,
}, nil
}
@@ -469,3 +469,57 @@ func validateValidatorUpdates(
}
return nil
}
+
+func fireEvents(
+ logger cometlog.Logger,
+ eventBus types.BlockEventPublisher,
+ block *types.Block,
+ abciResponses *cmtstate.ABCIResponses,
+ validatorUpdates []*types.Validator,
+) {
+ if err := eventBus.PublishEventNewBlock(types.EventDataNewBlock{
+ Block: block,
+ ResultBeginBlock: *abciResponses.BeginBlock,
+ ResultEndBlock: *abciResponses.EndBlock,
+ }); err != nil {
+ logger.Error("failed publishing new block", "err", err)
+ }
+
+ if err := eventBus.PublishEventNewBlockHeader(types.EventDataNewBlockHeader{
+ Header: block.Header,
+ NumTxs: int64(len(block.Txs)),
+ ResultBeginBlock: *abciResponses.BeginBlock,
+ ResultEndBlock: *abciResponses.EndBlock,
+ }); err != nil {
+ logger.Error("failed publishing new block header", "err", err)
+ }
+
+ if len(block.Evidence.Evidence) != 0 {
+ for _, ev := range block.Evidence.Evidence {
+ if err := eventBus.PublishEventNewEvidence(types.EventDataNewEvidence{
+ Evidence: ev,
+ Height: block.Height,
+ }); err != nil {
+ logger.Error("failed publishing new evidence", "err", err)
+ }
+ }
+ }
+
+ for i, tx := range block.Data.Txs {
+ if err := eventBus.PublishEventTx(types.EventDataTx{TxResult: abcitypes.TxResult{
+ Height: block.Height,
+ Index: uint32(i),
+ Tx: tx,
+ Result: *(abciResponses.DeliverTxs[i]),
+ }}); err != nil {
+ logger.Error("failed publishing event TX", "err", err)
+ }
+ }
+
+ if len(validatorUpdates) > 0 {
+ if err := eventBus.PublishEventValidatorSetUpdates(
+ types.EventDataValidatorSetUpdates{ValidatorUpdates: validatorUpdates}); err != nil {
+ logger.Error("failed publishing event", "err", err)
+ }
+ }
+}
diff --git a/cometmock/main.go b/cometmock/main.go
index 7022196..d8d0142 100644
--- a/cometmock/main.go
+++ b/cometmock/main.go
@@ -8,10 +8,20 @@ import (
comet_abciclient "github.com/cometbft/cometbft/abci/client"
cometlog "github.com/cometbft/cometbft/libs/log"
"github.com/cometbft/cometbft/state"
+ "github.com/cometbft/cometbft/types"
"github.com/p-offtermatt/CometMock/cometmock/abci_client"
"github.com/p-offtermatt/CometMock/cometmock/rpc_server"
)
+func CreateAndStartEventBus(logger cometlog.Logger) (*types.EventBus, error) {
+ eventBus := types.NewEventBus()
+ eventBus.SetLogger(logger.With("module", "events"))
+ if err := eventBus.Start(); err != nil {
+ return nil, err
+ }
+ return eventBus, nil
+}
+
func main() {
logger := cometlog.NewTMLogger(cometlog.NewSyncWriter(os.Stdout))
@@ -45,11 +55,18 @@ func main() {
clients = append(clients, client)
}
+ eventBus, err := CreateAndStartEventBus(logger)
+ if err != nil {
+ logger.Error(err.Error())
+ panic(err)
+ }
+
abci_client.GlobalClient = &abci_client.AbciClient{
Clients: clients,
Logger: logger,
CurState: curState,
ErrorOnUnequalResponses: true,
+ EventBus: *eventBus,
}
// initialize chain
@@ -60,13 +77,13 @@ func main() {
}
// run an empty block
- abci_client.GlobalClient.RunBlock(nil)
+ abci_client.GlobalClient.RunBlock(nil, time.Now(), abci_client.GlobalClient.CurState.LastValidators.Proposer)
go rpc_server.StartRPCServerWithDefaultConfig(cometMockListenAddress, logger)
// produce a block every second
for {
- abci_client.GlobalClient.RunBlock(nil)
+ abci_client.GlobalClient.RunBlock(nil, time.Now(), abci_client.GlobalClient.CurState.LastValidators.Proposer)
time.Sleep(1 * time.Second)
}
}
diff --git a/cometmock/rpc_server/routes.go b/cometmock/rpc_server/routes.go
index dcb0a5a..adcbeaf 100644
--- a/cometmock/rpc_server/routes.go
+++ b/cometmock/rpc_server/routes.go
@@ -2,6 +2,7 @@ package rpc_server
import (
"fmt"
+ "time"
"github.com/cometbft/cometbft/libs/bytes"
cmtmath "github.com/cometbft/cometbft/libs/math"
@@ -19,11 +20,18 @@ const (
)
var Routes = map[string]*rpc.RPCFunc{
+ // websocket
+ "subscribe": rpc.NewWSRPCFunc(Subscribe, "query"),
+ "unsubscribe": rpc.NewWSRPCFunc(Unsubscribe, "query"),
+ "unsubscribe_all": rpc.NewWSRPCFunc(UnsubscribeAll, ""),
+
// info API
- "health": rpc.NewRPCFunc(Health, ""),
- "status": rpc.NewRPCFunc(Status, ""),
- "validators": rpc.NewRPCFunc(Validators, "height,page,per_page"),
- "block": rpc.NewRPCFunc(Block, "height", rpc.Cacheable("height")),
+ "health": rpc.NewRPCFunc(Health, ""),
+ "status": rpc.NewRPCFunc(Status, ""),
+ "validators": rpc.NewRPCFunc(Validators, "height,page,per_page"),
+ "block": rpc.NewRPCFunc(Block, "height", rpc.Cacheable("height")),
+ "consensus_params": rpc.NewRPCFunc(ConsensusParams, "height", rpc.Cacheable("height")),
+ "header": rpc.NewRPCFunc(Header, "height", rpc.Cacheable("height")),
// // tx broadcast API
"broadcast_tx_commit": rpc.NewRPCFunc(BroadcastTxCommit, "tx"),
@@ -34,13 +42,57 @@ var Routes = map[string]*rpc.RPCFunc{
"abci_query": rpc.NewRPCFunc(ABCIQuery, "path,data,height,prove"),
}
+// Header gets block header at a given height.
+// If no height is provided, it will fetch the latest header.
+// More: https://docs.cometbft.com/v0.37/rpc/#/Info/header
+// TODO: currently unfilled
+func Header(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultHeader, error) {
+ // only the last height is available, since we do not keep past heights at the moment
+ if heightPtr != nil {
+ return nil, fmt.Errorf("height parameter is not supported, use version of the function without height")
+ }
+
+ return &ctypes.ResultHeader{}, nil
+}
+
+// ConsensusParams gets the consensus parameters at the given block height.
+// If no height is provided, it will fetch the latest consensus params.
+// More: https://docs.cometbft.com/v0.37/rpc/#/Info/consensus_params
+func ConsensusParams(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultConsensusParams, error) {
+ // only the last height is available, since we do not keep past heights at the moment
+ if heightPtr != nil {
+ return nil, fmt.Errorf("height parameter is not supported, use version of the function without height")
+ }
+
+ height := abci_client.GlobalClient.CurState.LastBlockHeight
+ consensusParams := abci_client.GlobalClient.CurState.ConsensusParams
+
+ return &ctypes.ResultConsensusParams{
+ BlockHeight: height,
+ ConsensusParams: consensusParams,
+ }, nil
+}
+
// Status returns CometBFT status including node info, pubkey, latest block
// hash, app hash, block height and time.
// More: https://docs.cometbft.com/v0.37/rpc/#/Info/status
func Status(ctx *rpctypes.Context) (*ctypes.ResultStatus, error) {
- nodeInfo := cometp2p.DefaultNodeInfo{}
+ // return status as if we are the first validator
+ validator := abci_client.GlobalClient.CurState.Validators.Validators[0]
+
+ nodeInfo := cometp2p.DefaultNodeInfo{
+ DefaultNodeID: cometp2p.PubKeyToID(validator.PubKey),
+ Network: abci_client.GlobalClient.CurState.ChainID,
+ Other: cometp2p.DefaultNodeInfoOther{
+ TxIndex: "on",
+ },
+ }
syncInfo := ctypes.SyncInfo{}
- validatorInfo := ctypes.ValidatorInfo{}
+ validatorInfo := ctypes.ValidatorInfo{
+ Address: validator.Address,
+ PubKey: validator.PubKey,
+ VotingPower: validator.VotingPower,
+ }
result := &ctypes.ResultStatus{
NodeInfo: nodeInfo,
SyncInfo: syncInfo,
@@ -104,7 +156,7 @@ func BroadcastTx(tx *types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
byteTx := []byte(*tx)
- _, _, _, _, err := abci_client.GlobalClient.RunBlock(&byteTx)
+ _, _, _, _, err := abci_client.GlobalClient.RunBlock(&byteTx, time.Now(), abci_client.GlobalClient.CurState.LastValidators.Proposer)
if err != nil {
return nil, err
}
diff --git a/cometmock/rpc_server/websocket.go b/cometmock/rpc_server/websocket.go
new file mode 100644
index 0000000..6a7a0d0
--- /dev/null
+++ b/cometmock/rpc_server/websocket.go
@@ -0,0 +1,125 @@
+package rpc_server
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "time"
+
+ cmtpubsub "github.com/cometbft/cometbft/libs/pubsub"
+ cmtquery "github.com/cometbft/cometbft/libs/pubsub/query"
+ ctypes "github.com/cometbft/cometbft/rpc/core/types"
+ rpctypes "github.com/cometbft/cometbft/rpc/jsonrpc/types"
+ "github.com/p-offtermatt/CometMock/cometmock/abci_client"
+)
+
+const (
+ // maxQueryLength is the maximum length of a query string that will be
+ // accepted. This is just a safety check to avoid outlandish queries.
+ maxQueryLength = 512
+ SubscribeTimeout = 10 * time.Second
+ SubscriptionBufferSize = 100
+)
+
+// Subscribe for events via WebSocket.
+// More: https://docs.cometbft.com/v0.37/rpc/#/Websocket/subscribe
+func Subscribe(ctx *rpctypes.Context, query string) (*ctypes.ResultSubscribe, error) {
+ addr := ctx.RemoteAddr()
+
+ abci_client.GlobalClient.Logger.Info("Subscribe to query", "remote", addr, "query", query)
+
+ q, err := cmtquery.New(query)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse query: %w", err)
+ }
+
+ subCtx, cancel := context.WithTimeout(ctx.Context(), SubscribeTimeout)
+ defer cancel()
+
+ sub, err := abci_client.GlobalClient.EventBus.Subscribe(subCtx, addr, q, SubscriptionBufferSize)
+ if err != nil {
+ return nil, err
+ }
+
+ closeIfSlow := false
+
+ // Capture the current ID, since it can change in the future.
+ subscriptionID := ctx.JSONReq.ID
+ go func() {
+ for {
+ select {
+ case msg := <-sub.Out():
+ var (
+ resultEvent = &ctypes.ResultEvent{Query: query, Data: msg.Data(), Events: msg.Events()}
+ resp = rpctypes.NewRPCSuccessResponse(subscriptionID, resultEvent)
+ )
+ writeCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ defer cancel()
+ if err := ctx.WSConn.WriteRPCResponse(writeCtx, resp); err != nil {
+ abci_client.GlobalClient.Logger.Info("Can't write response (slow client)",
+ "to", addr, "subscriptionID", subscriptionID, "err", err)
+
+ if closeIfSlow {
+ var (
+ err = errors.New("subscription was canceled (reason: slow client)")
+ resp = rpctypes.RPCServerError(subscriptionID, err)
+ )
+ if !ctx.WSConn.TryWriteRPCResponse(resp) {
+ abci_client.GlobalClient.Logger.Info("Can't write response (slow client)",
+ "to", addr, "subscriptionID", subscriptionID, "err", err)
+ }
+ return
+ }
+ }
+ case <-sub.Cancelled():
+ if sub.Err() != cmtpubsub.ErrUnsubscribed {
+ var reason string
+ if sub.Err() == nil {
+ reason = "CometBFT exited"
+ } else {
+ reason = sub.Err().Error()
+ }
+ var (
+ err = fmt.Errorf("subscription was canceled (reason: %s)", reason)
+ resp = rpctypes.RPCServerError(subscriptionID, err)
+ )
+ if !ctx.WSConn.TryWriteRPCResponse(resp) {
+ abci_client.GlobalClient.Logger.Info("Can't write response (slow client)",
+ "to", addr, "subscriptionID", subscriptionID, "err", err)
+ }
+ }
+ return
+ }
+ }
+ }()
+
+ return &ctypes.ResultSubscribe{}, nil
+}
+
+// Unsubscribe from events via WebSocket.
+// More: https://docs.cometbft.com/v0.37/rpc/#/Websocket/unsubscribe
+func Unsubscribe(ctx *rpctypes.Context, query string) (*ctypes.ResultUnsubscribe, error) {
+ addr := ctx.RemoteAddr()
+ abci_client.GlobalClient.Logger.Info("Unsubscribe from query", "remote", addr, "query", query)
+ q, err := cmtquery.New(query)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse query: %w", err)
+ }
+ err = abci_client.GlobalClient.EventBus.Unsubscribe(context.Background(), addr, q)
+ if err != nil {
+ return nil, err
+ }
+ return &ctypes.ResultUnsubscribe{}, nil
+}
+
+// UnsubscribeAll from all events via WebSocket.
+// More: https://docs.cometbft.com/v0.37/rpc/#/Websocket/unsubscribe_all
+func UnsubscribeAll(ctx *rpctypes.Context) (*ctypes.ResultUnsubscribe, error) {
+ addr := ctx.RemoteAddr()
+ abci_client.GlobalClient.Logger.Info("Unsubscribe from all", "remote", addr)
+ err := abci_client.GlobalClient.EventBus.UnsubscribeAll(context.Background(), addr)
+ if err != nil {
+ return nil, err
+ }
+ return &ctypes.ResultUnsubscribe{}, nil
+}
From 39858c0ab81e88b0b9d37170d6ae07f277e2714b Mon Sep 17 00:00:00 2001
From: Philip Offtermatt
Date: Fri, 12 May 2023 13:47:06 +0200
Subject: [PATCH 10/26] Add example run command to README
---
README.md | 5 +-
genesis.json | 270 +++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 274 insertions(+), 1 deletion(-)
create mode 100644 genesis.json
diff --git a/README.md b/README.md
index f597bb3..d214a57 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,4 @@
-# CometMock
\ No newline at end of file
+# CometMock
+
+To run CometMock:
+`go run ./cometmock localhost:36658,localhost:26658 genesis.json tcp://localhost:26657`
\ No newline at end of file
diff --git a/genesis.json b/genesis.json
new file mode 100644
index 0000000..d212185
--- /dev/null
+++ b/genesis.json
@@ -0,0 +1,270 @@
+{
+ "genesis_time": "2022-09-27T14:43:24.802423659Z",
+ "chain_id": "tendermock",
+ "initial_height": "1",
+ "consensus_params": {
+ "block": {
+ "max_bytes": "22020096",
+ "max_gas": "-1",
+ "time_iota_ms": "1000"
+ },
+ "evidence": {
+ "max_age_num_blocks": "100000",
+ "max_age_duration": "172800",
+ "max_bytes": "1048576"
+ },
+ "validator": {
+ "pub_key_types": [
+ "ed25519"
+ ]
+ },
+ "version": {}
+ },
+ "app_hash": "",
+ "app_state": {
+ "auth": {
+ "params": {
+ "max_memo_characters": "256",
+ "tx_sig_limit": "7",
+ "tx_size_cost_per_byte": "10",
+ "sig_verify_cost_ed25519": "590",
+ "sig_verify_cost_secp256k1": "1000"
+ },
+ "accounts": [
+ {
+ "@type": "/cosmos.auth.v1beta1.BaseAccount",
+ "address": "cosmos134r9s82qv8fprz3y7fw5lv40yuvsh285vxev02",
+ "pub_key": null,
+ "account_number": "0",
+ "sequence": "0"
+ },
+ {
+ "@type": "/cosmos.auth.v1beta1.BaseAccount",
+ "address": "cosmos153rpdnp3jcq4kpac8njlyf4gmf724hm6repu72",
+ "pub_key": null,
+ "account_number": "0",
+ "sequence": "0"
+ },
+ {
+ "@type": "/cosmos.auth.v1beta1.BaseAccount",
+ "address": "cosmos1x63y2p7wzsyf9ln0at56vdpe3x66jaf9qzh86t",
+ "pub_key": null,
+ "account_number": "0",
+ "sequence": "0"
+ }
+ ]
+ },
+ "authz": {
+ "authorization": []
+ },
+ "bank": {
+ "params": {
+ "send_enabled": [],
+ "default_send_enabled": true
+ },
+ "balances": [
+ {
+ "address": "cosmos1x63y2p7wzsyf9ln0at56vdpe3x66jaf9qzh86t",
+ "coins": [
+ {
+ "denom": "stake",
+ "amount": "5000000000"
+ }
+ ]
+ },
+ {
+ "address": "cosmos134r9s82qv8fprz3y7fw5lv40yuvsh285vxev02",
+ "coins": [
+ {
+ "denom": "stake",
+ "amount": "5000000000"
+ }
+ ]
+ },
+ {
+ "address": "cosmos153rpdnp3jcq4kpac8njlyf4gmf724hm6repu72",
+ "coins": [
+ {
+ "denom": "stake",
+ "amount": "5000000000"
+ }
+ ]
+ }
+ ],
+ "supply": [
+ {
+ "denom": "stake",
+ "amount": "15000000000"
+ }
+ ],
+ "denom_metadata": []
+ },
+ "capability": {
+ "index": "1",
+ "owners": []
+ },
+ "crisis": {
+ "constant_fee": {
+ "denom": "stake",
+ "amount": "1000"
+ }
+ },
+ "distribution": {
+ "params": {
+ "community_tax": "0.020000000000000000",
+ "base_proposer_reward": "0.010000000000000000",
+ "bonus_proposer_reward": "0.040000000000000000",
+ "withdraw_addr_enabled": true
+ },
+ "fee_pool": {
+ "community_pool": []
+ },
+ "delegator_withdraw_infos": [],
+ "previous_proposer": "",
+ "outstanding_rewards": [],
+ "validator_accumulated_commissions": [],
+ "validator_historical_rewards": [],
+ "validator_current_rewards": [],
+ "delegator_starting_infos": [],
+ "validator_slash_events": []
+ },
+ "evidence": {
+ "evidence": []
+ },
+ "feegrant": {
+ "allowances": []
+ },
+ "genutil": {
+ "gen_txs": [
+ {
+ "body": {
+ "messages": [
+ {
+ "@type": "/cosmos.staking.v1beta1.MsgCreateValidator",
+ "description": {
+ "moniker": "node",
+ "identity": "",
+ "website": "",
+ "security_contact": "",
+ "details": ""
+ },
+ "commission": {
+ "rate": "0.100000000000000000",
+ "max_rate": "0.200000000000000000",
+ "max_change_rate": "0.010000000000000000"
+ },
+ "min_self_delegation": "1",
+ "delegator_address": "cosmos1x63y2p7wzsyf9ln0at56vdpe3x66jaf9qzh86t",
+ "validator_address": "cosmosvaloper1x63y2p7wzsyf9ln0at56vdpe3x66jaf99krjkc",
+ "pubkey": {
+ "@type": "/cosmos.crypto.ed25519.PubKey",
+ "key": "pZR7fq8nmVbyJaUhV9jvlzHOG01vJQjCHi8Pb5k8m/8="
+ },
+ "value": {
+ "denom": "stake",
+ "amount": "5000000000"
+ }
+ }
+ ],
+ "memo": "919555b1567d03bc0ff2a6a885cc9a3e8098db49@172.17.0.2:26656",
+ "timeout_height": "0",
+ "extension_options": [],
+ "non_critical_extension_options": []
+ },
+ "auth_info": {
+ "signer_infos": [
+ {
+ "public_key": {
+ "@type": "/cosmos.crypto.secp256k1.PubKey",
+ "key": "AsBiSVZ2Ht/+o6qgBrjlRbkfRw3sJDkb7ew1GedJCbsM"
+ },
+ "mode_info": {
+ "single": {
+ "mode": "SIGN_MODE_DIRECT"
+ }
+ },
+ "sequence": "0"
+ }
+ ],
+ "fee": {
+ "amount": [],
+ "gas_limit": "200000",
+ "payer": "",
+ "granter": ""
+ }
+ },
+ "signatures": [
+ "LT99RZPm6rpPsJ0ERVQkCnkI85pk57QYUkoPoTAYnX8QghrUFRIAIqFHx/SgxiRpq6XBl3hZTusNRgyqxweyNA=="
+ ]
+ }
+ ]
+ },
+ "gov": {
+ "starting_proposal_id": "1",
+ "deposits": [],
+ "votes": [],
+ "proposals": [],
+ "deposit_params": {
+ "min_deposit": [
+ {
+ "denom": "stake",
+ "amount": "10000000"
+ }
+ ],
+ "max_deposit_period": "172800s"
+ },
+ "voting_params": {
+ "voting_period": "172800s"
+ },
+ "tally_params": {
+ "quorum": "0.334000000000000000",
+ "threshold": "0.500000000000000000",
+ "veto_threshold": "0.334000000000000000"
+ }
+ },
+ "mint": {
+ "minter": {
+ "inflation": "0.130000000000000000",
+ "annual_provisions": "0.000000000000000000"
+ },
+ "params": {
+ "mint_denom": "stake",
+ "inflation_rate_change": "0.130000000000000000",
+ "inflation_max": "0.200000000000000000",
+ "inflation_min": "0.070000000000000000",
+ "goal_bonded": "0.670000000000000000",
+ "blocks_per_year": "6311520"
+ }
+ },
+ "params": null,
+ "slashing": {
+ "params": {
+ "signed_blocks_window": "100",
+ "min_signed_per_window": "0.500000000000000000",
+ "downtime_jail_duration": "600s",
+ "slash_fraction_double_sign": "0.050000000000000000",
+ "slash_fraction_downtime": "0.010000000000000000"
+ },
+ "signing_infos": [],
+ "missed_blocks": []
+ },
+ "staking": {
+ "params": {
+ "unbonding_time": "1814400s",
+ "max_validators": 100,
+ "max_entries": 7,
+ "historical_entries": 10000,
+ "bond_denom": "stake"
+ },
+ "last_total_power": "0",
+ "last_validator_powers": [],
+ "validators": [],
+ "delegations": [],
+ "unbonding_delegations": [],
+ "redelegations": [],
+ "exported": false
+ },
+ "upgrade": {},
+ "vesting": {}
+ }
+}
\ No newline at end of file
From ccba3a2b57033c3903abfb9618daf3d803824c83 Mon Sep 17 00:00:00 2001
From: Philip Offtermatt
Date: Mon, 15 May 2023 17:26:02 +0200
Subject: [PATCH 11/26] Keep info about last block and use in responses
---
cometmock/abci_client/client.go | 45 ++++++++++++++++++++++-----------
cometmock/main.go | 1 +
cometmock/rpc_server/routes.go | 13 ++++++----
3 files changed, 39 insertions(+), 20 deletions(-)
diff --git a/cometmock/abci_client/client.go b/cometmock/abci_client/client.go
index a7c34e2..6e5bc48 100644
--- a/cometmock/abci_client/client.go
+++ b/cometmock/abci_client/client.go
@@ -24,10 +24,12 @@ var blockMutex = sync.Mutex{}
// AbciClient facilitates calls to the ABCI interface of multiple nodes.
// It also tracks the current state and a common logger.
type AbciClient struct {
- Clients []abciclient.Client
- Logger cometlog.Logger
- CurState state.State
- EventBus types.EventBus
+ Clients []abciclient.Client
+ Logger cometlog.Logger
+ CurState state.State
+ EventBus types.EventBus
+ LastBlock *types.Block
+ LastCommit *types.Commit
// if this is true, then an error will be returned if the responses from the clients are not all equal.
// can be used to check for nondeterminism in apps, but also slows down execution a bit,
@@ -282,7 +284,7 @@ func (a *AbciClient) RunBlock(tx *[]byte, blockTime time.Time, proposer *types.V
proposerAddress = proposer.Address
}
- block := a.CurState.MakeBlock(a.CurState.LastBlockHeight+1, txs, &types.Commit{}, []types.Evidence{}, proposerAddress)
+ block := a.CurState.MakeBlock(a.CurState.LastBlockHeight+1, txs, a.LastCommit, []types.Evidence{}, proposerAddress)
// override the block time, since we do not actually get votes from peers to median the time out of
block.Time = blockTime
blockId, err := GetBlockIdFromBlock(block)
@@ -315,12 +317,32 @@ func (a *AbciClient) RunBlock(tx *[]byte, blockTime time.Time, proposer *types.V
return nil, nil, nil, nil, err
}
+ deliverTxResponses := []*abcitypes.ResponseDeliverTx{}
+ if tx != nil {
+ deliverTxResponses = append(deliverTxResponses, resDeliverTx)
+ }
+
+ // build components of the state update, then call the update function
+ abciResponses := cmtstate.ABCIResponses{
+ DeliverTxs: deliverTxResponses,
+ EndBlock: resEndBlock,
+ BeginBlock: resBeginBlock,
+ }
+
// updates state as a side effect. returns an error if the state update fails
- err = a.UpdateStateFromBlock(blockId, block, resBeginBlock, resEndBlock, resCommit)
+ err = a.UpdateStateFromBlock(blockId, block, abciResponses, resCommit)
if err != nil {
return nil, nil, nil, nil, err
}
+ a.LastBlock = block
+ a.LastCommit = types.NewCommit(
+ block.Height,
+ 1,
+ *blockId,
+ []types.CommitSig{},
+ )
+
// unlock mutex
blockMutex.Unlock()
@@ -335,18 +357,11 @@ func (a *AbciClient) RunBlock(tx *[]byte, blockTime time.Time, proposer *types.V
func (a *AbciClient) UpdateStateFromBlock(
blockId *types.BlockID,
block *types.Block,
- beginBlockResponse *abcitypes.ResponseBeginBlock,
- endBlockResponse *abcitypes.ResponseEndBlock,
+ abciResponses cmtstate.ABCIResponses,
commitResponse *abcitypes.ResponseCommit,
) error {
// build components of the state update, then call the update function
- abciResponses := cmtstate.ABCIResponses{
- DeliverTxs: []*abcitypes.ResponseDeliverTx{},
- EndBlock: endBlockResponse,
- BeginBlock: beginBlockResponse,
- }
-
- abciValidatorUpdates := endBlockResponse.ValidatorUpdates
+ abciValidatorUpdates := abciResponses.EndBlock.ValidatorUpdates
err := validateValidatorUpdates(abciValidatorUpdates, a.CurState.ConsensusParams.Validator)
if err != nil {
return fmt.Errorf("error in validator updates: %v", err)
diff --git a/cometmock/main.go b/cometmock/main.go
index d8d0142..095802b 100644
--- a/cometmock/main.go
+++ b/cometmock/main.go
@@ -67,6 +67,7 @@ func main() {
CurState: curState,
ErrorOnUnequalResponses: true,
EventBus: *eventBus,
+ LastCommit: &types.Commit{},
}
// initialize chain
diff --git a/cometmock/rpc_server/routes.go b/cometmock/rpc_server/routes.go
index adcbeaf..2c9a4ae 100644
--- a/cometmock/rpc_server/routes.go
+++ b/cometmock/rpc_server/routes.go
@@ -87,7 +87,13 @@ func Status(ctx *rpctypes.Context) (*ctypes.ResultStatus, error) {
TxIndex: "on",
},
}
- syncInfo := ctypes.SyncInfo{}
+ syncInfo := ctypes.SyncInfo{
+ LatestBlockHash: abci_client.GlobalClient.LastBlock.Hash(),
+ LatestAppHash: abci_client.GlobalClient.LastBlock.AppHash,
+ LatestBlockHeight: abci_client.GlobalClient.LastBlock.Height,
+ LatestBlockTime: abci_client.GlobalClient.CurState.LastBlockTime,
+ CatchingUp: false,
+ }
validatorInfo := ctypes.ValidatorInfo{
Address: validator.Address,
PubKey: validator.PubKey,
@@ -263,8 +269,5 @@ func Block(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlock, error)
blockID := abci_client.GlobalClient.CurState.LastBlockID
- // TODO: return an actual block if it is needed, for now return en empty block
- block := &types.Block{Header: types.Header{Height: abci_client.GlobalClient.CurState.LastBlockHeight}}
-
- return &ctypes.ResultBlock{BlockID: blockID, Block: block}, nil
+ return &ctypes.ResultBlock{BlockID: blockID, Block: abci_client.GlobalClient.LastBlock}, nil
}
From 2705bf84cc926d9eb3d40cad16f997be84614dee Mon Sep 17 00:00:00 2001
From: Philip Offtermatt
Date: Mon, 15 May 2023 17:43:06 +0200
Subject: [PATCH 12/26] Implement Commit endpoint
---
cometmock/rpc_server/routes.go | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/cometmock/rpc_server/routes.go b/cometmock/rpc_server/routes.go
index 2c9a4ae..4f72b84 100644
--- a/cometmock/rpc_server/routes.go
+++ b/cometmock/rpc_server/routes.go
@@ -32,6 +32,7 @@ var Routes = map[string]*rpc.RPCFunc{
"block": rpc.NewRPCFunc(Block, "height", rpc.Cacheable("height")),
"consensus_params": rpc.NewRPCFunc(ConsensusParams, "height", rpc.Cacheable("height")),
"header": rpc.NewRPCFunc(Header, "height", rpc.Cacheable("height")),
+ "commit": rpc.NewRPCFunc(Commit, "height", rpc.Cacheable("height")),
// // tx broadcast API
"broadcast_tx_commit": rpc.NewRPCFunc(BroadcastTxCommit, "tx"),
@@ -55,6 +56,23 @@ func Header(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultHeader, erro
return &ctypes.ResultHeader{}, nil
}
+// Commit gets block commit at a given height.
+// If no height is provided, it will fetch the commit for the latest block.
+// More: https://docs.cometbft.com/main/rpc/#/Info/commit
+func Commit(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultCommit, error) {
+ // only the last height is available, since we do not keep past heights at the moment
+ if heightPtr != nil {
+ return nil, fmt.Errorf("height parameter is not supported, use version of the function without height")
+ }
+
+ // If the next block has not been committed yet,
+ // use a non-canonical commit
+ lastCommit := abci_client.GlobalClient.LastCommit
+ lastHeader := abci_client.GlobalClient.LastBlock.Header
+
+ return ctypes.NewResultCommit(&lastHeader, lastCommit, true), nil
+}
+
// ConsensusParams gets the consensus parameters at the given block height.
// If no height is provided, it will fetch the latest consensus params.
// More: https://docs.cometbft.com/v0.37/rpc/#/Info/consensus_params
From 40bf1bf44865808c95677e20208120fc30f488d1 Mon Sep 17 00:00:00 2001
From: Philip Offtermatt
Date: Fri, 19 May 2023 14:03:37 +0200
Subject: [PATCH 13/26] Add docker info to README
---
README.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/README.md b/README.md
index d214a57..7984ecb 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,6 @@
# CometMock
To run CometMock:
+`docker stop simapp; docker rm simapp; docker run --add-host=host.docker.internal:host-gateway --name simapp -p 26658:26658 -ti informalofftermatt/testnet:tendermock simd start --transport=grpc --with-tendermint=false --grpc-only --rpc.laddr=tcp://host.docker.internal:99999`
+`docker stop simapp2; docker rm simapp2; docker run --add-host=host.docker.internal:host-gateway --name simapp2 -p 36658:26658 -ti informalofftermatt/testnet:tendermock simd start --transport=grpc --with-tendermint=false --grpc-only --rpc.laddr=tcp://host.docker.internal:99999`
`go run ./cometmock localhost:36658,localhost:26658 genesis.json tcp://localhost:26657`
\ No newline at end of file
From 575f9234fad9e716dcaba928e1eab05010644ebb Mon Sep 17 00:00:00 2001
From: Philip Offtermatt
Date: Fri, 19 May 2023 14:37:04 +0200
Subject: [PATCH 14/26] Store and output infos for previous heights
---
cometmock/abci_client/client.go | 41 ++++--
cometmock/main.go | 2 +
cometmock/rpc_server/routes.go | 111 +++++++++++---
cometmock/storage/storage.go | 124 ++++++++++++++++
cometmock/test_utils/helpers.go | 248 ++++++++++++++++++++++++++++++++
cometmock/utils/blocks.go | 17 +++
6 files changed, 505 insertions(+), 38 deletions(-)
create mode 100644 cometmock/storage/storage.go
create mode 100644 cometmock/test_utils/helpers.go
create mode 100644 cometmock/utils/blocks.go
diff --git a/cometmock/abci_client/client.go b/cometmock/abci_client/client.go
index 6e5bc48..06161ff 100644
--- a/cometmock/abci_client/client.go
+++ b/cometmock/abci_client/client.go
@@ -14,6 +14,8 @@ import (
cmtstate "github.com/cometbft/cometbft/proto/tendermint/state"
"github.com/cometbft/cometbft/state"
"github.com/cometbft/cometbft/types"
+ "github.com/p-offtermatt/CometMock/cometmock/storage"
+ "github.com/p-offtermatt/CometMock/cometmock/utils"
)
var GlobalClient *AbciClient
@@ -30,6 +32,7 @@ type AbciClient struct {
EventBus types.EventBus
LastBlock *types.Block
LastCommit *types.Commit
+ Storage storage.Storage
// if this is true, then an error will be returned if the responses from the clients are not all equal.
// can be used to check for nondeterminism in apps, but also slows down execution a bit,
@@ -248,20 +251,6 @@ func (a *AbciClient) SendAbciQuery(data []byte, path string, height int64, prove
return client.QuerySync(request)
}
-func GetBlockIdFromBlock(block *types.Block) (*types.BlockID, error) {
- partSet, error := block.MakePartSet(2)
- if error != nil {
- return nil, error
- }
-
- partSetHeader := partSet.Header()
- blockID := types.BlockID{
- Hash: block.Hash(),
- PartSetHeader: partSetHeader,
- }
- return &blockID, nil
-}
-
// RunBlock runs a block with a specified transaction through the ABCI application.
// It calls BeginBlock, DeliverTx, EndBlock, Commit and then
// updates the state.
@@ -273,6 +262,8 @@ func (a *AbciClient) RunBlock(tx *[]byte, blockTime time.Time, proposer *types.V
a.Logger.Info("Running block")
a.Logger.Info("State at start of block", "state", a.CurState)
+ newHeight := a.CurState.LastBlockHeight + 1
+
txs := make([]types.Tx, 0)
if tx != nil {
txs = append(txs, *tx)
@@ -287,7 +278,7 @@ func (a *AbciClient) RunBlock(tx *[]byte, blockTime time.Time, proposer *types.V
block := a.CurState.MakeBlock(a.CurState.LastBlockHeight+1, txs, a.LastCommit, []types.Evidence{}, proposerAddress)
// override the block time, since we do not actually get votes from peers to median the time out of
block.Time = blockTime
- blockId, err := GetBlockIdFromBlock(block)
+ blockId, err := utils.GetBlockIdFromBlock(block)
if err != nil {
return nil, nil, nil, nil, err
}
@@ -343,6 +334,26 @@ func (a *AbciClient) RunBlock(tx *[]byte, blockTime time.Time, proposer *types.V
[]types.CommitSig{},
)
+ err = a.Storage.InsertBlock(newHeight, block)
+ if err != nil {
+ return nil, nil, nil, nil, err
+ }
+
+ err = a.Storage.InsertCommit(newHeight, a.LastCommit)
+ if err != nil {
+ return nil, nil, nil, nil, err
+ }
+
+ err = a.Storage.InsertState(newHeight, &a.CurState)
+ if err != nil {
+ return nil, nil, nil, nil, err
+ }
+
+ err = a.Storage.InsertResponses(newHeight, &abciResponses)
+ if err != nil {
+ return nil, nil, nil, nil, err
+ }
+
// unlock mutex
blockMutex.Unlock()
diff --git a/cometmock/main.go b/cometmock/main.go
index 095802b..deebb7a 100644
--- a/cometmock/main.go
+++ b/cometmock/main.go
@@ -11,6 +11,7 @@ import (
"github.com/cometbft/cometbft/types"
"github.com/p-offtermatt/CometMock/cometmock/abci_client"
"github.com/p-offtermatt/CometMock/cometmock/rpc_server"
+ "github.com/p-offtermatt/CometMock/cometmock/storage"
)
func CreateAndStartEventBus(logger cometlog.Logger) (*types.EventBus, error) {
@@ -68,6 +69,7 @@ func main() {
ErrorOnUnequalResponses: true,
EventBus: *eventBus,
LastCommit: &types.Commit{},
+ Storage: &storage.MapStorage{},
}
// initialize chain
diff --git a/cometmock/rpc_server/routes.go b/cometmock/rpc_server/routes.go
index 4f72b84..01448ef 100644
--- a/cometmock/rpc_server/routes.go
+++ b/cometmock/rpc_server/routes.go
@@ -12,6 +12,7 @@ import (
rpctypes "github.com/cometbft/cometbft/rpc/jsonrpc/types"
"github.com/cometbft/cometbft/types"
"github.com/p-offtermatt/CometMock/cometmock/abci_client"
+ "github.com/p-offtermatt/CometMock/cometmock/utils"
)
const (
@@ -33,6 +34,7 @@ var Routes = map[string]*rpc.RPCFunc{
"consensus_params": rpc.NewRPCFunc(ConsensusParams, "height", rpc.Cacheable("height")),
"header": rpc.NewRPCFunc(Header, "height", rpc.Cacheable("height")),
"commit": rpc.NewRPCFunc(Commit, "height", rpc.Cacheable("height")),
+ "block_results": rpc.NewRPCFunc(BlockResults, "height", rpc.Cacheable("height")),
// // tx broadcast API
"broadcast_tx_commit": rpc.NewRPCFunc(BroadcastTxCommit, "tx"),
@@ -43,47 +45,74 @@ var Routes = map[string]*rpc.RPCFunc{
"abci_query": rpc.NewRPCFunc(ABCIQuery, "path,data,height,prove"),
}
+func getHeight(latestHeight int64, heightPtr *int64) (int64, error) {
+ if heightPtr != nil {
+ height := *heightPtr
+ if height <= 0 {
+ return 0, fmt.Errorf("height must be greater than 0, but got %d", height)
+ }
+ if height > latestHeight {
+ return 0, fmt.Errorf("height %d must be less than or equal to the current blockchain height %d",
+ height, latestHeight)
+ }
+ }
+ return latestHeight, nil
+}
+
// Header gets block header at a given height.
// If no height is provided, it will fetch the latest header.
// More: https://docs.cometbft.com/v0.37/rpc/#/Info/header
-// TODO: currently unfilled
func Header(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultHeader, error) {
- // only the last height is available, since we do not keep past heights at the moment
- if heightPtr != nil {
- return nil, fmt.Errorf("height parameter is not supported, use version of the function without height")
+ height, err := getHeight(abci_client.GlobalClient.LastBlock.Height, heightPtr)
+ if err != nil {
+ return nil, err
}
- return &ctypes.ResultHeader{}, nil
+ block, err := abci_client.GlobalClient.Storage.GetBlock(height)
+ if err != nil {
+ return nil, err
+ }
+
+ return &ctypes.ResultHeader{Header: &block.Header}, nil
}
// Commit gets block commit at a given height.
// If no height is provided, it will fetch the commit for the latest block.
// More: https://docs.cometbft.com/main/rpc/#/Info/commit
func Commit(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultCommit, error) {
- // only the last height is available, since we do not keep past heights at the moment
- if heightPtr != nil {
- return nil, fmt.Errorf("height parameter is not supported, use version of the function without height")
+ height, err := getHeight(abci_client.GlobalClient.LastBlock.Height, heightPtr)
+ if err != nil {
+ return nil, err
+ }
+
+ commit, err := abci_client.GlobalClient.Storage.GetCommit(height)
+ if err != nil {
+ return nil, err
}
- // If the next block has not been committed yet,
- // use a non-canonical commit
- lastCommit := abci_client.GlobalClient.LastCommit
- lastHeader := abci_client.GlobalClient.LastBlock.Header
+ block, err := abci_client.GlobalClient.Storage.GetBlock(height)
+ if err != nil {
+ return nil, err
+ }
- return ctypes.NewResultCommit(&lastHeader, lastCommit, true), nil
+ return ctypes.NewResultCommit(&block.Header, commit, true), nil
}
// ConsensusParams gets the consensus parameters at the given block height.
// If no height is provided, it will fetch the latest consensus params.
// More: https://docs.cometbft.com/v0.37/rpc/#/Info/consensus_params
func ConsensusParams(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultConsensusParams, error) {
- // only the last height is available, since we do not keep past heights at the moment
- if heightPtr != nil {
- return nil, fmt.Errorf("height parameter is not supported, use version of the function without height")
+ height, err := getHeight(abci_client.GlobalClient.LastBlock.Height, heightPtr)
+ if err != nil {
+ return nil, err
}
- height := abci_client.GlobalClient.CurState.LastBlockHeight
- consensusParams := abci_client.GlobalClient.CurState.ConsensusParams
+ stateForHeight, err := abci_client.GlobalClient.Storage.GetState(height)
+ if err != nil {
+ return nil, err
+ }
+
+ consensusParams := stateForHeight.ConsensusParams
return &ctypes.ResultConsensusParams{
BlockHeight: height,
@@ -280,12 +309,48 @@ func validateSkipCount(page, perPage int) int {
}
func Block(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlock, error) {
- // only the last height is available, since we do not keep past heights at the moment
- if heightPtr != nil {
- return nil, fmt.Errorf("height parameter is not supported, use version of the function without height")
+ height, err := getHeight(abci_client.GlobalClient.LastBlock.Height, heightPtr)
+ if err != nil {
+ return nil, err
}
- blockID := abci_client.GlobalClient.CurState.LastBlockID
+ block, err := abci_client.GlobalClient.Storage.GetBlock(height)
+ if err != nil {
+ return nil, err
+ }
- return &ctypes.ResultBlock{BlockID: blockID, Block: abci_client.GlobalClient.LastBlock}, nil
+ blockID, err := utils.GetBlockIdFromBlock(block)
+ if err != nil {
+ return nil, err
+ }
+
+ return &ctypes.ResultBlock{BlockID: *blockID, Block: abci_client.GlobalClient.LastBlock}, nil
+}
+
+// BlockResults gets ABCIResults at a given height.
+// If no height is provided, it will fetch results for the latest block.
+//
+// Results are for the height of the block containing the txs.
+// Thus response.results.deliver_tx[5] is the results of executing
+// getBlock(h).Txs[5]
+// More: https://docs.cometbft.com/v0.37/rpc/#/Info/block_results
+func BlockResults(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlockResults, error) {
+ height, err := getHeight(abci_client.GlobalClient.LastBlock.Height, heightPtr)
+ if err != nil {
+ return nil, err
+ }
+
+ results, err := abci_client.GlobalClient.Storage.GetResponses(height)
+ if err != nil {
+ return nil, err
+ }
+
+ return &ctypes.ResultBlockResults{
+ Height: height,
+ TxsResults: results.DeliverTxs,
+ BeginBlockEvents: results.BeginBlock.Events,
+ EndBlockEvents: results.EndBlock.Events,
+ ValidatorUpdates: results.EndBlock.ValidatorUpdates,
+ ConsensusParamUpdates: results.EndBlock.ConsensusParamUpdates,
+ }, nil
}
diff --git a/cometmock/storage/storage.go b/cometmock/storage/storage.go
new file mode 100644
index 0000000..1a0ba59
--- /dev/null
+++ b/cometmock/storage/storage.go
@@ -0,0 +1,124 @@
+package storage
+
+import (
+ "fmt"
+
+ protostate "github.com/cometbft/cometbft/proto/tendermint/state"
+ cometstate "github.com/cometbft/cometbft/state"
+ "github.com/cometbft/cometbft/types"
+)
+
+// Storage is an interface for storing blocks, commits and states by height.
+type Storage interface {
+ // InsertBlock inserts a block at a given height.
+ // If there is already a block at that height, it should be overwritten.
+ InsertBlock(height int64, block *types.Block) error
+ // GetBlock returns the block at a given height.
+ GetBlock(height int64) (*types.Block, error)
+
+ // InsertCommit inserts a commit at a given height.
+ // If there is already a commit at that height, it should be overwritten.
+ InsertCommit(height int64, commit *types.Commit) error
+ // GetCommit returns the commit at a given height.
+ GetCommit(height int64) (*types.Commit, error)
+
+ // InsertState inserts a state at a given height. This is the state after
+ // applying the block at that height.
+ // If there is already a state at that height, it should be overwritten.
+ InsertState(height int64, state *cometstate.State) error
+ // GetState returns the state at a given height. This is the state after
+ // applying the block at that height.
+ GetState(height int64) (*cometstate.State, error)
+
+ // InsertResponses inserts the ABCI responses from a given height.
+ // If there are already responses at that height, they should be overwritten.
+ InsertResponses(height int64, responses *protostate.ABCIResponses) error
+ // GetResponses returns the ABCI responses from a given height.
+ GetResponses(height int64) (*protostate.ABCIResponses, error)
+}
+
+// MapStorage is a simple in-memory implementation of Storage.
+type MapStorage struct {
+ blocks map[int64]*types.Block
+ commits map[int64]*types.Commit
+ states map[int64]*cometstate.State
+ responses map[int64]*protostate.ABCIResponses
+}
+
+func (m *MapStorage) InsertBlock(height int64, block *types.Block) error {
+ if m.blocks == nil {
+ m.blocks = make(map[int64]*types.Block)
+ }
+ m.blocks[height] = block
+ return nil
+}
+
+func (m *MapStorage) GetBlock(height int64) (*types.Block, error) {
+ if m.blocks == nil {
+ m.blocks = make(map[int64]*types.Block)
+ }
+ if block, ok := m.blocks[height]; ok {
+ return block, nil
+ }
+ return nil, fmt.Errorf("block for height %v not found", height)
+}
+
+func (m *MapStorage) InsertCommit(height int64, commit *types.Commit) error {
+ if m.commits == nil {
+ m.commits = make(map[int64]*types.Commit)
+ }
+
+ m.commits[height] = commit
+ return nil
+}
+
+func (m *MapStorage) GetCommit(height int64) (*types.Commit, error) {
+ if m.commits == nil {
+ m.commits = make(map[int64]*types.Commit)
+ }
+
+ if commit, ok := m.commits[height]; ok {
+ return commit, nil
+ }
+ return nil, fmt.Errorf("commit for height %v not found", height)
+}
+
+func (m *MapStorage) InsertState(height int64, state *cometstate.State) error {
+ if m.states == nil {
+ m.states = make(map[int64]*cometstate.State)
+ }
+
+ m.states[height] = state
+ return nil
+}
+
+func (m *MapStorage) GetState(height int64) (*cometstate.State, error) {
+ if m.states == nil {
+ m.states = make(map[int64]*cometstate.State)
+ }
+
+ if state, ok := m.states[height]; ok {
+ return state, nil
+ }
+ return nil, fmt.Errorf("state for height %v not found", height)
+}
+
+func (m *MapStorage) InsertResponses(height int64, responses *protostate.ABCIResponses) error {
+ if m.responses == nil {
+ m.responses = make(map[int64]*protostate.ABCIResponses)
+ }
+
+ m.responses[height] = responses
+ return nil
+}
+
+func (m *MapStorage) GetResponses(height int64) (*protostate.ABCIResponses, error) {
+ if m.responses == nil {
+ m.responses = make(map[int64]*protostate.ABCIResponses)
+ }
+
+ if responses, ok := m.responses[height]; ok {
+ return responses, nil
+ }
+ return nil, fmt.Errorf("responses for height %v not found", height)
+}
diff --git a/cometmock/test_utils/helpers.go b/cometmock/test_utils/helpers.go
new file mode 100644
index 0000000..fa3140b
--- /dev/null
+++ b/cometmock/test_utils/helpers.go
@@ -0,0 +1,248 @@
+package test_utils
+
+import (
+ "time"
+
+ "github.com/cometbft/cometbft/crypto"
+ "github.com/cometbft/cometbft/crypto/ed25519"
+ "github.com/cometbft/cometbft/crypto/tmhash"
+ cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
+ cmtversion "github.com/cometbft/cometbft/proto/tendermint/version"
+ "github.com/cometbft/cometbft/types"
+ cmttime "github.com/cometbft/cometbft/types/time"
+ "github.com/cometbft/cometbft/version"
+)
+
+// file is adapted from https://github.com/cometbft/cometbft/blob/9267594e0a17c01cc4a97b399ada5eaa8a734db5/light/helpers_test.go#L16
+
+// privKeys is a helper type for testing.
+//
+// It lets us simulate signing with many keys. The main use case is to create
+// a set, and call GenSignedHeader to get properly signed header for testing.
+//
+// You can set different weights of validators each time you call ToValidators,
+// and can optionally extend the validator set later with Extend.
+type privKeys []crypto.PrivKey
+
+// genPrivKeys produces an array of private keys to generate commits.
+func GenPrivKeys(n int) privKeys {
+ res := make(privKeys, n)
+ for i := range res {
+ res[i] = ed25519.GenPrivKey()
+ }
+ return res
+}
+
+// // Change replaces the key at index i.
+// func (pkz privKeys) Change(i int) privKeys {
+// res := make(privKeys, len(pkz))
+// copy(res, pkz)
+// res[i] = ed25519.GenPrivKey()
+// return res
+// }
+
+// Extend adds n more keys (to remove, just take a slice).
+func (pkz privKeys) Extend(n int) privKeys {
+ extra := GenPrivKeys(n)
+ return append(pkz, extra...)
+}
+
+// // GenSecpPrivKeys produces an array of secp256k1 private keys to generate commits.
+// func GenSecpPrivKeys(n int) privKeys {
+// res := make(privKeys, n)
+// for i := range res {
+// res[i] = secp256k1.GenPrivKey()
+// }
+// return res
+// }
+
+// // ExtendSecp adds n more secp256k1 keys (to remove, just take a slice).
+// func (pkz privKeys) ExtendSecp(n int) privKeys {
+// extra := GenSecpPrivKeys(n)
+// return append(pkz, extra...)
+// }
+
+// ToValidators produces a valset from the set of keys.
+// The first key has weight `init` and it increases by `inc` every step
+// so we can have all the same weight, or a simple linear distribution
+// (should be enough for testing).
+func (pkz privKeys) ToValidators(init, inc int64) *types.ValidatorSet {
+ res := make([]*types.Validator, len(pkz))
+ for i, k := range pkz {
+ res[i] = types.NewValidator(k.PubKey(), init+int64(i)*inc)
+ }
+ return types.NewValidatorSet(res)
+}
+
+// signHeader properly signs the header with all keys from first to last exclusive.
+func (pkz privKeys) signHeader(header *types.Header, valSet *types.ValidatorSet, first, last int) *types.Commit {
+ commitSigs := make([]types.CommitSig, len(pkz))
+ for i := 0; i < len(pkz); i++ {
+ commitSigs[i] = types.NewCommitSigAbsent()
+ }
+
+ blockID := types.BlockID{
+ Hash: header.Hash(),
+ PartSetHeader: types.PartSetHeader{Total: 1, Hash: crypto.CRandBytes(32)},
+ }
+
+ // Fill in the votes we want.
+ for i := first; i < last && i < len(pkz); i++ {
+ vote := makeVote(header, valSet, pkz[i], blockID)
+ commitSigs[vote.ValidatorIndex] = vote.CommitSig()
+ }
+
+ return types.NewCommit(header.Height, 1, blockID, commitSigs)
+}
+
+func makeVote(header *types.Header, valset *types.ValidatorSet,
+ key crypto.PrivKey, blockID types.BlockID,
+) *types.Vote {
+ addr := key.PubKey().Address()
+ idx, _ := valset.GetByAddress(addr)
+ vote := &types.Vote{
+ ValidatorAddress: addr,
+ ValidatorIndex: idx,
+ Height: header.Height,
+ Round: 1,
+ Timestamp: cmttime.Now(),
+ Type: cmtproto.PrecommitType,
+ BlockID: blockID,
+ }
+
+ v := vote.ToProto()
+ // Sign it
+ signBytes := types.VoteSignBytes(header.ChainID, v)
+ sig, err := key.Sign(signBytes)
+ if err != nil {
+ panic(err)
+ }
+
+ vote.Signature = sig
+
+ return vote
+}
+
+func genHeader(chainID string, height int64, bTime time.Time, txs types.Txs,
+ valset, nextValset *types.ValidatorSet, appHash, consHash, resHash []byte,
+) *types.Header {
+ return &types.Header{
+ Version: cmtversion.Consensus{Block: version.BlockProtocol, App: 0},
+ ChainID: chainID,
+ Height: height,
+ Time: bTime,
+ // LastBlockID
+ // LastCommitHash
+ ValidatorsHash: valset.Hash(),
+ NextValidatorsHash: nextValset.Hash(),
+ DataHash: txs.Hash(),
+ AppHash: appHash,
+ ConsensusHash: consHash,
+ LastResultsHash: resHash,
+ ProposerAddress: valset.Validators[0].Address,
+ }
+}
+
+// GenSignedHeader calls genHeader and signHeader and combines them into a SignedHeader.
+func (pkz privKeys) GenSignedHeader(chainID string, height int64, bTime time.Time, txs types.Txs,
+ valset, nextValset *types.ValidatorSet, appHash, consHash, resHash []byte, first, last int,
+) *types.SignedHeader {
+ header := genHeader(chainID, height, bTime, txs, valset, nextValset, appHash, consHash, resHash)
+ return &types.SignedHeader{
+ Header: header,
+ Commit: pkz.signHeader(header, valset, first, last),
+ }
+}
+
+// GenSignedHeaderLastBlockID calls genHeader and signHeader and combines them into a SignedHeader.
+func (pkz privKeys) GenSignedHeaderLastBlockID(chainID string, height int64, bTime time.Time, txs types.Txs,
+ valset, nextValset *types.ValidatorSet, appHash, consHash, resHash []byte, first, last int,
+ lastBlockID types.BlockID,
+) *types.SignedHeader {
+ header := genHeader(chainID, height, bTime, txs, valset, nextValset, appHash, consHash, resHash)
+ header.LastBlockID = lastBlockID
+ return &types.SignedHeader{
+ Header: header,
+ Commit: pkz.signHeader(header, valset, first, last),
+ }
+}
+
+func (pkz privKeys) ChangeKeys(delta int) privKeys {
+ newKeys := pkz[delta:]
+ return newKeys.Extend(delta)
+}
+
+// Generates the header and validator set to create a full entire mock node with blocks to height (
+// blockSize) and with variation in validator sets. BlockIntervals are in per minute.
+// NOTE: Expected to have a large validator set size ~ 100 validators.
+func genMockNodeWithKeys(
+ chainID string,
+ blockSize int64,
+ valSize int,
+ valVariation float32,
+ bTime time.Time) (
+ map[int64]*types.SignedHeader,
+ map[int64]*types.ValidatorSet,
+ map[int64]privKeys,
+) {
+ var (
+ headers = make(map[int64]*types.SignedHeader, blockSize)
+ valset = make(map[int64]*types.ValidatorSet, blockSize+1)
+ keymap = make(map[int64]privKeys, blockSize+1)
+ keys = GenPrivKeys(valSize)
+ totalVariation = valVariation
+ valVariationInt int
+ newKeys privKeys
+ )
+
+ valVariationInt = int(totalVariation)
+ totalVariation = -float32(valVariationInt)
+ newKeys = keys.ChangeKeys(valVariationInt)
+ keymap[1] = keys
+ keymap[2] = newKeys
+
+ // genesis header and vals
+ lastHeader := keys.GenSignedHeader(chainID, 1, bTime.Add(1*time.Minute), nil,
+ keys.ToValidators(2, 0), newKeys.ToValidators(2, 0), hash("app_hash"), hash("cons_hash"),
+ hash("results_hash"), 0, len(keys))
+ currentHeader := lastHeader
+ headers[1] = currentHeader
+ valset[1] = keys.ToValidators(2, 0)
+ keys = newKeys
+
+ for height := int64(2); height <= blockSize; height++ {
+ totalVariation += valVariation
+ valVariationInt = int(totalVariation)
+ totalVariation = -float32(valVariationInt)
+ newKeys = keys.ChangeKeys(valVariationInt)
+ currentHeader = keys.GenSignedHeaderLastBlockID(chainID, height, bTime.Add(time.Duration(height)*time.Minute),
+ nil,
+ keys.ToValidators(2, 0), newKeys.ToValidators(2, 0), hash("app_hash"), hash("cons_hash"),
+ hash("results_hash"), 0, len(keys), types.BlockID{Hash: lastHeader.Hash()})
+ headers[height] = currentHeader
+ valset[height] = keys.ToValidators(2, 0)
+ lastHeader = currentHeader
+ keys = newKeys
+ keymap[height+1] = keys
+ }
+
+ return headers, valset, keymap
+}
+
+func genMockNode(
+ chainID string,
+ blockSize int64,
+ valSize int,
+ valVariation float32,
+ bTime time.Time) (
+ string,
+ map[int64]*types.SignedHeader,
+ map[int64]*types.ValidatorSet,
+) {
+ headers, valset, _ := genMockNodeWithKeys(chainID, blockSize, valSize, valVariation, bTime)
+ return chainID, headers, valset
+}
+
+func hash(s string) []byte {
+ return tmhash.Sum([]byte(s))
+}
diff --git a/cometmock/utils/blocks.go b/cometmock/utils/blocks.go
new file mode 100644
index 0000000..6540254
--- /dev/null
+++ b/cometmock/utils/blocks.go
@@ -0,0 +1,17 @@
+package utils
+
+import "github.com/cometbft/cometbft/types"
+
+func GetBlockIdFromBlock(block *types.Block) (*types.BlockID, error) {
+ partSet, error := block.MakePartSet(2)
+ if error != nil {
+ return nil, error
+ }
+
+ partSetHeader := partSet.Header()
+ blockID := types.BlockID{
+ Hash: block.Hash(),
+ PartSetHeader: partSetHeader,
+ }
+ return &blockID, nil
+}
From 4aebcd69631882763029aafbabd875394d3bdfdb Mon Sep 17 00:00:00 2001
From: Philip Offtermatt
Date: Mon, 22 May 2023 14:43:04 +0200
Subject: [PATCH 15/26] Add signing commits
---
README.md | 3 ++-
cometmock/abci_client/client.go | 40 ++++++++++++++++++++++++------
cometmock/main.go | 26 +++++++++++++++++++
cometmock/rpc_server/routes.go | 14 +++++++----
cometmock/storage/storage.go | 3 +++
cometmock/utils/votes.go | 44 +++++++++++++++++++++++++++++++++
go.mod | 4 +++
go.sum | 10 ++++++++
8 files changed, 130 insertions(+), 14 deletions(-)
create mode 100644 cometmock/utils/votes.go
diff --git a/README.md b/README.md
index 7984ecb..7e3722e 100644
--- a/README.md
+++ b/README.md
@@ -3,4 +3,5 @@
To run CometMock:
`docker stop simapp; docker rm simapp; docker run --add-host=host.docker.internal:host-gateway --name simapp -p 26658:26658 -ti informalofftermatt/testnet:tendermock simd start --transport=grpc --with-tendermint=false --grpc-only --rpc.laddr=tcp://host.docker.internal:99999`
`docker stop simapp2; docker rm simapp2; docker run --add-host=host.docker.internal:host-gateway --name simapp2 -p 36658:26658 -ti informalofftermatt/testnet:tendermock simd start --transport=grpc --with-tendermint=false --grpc-only --rpc.laddr=tcp://host.docker.internal:99999`
-`go run ./cometmock localhost:36658,localhost:26658 genesis.json tcp://localhost:26657`
\ No newline at end of file
+`go run ./cometmock localhost:36658,localhost:26658 genesis.json tcp://localhost:26657`
+`curl -H 'Content-Type: application/json' -H 'Accept:application/json' --data '{"jsonrpc":"2.0","method":"commit","params":{},"id":1}' 127.0.0.1:26657`
\ No newline at end of file
diff --git a/cometmock/abci_client/client.go b/cometmock/abci_client/client.go
index 06161ff..792f209 100644
--- a/cometmock/abci_client/client.go
+++ b/cometmock/abci_client/client.go
@@ -12,6 +12,7 @@ import (
"github.com/cometbft/cometbft/crypto/merkle"
cometlog "github.com/cometbft/cometbft/libs/log"
cmtstate "github.com/cometbft/cometbft/proto/tendermint/state"
+ cmttypes "github.com/cometbft/cometbft/proto/tendermint/types"
"github.com/cometbft/cometbft/state"
"github.com/cometbft/cometbft/types"
"github.com/p-offtermatt/CometMock/cometmock/storage"
@@ -26,13 +27,14 @@ var blockMutex = sync.Mutex{}
// AbciClient facilitates calls to the ABCI interface of multiple nodes.
// It also tracks the current state and a common logger.
type AbciClient struct {
- Clients []abciclient.Client
- Logger cometlog.Logger
- CurState state.State
- EventBus types.EventBus
- LastBlock *types.Block
- LastCommit *types.Commit
- Storage storage.Storage
+ Clients []abciclient.Client
+ Logger cometlog.Logger
+ CurState state.State
+ EventBus types.EventBus
+ LastBlock *types.Block
+ LastCommit *types.Commit
+ Storage storage.Storage
+ PrivValidators []types.PrivValidator
// if this is true, then an error will be returned if the responses from the clients are not all equal.
// can be used to check for nondeterminism in apps, but also slows down execution a bit,
@@ -327,11 +329,33 @@ func (a *AbciClient) RunBlock(tx *[]byte, blockTime time.Time, proposer *types.V
}
a.LastBlock = block
+
+ commitSigs := []types.CommitSig{}
+
+ precommitType := cmttypes.SignedMsgType_value["SIGNED_MSG_TYPE_PRECOMMIT"]
+ for index, pv := range a.PrivValidators {
+ // create and sign a precommit
+ vote, err := utils.MakeVote(
+ pv,
+ a.CurState.ChainID,
+ int32(index),
+ block.Height,
+ 2, // round to consensus - can be arbitrary
+ int(precommitType), // for which step the vote is - we use precommit
+ *blockId,
+ time.Now(),
+ )
+ if err != nil {
+ return nil, nil, nil, nil, err
+ }
+ commitSigs = append(commitSigs, vote.CommitSig())
+ }
+
a.LastCommit = types.NewCommit(
block.Height,
1,
*blockId,
- []types.CommitSig{},
+ commitSigs,
)
err = a.Storage.InsertBlock(newHeight, block)
diff --git a/cometmock/main.go b/cometmock/main.go
index deebb7a..670d92e 100644
--- a/cometmock/main.go
+++ b/cometmock/main.go
@@ -7,6 +7,7 @@ import (
comet_abciclient "github.com/cometbft/cometbft/abci/client"
cometlog "github.com/cometbft/cometbft/libs/log"
+ "github.com/cometbft/cometbft/privval"
"github.com/cometbft/cometbft/state"
"github.com/cometbft/cometbft/types"
"github.com/p-offtermatt/CometMock/cometmock/abci_client"
@@ -23,6 +24,23 @@ func CreateAndStartEventBus(logger cometlog.Logger) (*types.EventBus, error) {
return eventBus, nil
}
+// GetMockPVsFromNodeHomes returns a list of MockPVs, created with the priv_validator_key's from the specified node homes
+// We use MockPV because they do not do sanity checks that would e.g. prevent double signing
+func GetMockPVsFromNodeHomes(nodeHomes []string) []types.PrivValidator {
+ mockPVs := make([]types.PrivValidator, 0)
+
+ for _, nodeHome := range nodeHomes {
+ privValidatorKeyFile := nodeHome + "/config/priv_validator_key.json"
+ privValidatorStateFile := nodeHome + "/data/priv_validator_state.json"
+ validator := privval.LoadFilePV(privValidatorKeyFile, privValidatorStateFile)
+
+ mockPV := types.NewMockPVWithParams(validator.Key.PrivKey, false, false)
+ mockPVs = append(mockPVs, mockPV)
+ }
+
+ return mockPVs
+}
+
func main() {
logger := cometlog.NewTMLogger(cometlog.NewSyncWriter(os.Stdout))
@@ -35,6 +53,13 @@ func main() {
appAddresses := strings.Split(args[0], ",")
genesisFile := args[1]
cometMockListenAddress := args[2]
+ nodeHomesString := args[3]
+
+ // read node homes from args
+ nodeHomes := strings.Split(nodeHomesString, ",")
+
+ // get priv validators from node Homes
+ privVals := GetMockPVsFromNodeHomes(nodeHomes)
genesisDoc, err := state.MakeGenesisDocFromFile(genesisFile)
if err != nil {
@@ -70,6 +95,7 @@ func main() {
EventBus: *eventBus,
LastCommit: &types.Commit{},
Storage: &storage.MapStorage{},
+ PrivValidators: privVals,
}
// initialize chain
diff --git a/cometmock/rpc_server/routes.go b/cometmock/rpc_server/routes.go
index 01448ef..4d95c58 100644
--- a/cometmock/rpc_server/routes.go
+++ b/cometmock/rpc_server/routes.go
@@ -55,6 +55,7 @@ func getHeight(latestHeight int64, heightPtr *int64) (int64, error) {
return 0, fmt.Errorf("height %d must be less than or equal to the current blockchain height %d",
height, latestHeight)
}
+ return height, nil
}
return latestHeight, nil
}
@@ -233,14 +234,17 @@ func ABCIQuery(
}
func Validators(ctx *rpctypes.Context, heightPtr *int64, pagePtr, perPagePtr *int) (*ctypes.ResultValidators, error) {
- // only the last height is available, since we do not keep past heights at the moment
- if heightPtr != nil {
- return nil, fmt.Errorf("height parameter is not supported, use version of the function without height")
+ height, err := getHeight(abci_client.GlobalClient.LastBlock.Height, heightPtr)
+ if err != nil {
+ return nil, err
}
- height := abci_client.GlobalClient.CurState.LastBlockHeight
+ pastState, err := abci_client.GlobalClient.Storage.GetState(height)
+ if err != nil {
+ return nil, err
+ }
- validators := abci_client.GlobalClient.CurState.LastValidators
+ validators := pastState.Validators
totalCount := len(validators.Validators)
perPage := validatePerPage(perPagePtr)
diff --git a/cometmock/storage/storage.go b/cometmock/storage/storage.go
index 1a0ba59..38ab2e7 100644
--- a/cometmock/storage/storage.go
+++ b/cometmock/storage/storage.go
@@ -45,6 +45,9 @@ type MapStorage struct {
responses map[int64]*protostate.ABCIResponses
}
+// ensure MapStorage implements Storage
+var _ Storage = (*MapStorage)(nil)
+
func (m *MapStorage) InsertBlock(height int64, block *types.Block) error {
if m.blocks == nil {
m.blocks = make(map[int64]*types.Block)
diff --git a/cometmock/utils/votes.go b/cometmock/utils/votes.go
new file mode 100644
index 0000000..4a10b0d
--- /dev/null
+++ b/cometmock/utils/votes.go
@@ -0,0 +1,44 @@
+package utils
+
+import (
+ "time"
+
+ cmttypes "github.com/cometbft/cometbft/proto/tendermint/types"
+ "github.com/cometbft/cometbft/types"
+)
+
+// MakeVote creates a signed vote.
+// Adapted from https://github.com/cometbft/cometbft/blob/9267594e0a17c01cc4a97b399ada5eaa8a734db5/internal/test/vote.go#L10.
+func MakeVote(
+ val types.PrivValidator, // PrivValidator is the validator that will sign the vote.
+ chainID string,
+ valIndex int32,
+ height int64,
+ round int32,
+ step int, // StepType is the step in the consensus process, see https://github.com/cometbft/cometbft/blob/9267594e0a17c01cc4a97b399ada5eaa8a734db5/proto/tendermint/types/types.pb.go#L68
+ blockID types.BlockID,
+ time time.Time,
+) (*types.Vote, error) {
+ pubKey, err := val.GetPubKey()
+ if err != nil {
+ return nil, err
+ }
+
+ v := &types.Vote{
+ ValidatorAddress: pubKey.Address(),
+ ValidatorIndex: valIndex,
+ Height: height,
+ Round: round,
+ Type: cmttypes.SignedMsgType(step),
+ BlockID: blockID,
+ Timestamp: time,
+ }
+
+ vpb := v.ToProto()
+ if err := val.SignVote(chainID, vpb); err != nil {
+ return nil, err
+ }
+
+ v.Signature = vpb.Signature
+ return v, nil
+}
diff --git a/go.mod b/go.mod
index 828dfec..1385c55 100644
--- a/go.mod
+++ b/go.mod
@@ -11,6 +11,7 @@ require (
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/cometbft/cometbft-db v0.7.0 // indirect
github.com/cosmos/gogoproto v1.4.1 // indirect
+ github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/dgraph-io/badger/v2 v2.2007.4 // indirect
github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de // indirect
@@ -31,11 +32,13 @@ require (
github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect
github.com/pkg/errors v0.9.1 // indirect
+ github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.14.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
github.com/sasha-s/go-deadlock v0.3.1 // indirect
+ github.com/stretchr/testify v1.8.1 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect
github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c // indirect
go.etcd.io/bbolt v1.3.6 // indirect
@@ -46,4 +49,5 @@ require (
google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 // indirect
google.golang.org/grpc v1.52.0 // indirect
google.golang.org/protobuf v1.28.2-0.20220831092852-f930b1dc76e8 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
)
diff --git a/go.sum b/go.sum
index 0d30300..76de9d0 100644
--- a/go.sum
+++ b/go.sum
@@ -204,8 +204,10 @@ github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrD
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
+github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8=
github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg=
@@ -287,11 +289,16 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c h1:g+WoO5jjkqGAzHWCjJB1zZfXPIAaDpzXIEJ0eS6B5Ok=
@@ -591,6 +598,7 @@ google.golang.org/protobuf v1.28.2-0.20220831092852-f930b1dc76e8/go.mod h1:HV8QO
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
@@ -603,7 +611,9 @@ gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
From 5de2fd6ae3371855fefc7d0b327df3abb25d5cf0 Mon Sep 17 00:00:00 2001
From: Philip Offtermatt
Date: Mon, 22 May 2023 14:44:38 +0200
Subject: [PATCH 16/26] Adjust expected params
---
cometmock/main.go | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/cometmock/main.go b/cometmock/main.go
index 670d92e..0b7ca40 100644
--- a/cometmock/main.go
+++ b/cometmock/main.go
@@ -44,8 +44,8 @@ func GetMockPVsFromNodeHomes(nodeHomes []string) []types.PrivValidator {
func main() {
logger := cometlog.NewTMLogger(cometlog.NewSyncWriter(os.Stdout))
- if len(os.Args) != 4 {
- logger.Error("Usage: ")
+ if len(os.Args) != 5 {
+ logger.Error("Usage: ")
}
args := os.Args[1:]
From b6c64d0ce2b0868a60e18a770408cff7076daf2d Mon Sep 17 00:00:00 2001
From: Philip Offtermatt
Date: Tue, 23 May 2023 13:20:44 +0200
Subject: [PATCH 17/26] Return updated node version in Status
---
cometmock/rpc_server/routes.go | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/cometmock/rpc_server/routes.go b/cometmock/rpc_server/routes.go
index 4d95c58..99df9b1 100644
--- a/cometmock/rpc_server/routes.go
+++ b/cometmock/rpc_server/routes.go
@@ -6,11 +6,13 @@ import (
"github.com/cometbft/cometbft/libs/bytes"
cmtmath "github.com/cometbft/cometbft/libs/math"
+ "github.com/cometbft/cometbft/p2p"
cometp2p "github.com/cometbft/cometbft/p2p"
ctypes "github.com/cometbft/cometbft/rpc/core/types"
rpc "github.com/cometbft/cometbft/rpc/jsonrpc/server"
rpctypes "github.com/cometbft/cometbft/rpc/jsonrpc/types"
"github.com/cometbft/cometbft/types"
+ "github.com/cometbft/cometbft/version"
"github.com/p-offtermatt/CometMock/cometmock/abci_client"
"github.com/p-offtermatt/CometMock/cometmock/utils"
)
@@ -126,7 +128,8 @@ func ConsensusParams(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultCon
// More: https://docs.cometbft.com/v0.37/rpc/#/Info/status
func Status(ctx *rpctypes.Context) (*ctypes.ResultStatus, error) {
// return status as if we are the first validator
- validator := abci_client.GlobalClient.CurState.Validators.Validators[0]
+ curState := abci_client.GlobalClient.CurState
+ validator := curState.Validators.Validators[0]
nodeInfo := cometp2p.DefaultNodeInfo{
DefaultNodeID: cometp2p.PubKeyToID(validator.PubKey),
@@ -134,6 +137,12 @@ func Status(ctx *rpctypes.Context) (*ctypes.ResultStatus, error) {
Other: cometp2p.DefaultNodeInfoOther{
TxIndex: "on",
},
+ Version: "v0.37.1",
+ ProtocolVersion: p2p.NewProtocolVersion(
+ version.P2PProtocol, // global
+ curState.Version.Consensus.Block,
+ curState.Version.Consensus.App,
+ ),
}
syncInfo := ctypes.SyncInfo{
LatestBlockHash: abci_client.GlobalClient.LastBlock.Hash(),
From 93ecb8081500785abab2e4755a04b5021e267db5 Mon Sep 17 00:00:00 2001
From: Philip Offtermatt
Date: Tue, 23 May 2023 13:56:30 +0200
Subject: [PATCH 18/26] Add return values for broadcasts
---
cometmock/abci_client/client.go | 59 +++++++++++++++++++++++++--------
cometmock/rpc_server/routes.go | 19 ++++++++---
2 files changed, 61 insertions(+), 17 deletions(-)
diff --git a/cometmock/abci_client/client.go b/cometmock/abci_client/client.go
index 792f209..dce3b62 100644
--- a/cometmock/abci_client/client.go
+++ b/cometmock/abci_client/client.go
@@ -214,6 +214,34 @@ func (a *AbciClient) SendCommit() (*abcitypes.ResponseCommit, error) {
return responses[0], nil
}
+func (a *AbciClient) SendCheckTx(tx *[]byte) (*abcitypes.ResponseCheckTx, error) {
+ // build the CheckTx request
+ checkTxRequest := abcitypes.RequestCheckTx{
+ Tx: *tx,
+ }
+
+ // send CheckTx to all clients and collect the responses
+ responses := []*abcitypes.ResponseCheckTx{}
+ for _, client := range a.Clients {
+ response, err := client.CheckTxSync(checkTxRequest)
+ if err != nil {
+ return nil, err
+ }
+ responses = append(responses, response)
+ }
+
+ if a.ErrorOnUnequalResponses {
+ // return an error if the responses are not all equal
+ for i := 1; i < len(responses); i++ {
+ if !reflect.DeepEqual(responses[i], responses[0]) {
+ return nil, fmt.Errorf("responses are not all equal: %v is not equal to %v", responses[i], responses[0])
+ }
+ }
+ }
+
+ return responses[0], nil
+}
+
func (a *AbciClient) SendDeliverTx(tx *[]byte) (*abcitypes.ResponseDeliverTx, error) {
// build the DeliverTx request
deliverTxRequest := abcitypes.RequestDeliverTx{
@@ -257,7 +285,7 @@ func (a *AbciClient) SendAbciQuery(data []byte, path string, height int64, prove
// It calls BeginBlock, DeliverTx, EndBlock, Commit and then
// updates the state.
// RunBlock is safe for use by multiple goroutines simultaneously.
-func (a *AbciClient) RunBlock(tx *[]byte, blockTime time.Time, proposer *types.Validator) (*abcitypes.ResponseBeginBlock, *abcitypes.ResponseDeliverTx, *abcitypes.ResponseEndBlock, *abcitypes.ResponseCommit, error) {
+func (a *AbciClient) RunBlock(tx *[]byte, blockTime time.Time, proposer *types.Validator) (*abcitypes.ResponseBeginBlock, *abcitypes.ResponseCheckTx, *abcitypes.ResponseDeliverTx, *abcitypes.ResponseEndBlock, *abcitypes.ResponseCommit, error) {
// lock mutex to avoid running two blocks at the same time
blockMutex.Lock()
@@ -271,6 +299,11 @@ func (a *AbciClient) RunBlock(tx *[]byte, blockTime time.Time, proposer *types.V
txs = append(txs, *tx)
}
+ resCheckTx, err := a.SendCheckTx(tx)
+ if err != nil {
+ return nil, nil, nil, nil, nil, err
+ }
+
// TODO: handle special case where proposer is nil
var proposerAddress types.Address
if proposer != nil {
@@ -282,19 +315,19 @@ func (a *AbciClient) RunBlock(tx *[]byte, blockTime time.Time, proposer *types.V
block.Time = blockTime
blockId, err := utils.GetBlockIdFromBlock(block)
if err != nil {
- return nil, nil, nil, nil, err
+ return nil, nil, nil, nil, nil, err
}
resBeginBlock, err := a.SendBeginBlock(block)
if err != nil {
- return nil, nil, nil, nil, err
+ return nil, nil, nil, nil, nil, err
}
var resDeliverTx *abcitypes.ResponseDeliverTx
if tx != nil {
resDeliverTx, err = a.SendDeliverTx(tx)
if err != nil {
- return nil, nil, nil, nil, err
+ return nil, nil, nil, nil, nil, err
}
} else {
resDeliverTx = nil
@@ -302,12 +335,12 @@ func (a *AbciClient) RunBlock(tx *[]byte, blockTime time.Time, proposer *types.V
resEndBlock, err := a.SendEndBlock()
if err != nil {
- return nil, nil, nil, nil, err
+ return nil, nil, nil, nil, nil, err
}
resCommit, err := a.SendCommit()
if err != nil {
- return nil, nil, nil, nil, err
+ return nil, nil, nil, nil, nil, err
}
deliverTxResponses := []*abcitypes.ResponseDeliverTx{}
@@ -325,7 +358,7 @@ func (a *AbciClient) RunBlock(tx *[]byte, blockTime time.Time, proposer *types.V
// updates state as a side effect. returns an error if the state update fails
err = a.UpdateStateFromBlock(blockId, block, abciResponses, resCommit)
if err != nil {
- return nil, nil, nil, nil, err
+ return nil, nil, nil, nil, nil, err
}
a.LastBlock = block
@@ -346,7 +379,7 @@ func (a *AbciClient) RunBlock(tx *[]byte, blockTime time.Time, proposer *types.V
time.Now(),
)
if err != nil {
- return nil, nil, nil, nil, err
+ return nil, nil, nil, nil, nil, err
}
commitSigs = append(commitSigs, vote.CommitSig())
}
@@ -360,28 +393,28 @@ func (a *AbciClient) RunBlock(tx *[]byte, blockTime time.Time, proposer *types.V
err = a.Storage.InsertBlock(newHeight, block)
if err != nil {
- return nil, nil, nil, nil, err
+ return nil, nil, nil, nil, nil, err
}
err = a.Storage.InsertCommit(newHeight, a.LastCommit)
if err != nil {
- return nil, nil, nil, nil, err
+ return nil, nil, nil, nil, nil, err
}
err = a.Storage.InsertState(newHeight, &a.CurState)
if err != nil {
- return nil, nil, nil, nil, err
+ return nil, nil, nil, nil, nil, err
}
err = a.Storage.InsertResponses(newHeight, &abciResponses)
if err != nil {
- return nil, nil, nil, nil, err
+ return nil, nil, nil, nil, nil, err
}
// unlock mutex
blockMutex.Unlock()
- return resBeginBlock, resDeliverTx, resEndBlock, resCommit, nil
+ return resBeginBlock, resCheckTx, resDeliverTx, resEndBlock, resCommit, nil
}
// UpdateStateFromBlock updates the AbciClients state
diff --git a/cometmock/rpc_server/routes.go b/cometmock/rpc_server/routes.go
index 99df9b1..f844a2f 100644
--- a/cometmock/rpc_server/routes.go
+++ b/cometmock/rpc_server/routes.go
@@ -189,12 +189,18 @@ func BroadcastTxSync(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadcas
abci_client.GlobalClient.Logger.Info(
"BroadcastTxSync called", "tx", tx)
- _, err := BroadcastTx(&tx)
+ resBroadcastTx, err := BroadcastTx(&tx)
if err != nil {
return nil, err
}
- return &ctypes.ResultBroadcastTx{}, nil
+ return &ctypes.ResultBroadcastTx{
+ Code: resBroadcastTx.CheckTx.Code,
+ Data: resBroadcastTx.CheckTx.Data,
+ Log: resBroadcastTx.CheckTx.Log,
+ Hash: resBroadcastTx.Hash,
+ Codespace: resBroadcastTx.CheckTx.Codespace,
+ }, nil
}
// BroadcastTxAsync would normally broadcast a transaction and return immediately.
@@ -219,13 +225,18 @@ func BroadcastTx(tx *types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
byteTx := []byte(*tx)
- _, _, _, _, err := abci_client.GlobalClient.RunBlock(&byteTx, time.Now(), abci_client.GlobalClient.CurState.LastValidators.Proposer)
+ _, responseCheckTx, responseDeliverTx, _, _, err := abci_client.GlobalClient.RunBlock(&byteTx, time.Now(), abci_client.GlobalClient.CurState.LastValidators.Proposer)
if err != nil {
return nil, err
}
// TODO: fill the return value if necessary
- return &ctypes.ResultBroadcastTxCommit{}, nil
+ return &ctypes.ResultBroadcastTxCommit{
+ CheckTx: *responseCheckTx,
+ DeliverTx: *responseDeliverTx,
+ Height: abci_client.GlobalClient.LastBlock.Height,
+ Hash: tx.Hash(),
+ }, nil
}
func ABCIQuery(
From 49933f8febfc50fa2d786a4b1dda0021c8fd872f Mon Sep 17 00:00:00 2001
From: Philip Offtermatt
Date: Tue, 23 May 2023 18:22:51 +0200
Subject: [PATCH 19/26] Drop comet to v0.34.27-alpha for compatibility
---
README.md | 2 +-
cometmock/abci_client/client.go | 30 ++++++++++++++------------
cometmock/rpc_server/routes.go | 37 +++++++++++++++++----------------
cometmock/utils/blocks.go | 5 +----
go.mod | 10 ++++-----
go.sum | 37 ++++++++++++++++++---------------
6 files changed, 62 insertions(+), 59 deletions(-)
diff --git a/README.md b/README.md
index 7e3722e..f7b5bc6 100644
--- a/README.md
+++ b/README.md
@@ -3,5 +3,5 @@
To run CometMock:
`docker stop simapp; docker rm simapp; docker run --add-host=host.docker.internal:host-gateway --name simapp -p 26658:26658 -ti informalofftermatt/testnet:tendermock simd start --transport=grpc --with-tendermint=false --grpc-only --rpc.laddr=tcp://host.docker.internal:99999`
`docker stop simapp2; docker rm simapp2; docker run --add-host=host.docker.internal:host-gateway --name simapp2 -p 36658:26658 -ti informalofftermatt/testnet:tendermock simd start --transport=grpc --with-tendermint=false --grpc-only --rpc.laddr=tcp://host.docker.internal:99999`
-`go run ./cometmock localhost:36658,localhost:26658 genesis.json tcp://localhost:26657`
+`go run ./cometmock localhost:36658,localhost:26658 genesis.json tcp://localhost:26657 /Users/offtermatt/simapp1,/Users/offtermatt/simapp2`
`curl -H 'Content-Type: application/json' -H 'Accept:application/json' --data '{"jsonrpc":"2.0","method":"commit","params":{},"id":1}' 127.0.0.1:26657`
\ No newline at end of file
diff --git a/cometmock/abci_client/client.go b/cometmock/abci_client/client.go
index dce3b62..669fa74 100644
--- a/cometmock/abci_client/client.go
+++ b/cometmock/abci_client/client.go
@@ -72,7 +72,7 @@ func (a *AbciClient) SendBeginBlock(block *types.Block) (*abcitypes.ResponseBegi
func CreateBeginBlockRequest(header *types.Header, lastCommit *types.Commit) *abcitypes.RequestBeginBlock {
return &abcitypes.RequestBeginBlock{
// TODO: fill in Votes
- LastCommitInfo: abcitypes.CommitInfo{Round: lastCommit.Round, Votes: []abcitypes.VoteInfo{}},
+ LastCommitInfo: abcitypes.LastCommitInfo{Round: lastCommit.Round, Votes: []abcitypes.VoteInfo{}},
Header: *header.ToProto(),
}
}
@@ -111,7 +111,7 @@ func (a *AbciClient) SendInitChain(genesisState state.State, genesisDoc *types.G
}
func CreateInitChainRequest(genesisState state.State, genesisDoc *types.GenesisDoc) *abcitypes.RequestInitChain {
- consensusParams := genesisState.ConsensusParams.ToProto()
+ consensusParams := types.TM2PB.ConsensusParams(&genesisState.ConsensusParams)
genesisValidators := genesisDoc.Validators
@@ -127,7 +127,7 @@ func CreateInitChainRequest(genesisState state.State, genesisDoc *types.GenesisD
InitialHeight: genesisState.InitialHeight,
Time: genesisDoc.GenesisTime,
ChainId: genesisState.ChainID,
- ConsensusParams: &consensusParams,
+ ConsensusParams: consensusParams,
AppStateBytes: genesisDoc.AppState,
}
return &initChainRequest
@@ -153,8 +153,8 @@ func (a *AbciClient) UpdateStateFromInit(res *abcitypes.ResponseInitChain) error
// if response specified consensus params, update the consensus params, otherwise we keep the ones from the genesis file
if res.ConsensusParams != nil {
- a.CurState.ConsensusParams = a.CurState.ConsensusParams.Update(res.ConsensusParams)
- a.CurState.Version.Consensus.App = a.CurState.ConsensusParams.Version.App
+ a.CurState.ConsensusParams = types.UpdateConsensusParams(a.CurState.ConsensusParams, res.ConsensusParams)
+ a.CurState.Version.Consensus.App = a.CurState.ConsensusParams.Version.AppVersion
}
// to conform with RFC-6962
@@ -299,9 +299,13 @@ func (a *AbciClient) RunBlock(tx *[]byte, blockTime time.Time, proposer *types.V
txs = append(txs, *tx)
}
- resCheckTx, err := a.SendCheckTx(tx)
- if err != nil {
- return nil, nil, nil, nil, nil, err
+ var resCheckTx *abcitypes.ResponseCheckTx
+ var err error
+ if tx != nil {
+ resCheckTx, err = a.SendCheckTx(tx)
+ if err != nil {
+ return nil, nil, nil, nil, nil, err
+ }
}
// TODO: handle special case where proposer is nil
@@ -310,7 +314,7 @@ func (a *AbciClient) RunBlock(tx *[]byte, blockTime time.Time, proposer *types.V
proposerAddress = proposer.Address
}
- block := a.CurState.MakeBlock(a.CurState.LastBlockHeight+1, txs, a.LastCommit, []types.Evidence{}, proposerAddress)
+ block, _ := a.CurState.MakeBlock(a.CurState.LastBlockHeight+1, txs, a.LastCommit, []types.Evidence{}, proposerAddress)
// override the block time, since we do not actually get votes from peers to median the time out of
block.Time = blockTime
blockId, err := utils.GetBlockIdFromBlock(block)
@@ -493,13 +497,13 @@ func UpdateState(
lastHeightParamsChanged := curState.LastHeightConsensusParamsChanged
if abciResponses.EndBlock.ConsensusParamUpdates != nil {
// NOTE: must not mutate s.ConsensusParams
- nextParams = curState.ConsensusParams.Update(abciResponses.EndBlock.ConsensusParamUpdates)
- err := nextParams.ValidateBasic()
+ nextParams = types.UpdateConsensusParams(curState.ConsensusParams, abciResponses.EndBlock.ConsensusParamUpdates)
+ err := types.ValidateConsensusParams(nextParams)
if err != nil {
return curState, fmt.Errorf("error updating consensus params: %v", err)
}
- curState.Version.Consensus.App = nextParams.Version.App
+ curState.Version.Consensus.App = nextParams.Version.AppVersion
// Change results from this height but only applies to the next height.
lastHeightParamsChanged = blockHeader.Height + 1
@@ -528,7 +532,7 @@ func UpdateState(
// adapted from https://github.com/cometbft/cometbft/blob/9267594e0a17c01cc4a97b399ada5eaa8a734db5/state/execution.go#L452
func validateValidatorUpdates(
abciUpdates []abcitypes.ValidatorUpdate,
- params types.ValidatorParams,
+ params cmttypes.ValidatorParams,
) error {
for _, valUpdate := range abciUpdates {
if valUpdate.GetPower() < 0 {
diff --git a/cometmock/rpc_server/routes.go b/cometmock/rpc_server/routes.go
index f844a2f..2539188 100644
--- a/cometmock/rpc_server/routes.go
+++ b/cometmock/rpc_server/routes.go
@@ -34,9 +34,9 @@ var Routes = map[string]*rpc.RPCFunc{
"validators": rpc.NewRPCFunc(Validators, "height,page,per_page"),
"block": rpc.NewRPCFunc(Block, "height", rpc.Cacheable("height")),
"consensus_params": rpc.NewRPCFunc(ConsensusParams, "height", rpc.Cacheable("height")),
- "header": rpc.NewRPCFunc(Header, "height", rpc.Cacheable("height")),
- "commit": rpc.NewRPCFunc(Commit, "height", rpc.Cacheable("height")),
- "block_results": rpc.NewRPCFunc(BlockResults, "height", rpc.Cacheable("height")),
+ // "header": rpc.NewRPCFunc(Header, "height", rpc.Cacheable("height")), // not available in 0.34.x
+ "commit": rpc.NewRPCFunc(Commit, "height", rpc.Cacheable("height")),
+ "block_results": rpc.NewRPCFunc(BlockResults, "height", rpc.Cacheable("height")),
// // tx broadcast API
"broadcast_tx_commit": rpc.NewRPCFunc(BroadcastTxCommit, "tx"),
@@ -62,22 +62,22 @@ func getHeight(latestHeight int64, heightPtr *int64) (int64, error) {
return latestHeight, nil
}
-// Header gets block header at a given height.
-// If no height is provided, it will fetch the latest header.
-// More: https://docs.cometbft.com/v0.37/rpc/#/Info/header
-func Header(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultHeader, error) {
- height, err := getHeight(abci_client.GlobalClient.LastBlock.Height, heightPtr)
- if err != nil {
- return nil, err
- }
+// // Header gets block header at a given height.
+// // If no height is provided, it will fetch the latest header.
+// // More: https://docs.cometbft.com/v0.37/rpc/#/Info/header
+// func Header(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultHeader, error) {
+// height, err := getHeight(abci_client.GlobalClient.LastBlock.Height, heightPtr)
+// if err != nil {
+// return nil, err
+// }
- block, err := abci_client.GlobalClient.Storage.GetBlock(height)
- if err != nil {
- return nil, err
- }
+// block, err := abci_client.GlobalClient.Storage.GetBlock(height)
+// if err != nil {
+// return nil, err
+// }
- return &ctypes.ResultHeader{Header: &block.Header}, nil
-}
+// return &ctypes.ResultHeader{Header: &block.Header}, nil
+// }
// Commit gets block commit at a given height.
// If no height is provided, it will fetch the commit for the latest block.
@@ -179,7 +179,8 @@ func BroadcastTxCommit(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadc
abci_client.GlobalClient.Logger.Info(
"BroadcastTxCommit called", "tx", tx)
- return BroadcastTx(&tx)
+ res, err := BroadcastTx(&tx)
+ return res, err
}
// BroadcastTxSync would normally broadcast a transaction and wait until it gets the result from CheckTx.
diff --git a/cometmock/utils/blocks.go b/cometmock/utils/blocks.go
index 6540254..458cc03 100644
--- a/cometmock/utils/blocks.go
+++ b/cometmock/utils/blocks.go
@@ -3,10 +3,7 @@ package utils
import "github.com/cometbft/cometbft/types"
func GetBlockIdFromBlock(block *types.Block) (*types.BlockID, error) {
- partSet, error := block.MakePartSet(2)
- if error != nil {
- return nil, error
- }
+ partSet := block.MakePartSet(2)
partSetHeader := partSet.Header()
blockID := types.BlockID{
diff --git a/go.mod b/go.mod
index 1385c55..aae678c 100644
--- a/go.mod
+++ b/go.mod
@@ -2,7 +2,7 @@ module github.com/p-offtermatt/CometMock
go 1.20
-require github.com/cometbft/cometbft v0.37.1
+require github.com/cometbft/cometbft v0.34.27-alpha.1
require (
github.com/beorn7/perks v1.0.1 // indirect
@@ -10,8 +10,6 @@ require (
github.com/cespare/xxhash v1.1.0 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/cometbft/cometbft-db v0.7.0 // indirect
- github.com/cosmos/gogoproto v1.4.1 // indirect
- github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/dgraph-io/badger/v2 v2.2007.4 // indirect
github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de // indirect
@@ -20,6 +18,7 @@ require (
github.com/go-kit/kit v0.12.0 // indirect
github.com/go-kit/log v0.2.1 // indirect
github.com/go-logfmt/logfmt v0.5.1 // indirect
+ github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/btree v1.1.2 // indirect
@@ -30,15 +29,15 @@ require (
github.com/libp2p/go-buffer-pool v0.1.0 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect
+ github.com/onsi/ginkgo v1.16.4 // indirect
+ github.com/onsi/gomega v1.19.0 // indirect
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect
github.com/pkg/errors v0.9.1 // indirect
- github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.14.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
github.com/sasha-s/go-deadlock v0.3.1 // indirect
- github.com/stretchr/testify v1.8.1 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect
github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c // indirect
go.etcd.io/bbolt v1.3.6 // indirect
@@ -49,5 +48,4 @@ require (
google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 // indirect
google.golang.org/grpc v1.52.0 // indirect
google.golang.org/protobuf v1.28.2-0.20220831092852-f930b1dc76e8 // indirect
- gopkg.in/yaml.v3 v3.0.1 // indirect
)
diff --git a/go.sum b/go.sum
index 76de9d0..7476198 100644
--- a/go.sum
+++ b/go.sum
@@ -62,16 +62,14 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
-github.com/cometbft/cometbft v0.37.1 h1:KLxkQTK2hICXYq21U2hn1W5hOVYUdQgDQ1uB+90xPIg=
-github.com/cometbft/cometbft v0.37.1/go.mod h1:Y2MMMN//O5K4YKd8ze4r9jmk4Y7h0ajqILXbH5JQFVs=
+github.com/cometbft/cometbft v0.34.27-alpha.1 h1:GvmlB422TYhj+6a/A7VV33iRxp/F2IT3aiqExc3s2Ew=
+github.com/cometbft/cometbft v0.34.27-alpha.1/go.mod h1:hct3hasQ2hIF3HoD7foVw4RaqTNSSeJ/lgcrVK6uDvs=
github.com/cometbft/cometbft-db v0.7.0 h1:uBjbrBx4QzU0zOEnU8KxoDl18dMNgDh+zZRUE0ucsbo=
github.com/cometbft/cometbft-db v0.7.0/go.mod h1:yiKJIm2WKrt6x8Cyxtq9YTEcIMPcEe4XPxhgX59Fzf0=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d h1:49RLWk1j44Xu4fjHb6JFYmeUnDORVwHNkDxaQ0ctCVU=
-github.com/cosmos/gogoproto v1.4.1 h1:WoyH+0/jbCTzpKNvyav5FL1ZTWsp1im1MxEpJEzKUB8=
-github.com/cosmos/gogoproto v1.4.1/go.mod h1:Ac9lzL4vFpBMcptJROQ6dQ4M3pOEK5Z/l0Q9p+LoCr4=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@@ -116,7 +114,10 @@ github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG
github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA=
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -197,6 +198,7 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
+github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c=
@@ -204,10 +206,8 @@ github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrD
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
-github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8=
github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg=
@@ -227,15 +227,18 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
-github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
+github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
+github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
+github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
-github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
+github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw=
+github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ=
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o=
@@ -289,16 +292,12 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
-github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
-github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
-github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c h1:g+WoO5jjkqGAzHWCjJB1zZfXPIAaDpzXIEJ0eS6B5Ok=
@@ -308,6 +307,7 @@ github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
@@ -385,6 +385,7 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
@@ -405,6 +406,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -446,7 +448,9 @@ golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -506,13 +510,15 @@ golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roY
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
@@ -598,7 +604,6 @@ google.golang.org/protobuf v1.28.2-0.20220831092852-f930b1dc76e8/go.mod h1:HV8QO
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
@@ -611,9 +616,7 @@ gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
-gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
-gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
From c20dc7e7e19260a1c45598de6b70883ee555aa0f Mon Sep 17 00:00:00 2001
From: Philip Offtermatt
Date: Thu, 25 May 2023 09:39:25 +0200
Subject: [PATCH 20/26] Add indexers
---
cometmock/abci_client/client.go | 12 +-
cometmock/main.go | 23 ++++
cometmock/rpc_server/routes.go | 193 ++++++++++++++++++++++++++++++++
go.mod | 7 +-
go.sum | 2 +
5 files changed, 233 insertions(+), 4 deletions(-)
diff --git a/cometmock/abci_client/client.go b/cometmock/abci_client/client.go
index 669fa74..1905cd5 100644
--- a/cometmock/abci_client/client.go
+++ b/cometmock/abci_client/client.go
@@ -14,6 +14,9 @@ import (
cmtstate "github.com/cometbft/cometbft/proto/tendermint/state"
cmttypes "github.com/cometbft/cometbft/proto/tendermint/types"
"github.com/cometbft/cometbft/state"
+ blockindexkv "github.com/cometbft/cometbft/state/indexer/block/kv"
+ "github.com/cometbft/cometbft/state/txindex"
+ indexerkv "github.com/cometbft/cometbft/state/txindex/kv"
"github.com/cometbft/cometbft/types"
"github.com/p-offtermatt/CometMock/cometmock/storage"
"github.com/p-offtermatt/CometMock/cometmock/utils"
@@ -35,6 +38,9 @@ type AbciClient struct {
LastCommit *types.Commit
Storage storage.Storage
PrivValidators []types.PrivValidator
+ IndexerService *txindex.IndexerService
+ TxIndex *indexerkv.TxIndex
+ BlockIndex *blockindexkv.BlockerIndexer
// if this is true, then an error will be returned if the responses from the clients are not all equal.
// can be used to check for nondeterminism in apps, but also slows down execution a bit,
@@ -572,12 +578,14 @@ func fireEvents(
logger.Error("failed publishing new block", "err", err)
}
- if err := eventBus.PublishEventNewBlockHeader(types.EventDataNewBlockHeader{
+ eventDataNewBlockHeader := types.EventDataNewBlockHeader{
Header: block.Header,
NumTxs: int64(len(block.Txs)),
ResultBeginBlock: *abciResponses.BeginBlock,
ResultEndBlock: *abciResponses.EndBlock,
- }); err != nil {
+ }
+
+ if err := eventBus.PublishEventNewBlockHeader(eventDataNewBlockHeader); err != nil {
logger.Error("failed publishing new block header", "err", err)
}
diff --git a/cometmock/main.go b/cometmock/main.go
index 0b7ca40..f9f217b 100644
--- a/cometmock/main.go
+++ b/cometmock/main.go
@@ -5,10 +5,14 @@ import (
"strings"
"time"
+ db "github.com/cometbft/cometbft-db"
comet_abciclient "github.com/cometbft/cometbft/abci/client"
cometlog "github.com/cometbft/cometbft/libs/log"
"github.com/cometbft/cometbft/privval"
"github.com/cometbft/cometbft/state"
+ blockindexkv "github.com/cometbft/cometbft/state/indexer/block/kv"
+ "github.com/cometbft/cometbft/state/txindex"
+ indexerkv "github.com/cometbft/cometbft/state/txindex/kv"
"github.com/cometbft/cometbft/types"
"github.com/p-offtermatt/CometMock/cometmock/abci_client"
"github.com/p-offtermatt/CometMock/cometmock/rpc_server"
@@ -41,6 +45,16 @@ func GetMockPVsFromNodeHomes(nodeHomes []string) []types.PrivValidator {
return mockPVs
}
+func CreateAndStartIndexerService(eventBus *types.EventBus, logger cometlog.Logger) (*txindex.IndexerService, *indexerkv.TxIndex, *blockindexkv.BlockerIndexer, error) {
+ txIndexer := indexerkv.NewTxIndex(db.NewMemDB())
+ blockIndexer := blockindexkv.New(db.NewMemDB())
+
+ indexerService := txindex.NewIndexerService(txIndexer, blockIndexer, eventBus, false)
+ indexerService.SetLogger(logger.With("module", "txindex"))
+
+ return indexerService, txIndexer, blockIndexer, indexerService.Start()
+}
+
func main() {
logger := cometlog.NewTMLogger(cometlog.NewSyncWriter(os.Stdout))
@@ -87,6 +101,12 @@ func main() {
panic(err)
}
+ indexerService, txIndex, blockIndex, err := CreateAndStartIndexerService(eventBus, logger)
+ if err != nil {
+ logger.Error(err.Error())
+ panic(err)
+ }
+
abci_client.GlobalClient = &abci_client.AbciClient{
Clients: clients,
Logger: logger,
@@ -96,6 +116,9 @@ func main() {
LastCommit: &types.Commit{},
Storage: &storage.MapStorage{},
PrivValidators: privVals,
+ IndexerService: indexerService,
+ TxIndex: txIndex,
+ BlockIndex: blockIndex,
}
// initialize chain
diff --git a/cometmock/rpc_server/routes.go b/cometmock/rpc_server/routes.go
index 2539188..d62c74f 100644
--- a/cometmock/rpc_server/routes.go
+++ b/cometmock/rpc_server/routes.go
@@ -1,11 +1,14 @@
package rpc_server
import (
+ "errors"
"fmt"
+ "sort"
"time"
"github.com/cometbft/cometbft/libs/bytes"
cmtmath "github.com/cometbft/cometbft/libs/math"
+ cmtquery "github.com/cometbft/cometbft/libs/pubsub/query"
"github.com/cometbft/cometbft/p2p"
cometp2p "github.com/cometbft/cometbft/p2p"
ctypes "github.com/cometbft/cometbft/rpc/core/types"
@@ -37,6 +40,9 @@ var Routes = map[string]*rpc.RPCFunc{
// "header": rpc.NewRPCFunc(Header, "height", rpc.Cacheable("height")), // not available in 0.34.x
"commit": rpc.NewRPCFunc(Commit, "height", rpc.Cacheable("height")),
"block_results": rpc.NewRPCFunc(BlockResults, "height", rpc.Cacheable("height")),
+ "tx": rpc.NewRPCFunc(Tx, "hash,prove", rpc.Cacheable()),
+ "tx_search": rpc.NewRPCFunc(TxSearch, "query,prove,page,per_page,order_by"),
+ "block_search": rpc.NewRPCFunc(BlockSearch, "query,page,per_page,order_by"),
// // tx broadcast API
"broadcast_tx_commit": rpc.NewRPCFunc(BroadcastTxCommit, "tx"),
@@ -47,6 +53,193 @@ var Routes = map[string]*rpc.RPCFunc{
"abci_query": rpc.NewRPCFunc(ABCIQuery, "path,data,height,prove"),
}
+// BlockSearch searches for a paginated set of blocks matching BeginBlock and
+// EndBlock event search criteria.
+func BlockSearch(
+ ctx *rpctypes.Context,
+ query string,
+ pagePtr, perPagePtr *int,
+ orderBy string,
+) (*ctypes.ResultBlockSearch, error) {
+ q, err := cmtquery.New(query)
+ if err != nil {
+ return nil, err
+ }
+
+ results, err := abci_client.GlobalClient.BlockIndex.Search(ctx.Context(), q)
+ if err != nil {
+ return nil, err
+ }
+
+ // sort results (must be done before pagination)
+ switch orderBy {
+ case "desc", "":
+ sort.Slice(results, func(i, j int) bool { return results[i] > results[j] })
+
+ case "asc":
+ sort.Slice(results, func(i, j int) bool { return results[i] < results[j] })
+
+ default:
+ return nil, errors.New("expected order_by to be either `asc` or `desc` or empty")
+ }
+
+ // paginate results
+ totalCount := len(results)
+ perPage := validatePerPage(perPagePtr)
+
+ page, err := validatePage(pagePtr, perPage, totalCount)
+ if err != nil {
+ return nil, err
+ }
+
+ skipCount := validateSkipCount(page, perPage)
+ pageSize := cmtmath.MinInt(perPage, totalCount-skipCount)
+
+ apiResults := make([]*ctypes.ResultBlock, 0, pageSize)
+ for i := skipCount; i < skipCount+pageSize; i++ {
+ block, err := abci_client.GlobalClient.Storage.GetBlock(results[i])
+ if err != nil {
+ return nil, err
+ }
+ if block != nil {
+ if err != nil {
+ return nil, err
+ }
+ blockId, err := utils.GetBlockIdFromBlock(block)
+ if err != nil {
+ return nil, err
+ }
+
+ apiResults = append(apiResults, &ctypes.ResultBlock{
+ Block: block,
+ BlockID: *blockId,
+ })
+ }
+ }
+
+ return &ctypes.ResultBlockSearch{Blocks: apiResults, TotalCount: totalCount}, nil
+}
+
+// Tx allows you to query the transaction results. `nil` could mean the
+// transaction is in the mempool, invalidated, or was not sent in the first
+// place.
+// More: https://docs.tendermint.com/v0.34/rpc/#/Info/tx
+func Tx(ctx *rpctypes.Context, hash []byte, prove bool) (*ctypes.ResultTx, error) {
+ txIndexer := abci_client.GlobalClient.TxIndex
+
+ r, err := txIndexer.Get(hash)
+ if err != nil {
+ return nil, err
+ }
+
+ if r == nil {
+ return nil, fmt.Errorf("tx (%X) not found", hash)
+ }
+
+ height := r.Height
+ index := r.Index
+
+ var proof types.TxProof
+ if prove {
+ block, err := abci_client.GlobalClient.Storage.GetBlock(height)
+ if err != nil {
+ return nil, err
+ }
+ proof = block.Data.Txs.Proof(int(index)) // XXX: overflow on 32-bit machines
+ }
+
+ return &ctypes.ResultTx{
+ Hash: hash,
+ Height: height,
+ Index: index,
+ TxResult: r.Result,
+ Tx: r.Tx,
+ Proof: proof,
+ }, nil
+}
+
+// TxSearch allows you to query for multiple transactions results. It returns a
+// list of transactions (maximum ?per_page entries) and the total count.
+// More: https://docs.tendermint.com/v0.34/rpc/#/Info/tx_search
+func TxSearch(
+ ctx *rpctypes.Context,
+ query string,
+ prove bool,
+ pagePtr, perPagePtr *int,
+ orderBy string,
+) (*ctypes.ResultTxSearch, error) {
+ if len(query) > maxQueryLength {
+ return nil, errors.New("maximum query length exceeded")
+ }
+
+ q, err := cmtquery.New(query)
+ if err != nil {
+ return nil, err
+ }
+
+ results, err := abci_client.GlobalClient.TxIndex.Search(ctx.Context(), q)
+ if err != nil {
+ return nil, err
+ }
+
+ // sort results (must be done before pagination)
+ switch orderBy {
+ case "desc":
+ sort.Slice(results, func(i, j int) bool {
+ if results[i].Height == results[j].Height {
+ return results[i].Index > results[j].Index
+ }
+ return results[i].Height > results[j].Height
+ })
+ case "asc", "":
+ sort.Slice(results, func(i, j int) bool {
+ if results[i].Height == results[j].Height {
+ return results[i].Index < results[j].Index
+ }
+ return results[i].Height < results[j].Height
+ })
+ default:
+ return nil, errors.New("expected order_by to be either `asc` or `desc` or empty")
+ }
+
+ // paginate results
+ totalCount := len(results)
+ perPage := validatePerPage(perPagePtr)
+
+ page, err := validatePage(pagePtr, perPage, totalCount)
+ if err != nil {
+ return nil, err
+ }
+
+ skipCount := validateSkipCount(page, perPage)
+ pageSize := cmtmath.MinInt(perPage, totalCount-skipCount)
+
+ apiResults := make([]*ctypes.ResultTx, 0, pageSize)
+ for i := skipCount; i < skipCount+pageSize; i++ {
+ r := results[i]
+
+ var proof types.TxProof
+ if prove {
+ block, err := abci_client.GlobalClient.Storage.GetBlock(r.Height)
+ if err != nil {
+ return nil, err
+ }
+ proof = block.Data.Txs.Proof(int(r.Index)) // XXX: overflow on 32-bit machines
+ }
+
+ apiResults = append(apiResults, &ctypes.ResultTx{
+ Hash: types.Tx(r.Tx).Hash(),
+ Height: r.Height,
+ Index: r.Index,
+ TxResult: r.Result,
+ Tx: r.Tx,
+ Proof: proof,
+ })
+ }
+
+ return &ctypes.ResultTxSearch{Txs: apiResults, TotalCount: totalCount}, nil
+}
+
func getHeight(latestHeight int64, heightPtr *int64) (int64, error) {
if heightPtr != nil {
height := *heightPtr
diff --git a/go.mod b/go.mod
index aae678c..49a5b73 100644
--- a/go.mod
+++ b/go.mod
@@ -2,14 +2,16 @@ module github.com/p-offtermatt/CometMock
go 1.20
-require github.com/cometbft/cometbft v0.34.27-alpha.1
+require (
+ github.com/cometbft/cometbft v0.34.27-alpha.1
+ github.com/cometbft/cometbft-db v0.7.0
+)
require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.2.1 // indirect
github.com/cespare/xxhash v1.1.0 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
- github.com/cometbft/cometbft-db v0.7.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/dgraph-io/badger/v2 v2.2007.4 // indirect
github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de // indirect
@@ -22,6 +24,7 @@ require (
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/btree v1.1.2 // indirect
+ github.com/google/orderedcode v0.0.1 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/gtank/merlin v0.1.1 // indirect
github.com/jmhodges/levigo v1.0.0 // indirect
diff --git a/go.sum b/go.sum
index 7476198..f2560b7 100644
--- a/go.sum
+++ b/go.sum
@@ -166,6 +166,8 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
+github.com/google/orderedcode v0.0.1 h1:UzfcAexk9Vhv8+9pNOgRu41f16lHq725vPwnSeiG/Us=
+github.com/google/orderedcode v0.0.1/go.mod h1:iVyU4/qPKHY5h/wSd6rZZCDcLJNxiWO6dvsYES2Sb20=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
From 0d26b164580cf729627a6b0d0f1fbb05fa2d4e59 Mon Sep 17 00:00:00 2001
From: Philip Offtermatt
Date: Thu, 25 May 2023 14:23:13 +0200
Subject: [PATCH 21/26] Fix signing
---
cometmock/abci_client/client.go | 41 ++++++++++++++++++++++-----------
cometmock/main.go | 20 ++++++++++++++--
2 files changed, 45 insertions(+), 16 deletions(-)
diff --git a/cometmock/abci_client/client.go b/cometmock/abci_client/client.go
index 1905cd5..12dabe1 100644
--- a/cometmock/abci_client/client.go
+++ b/cometmock/abci_client/client.go
@@ -11,6 +11,7 @@ import (
cryptoenc "github.com/cometbft/cometbft/crypto/encoding"
"github.com/cometbft/cometbft/crypto/merkle"
cometlog "github.com/cometbft/cometbft/libs/log"
+ cmtmath "github.com/cometbft/cometbft/libs/math"
cmtstate "github.com/cometbft/cometbft/proto/tendermint/state"
cmttypes "github.com/cometbft/cometbft/proto/tendermint/types"
"github.com/cometbft/cometbft/state"
@@ -37,7 +38,7 @@ type AbciClient struct {
LastBlock *types.Block
LastCommit *types.Commit
Storage storage.Storage
- PrivValidators []types.PrivValidator
+ PrivValidators map[string]types.PrivValidator
IndexerService *txindex.IndexerService
TxIndex *indexerkv.TxIndex
BlockIndex *blockindexkv.BlockerIndexer
@@ -375,23 +376,30 @@ func (a *AbciClient) RunBlock(tx *[]byte, blockTime time.Time, proposer *types.V
commitSigs := []types.CommitSig{}
- precommitType := cmttypes.SignedMsgType_value["SIGNED_MSG_TYPE_PRECOMMIT"]
- for index, pv := range a.PrivValidators {
+ for index, val := range a.CurState.Validators.Validators {
+ privVal := a.PrivValidators[val.Address.String()]
+
// create and sign a precommit
- vote, err := utils.MakeVote(
- pv,
- a.CurState.ChainID,
- int32(index),
- block.Height,
- 2, // round to consensus - can be arbitrary
- int(precommitType), // for which step the vote is - we use precommit
- *blockId,
- time.Now(),
- )
+ vote := &cmttypes.Vote{
+ ValidatorAddress: val.Address,
+ ValidatorIndex: int32(index),
+ Height: a.LastBlock.Height,
+ Round: 1,
+ Timestamp: time.Now(),
+ Type: cmttypes.PrecommitType,
+ BlockID: blockId.ToProto(),
+ }
+
+ privVal.SignVote(a.CurState.ChainID, vote)
+
+ convertedVote, err := types.VoteFromProto(vote)
if err != nil {
return nil, nil, nil, nil, nil, err
}
- commitSigs = append(commitSigs, vote.CommitSig())
+
+ commitSig := convertedVote.CommitSig()
+
+ commitSigs = append(commitSigs, commitSig)
}
a.LastCommit = types.NewCommit(
@@ -401,6 +409,11 @@ func (a *AbciClient) RunBlock(tx *[]byte, blockTime time.Time, proposer *types.V
commitSigs,
)
+ err = a.CurState.Validators.VerifyCommitLightTrusting(a.CurState.ChainID, a.LastCommit, cmtmath.Fraction{Numerator: 1, Denominator: 3})
+ if err != nil {
+ return nil, nil, nil, nil, nil, err
+ }
+
err = a.Storage.InsertBlock(newHeight, block)
if err != nil {
return nil, nil, nil, nil, nil, err
diff --git a/cometmock/main.go b/cometmock/main.go
index f9f217b..2216869 100644
--- a/cometmock/main.go
+++ b/cometmock/main.go
@@ -107,6 +107,18 @@ func main() {
panic(err)
}
+ privValsMap := make(map[string]types.PrivValidator)
+ for _, privVal := range privVals {
+ pubkey, err := privVal.GetPubKey()
+ if err != nil {
+ logger.Error(err.Error())
+ panic(err)
+ }
+ addr := pubkey.Address()
+
+ privValsMap[addr.String()] = privVal
+ }
+
abci_client.GlobalClient = &abci_client.AbciClient{
Clients: clients,
Logger: logger,
@@ -115,7 +127,7 @@ func main() {
EventBus: *eventBus,
LastCommit: &types.Commit{},
Storage: &storage.MapStorage{},
- PrivValidators: privVals,
+ PrivValidators: privValsMap,
IndexerService: indexerService,
TxIndex: txIndex,
BlockIndex: blockIndex,
@@ -129,7 +141,11 @@ func main() {
}
// run an empty block
- abci_client.GlobalClient.RunBlock(nil, time.Now(), abci_client.GlobalClient.CurState.LastValidators.Proposer)
+ _, _, _, _, _, err = abci_client.GlobalClient.RunBlock(nil, time.Now(), abci_client.GlobalClient.CurState.LastValidators.Proposer)
+ if err != nil {
+ logger.Error(err.Error())
+ panic(err)
+ }
go rpc_server.StartRPCServerWithDefaultConfig(cometMockListenAddress, logger)
From 0af84efcd372fea5a7cbbc5275fd1dd2f3df191b Mon Sep 17 00:00:00 2001
From: Philip Offtermatt
Date: Thu, 25 May 2023 14:26:50 +0200
Subject: [PATCH 22/26] Add comment about commit verification
---
cometmock/abci_client/client.go | 1 +
1 file changed, 1 insertion(+)
diff --git a/cometmock/abci_client/client.go b/cometmock/abci_client/client.go
index 12dabe1..c7d7853 100644
--- a/cometmock/abci_client/client.go
+++ b/cometmock/abci_client/client.go
@@ -409,6 +409,7 @@ func (a *AbciClient) RunBlock(tx *[]byte, blockTime time.Time, proposer *types.V
commitSigs,
)
+ // sanity check that the commit is signed correctly
err = a.CurState.Validators.VerifyCommitLightTrusting(a.CurState.ChainID, a.LastCommit, cmtmath.Fraction{Numerator: 1, Denominator: 3})
if err != nil {
return nil, nil, nil, nil, nil, err
From b292a364e8ce9be0813aff3ba6563cce4327e998 Mon Sep 17 00:00:00 2001
From: Philip Offtermatt
Date: Wed, 31 May 2023 15:07:03 +0200
Subject: [PATCH 23/26] Add light block verification
---
cometmock/abci_client/client.go | 14 ++++++++++++++
cometmock/rpc_server/routes.go | 3 +++
2 files changed, 17 insertions(+)
diff --git a/cometmock/abci_client/client.go b/cometmock/abci_client/client.go
index c7d7853..91e2516 100644
--- a/cometmock/abci_client/client.go
+++ b/cometmock/abci_client/client.go
@@ -415,6 +415,20 @@ func (a *AbciClient) RunBlock(tx *[]byte, blockTime time.Time, proposer *types.V
return nil, nil, nil, nil, nil, err
}
+ // sanity check that the commit makes a proper light block
+ signedHeader := types.SignedHeader{
+ Header: &block.Header,
+ Commit: a.LastCommit,
+ }
+
+ lightBlock := types.LightBlock{
+ SignedHeader: &signedHeader,
+ ValidatorSet: a.CurState.Validators,
+ }
+
+ lightBlock.ValidateBasic(a.CurState.ChainID)
+
+ // insert entries into the storage
err = a.Storage.InsertBlock(newHeight, block)
if err != nil {
return nil, nil, nil, nil, nil, err
diff --git a/cometmock/rpc_server/routes.go b/cometmock/rpc_server/routes.go
index d62c74f..83beed3 100644
--- a/cometmock/rpc_server/routes.go
+++ b/cometmock/rpc_server/routes.go
@@ -444,6 +444,9 @@ func ABCIQuery(
"ABCIQuery called", "path", "data", "height", "prove", path, data, height, prove)
response, err := abci_client.GlobalClient.SendAbciQuery(data, path, height, prove)
+
+ abci_client.GlobalClient.Logger.Info(
+ "Response to ABCI query", response.String())
return &ctypes.ResultABCIQuery{Response: *response}, err
}
From 4eea082206ba1db7cad3eaaba04867a632bf4fed Mon Sep 17 00:00:00 2001
From: Philip Offtermatt
Date: Wed, 31 May 2023 15:48:02 +0200
Subject: [PATCH 24/26] Put block validation before abci calls
---
cometmock/abci_client/client.go | 100 +++++++++++++++++---------------
1 file changed, 52 insertions(+), 48 deletions(-)
diff --git a/cometmock/abci_client/client.go b/cometmock/abci_client/client.go
index 91e2516..918d800 100644
--- a/cometmock/abci_client/client.go
+++ b/cometmock/abci_client/client.go
@@ -329,49 +329,6 @@ func (a *AbciClient) RunBlock(tx *[]byte, blockTime time.Time, proposer *types.V
return nil, nil, nil, nil, nil, err
}
- resBeginBlock, err := a.SendBeginBlock(block)
- if err != nil {
- return nil, nil, nil, nil, nil, err
- }
-
- var resDeliverTx *abcitypes.ResponseDeliverTx
- if tx != nil {
- resDeliverTx, err = a.SendDeliverTx(tx)
- if err != nil {
- return nil, nil, nil, nil, nil, err
- }
- } else {
- resDeliverTx = nil
- }
-
- resEndBlock, err := a.SendEndBlock()
- if err != nil {
- return nil, nil, nil, nil, nil, err
- }
-
- resCommit, err := a.SendCommit()
- if err != nil {
- return nil, nil, nil, nil, nil, err
- }
-
- deliverTxResponses := []*abcitypes.ResponseDeliverTx{}
- if tx != nil {
- deliverTxResponses = append(deliverTxResponses, resDeliverTx)
- }
-
- // build components of the state update, then call the update function
- abciResponses := cmtstate.ABCIResponses{
- DeliverTxs: deliverTxResponses,
- EndBlock: resEndBlock,
- BeginBlock: resBeginBlock,
- }
-
- // updates state as a side effect. returns an error if the state update fails
- err = a.UpdateStateFromBlock(blockId, block, abciResponses, resCommit)
- if err != nil {
- return nil, nil, nil, nil, nil, err
- }
-
a.LastBlock = block
commitSigs := []types.CommitSig{}
@@ -426,7 +383,56 @@ func (a *AbciClient) RunBlock(tx *[]byte, blockTime time.Time, proposer *types.V
ValidatorSet: a.CurState.Validators,
}
- lightBlock.ValidateBasic(a.CurState.ChainID)
+ err = lightBlock.ValidateBasic(a.CurState.ChainID)
+ if err != nil {
+ a.Logger.Error("Light block validation failed", "err", err)
+ return nil, nil, nil, nil, nil, err
+ }
+
+ resBeginBlock, err := a.SendBeginBlock(block)
+ if err != nil {
+ return nil, nil, nil, nil, nil, err
+ }
+
+ var resDeliverTx *abcitypes.ResponseDeliverTx
+ if tx != nil {
+ resDeliverTx, err = a.SendDeliverTx(tx)
+ if err != nil {
+ return nil, nil, nil, nil, nil, err
+ }
+ } else {
+ resDeliverTx = nil
+ }
+
+ resEndBlock, err := a.SendEndBlock()
+ if err != nil {
+ return nil, nil, nil, nil, nil, err
+ }
+
+ deliverTxResponses := []*abcitypes.ResponseDeliverTx{}
+ if tx != nil {
+ deliverTxResponses = append(deliverTxResponses, resDeliverTx)
+ }
+
+ // build components of the state update, then call the update function
+ abciResponses := cmtstate.ABCIResponses{
+ DeliverTxs: deliverTxResponses,
+ EndBlock: resEndBlock,
+ BeginBlock: resBeginBlock,
+ }
+
+ // updates state as a side effect. returns an error if the state update fails
+ err = a.UpdateStateFromBlock(blockId, block, abciResponses)
+ if err != nil {
+ return nil, nil, nil, nil, nil, err
+ }
+
+ resCommit, err := a.SendCommit()
+ if err != nil {
+ return nil, nil, nil, nil, nil, err
+ }
+
+ a.CurState.AppHash = resCommit.Data
// insert entries into the storage
err = a.Storage.InsertBlock(newHeight, block)
@@ -464,7 +470,6 @@ func (a *AbciClient) UpdateStateFromBlock(
blockId *types.BlockID,
block *types.Block,
abciResponses cmtstate.ABCIResponses,
- commitResponse *abcitypes.ResponseCommit,
) error {
// build components of the state update, then call the update function
abciValidatorUpdates := abciResponses.EndBlock.ValidatorUpdates
@@ -483,7 +488,6 @@ func (a *AbciClient) UpdateStateFromBlock(
blockId,
&block.Header,
&abciResponses,
- commitResponse,
validatorUpdates,
)
if err != nil {
@@ -505,7 +509,6 @@ func UpdateState(
blockId *types.BlockID,
blockHeader *types.Header,
abciResponses *cmtstate.ABCIResponses,
- commitResponse *abcitypes.ResponseCommit,
validatorUpdates []*types.Validator,
) (state.State, error) {
// Copy the valset so we can apply changes from EndBlock
@@ -559,7 +562,8 @@ func UpdateState(
ConsensusParams: nextParams,
LastHeightConsensusParamsChanged: lastHeightParamsChanged,
LastResultsHash: state.ABCIResponsesResultsHash(abciResponses),
- AppHash: commitResponse.Data,
+ // app hash will be populated after commit
+ AppHash: nil,
}, nil
}
From 151c50c00a64d370fd3213cd82453f53ab6d493b Mon Sep 17 00:00:00 2001
From: Philip Offtermatt
Date: Wed, 31 May 2023 15:48:52 +0200
Subject: [PATCH 25/26] Use block instead of a.LastBlock to be clearer
---
cometmock/abci_client/client.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cometmock/abci_client/client.go b/cometmock/abci_client/client.go
index 918d800..f8ff586 100644
--- a/cometmock/abci_client/client.go
+++ b/cometmock/abci_client/client.go
@@ -340,7 +340,7 @@ func (a *AbciClient) RunBlock(tx *[]byte, blockTime time.Time, proposer *types.V
vote := &cmttypes.Vote{
ValidatorAddress: val.Address,
ValidatorIndex: int32(index),
- Height: a.LastBlock.Height,
+ Height: block.Height,
Round: 1,
Timestamp: time.Now(),
Type: cmttypes.PrecommitType,
From a5ddcee2faddfec0f02f8fc7b854f7ed48dea573 Mon Sep 17 00:00:00 2001
From: Philip Offtermatt
Date: Thu, 1 Jun 2023 10:45:57 +0200
Subject: [PATCH 26/26] Fix storing right state for right height
---
cometmock/abci_client/client.go | 52 ++++++++++++++++++++-------------
1 file changed, 32 insertions(+), 20 deletions(-)
diff --git a/cometmock/abci_client/client.go b/cometmock/abci_client/client.go
index f8ff586..8261162 100644
--- a/cometmock/abci_client/client.go
+++ b/cometmock/abci_client/client.go
@@ -28,6 +28,8 @@ var GlobalClient *AbciClient
// store a mutex that allows only running one block at a time
var blockMutex = sync.Mutex{}
+var verbose = false
+
// AbciClient facilitates calls to the ABCI interface of multiple nodes.
// It also tracks the current state and a common logger.
type AbciClient struct {
@@ -50,7 +52,9 @@ type AbciClient struct {
}
func (a *AbciClient) SendBeginBlock(block *types.Block) (*abcitypes.ResponseBeginBlock, error) {
- a.Logger.Info("Sending BeginBlock to clients")
+ if verbose {
+ a.Logger.Info("Sending BeginBlock to clients")
+ }
// build the BeginBlock request
beginBlockRequest := CreateBeginBlockRequest(&block.Header, block.LastCommit)
@@ -85,7 +89,9 @@ func CreateBeginBlockRequest(header *types.Header, lastCommit *types.Commit) *ab
}
func (a *AbciClient) SendInitChain(genesisState state.State, genesisDoc *types.GenesisDoc) error {
- a.Logger.Info("Sending InitChain to clients")
+ if verbose {
+ a.Logger.Info("Sending InitChain to clients")
+ }
// build the InitChain request
initChainRequest := CreateInitChainRequest(genesisState, genesisDoc)
@@ -171,7 +177,9 @@ func (a *AbciClient) UpdateStateFromInit(res *abcitypes.ResponseInitChain) error
}
func (a *AbciClient) SendEndBlock() (*abcitypes.ResponseEndBlock, error) {
- a.Logger.Info("Sending EndBlock to clients")
+ if verbose {
+ a.Logger.Info("Sending EndBlock to clients")
+ }
// build the EndBlock request
endBlockRequest := abcitypes.RequestEndBlock{
Height: a.CurState.LastBlockHeight + 1,
@@ -297,7 +305,9 @@ func (a *AbciClient) RunBlock(tx *[]byte, blockTime time.Time, proposer *types.V
blockMutex.Lock()
a.Logger.Info("Running block")
- a.Logger.Info("State at start of block", "state", a.CurState)
+ if verbose {
+ a.Logger.Info("State at start of block", "state", a.CurState)
+ }
newHeight := a.CurState.LastBlockHeight + 1
@@ -414,46 +424,48 @@ func (a *AbciClient) RunBlock(tx *[]byte, blockTime time.Time, proposer *types.V
deliverTxResponses = append(deliverTxResponses, resDeliverTx)
}
- // build components of the state update, then call the update function
- abciResponses := cmtstate.ABCIResponses{
- DeliverTxs: deliverTxResponses,
- EndBlock: resEndBlock,
- BeginBlock: resBeginBlock,
- }
-
- // updates state as a side effect. returns an error if the state update fails
- err = a.UpdateStateFromBlock(blockId, block, abciResponses)
+ // insert entries into the storage
+ err = a.Storage.InsertBlock(newHeight, block)
if err != nil {
return nil, nil, nil, nil, nil, err
}
- resCommit, err := a.SendCommit()
+ err = a.Storage.InsertCommit(newHeight, a.LastCommit)
if err != nil {
return nil, nil, nil, nil, nil, err
}
- a.CurState.AppHash = resCommit.Data
+ // copy state so that the historical state is not mutated
+ state := a.CurState.Copy()
- // insert entries into the storage
- err = a.Storage.InsertBlock(newHeight, block)
+ err = a.Storage.InsertState(newHeight, &state)
if err != nil {
return nil, nil, nil, nil, nil, err
}
- err = a.Storage.InsertCommit(newHeight, a.LastCommit)
+ // build components of the state update, then call the update function
+ abciResponses := cmtstate.ABCIResponses{
+ DeliverTxs: deliverTxResponses,
+ EndBlock: resEndBlock,
+ BeginBlock: resBeginBlock,
+ }
+
+ err = a.Storage.InsertResponses(newHeight, &abciResponses)
if err != nil {
return nil, nil, nil, nil, nil, err
}
- err = a.Storage.InsertState(newHeight, &a.CurState)
+ // updates state as a side effect. returns an error if the state update fails
+ err = a.UpdateStateFromBlock(blockId, block, abciResponses)
if err != nil {
return nil, nil, nil, nil, nil, err
}
- err = a.Storage.InsertResponses(newHeight, &abciResponses)
+ resCommit, err := a.SendCommit()
if err != nil {
return nil, nil, nil, nil, nil, err
}
+ a.CurState.AppHash = resCommit.Data
// unlock mutex
blockMutex.Unlock()