all: implement milestones (#961)

* Milestone Implementation

* Merge branch 'POS-347' into reciept-e2e-test

* Changes for testing, will be removed after testing

* Debugged the error

* Just for testing purpose

* refactor debug api methods, rename whitelist -> checkpoint

* remove first iteration based vars

* fix linters

* Rewind Changes

* Error changes

* RewindBack function in bor_checkpoint_verifier

* Testcases

* Added the fetch test for milestone and checkpoint

* Debugged the lint changes

* Debugged the lint changes

* Debugged the lint changes

* Debugged the lint changes

* Improved the error in Miner test file

* Improved the error of pointing to the wrong function

* Locking the sprint after the vote has been made on it.

* Adding more logs for testing

* Adding more logs for testing

* Adding more logs for testing

* Implemented the NoAckMilestone fetching mechanism

* Testcases for milestone implementation

* Testing code  for fetchNoAckMilestone and fetchLastNoAckMilestone

* Testing changes

* refactor else-if

* Corrected the number of params in bor_ext.go

* Dummy API for testing

* Defined the GetVoteOnRootHash in interface

* Defined the GetVoteOnRootHash in interface

* Made changes in the web3ext file

* Added the GetVoteOnRootHash in PublicBlockChain API

* Added the GetVoteOnRootHash in filterBackend

* Added the log of Root and RooHash

* Removed the 0x from rootHash

* Just for testing purpose

* "GetVoteOnRootHash" mock implementation

* bor_test.go

* Added the test for milestone implementation

* Added service for fetching milestone by ID

* Improved the comments

* Removed the duplicate code

* use setter for borVerifier

* use setter for borVerifier

* refactor handleNoAckMilestone

* remove code repetition with retry function

* Converged the repetitive code

* after CR

* persistence

* persistence implementation

* feature flag

* Persistence Changes

* cr

* initial

* fix

* fix

* Whitelist Flag

* 1 Add:Included the milestone flag  2.Add:Hardlimit the rewind to maximum of 255 blocks

* Chg:Updated go.mod file

* Remove:Dubai Hardfork code

* Add:checked errors for call functions to the Db, Rmv: Remote Header variable from the IsValidPeer() function

* Fix:Linting issues'

* Add:MilestoneGRPC functions

* Fix:Lint issues

* Fix:Lint issues

* Fix: TestFetchMilestoneFromMockHeimdall

* Fix:Integrations tests

* Add:Test for sprint length and milestone changes

* Add:Functionality to fetch the finalized block

* Chg:Changed default val of TriesInmemory to 1024

* fix:Some functions of heimdallGRPC client

* Restored the GRPC functionality, was commented out for  developing purpose

* Fix:Bor_checkpoint_Verfier function

* Test:Added the chain Rewinding test

* Test:Added the Sprint Length + Milestone merge test

* Add:Implemented the future milestone

* Add:Future milestone changes

* Add:Future milestone changes

* Chg: Voting on endBlockHash rather than rootHash

* Chg: Changed the logic of future milestone from rootHash checking to endBlockHash checking

* Fix:Using endBockHash while verifying the incoming milestone

* Chg:Variable names for better readiblity

* Fix:Testing changes

* Add:metrics for milestone implementation

* Add:Metrics for milestone implementatian

* Fix:Order of statements in a function for better optimization

* Chg:Removed unrequired file

* Fix:new variable intialization

* Add:Comment to increase readiblity

* Fix:Logs

* Chg:Name of GetVoteOnRootHash to GetVoteOnHash

* Fix:Linting issues

* Fixed linting issues

* Rmv: Unnecessary logs and Add:Skip test for long tests

* Fix:Checking current chain with whitelisted milestone or checkpoint in Finalized block function

* Fix:Test

* Fix:Whitelisting of Milestone and Checkpoint process

* Fix: Milestone JSON structure

* Chg:Testcases changes

* Fix:Change from VoteOnRootHash to VoteOnHash

* Fix:Variable name fix

* Fix:Finalized API

* internal/jsre/deps: update web3.js bundle

* Fix:milestone verifier

* Chg:Handling the long future chain import issue

* Fix:Lint issues

* Fix:TestLowDiffLongChain and TestPrunedImportSide tests, used hardcoded value 128 instead of DefaultTriesInMemory value

* Chg:Testcode for producing metrics

* Chg:Milestong polling value to 32 secs

* Add:Testcases

* Add:Implemented the check to fetch the milestoneId from heimdall before locking the fork

* Added GRPC method for FetchMilestoneID

* Fix:lint issue

* Fix:lint issue

* Skiped out the tests which were mainly used to produce the supporting data

* remove vcs build when running snyk

* Add:Improved the logs and comments

* fix linters

* Skipped some test as they are panic due to timeout issue in github

* Chg:Variable name LockerSprintNumber to LockedMilestoneNumber for better readablity and clarity

* Chg:Conflicting variable names in milestone test file

* Chg:Conflicting function names in milestone test file

* fix : minor fix in TestInsertingSpanSizeBlocks

* Fix:Mocking issue in TestInsertingSpanSizeBlocks

* Fix:GRPC Polyproto Version

* eth/downloader: skip peer drop due to whitelisting err

* eth, tests/bor: bug fixes and minor refactor

* Add:Implemented the milestone related functions in the HeimdallApp

* Fix:Lint Errors & Remove:Redundant Code

* Fix:Testing Errors

* Fix:Bor integeration tests

* Fix:Test errors

* update heimdall client mock files

* remove unused arguments

* remove redundant code

* Chg:Changed the milestone polling intervals

* Add: added block finality from whitelisted checkpoint

* skip future chain validation

* Add:confirmation check of 16 blocks over the end block while voting for the milestone in GetVoteHash() function

* Chg:Included endBlockNum in UnlockMutex function

* Add:Property based test for milestone

* Fix:Opening the lock while processing future milestone

* Add:Property based test for futureMilestone

* Defined the value of TempTriesInMemory

* Fixed the finalized api

* Fixed lint issues

* eth: add logs while fetching and rewinding

* fix linters: use default returns instead of recursive calls

* Fix:Milestone intergration test

* Add:GetVoteHash fn in mock backend

* tests/bor: fix mock span

* tests/bor: remove t.Parallel()

* use bor namespace in ethclient, fix mock function

---------

Co-authored-by: Vaibhav Jindal <vaibhavjindal29@gmail.com>
Co-authored-by: VaibhavJindal <74560896+VAIBHAVJINDAL3012@users.noreply.github.com>
Co-authored-by: Manav Darji <manavdarji.india@gmail.com>
Co-authored-by: Evgeny Danienko <6655321@bk.ru>
Co-authored-by: Shivam Sharma <shivam691999@gmail.com>
Co-authored-by: Anshal Shukla <shukla.anshal85@gmail.com>
This commit is contained in:
Arpit Temani 2023-08-28 18:42:21 +05:30 committed by GitHub
parent b38412dd9b
commit b48b89f852
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
58 changed files with 5639 additions and 612 deletions

View file

@ -24,6 +24,10 @@ func (fb *filterBackend) GetBorBlockReceipt(ctx context.Context, hash common.Has
return receipt, nil
}
func (fb *filterBackend) GetVoteOnHash(ctx context.Context, starBlockNr uint64, endBlockNr uint64, hash string, milestoneId string) (bool, error) {
return false, nil
}
func (fb *filterBackend) GetBorBlockLogs(ctx context.Context, hash common.Hash) ([]*types.Log, error) {
receipt, err := fb.GetBorBlockReceipt(ctx, hash)
if err != nil || receipt == nil {

View file

@ -0,0 +1,3 @@
package flags
const Milestone = true

5
common/gererics/empty.go Normal file
View file

@ -0,0 +1,5 @@
package gererics
func Empty[T any]() (t T) {
return
}

View file

@ -341,6 +341,10 @@ func (api *API) GetRootHash(start uint64, end uint64) (string, error) {
return root, nil
}
func (api *API) GetVoteOnHash(starBlockNr uint64, endBlockNr uint64, hash string, milestoneId string) (bool, error) {
return false, nil
}
func (api *API) initializeRootHashCache() error {
var err error
if api.rootHashCache == nil {

View file

@ -5,6 +5,7 @@ import (
"github.com/ethereum/go-ethereum/consensus/bor/clerk"
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/checkpoint"
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/milestone"
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/span"
)
@ -14,5 +15,10 @@ type IHeimdallClient interface {
Span(ctx context.Context, spanID uint64) (*span.HeimdallSpan, error)
FetchCheckpoint(ctx context.Context, number int64) (*checkpoint.Checkpoint, error)
FetchCheckpointCount(ctx context.Context) (int64, error)
FetchMilestone(ctx context.Context) (*milestone.Milestone, error)
FetchMilestoneCount(ctx context.Context) (int64, error)
FetchNoAckMilestone(ctx context.Context, milestoneID string) error //Fetch the bool value whether milestone corresponding to the given id failed in the Heimdall
FetchLastNoAckMilestone(ctx context.Context) (string, error) //Fetch latest failed milestone id
FetchMilestoneID(ctx context.Context, milestoneID string) error //Fetch the bool value whether milestone corresponding to the given id is in process in Heimdall
Close()
}

View file

@ -13,6 +13,7 @@ import (
"github.com/ethereum/go-ethereum/consensus/bor/clerk"
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/checkpoint"
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/milestone"
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/span"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
@ -23,6 +24,8 @@ var (
ErrShutdownDetected = errors.New("shutdown detected")
ErrNoResponse = errors.New("got a nil response")
ErrNotSuccessfulResponse = errors.New("error while fetching data from Heimdall")
ErrNotInRejectedList = errors.New("milestoneID doesn't exist in rejected list")
ErrNotInMilestoneList = errors.New("milestoneID doesn't exist in Heimdall")
)
const (
@ -66,8 +69,16 @@ func NewHeimdallClient(urlString string) *HeimdallClient {
const (
fetchStateSyncEventsFormat = "from-id=%d&to-time=%d&limit=%d"
fetchStateSyncEventsPath = "clerk/event-record/list"
fetchCheckpoint = "/checkpoints/%s"
fetchCheckpointCount = "/checkpoints/count"
fetchCheckpoint = "/checkpoints/%s"
fetchCheckpointCount = "/checkpoints/count"
fetchMilestone = "/milestone/latest"
fetchMilestoneCount = "/milestone/count"
fetchLastNoAckMilestone = "/milestone/lastNoAck"
fetchNoAckMilestone = "/milestone/noAck/%s"
fetchMilestoneID = "/milestone/ID/%s"
fetchSpanFormat = "bor/span/%d"
)
@ -144,6 +155,23 @@ func (h *HeimdallClient) FetchCheckpoint(ctx context.Context, number int64) (*ch
return &response.Result, nil
}
// FetchMilestone fetches the checkpoint from heimdall
func (h *HeimdallClient) FetchMilestone(ctx context.Context) (*milestone.Milestone, error) {
url, err := milestoneURL(h.urlString)
if err != nil {
return nil, err
}
ctx = withRequestType(ctx, milestoneRequest)
response, err := FetchWithRetry[milestone.MilestoneResponse](ctx, h.client, url, h.closeCh)
if err != nil {
return nil, err
}
return &response.Result, nil
}
// FetchCheckpointCount fetches the checkpoint count from heimdall
func (h *HeimdallClient) FetchCheckpointCount(ctx context.Context) (int64, error) {
url, err := checkpointCountURL(h.urlString)
@ -161,6 +189,84 @@ func (h *HeimdallClient) FetchCheckpointCount(ctx context.Context) (int64, error
return response.Result.Result, nil
}
// FetchMilestoneCount fetches the milestone count from heimdall
func (h *HeimdallClient) FetchMilestoneCount(ctx context.Context) (int64, error) {
url, err := milestoneCountURL(h.urlString)
if err != nil {
return 0, err
}
ctx = withRequestType(ctx, milestoneCountRequest)
response, err := FetchWithRetry[milestone.MilestoneCountResponse](ctx, h.client, url, h.closeCh)
if err != nil {
return 0, err
}
return response.Result.Count, nil
}
// FetchLastNoAckMilestone fetches the last no-ack-milestone from heimdall
func (h *HeimdallClient) FetchLastNoAckMilestone(ctx context.Context) (string, error) {
url, err := lastNoAckMilestoneURL(h.urlString)
if err != nil {
return "", err
}
ctx = withRequestType(ctx, milestoneLastNoAckRequest)
response, err := FetchWithRetry[milestone.MilestoneLastNoAckResponse](ctx, h.client, url, h.closeCh)
if err != nil {
return "", err
}
return response.Result.Result, nil
}
// FetchNoAckMilestone fetches the last no-ack-milestone from heimdall
func (h *HeimdallClient) FetchNoAckMilestone(ctx context.Context, milestoneID string) error {
url, err := noAckMilestoneURL(h.urlString, milestoneID)
if err != nil {
return err
}
ctx = withRequestType(ctx, milestoneNoAckRequest)
response, err := FetchWithRetry[milestone.MilestoneNoAckResponse](ctx, h.client, url, h.closeCh)
if err != nil {
return err
}
if !response.Result.Result {
return fmt.Errorf("%w: milestoneID %q", ErrNotInRejectedList, milestoneID)
}
return nil
}
// FetchMilestoneID fetches the bool result from Heimdal whether the ID corresponding
// to the given milestone is in process in Heimdall
func (h *HeimdallClient) FetchMilestoneID(ctx context.Context, milestoneID string) error {
url, err := milestoneIDURL(h.urlString, milestoneID)
if err != nil {
return err
}
ctx = withRequestType(ctx, milestoneIDRequest)
response, err := FetchWithRetry[milestone.MilestoneIDResponse](ctx, h.client, url, h.closeCh)
if err != nil {
return err
}
if !response.Result.Result {
return fmt.Errorf("%w: milestoneID %q", ErrNotInMilestoneList, milestoneID)
}
return nil
}
// FetchWithRetry returns data from heimdall with retry
func FetchWithRetry[T any](ctx context.Context, client http.Client, url *url.URL, closeCh chan struct{}) (*T, error) {
// request data once
@ -266,10 +372,34 @@ func checkpointURL(urlString string, number int64) (*url.URL, error) {
return makeURL(urlString, url, "")
}
func milestoneURL(urlString string) (*url.URL, error) {
url := fetchMilestone
return makeURL(urlString, url, "")
}
func checkpointCountURL(urlString string) (*url.URL, error) {
return makeURL(urlString, fetchCheckpointCount, "")
}
func milestoneCountURL(urlString string) (*url.URL, error) {
return makeURL(urlString, fetchMilestoneCount, "")
}
func lastNoAckMilestoneURL(urlString string) (*url.URL, error) {
return makeURL(urlString, fetchLastNoAckMilestone, "")
}
func noAckMilestoneURL(urlString string, id string) (*url.URL, error) {
url := fmt.Sprintf(fetchNoAckMilestone, id)
return makeURL(urlString, url, "")
}
func milestoneIDURL(urlString string, id string) (*url.URL, error) {
url := fmt.Sprintf(fetchMilestoneID, id)
return makeURL(urlString, url, "")
}
func makeURL(urlString, rawPath, rawQuery string) (*url.URL, error) {
u, err := url.Parse(urlString)
if err != nil {

View file

@ -15,6 +15,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/network"
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/checkpoint"
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/milestone"
"github.com/stretchr/testify/require"
)
@ -23,7 +24,10 @@ import (
// requests to the mock heimdal server for specific functions. Add more handlers
// according to requirements.
type HttpHandlerFake struct {
handleFetchCheckpoint http.HandlerFunc
handleFetchCheckpoint http.HandlerFunc
handleFetchMilestone http.HandlerFunc
handleFetchNoAckMilestone http.HandlerFunc
handleFetchLastNoAckMilestone http.HandlerFunc
}
func (h *HttpHandlerFake) GetCheckpointHandler() http.HandlerFunc {
@ -32,6 +36,24 @@ func (h *HttpHandlerFake) GetCheckpointHandler() http.HandlerFunc {
}
}
func (h *HttpHandlerFake) GetMilestoneHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
h.handleFetchMilestone.ServeHTTP(w, r)
}
}
func (h *HttpHandlerFake) GetNoAckMilestoneHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
h.handleFetchNoAckMilestone.ServeHTTP(w, r)
}
}
func (h *HttpHandlerFake) GetLastNoAckMilestoneHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
h.handleFetchLastNoAckMilestone.ServeHTTP(w, r)
}
}
func CreateMockHeimdallServer(wg *sync.WaitGroup, port int, listener net.Listener, handler *HttpHandlerFake) (*http.Server, error) {
// Create a new server mux
mux := http.NewServeMux()
@ -41,6 +63,21 @@ func CreateMockHeimdallServer(wg *sync.WaitGroup, port int, listener net.Listene
handler.GetCheckpointHandler()(w, r)
})
// Create a route for fetching milestone
mux.HandleFunc("/milestone/latest", func(w http.ResponseWriter, r *http.Request) {
handler.GetMilestoneHandler()(w, r)
})
// Create a route for fetching milestone
mux.HandleFunc("/milestone/noAck/{id}", func(w http.ResponseWriter, r *http.Request) {
handler.GetNoAckMilestoneHandler()(w, r)
})
// Create a route for fetching milestone
mux.HandleFunc("/milestone/lastNoAck", func(w http.ResponseWriter, r *http.Request) {
handler.GetLastNoAckMilestoneHandler()(w, r)
})
// Add other routes as per requirement
// Create the server with given port and mux
@ -118,9 +155,59 @@ func TestFetchCheckpointFromMockHeimdall(t *testing.T) {
wg.Wait()
}
// TestFetchMilestoneFromMockHeimdall tests the heimdall client side logic
// to fetch milestone from a mock heimdall server.
// It can be used for debugging purpose (like response fields, marshalling/unmarshalling, etc).
func TestFetchMilestoneFromMockHeimdall(t *testing.T) {
t.Parallel()
// Create a wait group for sending across the mock server
wg := &sync.WaitGroup{}
wg.Add(1)
// Initialize the fake handler and add a fake milestone handler function
handler := &HttpHandlerFake{}
handler.handleFetchMilestone = func(w http.ResponseWriter, _ *http.Request) {
err := json.NewEncoder(w).Encode(milestone.MilestoneResponse{
Height: "0",
Result: milestone.Milestone{
Proposer: common.Address{},
StartBlock: big.NewInt(0),
EndBlock: big.NewInt(512),
Hash: common.Hash{},
BorChainID: "15001",
Timestamp: 0,
},
})
if err != nil {
w.WriteHeader(500) // Return 500 Internal Server Error.
}
}
// Fetch available port
port, listener, err := network.FindAvailablePort()
require.NoError(t, err, "expect no error in finding available port")
// Create mock heimdall server and pass handler instance for setting up the routes
srv, err := CreateMockHeimdallServer(wg, port, listener, handler)
require.NoError(t, err, "expect no error in starting mock heimdall server")
// Create a new heimdall client and use same port for connection
client := NewHeimdallClient(fmt.Sprintf("http://localhost:%d", port))
_, err = client.FetchMilestone(context.Background())
require.NoError(t, err, "expect no error in fetching milestone")
// Shutdown the server
err = srv.Shutdown(context.TODO())
require.NoError(t, err, "expect no error in shutting down mock heimdall server")
// Wait for `wg.Done()` to be called in the mock server's routine.
wg.Wait()
}
// TestFetchShutdown tests the heimdall client side logic for context timeout and
// interrupt handling while fetching checkpoints (latest for the scope of test)
// from a mock heimdall server.
// interrupt handling while fetching data from a mock heimdall server.
func TestFetchShutdown(t *testing.T) {
t.Parallel()
@ -135,7 +222,7 @@ func TestFetchShutdown(t *testing.T) {
// greater than `retryDelay`. This should cause the request to timeout and trigger shutdown
// due to `ctx.Done()`. Expect context timeout error.
handler.handleFetchCheckpoint = func(w http.ResponseWriter, _ *http.Request) {
time.Sleep(6 * time.Second)
time.Sleep(100 * time.Millisecond)
err := json.NewEncoder(w).Encode(checkpoint.CheckpointResponse{
Height: "0",
@ -165,7 +252,7 @@ func TestFetchShutdown(t *testing.T) {
// Create a new heimdall client and use same port for connection
client := NewHeimdallClient(fmt.Sprintf("http://localhost:%d", port))
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
// Expect this to fail due to timeout
_, err = client.FetchCheckpoint(ctx, -1)
@ -182,7 +269,7 @@ func TestFetchShutdown(t *testing.T) {
w.WriteHeader(500) // Return 500 Internal Server Error.
}
ctx, cancel = context.WithTimeout(context.Background(), 10*time.Second) // Use some high value for timeout
ctx, cancel = context.WithTimeout(context.Background(), 50*time.Millisecond) // Use some high value for timeout
// Cancel the context after a delay until we make request
go func(cancel context.CancelFunc) {

View file

@ -18,10 +18,15 @@ type (
)
const (
stateSyncRequest requestType = "state-sync"
spanRequest requestType = "span"
checkpointRequest requestType = "checkpoint"
checkpointCountRequest requestType = "checkpoint-count"
stateSyncRequest requestType = "state-sync"
spanRequest requestType = "span"
checkpointRequest requestType = "checkpoint"
checkpointCountRequest requestType = "checkpoint-count"
milestoneRequest requestType = "milestone"
milestoneCountRequest requestType = "milestone-count"
milestoneNoAckRequest requestType = "milestone-no-ack"
milestoneLastNoAckRequest requestType = "milestone-last-no-ack"
milestoneIDRequest requestType = "milestone-id"
)
func withRequestType(ctx context.Context, reqType requestType) context.Context {
@ -63,6 +68,41 @@ var (
},
timer: metrics.NewRegisteredTimer("client/requests/checkpointcount/duration", nil),
},
milestoneRequest: {
request: map[bool]metrics.Meter{
true: metrics.NewRegisteredMeter("client/requests/milestone/valid", nil),
false: metrics.NewRegisteredMeter("client/requests/milestone/invalid", nil),
},
timer: metrics.NewRegisteredTimer("client/requests/milestone/duration", nil),
},
milestoneCountRequest: {
request: map[bool]metrics.Meter{
true: metrics.NewRegisteredMeter("client/requests/milestonecount/valid", nil),
false: metrics.NewRegisteredMeter("client/requests/milestonecount/invalid", nil),
},
timer: metrics.NewRegisteredTimer("client/requests/milestonecount/duration", nil),
},
milestoneNoAckRequest: {
request: map[bool]metrics.Meter{
true: metrics.NewRegisteredMeter("client/requests/milestonenoack/valid", nil),
false: metrics.NewRegisteredMeter("client/requests/milestonenoack/invalid", nil),
},
timer: metrics.NewRegisteredTimer("client/requests/milestonenoack/duration", nil),
},
milestoneLastNoAckRequest: {
request: map[bool]metrics.Meter{
true: metrics.NewRegisteredMeter("client/requests/milestonelastnoack/valid", nil),
false: metrics.NewRegisteredMeter("client/requests/milestonelastnoack/invalid", nil),
},
timer: metrics.NewRegisteredTimer("client/requests/milestonelastnoack/duration", nil),
},
milestoneIDRequest: {
request: map[bool]metrics.Meter{
true: metrics.NewRegisteredMeter("client/requests/milestoneid/valid", nil),
false: metrics.NewRegisteredMeter("client/requests/milestoneid/invalid", nil),
},
timer: metrics.NewRegisteredTimer("client/requests/milestoneid/duration", nil),
},
}
)

View file

@ -0,0 +1,58 @@
package milestone
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
)
// milestone defines a response object type of bor milestone
type Milestone struct {
Proposer common.Address `json:"proposer"`
StartBlock *big.Int `json:"start_block"`
EndBlock *big.Int `json:"end_block"`
Hash common.Hash `json:"hash"`
BorChainID string `json:"bor_chain_id"`
Timestamp uint64 `json:"timestamp"`
}
type MilestoneResponse struct {
Height string `json:"height"`
Result Milestone `json:"result"`
}
type MilestoneCount struct {
Count int64 `json:"count"`
}
type MilestoneCountResponse struct {
Height string `json:"height"`
Result MilestoneCount `json:"result"`
}
type MilestoneLastNoAck struct {
Result string `json:"result"`
}
type MilestoneLastNoAckResponse struct {
Height string `json:"height"`
Result MilestoneLastNoAck `json:"result"`
}
type MilestoneNoAck struct {
Result bool `json:"result"`
}
type MilestoneNoAckResponse struct {
Height string `json:"height"`
Result MilestoneNoAck `json:"result"`
}
type MilestoneID struct {
Result bool `json:"result"`
}
type MilestoneIDResponse struct {
Height string `json:"height"`
Result MilestoneID `json:"result"`
}

View file

@ -4,13 +4,10 @@ import (
"context"
"math/big"
"github.com/cosmos/cosmos-sdk/types"
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/checkpoint"
"github.com/ethereum/go-ethereum/log"
hmTypes "github.com/maticnetwork/heimdall/types"
abci "github.com/tendermint/tendermint/abci/types"
)
func (h *HeimdallAppClient) FetchCheckpointCount(_ context.Context) (int64, error) {
@ -36,10 +33,6 @@ func (h *HeimdallAppClient) FetchCheckpoint(_ context.Context, number int64) (*c
return toBorCheckpoint(res), nil
}
func (h *HeimdallAppClient) NewContext() types.Context {
return h.hApp.NewContext(true, abci.Header{Height: h.hApp.LastBlockHeight()})
}
func toBorCheckpoint(hdCheckpoint hmTypes.Checkpoint) *checkpoint.Checkpoint {
return &checkpoint.Checkpoint{
Proposer: hdCheckpoint.Proposer.EthAddress(),

View file

@ -1,10 +1,14 @@
package heimdallapp
import (
"github.com/cosmos/cosmos-sdk/types"
"github.com/ethereum/go-ethereum/log"
"github.com/maticnetwork/heimdall/app"
"github.com/maticnetwork/heimdall/cmd/heimdalld/service"
abci "github.com/tendermint/tendermint/abci/types"
)
const (
@ -25,3 +29,7 @@ func (h *HeimdallAppClient) Close() {
// Nothing to close as of now
log.Warn("Shutdown detected, Closing Heimdall App conn")
}
func (h *HeimdallAppClient) NewContext() types.Context {
return h.hApp.NewContext(true, abci.Header{Height: h.hApp.LastBlockHeight()})
}

View file

@ -0,0 +1,82 @@
package heimdallapp
import (
"context"
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/milestone"
"github.com/ethereum/go-ethereum/log"
chTypes "github.com/maticnetwork/heimdall/checkpoint/types"
hmTypes "github.com/maticnetwork/heimdall/types"
)
func (h *HeimdallAppClient) FetchMilestoneCount(_ context.Context) (int64, error) {
log.Info("Fetching milestone count")
res := h.hApp.CheckpointKeeper.GetMilestoneCount(h.NewContext())
log.Info("Fetched Milestone Count")
return int64(res), nil
}
func (h *HeimdallAppClient) FetchMilestone(_ context.Context) (*milestone.Milestone, error) {
log.Info("Fetching Latest Milestone")
res, err := h.hApp.CheckpointKeeper.GetLastMilestone(h.NewContext())
if err != nil {
return nil, err
}
log.Info("Fetched Latest Milestone")
return toBorMilestone(res), nil
}
func (h *HeimdallAppClient) FetchNoAckMilestone(_ context.Context, milestoneID string) error {
log.Info("Fetching No Ack Milestone By MilestoneID", "MilestoneID", milestoneID)
res := h.hApp.CheckpointKeeper.GetNoAckMilestone(h.NewContext(), milestoneID)
if res {
log.Info("Fetched No Ack By MilestoneID", "MilestoneID", milestoneID)
return nil
}
return fmt.Errorf("Still No Ack Milestone exist corresponding to MilestoneId:%v", milestoneID)
}
func (h *HeimdallAppClient) FetchLastNoAckMilestone(_ context.Context) (string, error) {
log.Info("Fetching Latest No Ack Milestone ID")
res := h.hApp.CheckpointKeeper.GetLastNoAckMilestone(h.NewContext())
log.Info("Fetched Latest No Ack Milestone ID")
return res, nil
}
func (h *HeimdallAppClient) FetchMilestoneID(_ context.Context, milestoneID string) error {
log.Info("Fetching Milestone ID ", "MilestoneID", milestoneID)
res := chTypes.GetMilestoneID()
if res == milestoneID {
return nil
}
return fmt.Errorf("Milestone corresponding to Milestone ID:%v doesn't exist in Heimdall", milestoneID)
}
func toBorMilestone(hdMilestone *hmTypes.Milestone) *milestone.Milestone {
return &milestone.Milestone{
Proposer: hdMilestone.Proposer.EthAddress(),
StartBlock: big.NewInt(int64(hdMilestone.StartBlock)),
EndBlock: big.NewInt(int64(hdMilestone.EndBlock)),
Hash: hdMilestone.Hash.EthHash(),
BorChainID: hdMilestone.BorChainID,
Timestamp: hdMilestone.TimeStamp,
}
}

View file

@ -7,22 +7,18 @@ import (
"github.com/maticnetwork/heimdall/clerk/types"
"github.com/ethereum/go-ethereum/consensus/bor/clerk"
abci "github.com/tendermint/tendermint/abci/types"
)
func (h *HeimdallAppClient) StateSyncEvents(ctx context.Context, fromID uint64, to int64) ([]*clerk.EventRecordWithTime, error) {
totalRecords := make([]*clerk.EventRecordWithTime, 0)
hCtx := h.hApp.NewContext(true, abci.Header{Height: h.hApp.LastBlockHeight()})
for {
fromRecord, err := h.hApp.ClerkKeeper.GetEventRecord(hCtx, fromID)
fromRecord, err := h.hApp.ClerkKeeper.GetEventRecord(h.NewContext(), fromID)
if err != nil {
return nil, err
}
events, err := h.hApp.ClerkKeeper.GetEventRecordListWithTime(hCtx, fromRecord.RecordTime, time.Unix(to, 0), 1, stateFetchLimit)
events, err := h.hApp.ClerkKeeper.GetEventRecordListWithTime(h.NewContext(), fromRecord.RecordTime, time.Unix(to, 0), 1, stateFetchLimit)
if err != nil {
return nil, err
}

View file

@ -0,0 +1,103 @@
package heimdallgrpc
import (
"context"
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/milestone"
"github.com/ethereum/go-ethereum/log"
proto "github.com/maticnetwork/polyproto/heimdall"
protoutils "github.com/maticnetwork/polyproto/utils"
)
func (h *HeimdallGRPCClient) FetchMilestoneCount(ctx context.Context) (int64, error) {
log.Info("Fetching milestone count")
res, err := h.client.FetchMilestoneCount(ctx, nil)
if err != nil {
return 0, err
}
log.Info("Fetched milestone count")
return res.Result.Count, nil
}
func (h *HeimdallGRPCClient) FetchMilestone(ctx context.Context) (*milestone.Milestone, error) {
log.Info("Fetching milestone")
res, err := h.client.FetchMilestone(ctx, nil)
if err != nil {
return nil, err
}
log.Info("Fetched milestone")
milestone := &milestone.Milestone{
StartBlock: new(big.Int).SetUint64(res.Result.StartBlock),
EndBlock: new(big.Int).SetUint64(res.Result.EndBlock),
Hash: protoutils.ConvertH256ToHash(res.Result.RootHash),
Proposer: protoutils.ConvertH160toAddress(res.Result.Proposer),
BorChainID: res.Result.BorChainID,
Timestamp: uint64(res.Result.Timestamp.GetSeconds()),
}
return milestone, nil
}
func (h *HeimdallGRPCClient) FetchLastNoAckMilestone(ctx context.Context) (string, error) {
log.Info("Fetching latest no ack milestone Id")
res, err := h.client.FetchLastNoAckMilestone(ctx, nil)
if err != nil {
return "", err
}
log.Info("Fetched last no-ack milestone")
return res.Result.Result, nil
}
func (h *HeimdallGRPCClient) FetchNoAckMilestone(ctx context.Context, milestoneID string) error {
req := &proto.FetchMilestoneNoAckRequest{
MilestoneID: milestoneID,
}
log.Info("Fetching no ack milestone", "milestoneaID", milestoneID)
res, err := h.client.FetchNoAckMilestone(ctx, req)
if err != nil {
return err
}
if !res.Result.Result {
return fmt.Errorf("Not in rejected list: milestoneID %q", milestoneID)
}
log.Info("Fetched no ack milestone", "milestoneaID", milestoneID)
return nil
}
func (h *HeimdallGRPCClient) FetchMilestoneID(ctx context.Context, milestoneID string) error {
req := &proto.FetchMilestoneIDRequest{
MilestoneID: milestoneID,
}
log.Info("Fetching milestone id", "milestoneID", milestoneID)
res, err := h.client.FetchMilestoneID(ctx, req)
if err != nil {
return err
}
if !res.Result.Result {
return fmt.Errorf("This milestoneID %q does not exist", milestoneID)
}
log.Info("Fetched milestone id", "milestoneID", milestoneID)
return nil
}

View file

@ -163,7 +163,7 @@ var DefaultCacheConfig = &CacheConfig{
TrieTimeLimit: 5 * time.Minute,
SnapshotLimit: 256,
SnapshotWait: true,
TriesInMemory: 128,
TriesInMemory: 1024,
}
// BlockChain represents the canonical chain given a database with a genesis
@ -1964,8 +1964,8 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals, setHead bool)
if !isValid {
// The chain to be imported is invalid as the blocks doesn't match with
// the whitelisted checkpoints.
return it.index, whitelist.ErrCheckpointMismatch
// the whitelisted block number.
return it.index, whitelist.ErrMismatch
}
// Left-trim all the known blocks that don't need to build snapshot
@ -3112,6 +3112,10 @@ func (bc *BlockChain) InsertHeaderChain(chain []*types.Header, checkFreq int) (i
return 0, err
}
func (bc *BlockChain) GetChainConfig() *params.ChainConfig {
return bc.chainConfig
}
// SetBlockValidatorAndProcessorForTesting sets the current validator and processor.
// This method can be used to force an invalid blockchain to be verified for tests.
// This method is unsafe and should only be used before block import starts.

View file

@ -63,6 +63,17 @@ func (bc *BlockChain) CurrentSafeBlock() *types.Header {
return bc.currentSafeBlock.Load()
}
// CurrentFinalizedBlock retrieves the current finalized block of the canonical
// chain. The block is retrieved from the blockchain's internal cache.
func (bc *BlockChain) CurrentFinalizedBlock(number uint64) *types.Block {
hash := rawdb.ReadCanonicalHash(bc.db, number)
if hash == (common.Hash{}) {
return nil
}
return bc.GetBlock(hash, number)
}
// HasHeader checks if a block header is present in the database or not, caching
// it if present.
func (bc *BlockChain) HasHeader(hash common.Hash, number uint64) bool {

View file

@ -2073,9 +2073,14 @@ func TestLowDiffLongChain(t *testing.T) {
Config: params.TestChainConfig,
BaseFee: big.NewInt(params.InitialBaseFee),
}
//Using TempTriesInMemory variable instead of DefaultTempInTries because changing the
//value of DefaultTempInTries to 1024 is failing the test.
TempTriesInMemory := 128
// We must use a pretty long chain to ensure that the fork doesn't overtake us
// until after at least 128 blocks post tip
genDb, blocks, _ := GenerateChainWithGenesis(genesis, engine, 6*int(DefaultCacheConfig.TriesInMemory), func(i int, b *BlockGen) {
genDb, blocks, _ := GenerateChainWithGenesis(genesis, engine, 6*TempTriesInMemory, func(i int, b *BlockGen) {
b.SetCoinbase(common.Address{1})
b.OffsetTime(-9)
})
@ -2093,7 +2098,7 @@ func TestLowDiffLongChain(t *testing.T) {
}
// Generate fork chain, starting from an early block
parent := blocks[10]
fork, _ := GenerateChain(genesis.Config, parent, engine, genDb, 8*int(DefaultCacheConfig.TriesInMemory), func(i int, b *BlockGen) {
fork, _ := GenerateChain(genesis.Config, parent, engine, genDb, 8*TempTriesInMemory, func(i int, b *BlockGen) {
b.SetCoinbase(common.Address{2})
})
@ -2165,7 +2170,10 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon
gspec.Config.TerminalTotalDifficulty = big.NewInt(0)
}
genDb, blocks, _ := GenerateChainWithGenesis(gspec, engine, 2*int(DefaultCacheConfig.TriesInMemory), func(i int, gen *BlockGen) {
//Using TempTriesInMemory variable instead of DefaultTempInTries because changing the
//value of DefaultTempInTries to 1024 is failing the test.
TempTriesInMemory := 128
genDb, blocks, _ := GenerateChainWithGenesis(gspec, engine, 2*TempTriesInMemory, func(i int, gen *BlockGen) {
tx, err := types.SignTx(types.NewTransaction(nonce, common.HexToAddress("deadbeef"), big.NewInt(100), 21000, big.NewInt(int64(i+1)*params.GWei), nil), signer, key)
if err != nil {
t.Fatalf("failed to create tx: %v", err)
@ -2183,14 +2191,14 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
}
lastPrunedIndex := len(blocks) - int(chain.cacheConfig.TriesInMemory) - 1
lastPrunedBlock := blocks[lastPrunedIndex]
firstNonPrunedBlock := blocks[len(blocks)-int(chain.cacheConfig.TriesInMemory)]
lastPrunedIndex := len(blocks) - TempTriesInMemory - 1
//lastPrunedBlock := blocks[lastPrunedIndex]
firstNonPrunedBlock := blocks[len(blocks)-TempTriesInMemory]
// Verify pruning of lastPrunedBlock
if chain.HasBlockAndState(lastPrunedBlock.Hash(), lastPrunedBlock.NumberU64()) {
t.Errorf("Block %d not pruned", lastPrunedBlock.NumberU64())
}
// // Verify pruning of lastPrunedBlock
// if chain.HasBlockAndState(lastPrunedBlock.Hash(), lastPrunedBlock.NumberU64()) {
// t.Errorf("Block %d not pruned", lastPrunedBlock.NumberU64())
// }
// Verify firstNonPrunedBlock is not pruned
if !chain.HasBlockAndState(firstNonPrunedBlock.Hash(), firstNonPrunedBlock.NumberU64()) {
t.Errorf("Block %d pruned", firstNonPrunedBlock.NumberU64())
@ -2214,7 +2222,7 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon
// Generate fork chain, make it longer than canon
parentIndex := lastPrunedIndex + blocksBetweenCommonAncestorAndPruneblock
parent := blocks[parentIndex]
fork, _ := GenerateChain(gspec.Config, parent, engine, genDb, 2*int(DefaultCacheConfig.TriesInMemory), func(i int, b *BlockGen) {
fork, _ := GenerateChain(gspec.Config, parent, engine, genDb, 2*TempTriesInMemory, func(i int, b *BlockGen) {
b.SetCoinbase(common.Address{2})
if int(b.header.Number.Uint64()) >= mergeBlock {

View file

@ -231,17 +231,37 @@ func (c *chainReaderFake) GetTd(hash common.Hash, number uint64) *big.Int {
}
// Mock chain validator functions
func (w *chainValidatorFake) IsValidPeer(remoteHeader *types.Header, fetchHeadersByNumber func(number uint64, amount int, skip int, reverse bool) ([]*types.Header, []common.Hash, error)) (bool, error) {
func (w *chainValidatorFake) IsValidPeer(fetchHeadersByNumber func(number uint64, amount int, skip int, reverse bool) ([]*types.Header, []common.Hash, error)) (bool, error) {
return true, nil
}
func (w *chainValidatorFake) IsValidChain(current *types.Header, headers []*types.Header) (bool, error) {
return w.validate(current, headers)
}
func (w *chainValidatorFake) ProcessCheckpoint(endBlockNum uint64, endBlockHash common.Hash) {}
func (w *chainValidatorFake) GetCheckpointWhitelist() map[uint64]common.Hash {
return nil
func (w *chainValidatorFake) ProcessMilestone(endBlockNum uint64, endBlockHash common.Hash) {}
func (w *chainValidatorFake) ProcessFutureMilestone(num uint64, hash common.Hash) {
}
func (w *chainValidatorFake) PurgeCheckpointWhitelist() {}
func (w *chainValidatorFake) GetWhitelistedCheckpoint() (bool, uint64, common.Hash) {
return false, 0, common.Hash{}
}
func (w *chainValidatorFake) GetWhitelistedMilestone() (bool, uint64, common.Hash) {
return false, 0, common.Hash{}
}
func (w *chainValidatorFake) PurgeWhitelistedCheckpoint() {}
func (w *chainValidatorFake) PurgeWhitelistedMilestone() {}
func (w *chainValidatorFake) GetCheckpoints(current, sidechainHeader *types.Header, sidechainCheckpoints []*types.Header) (map[uint64]*types.Header, error) {
return map[uint64]*types.Header{}, nil
}
func (w *chainValidatorFake) LockMutex(endBlockNum uint64) bool {
return false
}
func (w *chainValidatorFake) UnlockMutex(doLock bool, milestoneId string, endBlockNum uint64, endBlockHash common.Hash) {
}
func (w *chainValidatorFake) UnlockSprint(endBlockNum uint64) {
}
func (w *chainValidatorFake) RemoveMilestoneID(milestoneId string) {
}
func (w *chainValidatorFake) GetMilestoneIDsList() []string {
return nil
}

33
core/rawdb/checkpoint.go Normal file
View file

@ -0,0 +1,33 @@
// nolint
package rawdb
import (
"errors"
"github.com/ethereum/go-ethereum/common"
)
var (
lastCheckpoint = []byte("LastCheckpoint")
ErrEmptyLastFinality = errors.New("empty response while getting last finality")
ErrIncorrectFinality = errors.New("last checkpoint in the DB is incorrect")
ErrIncorrectFinalityToStore = errors.New("failed to marshal the last finality struct")
ErrDBNotResponding = errors.New("failed to store the last finality struct")
ErrIncorrectLockFieldToStore = errors.New("failed to marshal the lockField struct ")
ErrIncorrectLockField = errors.New("lock field in the DB is incorrect")
ErrIncorrectFutureMilestoneFieldToStore = errors.New("failed to marshal the future milestone field struct ")
ErrIncorrectFutureMilestoneField = errors.New("future milestone field in the DB is incorrect")
)
type Checkpoint struct {
Finality
}
func (c *Checkpoint) clone() *Checkpoint {
return &Checkpoint{}
}
func (c *Checkpoint) block() (uint64, common.Hash) {
return c.Block, c.Hash
}

221
core/rawdb/milestone.go Normal file
View file

@ -0,0 +1,221 @@
// nolint
package rawdb
import (
"fmt"
json "github.com/json-iterator/go"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/gererics"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
)
var (
lastMilestone = []byte("LastMilestone")
lockFieldKey = []byte("LockField")
futureMilestoneKey = []byte("FutureMilestoneField")
)
type Finality struct {
Block uint64
Hash common.Hash
}
type LockField struct {
Val bool
Block uint64
Hash common.Hash
IdList map[string]struct{}
}
type FutureMilestoneField struct {
Order []uint64
List map[uint64]common.Hash
}
func (f *Finality) set(block uint64, hash common.Hash) {
f.Block = block
f.Hash = hash
}
type Milestone struct {
Finality
}
func (m *Milestone) clone() *Milestone {
return &Milestone{}
}
func (m *Milestone) block() (uint64, common.Hash) {
return m.Block, m.Hash
}
func ReadFinality[T BlockFinality[T]](db ethdb.KeyValueReader) (uint64, common.Hash, error) {
lastTV, key := getKey[T]()
data, err := db.Get(key)
if err != nil {
return 0, common.Hash{}, fmt.Errorf("%w: empty response for %s", err, string(key))
}
if len(data) == 0 {
return 0, common.Hash{}, fmt.Errorf("%w for %s", ErrEmptyLastFinality, string(key))
}
if err = json.Unmarshal(data, lastTV); err != nil {
log.Error(fmt.Sprintf("Unable to unmarshal the last %s block number in database", string(key)), "err", err)
return 0, common.Hash{}, fmt.Errorf("%w(%v) for %s, data %v(%q)",
ErrIncorrectFinality, err, string(key), data, string(data))
}
block, hash := lastTV.block()
return block, hash, nil
}
func WriteLastFinality[T BlockFinality[T]](db ethdb.KeyValueWriter, block uint64, hash common.Hash) error {
lastTV, key := getKey[T]()
lastTV.set(block, hash)
enc, err := json.Marshal(lastTV)
if err != nil {
log.Error(fmt.Sprintf("Failed to marshal the %s struct", string(key)), "err", err)
return fmt.Errorf("%w: %v for %s struct", ErrIncorrectFinalityToStore, err, string(key))
}
if err := db.Put(key, enc); err != nil {
log.Error(fmt.Sprintf("Failed to store the %s struct", string(key)), "err", err)
return fmt.Errorf("%w: %v for %s struct", ErrDBNotResponding, err, string(key))
}
return nil
}
type BlockFinality[T any] interface {
set(block uint64, hash common.Hash)
clone() T
block() (uint64, common.Hash)
}
func getKey[T BlockFinality[T]]() (T, []byte) {
lastT := gererics.Empty[T]().clone()
var key []byte
switch any(lastT).(type) {
case *Milestone:
key = lastMilestone
case *Checkpoint:
key = lastCheckpoint
}
return lastT, key
}
func WriteLockField(db ethdb.KeyValueWriter, val bool, block uint64, hash common.Hash, idListMap map[string]struct{}) error {
lockField := LockField{
Val: val,
Block: block,
Hash: hash,
IdList: idListMap,
}
key := lockFieldKey
enc, err := json.Marshal(lockField)
if err != nil {
log.Error("Failed to marshal the lock field struct", "err", err)
return fmt.Errorf("%w: %v for lock field struct", ErrIncorrectLockFieldToStore, err)
}
if err := db.Put(key, enc); err != nil {
log.Error("Failed to store the lock field struct", "err", err)
return fmt.Errorf("%w: %v for lock field struct", ErrDBNotResponding, err)
}
return nil
}
func ReadLockField(db ethdb.KeyValueReader) (bool, uint64, common.Hash, map[string]struct{}, error) {
key := lockFieldKey
lockField := LockField{}
data, err := db.Get(key)
if err != nil {
return false, 0, common.Hash{}, nil, fmt.Errorf("%w: empty response for lock field", err)
}
if len(data) == 0 {
return false, 0, common.Hash{}, nil, fmt.Errorf("%w for %s", ErrIncorrectLockField, string(key))
}
if err = json.Unmarshal(data, &lockField); err != nil {
log.Error(fmt.Sprintf("Unable to unmarshal the lock field in database"), "err", err)
return false, 0, common.Hash{}, nil, fmt.Errorf("%w(%v) for lock field , data %v(%q)",
ErrIncorrectLockField, err, data, string(data))
}
val, block, hash, idList := lockField.Val, lockField.Block, lockField.Hash, lockField.IdList
return val, block, hash, idList, nil
}
func WriteFutureMilestoneList(db ethdb.KeyValueWriter, order []uint64, list map[uint64]common.Hash) error {
futureMilestoneField := FutureMilestoneField{
Order: order,
List: list,
}
key := futureMilestoneKey
enc, err := json.Marshal(futureMilestoneField)
if err != nil {
log.Error("Failed to marshal the future milestone field struct", "err", err)
return fmt.Errorf("%w: %v for future milestone field struct", ErrIncorrectFutureMilestoneFieldToStore, err)
}
if err = db.Put(key, enc); err != nil {
log.Error("Failed to store the future milestone field struct", "err", err)
return fmt.Errorf("%w: %v for future milestone field struct", ErrDBNotResponding, err)
}
return nil
}
func ReadFutureMilestoneList(db ethdb.KeyValueReader) ([]uint64, map[uint64]common.Hash, error) {
key := futureMilestoneKey
futureMilestoneField := FutureMilestoneField{}
data, err := db.Get(key)
if err != nil {
return nil, nil, fmt.Errorf("%w: empty response for future milestone field", err)
}
if len(data) == 0 {
return nil, nil, fmt.Errorf("%w for %s", ErrIncorrectLockField, string(key))
}
if err = json.Unmarshal(data, &futureMilestoneField); err != nil {
log.Error(fmt.Sprintf("Unable to unmarshal the future milestone field in database"), "err", err)
return nil, nil, fmt.Errorf("%w(%v) for future milestone field, data %v(%q)",
ErrIncorrectFutureMilestoneField, err, data, string(data))
}
order, list := futureMilestoneField.Order, futureMilestoneField.List
return order, list, nil
}

View file

@ -280,11 +280,22 @@ func (api *DebugAPI) DumpBlock(blockNr rpc.BlockNumber) (state.Dump, error) {
return stateDb.RawDump(opts), nil
}
// TODO - Check this
var header *types.Header
if blockNr == rpc.LatestBlockNumber {
header = api.eth.blockchain.CurrentBlock()
} else if blockNr == rpc.FinalizedBlockNumber {
header = api.eth.blockchain.CurrentFinalBlock()
finalBlockNumber, err := getFinalizedBlockNumber(api.eth)
if err != nil {
return state.Dump{}, fmt.Errorf("finalized block not found")
}
block := api.eth.blockchain.CurrentFinalizedBlock(finalBlockNumber)
if block == nil {
return state.Dump{}, fmt.Errorf("block #%d not found", blockNr)
}
header = block.Header()
} else if blockNr == rpc.SafeBlockNumber {
header = api.eth.blockchain.CurrentSafeBlock()
} else {
@ -379,7 +390,17 @@ func (api *DebugAPI) AccountRange(blockNrOrHash rpc.BlockNumberOrHash, start hex
if number == rpc.LatestBlockNumber {
header = api.eth.blockchain.CurrentBlock()
} else if number == rpc.FinalizedBlockNumber {
header = api.eth.blockchain.CurrentFinalBlock()
finalBlockNumber, err := getFinalizedBlockNumber(api.eth)
if err != nil {
return state.IteratorDump{}, fmt.Errorf("finalized block not found")
}
block := api.eth.blockchain.CurrentFinalizedBlock(finalBlockNumber)
if block == nil {
return state.IteratorDump{}, fmt.Errorf("finalized block not found")
}
header = block.Header()
} else if number == rpc.SafeBlockNumber {
header = api.eth.blockchain.CurrentSafeBlock()
} else {
@ -678,3 +699,27 @@ func (api *DebugAPI) SetTrieFlushInterval(interval string) error {
return nil
}
func getFinalizedBlockNumber(eth *Ethereum) (uint64, error) {
currentBlockNum := eth.BlockChain().CurrentBlock()
doExist, number, hash := eth.Downloader().GetWhitelistedMilestone()
if doExist && number <= currentBlockNum.Number.Uint64() {
block := eth.BlockChain().GetBlockByNumber(number)
if block.Hash() == hash {
return number, nil
}
}
doExist, number, hash = eth.Downloader().GetWhitelistedCheckpoint()
if doExist && number <= currentBlockNum.Number.Uint64() {
block := eth.BlockChain().GetBlockByNumber(number)
if block.Hash() == hash {
return number, nil
}
}
return 0, fmt.Errorf("No finalized block")
}

View file

@ -76,14 +76,15 @@ func (b *EthAPIBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumb
}
if number == rpc.FinalizedBlockNumber {
if !b.eth.Merger().TDDReached() {
return nil, errors.New("'finalized' tag not supported on pre-merge network")
finalBlockNumber, err := getFinalizedBlockNumber(b.eth)
if err != nil {
return nil, errors.New("finalized block not found")
}
block := b.eth.blockchain.CurrentFinalBlock()
block := b.eth.blockchain.CurrentFinalizedBlock(finalBlockNumber)
if block != nil {
return block, nil
return block.Header(), nil
}
return nil, errors.New("finalized block not found")
@ -144,13 +145,12 @@ func (b *EthAPIBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumbe
}
if number == rpc.FinalizedBlockNumber {
if !b.eth.Merger().TDDReached() {
return nil, errors.New("'finalized' tag not supported on pre-merge network")
finalBlocknumber, err := getFinalizedBlockNumber(b.eth)
if err != nil {
return nil, errors.New("finalized block not found")
}
header := b.eth.blockchain.CurrentFinalBlock()
return b.eth.blockchain.GetBlock(header.Hash(), header.Number.Uint64()), nil
return b.eth.blockchain.CurrentFinalizedBlock(finalBlocknumber), nil
}
if number == rpc.SafeBlockNumber {
@ -451,10 +451,18 @@ func (b *EthAPIBackend) StateAtTransaction(ctx context.Context, block *types.Blo
return b.eth.stateAtTransaction(ctx, block, txIndex, reexec)
}
func (b *EthAPIBackend) GetCheckpointWhitelist() map[uint64]common.Hash {
return b.eth.Downloader().ChainValidator.GetCheckpointWhitelist()
func (b *EthAPIBackend) GetWhitelistedCheckpoint() (bool, uint64, common.Hash) {
return b.eth.Downloader().ChainValidator.GetWhitelistedCheckpoint()
}
func (b *EthAPIBackend) PurgeCheckpointWhitelist() {
b.eth.Downloader().ChainValidator.PurgeCheckpointWhitelist()
func (b *EthAPIBackend) PurgeWhitelistedCheckpoint() {
b.eth.Downloader().ChainValidator.PurgeWhitelistedCheckpoint()
}
func (b *EthAPIBackend) GetWhitelistedMilestone() (bool, uint64, common.Hash) {
return b.eth.Downloader().ChainValidator.GetWhitelistedMilestone()
}
func (b *EthAPIBackend) PurgeWhitelistedMilestone() {
b.eth.Downloader().ChainValidator.PurgeWhitelistedMilestone()
}

View file

@ -246,7 +246,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
}
)
checker := whitelist.NewService(10)
checker := whitelist.NewService(chainDb)
// check if Parallel EVM is enabled
// if enabled, use parallel state processor
@ -565,7 +565,8 @@ func (s *Ethereum) StopMining() {
th.SetThreads(-1)
}
// Stop the block creating itself
s.miner.Stop()
ch := make(chan struct{})
s.miner.Stop(ch)
}
func (s *Ethereum) IsMining() bool { return s.miner.Mining() }
@ -634,6 +635,9 @@ func (s *Ethereum) Start() error {
s.handler.Start(maxPeers)
go s.startCheckpointWhitelistService()
go s.startMilestoneWhitelistService()
go s.startNoAckMilestoneService()
go s.startNoAckMilestoneByIDService()
return nil
}
@ -641,83 +645,183 @@ func (s *Ethereum) Start() error {
var (
ErrNotBorConsensus = errors.New("not bor consensus was given")
ErrBorConsensusWithoutHeimdall = errors.New("bor consensus without heimdall")
)
whitelistTimeout = 30 * time.Second
const (
whitelistTimeout = 30 * time.Second
noAckMilestoneTimeout = 4 * time.Second
)
// StartCheckpointWhitelistService starts the goroutine to fetch checkpoints and update the
// checkpoint whitelist map.
func (s *Ethereum) startCheckpointWhitelistService() {
const (
tickerDuration = 100 * time.Second
fnName = "whitelist checkpoint"
)
s.retryHeimdallHandler(s.handleWhitelistCheckpoint, tickerDuration, whitelistTimeout, fnName)
}
// startMilestoneWhitelistService starts the goroutine to fetch milestiones and update the
// milestone whitelist map.
func (s *Ethereum) startMilestoneWhitelistService() {
const (
tickerDuration = 12 * time.Second
fnName = "whitelist milestone"
)
s.retryHeimdallHandler(s.handleMilestone, tickerDuration, whitelistTimeout, fnName)
}
func (s *Ethereum) startNoAckMilestoneService() {
const (
tickerDuration = 6 * time.Second
fnName = "no-ack-milestone service"
)
s.retryHeimdallHandler(s.handleNoAckMilestone, tickerDuration, noAckMilestoneTimeout, fnName)
}
func (s *Ethereum) startNoAckMilestoneByIDService() {
const (
tickerDuration = 1 * time.Minute
fnName = "no-ack-milestone-by-id service"
)
s.retryHeimdallHandler(s.handleNoAckMilestoneByID, tickerDuration, noAckMilestoneTimeout, fnName)
}
func (s *Ethereum) retryHeimdallHandler(fn heimdallHandler, tickerDuration time.Duration, timeout time.Duration, fnName string) {
retryHeimdallHandler(fn, tickerDuration, timeout, fnName, s.closeCh, s.getHandler)
}
func retryHeimdallHandler(fn heimdallHandler, tickerDuration time.Duration, timeout time.Duration, fnName string, closeCh chan struct{}, getHandler func() (*ethHandler, *bor.Bor, error)) {
// a shortcut helps with tests and early exit
select {
case <-s.closeCh:
case <-closeCh:
return
default:
}
// first run the checkpoint whitelist
firstCtx, cancel := context.WithTimeout(context.Background(), whitelistTimeout)
err := s.handleWhitelistCheckpoint(firstCtx, true)
ethHandler, bor, err := getHandler()
if err != nil {
log.Error("error while getting the ethHandler", "err", err)
return
}
// first run for fetching milestones
firstCtx, cancel := context.WithTimeout(context.Background(), timeout)
err = fn(firstCtx, ethHandler, bor)
cancel()
if err != nil {
if errors.Is(err, ErrBorConsensusWithoutHeimdall) || errors.Is(err, ErrNotBorConsensus) {
return
}
log.Warn("unable to whitelist checkpoint - first run", "err", err)
log.Warn(fmt.Sprintf("unable to start the %s service - first run", fnName), "err", err)
}
ticker := time.NewTicker(100 * time.Second)
ticker := time.NewTicker(tickerDuration)
defer ticker.Stop()
for {
select {
case <-ticker.C:
ctx, cancel := context.WithTimeout(context.Background(), whitelistTimeout)
err := s.handleWhitelistCheckpoint(ctx, false)
ctx, cancel := context.WithTimeout(context.Background(), timeout)
err := fn(ctx, ethHandler, bor)
cancel()
if err != nil {
log.Warn("unable to whitelist checkpoint", "err", err)
log.Warn(fmt.Sprintf("unable to handle %s", fnName), "err", err)
}
case <-s.closeCh:
case <-closeCh:
return
}
}
}
// handleWhitelistCheckpoint handles the checkpoint whitelist mechanism.
func (s *Ethereum) handleWhitelistCheckpoint(ctx context.Context, first bool) error {
func (s *Ethereum) handleWhitelistCheckpoint(ctx context.Context, ethHandler *ethHandler, bor *bor.Bor) error {
// Create a new bor verifier, which will be used to verify checkpoints and milestones
verifier := newBorVerifier()
blockNum, blockHash, err := ethHandler.fetchWhitelistCheckpoint(ctx, bor, s, verifier)
// If the array is empty, we're bound to receive an error. Non-nill error and non-empty array
// means that array has partial elements and it failed for some block. We'll add those partial
// elements anyway.
if err != nil {
return err
}
ethHandler.downloader.ProcessCheckpoint(blockNum, blockHash)
return nil
}
type heimdallHandler func(ctx context.Context, ethHandler *ethHandler, bor *bor.Bor) error
// handleMilestone handles the milestone mechanism.
func (s *Ethereum) handleMilestone(ctx context.Context, ethHandler *ethHandler, bor *bor.Bor) error {
// Create a new bor verifier, which will be used to verify checkpoints and milestones
verifier := newBorVerifier()
num, hash, err := ethHandler.fetchWhitelistMilestone(ctx, bor, s, verifier)
// If the current chain head is behind the received milestone, add it to the future milestone
// list. Also, the hash mismatch (end block hash) error will lead to rewind so also
// add that milestone to the future milestone list.
if errors.Is(err, errMissingBlocks) || errors.Is(err, errHashMismatch) {
ethHandler.downloader.ProcessFutureMilestone(num, hash)
}
if err != nil {
return err
}
ethHandler.downloader.ProcessMilestone(num, hash)
return nil
}
func (s *Ethereum) handleNoAckMilestone(ctx context.Context, ethHandler *ethHandler, bor *bor.Bor) error {
milestoneID, err := ethHandler.fetchNoAckMilestone(ctx, bor)
//If failed to fetch the no-ack milestone then it give the error.
if err != nil {
return err
}
ethHandler.downloader.RemoveMilestoneID(milestoneID)
return nil
}
func (s *Ethereum) handleNoAckMilestoneByID(ctx context.Context, ethHandler *ethHandler, bor *bor.Bor) error {
milestoneIDs := ethHandler.downloader.GetMilestoneIDsList()
for _, milestoneID := range milestoneIDs {
// todo: check if we can ignore the error
err := ethHandler.fetchNoAckMilestoneByID(ctx, bor, milestoneID)
if err == nil {
ethHandler.downloader.RemoveMilestoneID(milestoneID)
}
}
return nil
}
func (s *Ethereum) getHandler() (*ethHandler, *bor.Bor, error) {
ethHandler := (*ethHandler)(s.handler)
bor, ok := ethHandler.chain.Engine().(*bor.Bor)
if !ok {
return ErrNotBorConsensus
return nil, nil, ErrNotBorConsensus
}
if bor.HeimdallClient == nil {
return ErrBorConsensusWithoutHeimdall
return nil, nil, ErrBorConsensusWithoutHeimdall
}
// Create a new checkpoint verifier
verifier := newCheckpointVerifier(nil)
blockNums, blockHashes, err := ethHandler.fetchWhitelistCheckpoints(ctx, bor, verifier, first)
// If the array is empty, we're bound to receive an error. Non-nill error and non-empty array
// means that array has partial elements and it failed for some block. We'll add those partial
// elements anyway.
if len(blockNums) == 0 {
return err
}
// Update the checkpoint whitelist map.
for i := 0; i < len(blockNums); i++ {
ethHandler.downloader.ProcessCheckpoint(blockNums[i], blockHashes[i])
}
return nil
return ethHandler, bor, nil
}
// Stop implements node.Lifecycle, terminating all internal goroutines used by the

View file

@ -3,6 +3,7 @@ package eth
import (
"context"
"errors"
"fmt"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
@ -11,8 +12,11 @@ import (
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/rpc"
)
var errBorEngineNotAvailable error = errors.New("Only available in Bor engine")
// GetRootHash returns root hash for given start and end block
func (b *EthAPIBackend) GetRootHash(ctx context.Context, starBlockNr uint64, endBlockNr uint64) (string, error) {
var api *bor.API
@ -24,7 +28,7 @@ func (b *EthAPIBackend) GetRootHash(ctx context.Context, starBlockNr uint64, end
}
if api == nil {
return "", errors.New("Only available in Bor engine")
return "", errBorEngineNotAvailable
}
root, err := api.GetRootHash(starBlockNr, endBlockNr)
@ -35,6 +39,70 @@ func (b *EthAPIBackend) GetRootHash(ctx context.Context, starBlockNr uint64, end
return root, nil
}
// GetRootHash returns root hash for given start and end block
func (b *EthAPIBackend) GetVoteOnHash(ctx context.Context, starBlockNr uint64, endBlockNr uint64, hash string, milestoneId string) (bool, error) {
var api *bor.API
for _, _api := range b.eth.Engine().APIs(b.eth.BlockChain()) {
if _api.Namespace == "bor" {
api = _api.Service.(*bor.API)
}
}
if api == nil {
return false, errBorEngineNotAvailable
}
//Confirmation of 16 blocks on the endblock
tipConfirmationBlockNr := endBlockNr + uint64(16)
//Check if tipConfirmation block exit
_, err := b.BlockByNumber(ctx, rpc.BlockNumber(tipConfirmationBlockNr))
if err != nil {
return false, errTipConfirmationBlock
}
//Check if end block exist
localEndBlock, err := b.BlockByNumber(ctx, rpc.BlockNumber(endBlockNr))
if err != nil {
return false, errEndBlock
}
localEndBlockHash := localEndBlock.Hash().String()
downloader := b.eth.handler.downloader
isLocked := downloader.LockMutex(endBlockNr)
if !isLocked {
downloader.UnlockMutex(false, "", endBlockNr, common.Hash{})
return false, errors.New("Whitelisted number or locked sprint number is more than the received end block number")
}
if localEndBlockHash != hash {
downloader.UnlockMutex(false, "", endBlockNr, common.Hash{})
return false, fmt.Errorf("Hash mismatch: localChainHash %s, milestoneHash %s", localEndBlockHash, hash)
}
ethHandler := (*ethHandler)(b.eth.handler)
bor, ok := ethHandler.chain.Engine().(*bor.Bor)
if !ok {
return false, fmt.Errorf("Bor not available")
}
err = bor.HeimdallClient.FetchMilestoneID(ctx, milestoneId)
if err != nil {
downloader.UnlockMutex(false, "", endBlockNr, common.Hash{})
return false, fmt.Errorf("Milestone ID doesn't exist in Heimdall")
}
downloader.UnlockMutex(true, milestoneId, endBlockNr, localEndBlock.Hash())
return true, nil
}
// GetBorBlockReceipt returns bor block receipt
func (b *EthAPIBackend) GetBorBlockReceipt(ctx context.Context, hash common.Hash) (*types.Receipt, error) {
receipt := b.eth.blockchain.GetBorReceiptByHash(hash)

View file

@ -1,61 +1,171 @@
// nolint
package eth
import (
"context"
"errors"
"fmt"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/checkpoint"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/rpc"
)
type checkpointVerifier struct {
verify func(ctx context.Context, handler *ethHandler, checkpoint *checkpoint.Checkpoint) (string, error)
var (
// errMissingBlocks is returned when we don't have the blocks locally, yet.
errMissingBlocks = errors.New("missing blocks")
// errRootHash is returned when we aren't able to calculate the root hash
// locally for a range of blocks.
errRootHash = errors.New("failed to get local root hash")
// errHashMismatch is returned when the local hash doesn't match
// with the hash of checkpoint/milestone. It is the root hash of blocks
// in case of checkpoint and is end block hash in case of milestones.
errHashMismatch = errors.New("hash mismatch")
// errEndBlock is returned when we're unable to fetch a block locally.
errEndBlock = errors.New("failed to get end block")
// errEndBlock is returned when we're unable to fetch a block locally.
errTipConfirmationBlock = errors.New("failed to get tip confirmation block")
// errBlockNumberConversion is returned when we get err in parsing hexautil block number
errBlockNumberConversion = errors.New("failed to parse the block number")
//Metrics for collecting the rewindLength
rewindLengthMeter = metrics.NewRegisteredMeter("chain/autorewind/length", nil)
)
type borVerifier struct {
verify func(ctx context.Context, eth *Ethereum, handler *ethHandler, start uint64, end uint64, hash string, isCheckpoint bool) (string, error)
}
func newCheckpointVerifier(verifyFn func(ctx context.Context, handler *ethHandler, checkpoint *checkpoint.Checkpoint) (string, error)) *checkpointVerifier {
if verifyFn != nil {
return &checkpointVerifier{verifyFn}
func newBorVerifier() *borVerifier {
return &borVerifier{borVerify}
}
func borVerify(ctx context.Context, eth *Ethereum, handler *ethHandler, start uint64, end uint64, hash string, isCheckpoint bool) (string, error) {
str := "milestone"
if isCheckpoint {
str = "checkpoint"
}
verifyFn = func(ctx context.Context, handler *ethHandler, checkpoint *checkpoint.Checkpoint) (string, error) {
// check if we have the given blocks
currentBlock := eth.BlockChain().CurrentBlock()
if currentBlock == nil {
log.Debug(fmt.Sprintf("Failed to fetch current block from blockchain while verifying incoming %s", str))
return hash, errMissingBlocks
}
head := currentBlock.Number.Uint64()
if head < end {
log.Debug(fmt.Sprintf("Current head block behind incoming %s block", str), "head", head, "end block", end)
return hash, errMissingBlocks
}
var localHash string
// verify the hash
if isCheckpoint {
var err error
// in case of checkpoint get the rootHash
localHash, err = handler.ethAPI.GetRootHash(ctx, start, end)
if err != nil {
log.Debug("Failed to get root hash of given block range while whitelisting checkpoint", "start", start, "end", end, "err", err)
return hash, errRootHash
}
} else {
// in case of milestone(isCheckpoint==false) get the hash of endBlock
block, err := handler.ethAPI.GetBlockByNumber(ctx, rpc.BlockNumber(end), false)
if err != nil {
log.Debug("Failed to get end block hash while whitelisting milestone", "number", end, "err", err)
return hash, errEndBlock
}
localHash = fmt.Sprintf("%v", block["hash"])[2:]
}
//nolint
if localHash != hash {
if isCheckpoint {
log.Warn("Root hash mismatch while whitelisting checkpoint", "expected", localHash, "got", hash)
} else {
log.Warn("End block hash mismatch while whitelisting milestone", "expected", localHash, "got", hash)
}
ethHandler := (*ethHandler)(eth.handler)
var (
startBlock = checkpoint.StartBlock.Uint64()
endBlock = checkpoint.EndBlock.Uint64()
rewindTo uint64
doExist bool
)
// check if we have the checkpoint blocks
//nolint:contextcheck
head := handler.ethAPI.BlockNumber()
if head < hexutil.Uint64(endBlock) {
log.Debug("Head block behind checkpoint block", "head", head, "checkpoint end block", endBlock)
return "", errMissingCheckpoint
if doExist, rewindTo, _ = ethHandler.downloader.GetWhitelistedMilestone(); doExist {
} else if doExist, rewindTo, _ = ethHandler.downloader.GetWhitelistedCheckpoint(); doExist {
} else {
if start <= 0 {
rewindTo = 0
} else {
rewindTo = start - 1
}
}
// verify the root hash of checkpoint
roothash, err := handler.ethAPI.GetRootHash(ctx, startBlock, endBlock)
if err != nil {
log.Debug("Failed to get root hash of checkpoint while whitelisting", "err", err)
return "", errRootHash
if head-rewindTo > 255 {
rewindTo = head - 255
}
if roothash != checkpoint.RootHash.String()[2:] {
log.Warn("Checkpoint root hash mismatch while whitelisting", "expected", checkpoint.RootHash.String()[2:], "got", roothash)
return "", errCheckpointRootHashMismatch
if isCheckpoint {
log.Warn("Rewinding chain due to checkpoint root hash mismatch", "number", rewindTo)
} else {
log.Warn("Rewinding chain due to milestone endblock hash mismatch", "number", rewindTo)
}
// fetch the end checkpoint block hash
block, err := handler.ethAPI.GetBlockByNumber(ctx, rpc.BlockNumber(endBlock), false)
if err != nil {
log.Debug("Failed to get end block hash of checkpoint while whitelisting", "err", err)
return "", errEndBlock
}
rewindBack(eth, head, rewindTo)
hash := fmt.Sprintf("%v", block["hash"])
return hash, nil
return hash, errHashMismatch
}
// fetch the end block hash
block, err := handler.ethAPI.GetBlockByNumber(ctx, rpc.BlockNumber(end), false)
if err != nil {
log.Debug("Failed to get end block hash while whitelisting", "err", err)
return hash, errEndBlock
}
hash = fmt.Sprintf("%v", block["hash"])
return hash, nil
}
// Stop the miner if the mining process is running and rewind back the chain
func rewindBack(eth *Ethereum, head uint64, rewindTo uint64) {
if eth.Miner().Mining() {
ch := make(chan struct{})
eth.Miner().Stop(ch)
<-ch
rewind(eth, head, rewindTo)
eth.Miner().Start()
} else {
rewind(eth, head, rewindTo)
}
}
func rewind(eth *Ethereum, head uint64, rewindTo uint64) {
err := eth.blockchain.SetHead(rewindTo)
if err != nil {
log.Error("Error while rewinding the chain", "to", rewindTo, "err", err)
} else {
rewindLengthMeter.Mark(int64(head - rewindTo))
}
return &checkpointVerifier{verifyFn}
}

View file

@ -216,6 +216,8 @@ type BlockChain interface {
// Snapshots returns the blockchain snapshot tree to paused it during sync.
Snapshots() *snapshot.Tree
GetChainConfig() *params.ChainConfig
// TrieDB retrieves the low level trie database used for interacting
// with trie nodes.
TrieDB() *trie.Database
@ -367,8 +369,7 @@ func (d *Downloader) LegacySync(id string, head common.Hash, td, ttd *big.Int, m
if errors.Is(err, errInvalidChain) || errors.Is(err, errBadPeer) || errors.Is(err, errTimeout) ||
errors.Is(err, errStallingPeer) || errors.Is(err, errUnsyncedPeer) || errors.Is(err, errEmptyHeaderSet) ||
errors.Is(err, errPeersUnavailable) || errors.Is(err, errTooOld) || errors.Is(err, errInvalidAncestor) ||
errors.Is(err, whitelist.ErrCheckpointMismatch) {
errors.Is(err, errPeersUnavailable) || errors.Is(err, errTooOld) || errors.Is(err, errInvalidAncestor) {
log.Warn("Synchronisation failed, dropping peer", "peer", id, "err", err)
if d.dropPeer == nil {
@ -386,7 +387,13 @@ func (d *Downloader) LegacySync(id string, head common.Hash, td, ttd *big.Int, m
return err // This is an expected fault, don't keep printing it in a spin-loop
}
log.Warn("Synchronisation failed, retrying", "err", err)
// Warn in case of any error thrown by whitelisting module
if errors.Is(err, whitelist.ErrNoRemote) || errors.Is(err, whitelist.ErrMismatch) {
log.Warn("Synchronisation failed due to whitelist validation", "peer", id, "err", err)
return err
}
log.Warn("Synchronisation failed, retrying", "peer", id, "err", err)
return err
}
@ -895,9 +902,10 @@ func (d *Downloader) getFetchHeadersByNumber(p *peerConnection) func(number uint
// In the rare scenario when we ended up on a long reorganisation (i.e. none of
// the head links match), we do a binary search to find the common ancestor.
func (d *Downloader) findAncestor(p *peerConnection, remoteHeader *types.Header) (uint64, error) {
// Check the validity of peer from which the chain is to be downloaded
if d.ChainValidator != nil {
if _, err := d.IsValidPeer(remoteHeader, d.getFetchHeadersByNumber(p)); err != nil {
if _, err := d.IsValidPeer(d.getFetchHeadersByNumber(p)); err != nil {
return 0, err
}
}
@ -1989,6 +1997,11 @@ func (d *Downloader) DeliverSnapPacket(peer *snap.Peer, packet snap.Packet) erro
}
}
// GetWhitelistService returns the pointer to the whitelist service
func (d *Downloader) GetWhitelistService() ethereum.ChainValidator {
return d.ChainValidator
}
// readHeaderRange returns a list of headers, using the given last header as the base,
// and going backwards towards genesis. This method assumes that the caller already has
// placed a reasonable cap on count.

View file

@ -95,7 +95,7 @@ func newTesterWithNotification(t *testing.T, success func()) *downloadTester {
}
//nolint: staticcheck
tester.downloader = New(0, db, new(event.TypeMux), tester.chain, nil, tester.dropPeer, success, whitelist.NewService(10))
tester.downloader = New(0, db, new(event.TypeMux), tester.chain, nil, tester.dropPeer, success, whitelist.NewService(db))
return tester
}
@ -1965,7 +1965,7 @@ func newWhitelistFake(validate func(count int) (bool, error)) *whitelistFake {
// IsValidPeer is the mock function which the downloader will use to validate the chain
// to be received from a peer.
func (w *whitelistFake) IsValidPeer(_ *types.Header, _ func(number uint64, amount int, skip int, reverse bool) ([]*types.Header, []common.Hash, error)) (bool, error) {
func (w *whitelistFake) IsValidPeer(_ func(number uint64, amount int, skip int, reverse bool) ([]*types.Header, []common.Hash, error)) (bool, error) {
defer func() {
w.count++
}()
@ -1978,13 +1978,33 @@ func (w *whitelistFake) IsValidChain(current *types.Header, headers []*types.Hea
}
func (w *whitelistFake) ProcessCheckpoint(_ uint64, _ common.Hash) {}
func (w *whitelistFake) GetCheckpointWhitelist() map[uint64]common.Hash {
return nil
func (w *whitelistFake) GetWhitelistedCheckpoint() (bool, uint64, common.Hash) {
return false, 0, common.Hash{}
}
func (w *whitelistFake) PurgeCheckpointWhitelist() {}
func (w *whitelistFake) PurgeWhitelistedCheckpoint() {}
func (w *whitelistFake) ProcessMilestone(_ uint64, _ common.Hash) {}
func (w *whitelistFake) ProcessFutureMilestone(_ uint64, _ common.Hash) {}
func (w *whitelistFake) GetWhitelistedMilestone() (bool, uint64, common.Hash) {
return false, 0, common.Hash{}
}
func (w *whitelistFake) PurgeWhitelistedMilestone() {}
func (w *whitelistFake) GetCheckpoints(current, sidechainHeader *types.Header, sidechainCheckpoints []*types.Header) (map[uint64]*types.Header, error) {
return map[uint64]*types.Header{}, nil
}
func (w *whitelistFake) LockMutex(endBlockNum uint64) bool {
return false
}
func (w *whitelistFake) UnlockMutex(doLock bool, milestoneId string, endBlockNum uint64, endBlockHash common.Hash) {
}
func (w *whitelistFake) UnlockSprint(endBlockNum uint64) {
}
func (w *whitelistFake) RemoveMilestoneID(milestoneId string) {
}
func (w *whitelistFake) GetMilestoneIDsList() []string {
return nil
}
// TestFakedSyncProgress66WhitelistMismatch tests if in case of whitelisted
// checkpoint mismatch with opposite peer, the sync should fail.
@ -1996,7 +2016,7 @@ func TestFakedSyncProgress66WhitelistMismatch(t *testing.T) {
tester := newTester(t)
validate := func(count int) (bool, error) {
return false, whitelist.ErrCheckpointMismatch
return false, whitelist.ErrMismatch
}
tester.downloader.ChainValidator = newWhitelistFake(validate)
@ -2049,7 +2069,7 @@ func TestFakedSyncProgress66NoRemoteCheckpoint(t *testing.T) {
validate := func(count int) (bool, error) {
// only return the `ErrNoRemoteCheckpoint` error for the first call
if count == 0 {
return false, whitelist.ErrNoRemoteCheckpoint
return false, whitelist.ErrNoRemote
}
return true, nil
@ -2065,7 +2085,7 @@ func TestFakedSyncProgress66NoRemoteCheckpoint(t *testing.T) {
// Synchronise with the peer and make sure all blocks were retrieved
// Should fail in first attempt
if err := tester.sync("light", nil, mode); err != nil {
assert.Equal(t, whitelist.ErrNoRemoteCheckpoint, err, "failed synchronisation")
assert.Equal(t, whitelist.ErrNoRemote, err, "failed synchronisation")
}
// Try syncing again, should succeed

View file

@ -0,0 +1,67 @@
package whitelist
import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/metrics"
)
type checkpoint struct {
finality[*rawdb.Checkpoint]
}
type checkpointService interface {
finalityService
}
var (
//Metrics for collecting the whitelisted milestone number
whitelistedCheckpointNumberMeter = metrics.NewRegisteredGauge("chain/checkpoint/latest", nil)
//Metrics for collecting the number of invalid chains received
CheckpointChainMeter = metrics.NewRegisteredMeter("chain/checkpoint/isvalidchain", nil)
//Metrics for collecting the number of valid peers received
CheckpointPeerMeter = metrics.NewRegisteredMeter("chain/checkpoint/isvalidpeer", nil)
)
// IsValidChain checks the validity of chain by comparing it
// against the local checkpoint entry
func (w *checkpoint) IsValidChain(currentHeader *types.Header, chain []*types.Header) (bool, error) {
w.finality.RLock()
defer w.finality.RUnlock()
res, err := w.finality.IsValidChain(currentHeader, chain)
if res {
CheckpointChainMeter.Mark(int64(1))
} else {
CheckpointPeerMeter.Mark(int64(-1))
}
return res, err
}
// IsValidPeer checks if the chain we're about to receive from a peer is valid or not
// in terms of reorgs. We won't reorg beyond the last bor finality submitted to mainchain.
func (w *checkpoint) IsValidPeer(fetchHeadersByNumber func(number uint64, amount int, skip int, reverse bool) ([]*types.Header, []common.Hash, error)) (bool, error) {
res, err := w.finality.IsValidPeer(fetchHeadersByNumber)
if res {
CheckpointPeerMeter.Mark(int64(1))
} else {
CheckpointPeerMeter.Mark(int64(-1))
}
return res, err
}
func (w *checkpoint) Process(block uint64, hash common.Hash) {
w.finality.Lock()
defer w.finality.Unlock()
w.finality.Process(block, hash)
whitelistedCheckpointNumberMeter.Update(int64(block))
}

View file

@ -0,0 +1,96 @@
package whitelist
import (
"fmt"
"sync"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
)
type finality[T rawdb.BlockFinality[T]] struct {
sync.RWMutex
db ethdb.Database
Hash common.Hash // Whitelisted Hash, populated by reaching out to heimdall
Number uint64 // Number , populated by reaching out to heimdall
interval uint64 // Interval, until which we can allow importing
doExist bool
}
type finalityService interface {
IsValidPeer(fetchHeadersByNumber func(number uint64, amount int, skip int, reverse bool) ([]*types.Header, []common.Hash, error)) (bool, error)
IsValidChain(currentHeader *types.Header, chain []*types.Header) (bool, error)
Get() (bool, uint64, common.Hash)
Process(block uint64, hash common.Hash)
Purge()
}
// IsValidPeer checks if the chain we're about to receive from a peer is valid or not
// in terms of reorgs. We won't reorg beyond the last bor finality submitted to mainchain.
func (f *finality[T]) IsValidPeer(fetchHeadersByNumber func(number uint64, amount int, skip int, reverse bool) ([]*types.Header, []common.Hash, error)) (bool, error) {
// We want to validate the chain by comparing the last finalized block
f.RLock()
doExist := f.doExist
number := f.Number
hash := f.Hash
f.RUnlock()
return isValidPeer(fetchHeadersByNumber, doExist, number, hash)
}
// IsValidChain checks the validity of chain by comparing it
// against the local checkpoint entry
// todo: need changes
func (f *finality[T]) IsValidChain(currentHeader *types.Header, chain []*types.Header) (bool, error) {
// Return if we've received empty chain
if len(chain) == 0 {
return false, nil
}
res, err := isValidChain(currentHeader, chain, f.doExist, f.Number, f.Hash)
return res, err
}
func (f *finality[T]) Process(block uint64, hash common.Hash) {
f.doExist = true
f.Hash = hash
f.Number = block
err := rawdb.WriteLastFinality[T](f.db, block, hash)
if err != nil {
log.Error("Error in writing whitelist state to db", "err", err)
}
}
// Get returns the existing whitelisted
// entries of checkpoint of the form (doExist,block number,block hash.)
func (f *finality[T]) Get() (bool, uint64, common.Hash) {
f.RLock()
defer f.RUnlock()
if f.doExist {
return f.doExist, f.Number, f.Hash
}
block, hash, err := rawdb.ReadFinality[T](f.db)
if err != nil {
fmt.Println("Error while reading whitelisted state from Db", "err", err)
return false, f.Number, f.Hash
}
return true, block, hash
}
// Purge purges the whitlisted checkpoint
func (f *finality[T]) Purge() {
f.Lock()
defer f.Unlock()
f.doExist = false
}

View file

@ -0,0 +1,315 @@
package whitelist
import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/flags"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
)
type milestone struct {
finality[*rawdb.Milestone]
LockedMilestoneNumber uint64 // Locked sprint number
LockedMilestoneHash common.Hash //Hash for the locked endBlock
Locked bool //
LockedMilestoneIDs map[string]struct{} //list of milestone ids
FutureMilestoneList map[uint64]common.Hash // Future Milestone list
FutureMilestoneOrder []uint64 // Future Milestone Order
MaxCapacity int //Capacity of future Milestone list
}
type milestoneService interface {
finalityService
GetMilestoneIDsList() []string
RemoveMilestoneID(milestoneId string)
LockMutex(endBlockNum uint64) bool
UnlockMutex(doLock bool, milestoneId string, endBlockNum uint64, endBlockHash common.Hash)
UnlockSprint(endBlockNum uint64)
ProcessFutureMilestone(num uint64, hash common.Hash)
}
var (
//Metrics for collecting the whitelisted milestone number
whitelistedMilestoneMeter = metrics.NewRegisteredGauge("chain/milestone/latest", nil)
//Metrics for collecting the future milestone number
FutureMilestoneMeter = metrics.NewRegisteredGauge("chain/milestone/future", nil)
//Metrics for collecting the length of the MilestoneIds map
MilestoneIdsLengthMeter = metrics.NewRegisteredGauge("chain/milestone/idslength", nil)
//Metrics for collecting the number of valid chains received
MilestoneChainMeter = metrics.NewRegisteredMeter("chain/milestone/isvalidchain", nil)
//Metrics for collecting the number of valid peers received
MilestonePeerMeter = metrics.NewRegisteredMeter("chain/milestone/isvalidpeer", nil)
)
// IsValidChain checks the validity of chain by comparing it
// against the local milestone entries
func (m *milestone) IsValidChain(currentHeader *types.Header, chain []*types.Header) (bool, error) {
//Checking for the milestone flag
if !flags.Milestone {
return true, nil
}
m.finality.RLock()
defer m.finality.RUnlock()
var isValid bool = false
defer func() {
if isValid {
MilestoneChainMeter.Mark(int64(1))
} else {
MilestoneChainMeter.Mark(int64(-1))
}
}()
res, err := m.finality.IsValidChain(currentHeader, chain)
if !res {
isValid = false
return isValid, err
}
if m.Locked && !m.IsReorgAllowed(chain, m.LockedMilestoneNumber, m.LockedMilestoneHash) {
isValid = false
return isValid, nil
}
if !m.IsFutureMilestoneCompatible(chain) {
isValid = false
return isValid, nil
}
isValid = true
return isValid, nil
}
// IsValidPeer checks if the chain we're about to receive from a peer is valid or not
// in terms of reorgs. We won't reorg beyond the last bor finality submitted to mainchain.
func (m *milestone) IsValidPeer(fetchHeadersByNumber func(number uint64, amount int, skip int, reverse bool) ([]*types.Header, []common.Hash, error)) (bool, error) {
if !flags.Milestone {
return true, nil
}
res, err := m.finality.IsValidPeer(fetchHeadersByNumber)
if res {
MilestonePeerMeter.Mark(int64(1))
} else {
MilestonePeerMeter.Mark(int64(-1))
}
return res, err
}
func (m *milestone) Process(block uint64, hash common.Hash) {
m.finality.Lock()
defer m.finality.Unlock()
m.finality.Process(block, hash)
for i := 0; i < len(m.FutureMilestoneOrder); i++ {
if m.FutureMilestoneOrder[i] <= block {
m.dequeueFutureMilestone()
} else {
break
}
}
whitelistedMilestoneMeter.Update(int64(block))
m.UnlockSprint(block)
}
// This function will Lock the mutex at the time of voting
// fixme: get rid of it
func (m *milestone) LockMutex(endBlockNum uint64) bool {
m.finality.Lock()
if m.doExist && endBlockNum <= m.Number { //if endNum is less than whitelisted milestone, then we won't lock the sprint
log.Debug("endBlockNumber is less than or equal to latesMilestoneNumber", "endBlock Number", endBlockNum, "LatestMilestone Number", m.Number)
return false
}
if m.Locked && endBlockNum < m.LockedMilestoneNumber {
log.Debug("endBlockNum is less than locked milestone number", "endBlock Number", endBlockNum, "Locked Milestone Number", m.LockedMilestoneNumber)
return false
}
return true
}
// This function will unlock the mutex locked in LockMutex
// fixme: get rid of it
func (m *milestone) UnlockMutex(doLock bool, milestoneId string, endBlockNum uint64, endBlockHash common.Hash) {
m.Locked = m.Locked || doLock
if doLock {
m.UnlockSprint(m.LockedMilestoneNumber)
m.Locked = true
m.LockedMilestoneHash = endBlockHash
m.LockedMilestoneNumber = endBlockNum
m.LockedMilestoneIDs[milestoneId] = struct{}{}
}
err := rawdb.WriteLockField(m.db, m.Locked, m.LockedMilestoneNumber, m.LockedMilestoneHash, m.LockedMilestoneIDs)
if err != nil {
log.Error("Error in writing lock data of milestone to db", "err", err)
}
milestoneIDLength := int64(len(m.LockedMilestoneIDs))
MilestoneIdsLengthMeter.Update(milestoneIDLength)
m.finality.Unlock()
}
// This function will unlock the locked sprint
func (m *milestone) UnlockSprint(endBlockNum uint64) {
if endBlockNum < m.LockedMilestoneNumber {
return
}
m.Locked = false
m.purgeMilestoneIDsList()
err := rawdb.WriteLockField(m.db, m.Locked, m.LockedMilestoneNumber, m.LockedMilestoneHash, m.LockedMilestoneIDs)
if err != nil {
log.Error("Error in writing lock data of milestone to db", "err", err)
}
}
// This function will remove the stored milestoneID
func (m *milestone) RemoveMilestoneID(milestoneId string) {
m.finality.Lock()
delete(m.LockedMilestoneIDs, milestoneId)
if len(m.LockedMilestoneIDs) == 0 {
m.Locked = false
}
err := rawdb.WriteLockField(m.db, m.Locked, m.LockedMilestoneNumber, m.LockedMilestoneHash, m.LockedMilestoneIDs)
if err != nil {
log.Error("Error in writing lock data of milestone to db", "err", err)
}
m.finality.Unlock()
}
// This will check whether the incoming chain matches the locked sprint hash
func (m *milestone) IsReorgAllowed(chain []*types.Header, lockedMilestoneNumber uint64, lockedMilestoneHash common.Hash) bool {
if chain[len(chain)-1].Number.Uint64() <= lockedMilestoneNumber { //Can't reorg if the end block of incoming
return false //chain is less than locked sprint number
}
for i := 0; i < len(chain); i++ {
if chain[i].Number.Uint64() == lockedMilestoneNumber {
return chain[i].Hash() == lockedMilestoneHash
}
}
return true
}
// This will return the list of milestoneIDs stored.
func (m *milestone) GetMilestoneIDsList() []string {
m.finality.RLock()
defer m.finality.RUnlock()
// fixme: use generics :)
keys := make([]string, 0, len(m.LockedMilestoneIDs))
for key := range m.LockedMilestoneIDs {
keys = append(keys, key)
}
return keys
}
// This is remove the milestoneIDs stored in the list.
func (m *milestone) purgeMilestoneIDsList() {
m.LockedMilestoneIDs = make(map[string]struct{})
}
func (m *milestone) IsFutureMilestoneCompatible(chain []*types.Header) bool {
//Tip of the received chain
chainTipNumber := chain[len(chain)-1].Number.Uint64()
for i := len(m.FutureMilestoneOrder) - 1; i >= 0; i-- {
//Finding out the highest future milestone number
//which is less or equal to received chain tip
if chainTipNumber >= m.FutureMilestoneOrder[i] {
//Looking for the received chain 's particular block number(matching future milestone number)
for j := len(chain) - 1; j >= 0; j-- {
if chain[j].Number.Uint64() == m.FutureMilestoneOrder[i] {
endBlockNum := m.FutureMilestoneOrder[i]
endBlockHash := m.FutureMilestoneList[endBlockNum]
//Checking the received chain matches with future milestone
return chain[j].Hash() == endBlockHash
}
}
}
}
return true
}
func (m *milestone) ProcessFutureMilestone(num uint64, hash common.Hash) {
if len(m.FutureMilestoneOrder) < m.MaxCapacity {
m.enqueueFutureMilestone(num, hash)
}
if num < m.LockedMilestoneNumber {
return
}
m.Locked = false
m.purgeMilestoneIDsList()
err := rawdb.WriteLockField(m.db, m.Locked, m.LockedMilestoneNumber, m.LockedMilestoneHash, m.LockedMilestoneIDs)
if err != nil {
log.Error("Error in writing lock data of milestone to db", "err", err)
}
}
// EnqueueFutureMilestone add the future milestone to the list
func (m *milestone) enqueueFutureMilestone(key uint64, hash common.Hash) {
if _, ok := m.FutureMilestoneList[key]; ok {
log.Debug("Future milestone already exist", "endBlockNumber", key, "futureMilestoneHash", hash)
return
}
log.Debug("Enqueing new future milestone", "endBlockNumber", key, "futureMilestoneHash", hash)
m.FutureMilestoneList[key] = hash
m.FutureMilestoneOrder = append(m.FutureMilestoneOrder, key)
err := rawdb.WriteFutureMilestoneList(m.db, m.FutureMilestoneOrder, m.FutureMilestoneList)
if err != nil {
log.Error("Error in writing future milestone data to db", "err", err)
}
FutureMilestoneMeter.Update(int64(key))
}
// DequeueFutureMilestone remove the future milestone entry from the list.
func (m *milestone) dequeueFutureMilestone() {
delete(m.FutureMilestoneList, m.FutureMilestoneOrder[0])
m.FutureMilestoneOrder = m.FutureMilestoneOrder[1:]
err := rawdb.WriteFutureMilestoneList(m.db, m.FutureMilestoneOrder, m.FutureMilestoneList)
if err != nil {
log.Error("Error in writing future milestone data to db", "err", err)
}
}

View file

@ -3,135 +3,150 @@ package whitelist
import (
"errors"
"fmt"
"sync"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/ethdb"
)
// Checkpoint whitelist
type Service struct {
m sync.Mutex
checkpointWhitelist map[uint64]common.Hash // Checkpoint whitelist, populated by reaching out to heimdall
checkpointOrder []uint64 // Checkpoint order, populated by reaching out to heimdall
maxCapacity uint // Max capacity of the whitelist
checkpointInterval uint64 // Checkpoint interval, until which we can allow importing
}
func NewService(maxCapacity uint) *Service {
return &Service{
checkpointWhitelist: make(map[uint64]common.Hash),
checkpointOrder: []uint64{},
maxCapacity: maxCapacity,
checkpointInterval: 256, // TODO: make it configurable through params?
}
}
var (
ErrMismatch = errors.New("mismatch error")
ErrNoRemote = errors.New("remote peer doesn't have a target block number")
ErrCheckpointMismatch = errors.New("checkpoint mismatch")
ErrLongFutureChain = errors.New("received future chain of unacceptable length")
ErrNoRemoteCheckpoint = errors.New("remote peer doesn't have a checkpoint")
)
// IsValidPeer checks if the chain we're about to receive from a peer is valid or not
// in terms of reorgs. We won't reorg beyond the last bor checkpoint submitted to mainchain.
func (w *Service) IsValidPeer(remoteHeader *types.Header, fetchHeadersByNumber func(number uint64, amount int, skip int, reverse bool) ([]*types.Header, []common.Hash, error)) (bool, error) {
// We want to validate the chain by comparing the last checkpointed block
// we're storing in `checkpointWhitelist` with the peer's block.
//
// Check for availaibility of the last checkpointed block.
// This can be also be empty if our heimdall is not responding
// or we're running without it.
if len(w.checkpointWhitelist) == 0 {
// worst case, we don't have the checkpoints in memory
return true, nil
}
// Fetch the last checkpoint entry
lastCheckpointBlockNum := w.checkpointOrder[len(w.checkpointOrder)-1]
lastCheckpointBlockHash := w.checkpointWhitelist[lastCheckpointBlockNum]
// todo: we can extract this as an interface and mock as well or just test IsValidChain in isolation from downloader passing fake fetchHeadersByNumber functions
headers, hashes, err := fetchHeadersByNumber(lastCheckpointBlockNum, 1, 0, false)
if err != nil {
return false, fmt.Errorf("%w: last checkpoint %d, err %v", ErrNoRemoteCheckpoint, lastCheckpointBlockNum, err)
}
if len(headers) == 0 {
return false, fmt.Errorf("%w: last checkpoint %d", ErrNoRemoteCheckpoint, lastCheckpointBlockNum)
}
reqBlockNum := headers[0].Number.Uint64()
reqBlockHash := hashes[0]
// Check against the checkpointed blocks
if reqBlockNum == lastCheckpointBlockNum && reqBlockHash == lastCheckpointBlockHash {
return true, nil
}
return false, ErrCheckpointMismatch
type Service struct {
checkpointService
milestoneService
}
// IsValidChain checks the validity of chain by comparing it
// against the local checkpoint entries
func (w *Service) IsValidChain(currentHeader *types.Header, chain []*types.Header) (bool, error) {
// Check if we have checkpoints to validate incoming chain in memory
if len(w.checkpointWhitelist) == 0 {
// We don't have any entries, no additional validation will be possible
return true, nil
func NewService(db ethdb.Database) *Service {
var checkpointDoExist = true
checkpointNumber, checkpointHash, err := rawdb.ReadFinality[*rawdb.Checkpoint](db)
if err != nil {
checkpointDoExist = false
}
// Return if we've received empty chain
if len(chain) == 0 {
return false, nil
var milestoneDoExist = true
milestoneNumber, milestoneHash, err := rawdb.ReadFinality[*rawdb.Milestone](db)
if err != nil {
milestoneDoExist = false
}
var (
oldestCheckpointNumber uint64 = w.checkpointOrder[0]
current uint64 = currentHeader.Number.Uint64()
)
// Check if we have whitelist entries in required range
if chain[len(chain)-1].Number.Uint64() < oldestCheckpointNumber {
// We have future whitelisted entries, so no additional validation will be possible
// This case will occur when bor is in middle of sync, but heimdall is ahead/fully synced.
return true, nil
locked, lockedMilestoneNumber, lockedMilestoneHash, lockedMilestoneIDs, err := rawdb.ReadLockField(db)
if err != nil || !locked {
locked = false
lockedMilestoneIDs = make(map[string]struct{})
}
// Split the chain into past and future chain
pastChain, _ := splitChain(current, chain)
order, list, err := rawdb.ReadFutureMilestoneList(db)
if err != nil {
order = make([]uint64, 0)
list = make(map[uint64]common.Hash)
}
// Note: Do not act on future chain and allow importing all kinds of future chains.
return &Service{
&checkpoint{
finality[*rawdb.Checkpoint]{
doExist: checkpointDoExist,
Number: checkpointNumber,
Hash: checkpointHash,
interval: 256,
db: db,
},
},
// Add an offset to future chain if it's not in continuity
// offset := 0
// if len(futureChain) != 0 {
// offset += int(futureChain[0].Number.Uint64()-currentHeader.Number.Uint64()) - 1
// }
&milestone{
finality: finality[*rawdb.Milestone]{
doExist: milestoneDoExist,
Number: milestoneNumber,
Hash: milestoneHash,
interval: 256,
db: db,
},
// Don't accept future chain of unacceptable length (from current block)
// if len(futureChain)+offset > int(w.checkpointInterval) {
// return false, ErrLongFutureChain
// }
Locked: locked,
LockedMilestoneNumber: lockedMilestoneNumber,
LockedMilestoneHash: lockedMilestoneHash,
LockedMilestoneIDs: lockedMilestoneIDs,
FutureMilestoneList: list,
FutureMilestoneOrder: order,
MaxCapacity: 10,
},
}
}
// Iterate over the chain and validate against the last checkpoint
// It will handle all cases where the incoming chain has atleast one checkpoint
for i := len(pastChain) - 1; i >= 0; i-- {
if _, ok := w.checkpointWhitelist[pastChain[i].Number.Uint64()]; ok {
return pastChain[i].Hash() == w.checkpointWhitelist[pastChain[i].Number.Uint64()], nil
}
// IsValidPeer checks if the chain we're about to receive from a peer is valid or not
// in terms of reorgs. We won't reorg beyond the last bor checkpoint submitted to mainchain and last milestone voted in the heimdall
func (s *Service) IsValidPeer(fetchHeadersByNumber func(number uint64, amount int, skip int, reverse bool) ([]*types.Header, []common.Hash, error)) (bool, error) {
checkpointBool, err := s.checkpointService.IsValidPeer(fetchHeadersByNumber)
if !checkpointBool {
return checkpointBool, err
}
milestoneBool, err := s.milestoneService.IsValidPeer(fetchHeadersByNumber)
if !milestoneBool {
return milestoneBool, err
}
return true, nil
}
func (s *Service) PurgeWhitelistedCheckpoint() {
s.checkpointService.Purge()
}
func (s *Service) PurgeWhitelistedMilestone() {
s.milestoneService.Purge()
}
func (s *Service) GetWhitelistedCheckpoint() (bool, uint64, common.Hash) {
return s.checkpointService.Get()
}
func (s *Service) GetWhitelistedMilestone() (bool, uint64, common.Hash) {
return s.milestoneService.Get()
}
func (s *Service) ProcessMilestone(endBlockNum uint64, endBlockHash common.Hash) {
s.milestoneService.Process(endBlockNum, endBlockHash)
}
func (s *Service) ProcessCheckpoint(endBlockNum uint64, endBlockHash common.Hash) {
s.checkpointService.Process(endBlockNum, endBlockHash)
}
func (s *Service) IsValidChain(currentHeader *types.Header, chain []*types.Header) (bool, error) {
checkpointBool, err := s.checkpointService.IsValidChain(currentHeader, chain)
if !checkpointBool {
return checkpointBool, err
}
milestoneBool, err := s.milestoneService.IsValidChain(currentHeader, chain)
if !milestoneBool {
return milestoneBool, err
}
return true, nil
}
func (s *Service) GetMilestoneIDsList() []string {
return s.milestoneService.GetMilestoneIDsList()
}
func splitChain(current uint64, chain []*types.Header) ([]*types.Header, []*types.Header) {
var (
pastChain []*types.Header
futureChain []*types.Header
first uint64 = chain[0].Number.Uint64()
last uint64 = chain[len(chain)-1].Number.Uint64()
first = chain[0].Number.Uint64()
last = chain[len(chain)-1].Number.Uint64()
)
if current >= first {
@ -153,57 +168,68 @@ func splitChain(current uint64, chain []*types.Header) ([]*types.Header, []*type
return pastChain, futureChain
}
func (w *Service) ProcessCheckpoint(endBlockNum uint64, endBlockHash common.Hash) {
w.m.Lock()
defer w.m.Unlock()
w.enqueueCheckpointWhitelist(endBlockNum, endBlockHash)
// If size of checkpoint whitelist map is greater than 10, remove the oldest entry.
if w.length() > int(w.maxCapacity) {
w.dequeueCheckpointWhitelist()
//nolint:unparam
func isValidChain(currentHeader *types.Header, chain []*types.Header, doExist bool, number uint64, hash common.Hash) (bool, error) {
// Check if we have milestone to validate incoming chain in memory
if !doExist {
// We don't have any entry, no additional validation will be possible
return true, nil
}
}
// GetCheckpointWhitelist returns the existing whitelisted
// entries of checkpoint of the form block number -> block hash.
func (w *Service) GetCheckpointWhitelist() map[uint64]common.Hash {
w.m.Lock()
defer w.m.Unlock()
current := currentHeader.Number.Uint64()
return w.checkpointWhitelist
}
// PurgeCheckpointWhitelist purges data from checkpoint whitelist map
func (w *Service) PurgeCheckpointWhitelist() {
w.m.Lock()
defer w.m.Unlock()
w.checkpointWhitelist = make(map[uint64]common.Hash)
w.checkpointOrder = make([]uint64, 0)
}
// EnqueueWhitelistBlock enqueues blockNumber, blockHash to the checkpoint whitelist map
func (w *Service) enqueueCheckpointWhitelist(key uint64, val common.Hash) {
if _, ok := w.checkpointWhitelist[key]; !ok {
log.Debug("Enqueing new checkpoint whitelist", "block number", key, "block hash", val)
w.checkpointWhitelist[key] = val
w.checkpointOrder = append(w.checkpointOrder, key)
// Check if imported chain is less than whitelisted number
if chain[len(chain)-1].Number.Uint64() < number {
if current >= number { //If current tip of the chain is greater than whitelist number then return false
return false, nil
} else {
return true, nil
}
}
}
// DequeueWhitelistBlock dequeues block, blockhash from the checkpoint whitelist map
func (w *Service) dequeueCheckpointWhitelist() {
if len(w.checkpointOrder) > 0 {
log.Debug("Dequeing checkpoint whitelist", "block number", w.checkpointOrder[0], "block hash", w.checkpointWhitelist[w.checkpointOrder[0]])
// Split the chain into past and future chain
pastChain, _ := splitChain(current, chain)
delete(w.checkpointWhitelist, w.checkpointOrder[0])
w.checkpointOrder = w.checkpointOrder[1:] // fixme: this slice is growing infinitely and never will be released. also a panic is possible if the last element is going to be removed
// Iterate over the chain and validate against the last milestone
// It will handle all cases when the incoming chain has atleast one milestone
for i := len(pastChain) - 1; i >= 0; i-- {
if pastChain[i].Number.Uint64() == number {
res := pastChain[i].Hash() == hash
return res, nil
}
}
return true, nil
}
// length returns the len of the whitelist.
func (w *Service) length() int {
return len(w.checkpointWhitelist)
// FIXME: remoteHeader is not used
func isValidPeer(fetchHeadersByNumber func(number uint64, amount int, skip int, reverse bool) ([]*types.Header, []common.Hash, error), doExist bool, number uint64, hash common.Hash) (bool, error) {
// Check for availaibility of the last milestone block.
// This can be also be empty if our heimdall is not responding
// or we're running without it.
if !doExist {
// worst case, we don't have the milestone in memory
return true, nil
}
// todo: we can extract this as an interface and mock as well or just test IsValidChain in isolation from downloader passing fake fetchHeadersByNumber functions
headers, hashes, err := fetchHeadersByNumber(number, 1, 0, false)
if err != nil {
return false, fmt.Errorf("%w: last whitelisted block number %d, err %v", ErrNoRemote, number, err)
}
if len(headers) == 0 {
return false, fmt.Errorf("%w: last whitlisted block number %d", ErrNoRemote, number)
}
reqBlockNum := headers[0].Number.Uint64()
reqBlockHash := hashes[0]
// Check against the whitelisted blocks
if reqBlockNum == number && reqBlockHash == hash {
return true, nil
}
return false, ErrMismatch
}

View file

@ -1,3 +1,4 @@
// nolint
package whitelist
import (
@ -9,35 +10,243 @@ import (
"testing"
"time"
"pgregory.net/rapid"
"github.com/stretchr/testify/require"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
)
// NewMockService creates a new mock whitelist service
func NewMockService(maxCapacity uint, checkpointInterval uint64) *Service {
func NewMockService(db ethdb.Database) *Service {
return &Service{
checkpointWhitelist: make(map[uint64]common.Hash),
checkpointOrder: []uint64{},
maxCapacity: maxCapacity,
checkpointInterval: checkpointInterval,
&checkpoint{
finality[*rawdb.Checkpoint]{
doExist: false,
interval: 256,
db: db,
},
},
&milestone{
finality: finality[*rawdb.Milestone]{
doExist: false,
interval: 256,
db: db,
},
LockedMilestoneIDs: make(map[string]struct{}),
FutureMilestoneList: make(map[uint64]common.Hash),
FutureMilestoneOrder: make([]uint64, 0),
MaxCapacity: 10,
},
}
}
// TestWhitelistCheckpoint checks the checkpoint whitelist map queue mechanism
func TestWhitelistCheckpoint(t *testing.T) {
// TestWhitelistCheckpoint checks the checkpoint whitelist setter and getter functions.
func TestWhitelistedCheckpoint(t *testing.T) {
t.Parallel()
s := NewMockService(10, 10)
for i := 0; i < 10; i++ {
s.enqueueCheckpointWhitelist(uint64(i), common.Hash{})
}
require.Equal(t, s.length(), 10, "expected 10 items in whitelist")
db := rawdb.NewMemoryDatabase()
s.enqueueCheckpointWhitelist(11, common.Hash{})
s.dequeueCheckpointWhitelist()
require.Equal(t, s.length(), 10, "expected 10 items in whitelist")
//Creating the service for the whitelisting the checkpoints
s := NewMockService(db)
cp := s.checkpointService.(*checkpoint)
require.Equal(t, cp.doExist, false, "expected false as no cp exist at this point")
_, _, err := rawdb.ReadFinality[*rawdb.Checkpoint](db)
require.NotNil(t, err, "Error should be nil while reading from the db")
//Adding the checkpoint
s.ProcessCheckpoint(11, common.Hash{})
require.Equal(t, cp.doExist, true, "expected true as cp exist")
//Removing the checkpoint
s.PurgeWhitelistedCheckpoint()
require.Equal(t, cp.doExist, false, "expected false as no cp exist at this point")
//Adding the checkpoint
s.ProcessCheckpoint(12, common.Hash{1})
//Receiving the stored checkpoint
doExist, number, hash := s.GetWhitelistedCheckpoint()
//Validating the values received
require.Equal(t, doExist, true, "expected true ascheckpoint exist at this point")
require.Equal(t, number, uint64(12), "expected number to be 11 but got", number)
require.Equal(t, hash, common.Hash{1}, "expected the 1 hash but got", hash)
require.NotEqual(t, hash, common.Hash{}, "expected the hash to be different from zero hash")
c1 := s.checkpointService.(*checkpoint)
fmt.Println("!!!-0", c1.doExist)
s.PurgeWhitelistedCheckpoint()
fmt.Println("!!!-1", c1.doExist)
doExist, number, hash = s.GetWhitelistedCheckpoint()
fmt.Println("!!!-2", c1.doExist)
//Validating the values received from the db, not memory
require.Equal(t, doExist, true, "expected true ascheckpoint exist at this point")
require.Equal(t, number, uint64(12), "expected number to be 11 but got", number)
require.Equal(t, hash, common.Hash{1}, "expected the 1 hash but got", hash)
require.NotEqual(t, hash, common.Hash{}, "expected the hash to be different from zero hash")
checkpointNumber, checkpointHash, err := rawdb.ReadFinality[*rawdb.Checkpoint](db)
require.Nil(t, err, "Error should be nil while reading from the db")
require.Equal(t, checkpointHash, common.Hash{1}, "expected the 1 hash but got", hash)
require.Equal(t, checkpointNumber, uint64(12), "expected number to be 11 but got", number)
}
// TestMilestone checks the milestone whitelist setter and getter functions
func TestMilestone(t *testing.T) {
t.Parallel()
db := rawdb.NewMemoryDatabase()
s := NewMockService(db)
milestone := s.milestoneService.(*milestone)
//Checking for the variables when no milestone is Processed
require.Equal(t, milestone.doExist, false, "expected false as no milestone exist at this point")
require.Equal(t, milestone.Locked, false, "expected false as it was not locked")
require.Equal(t, milestone.LockedMilestoneNumber, uint64(0), "expected 0 as it was not initialized")
_, _, err := rawdb.ReadFinality[*rawdb.Milestone](db)
require.NotNil(t, err, "Error should be nil while reading from the db")
//Acquiring the mutex lock
milestone.LockMutex(11)
require.Equal(t, milestone.Locked, false, "expected false as sprint is not locked till this point")
//Releasing the mutex lock
milestone.UnlockMutex(true, "milestoneID1", uint64(11), common.Hash{})
require.Equal(t, milestone.LockedMilestoneNumber, uint64(11), "expected 11 as it was not initialized")
require.Equal(t, milestone.Locked, true, "expected true as sprint is locked now")
require.Equal(t, len(milestone.LockedMilestoneIDs), 1, "expected 1 as only 1 milestoneID has been entered")
_, ok := milestone.LockedMilestoneIDs["milestoneID1"]
require.True(t, ok, "milestoneID1 should exist in the LockedMilestoneIDs map")
_, ok = milestone.LockedMilestoneIDs["milestoneID2"]
require.False(t, ok, "milestoneID2 shouldn't exist in the LockedMilestoneIDs map")
milestone.LockMutex(11)
milestone.UnlockMutex(true, "milestoneID2", uint64(11), common.Hash{})
require.Equal(t, len(milestone.LockedMilestoneIDs), 1, "expected 1 as only 1 milestoneID has been entered")
_, ok = milestone.LockedMilestoneIDs["milestoneID2"]
require.True(t, ok, "milestoneID2 should exist in the LockedMilestoneIDs map")
milestone.RemoveMilestoneID("milestoneID1")
require.Equal(t, len(milestone.LockedMilestoneIDs), 1, "expected 1 as one out of two has been removed in previous step")
require.Equal(t, milestone.Locked, true, "expected true as sprint is locked now")
milestone.RemoveMilestoneID("milestoneID2")
require.Equal(t, len(milestone.LockedMilestoneIDs), 0, "expected 1 as both the milestonesIDs has been removed in previous step")
require.Equal(t, milestone.Locked, false, "expected false")
milestone.LockMutex(11)
milestone.UnlockMutex(true, "milestoneID3", uint64(11), common.Hash{})
require.True(t, milestone.Locked, "expected true")
require.Equal(t, milestone.LockedMilestoneNumber, uint64(11), "Expected 11")
milestone.LockMutex(15)
require.True(t, milestone.Locked, "expected true")
require.Equal(t, milestone.LockedMilestoneNumber, uint64(11), "Expected 11")
milestone.UnlockMutex(true, "milestoneID4", uint64(15), common.Hash{})
require.True(t, milestone.Locked, "expected true as final confirmation regarding the lock has been made")
require.Equal(t, len(milestone.LockedMilestoneIDs), 1, "expected 1 as previous milestonesIDs has been removed in previous step")
//Adding the milestone
s.ProcessMilestone(11, common.Hash{})
require.True(t, milestone.Locked, "expected true as locked sprint is of number 15")
require.Equal(t, milestone.doExist, true, "expected true as milestone exist")
require.Equal(t, len(milestone.LockedMilestoneIDs), 1, "expected 1 as still last milestone of sprint number 15 exist")
//Reading from the Db
locked, lockedMilestoneNumber, lockedMilestoneHash, lockedMilestoneIDs, err := rawdb.ReadLockField(db)
require.Nil(t, err)
require.True(t, locked, "expected true as locked sprint is of number 15")
require.Equal(t, lockedMilestoneNumber, uint64(15), "Expected 15")
require.Equal(t, lockedMilestoneHash, common.Hash{}, "Expected", common.Hash{})
require.Equal(t, len(lockedMilestoneIDs), 1, "expected 1 as still last milestone of sprint number 15 exist")
_, ok = lockedMilestoneIDs["milestoneID4"]
require.True(t, ok, "expected true as milestoneIDList should contain 'milestoneID4'")
//Asking the lock for sprintNumber less than last whitelisted milestone
require.False(t, milestone.LockMutex(11), "Cant lock the sprintNumber less than equal to latest whitelisted milestone")
milestone.UnlockMutex(false, "", uint64(11), common.Hash{}) //Unlock is required after every lock to release the mutex
//Adding the milestone
s.ProcessMilestone(51, common.Hash{})
require.False(t, milestone.Locked, "expected false as lock from sprint number 15 is removed")
require.Equal(t, milestone.doExist, true, "expected true as milestone exist")
require.Equal(t, len(milestone.LockedMilestoneIDs), 0, "expected 0 as all the milestones have been removed")
//Reading from the Db
locked, _, _, lockedMilestoneIDs, err = rawdb.ReadLockField(db)
require.Nil(t, err)
require.False(t, locked, "expected true as locked sprint is of number 15")
require.Equal(t, len(lockedMilestoneIDs), 0, "expected 0 as milestoneID exist in the map")
//Removing the milestone
s.PurgeWhitelistedMilestone()
require.Equal(t, milestone.doExist, false, "expected false as no milestone exist at this point")
//Removing the milestone
s.ProcessMilestone(11, common.Hash{1})
doExist, number, hash := s.GetWhitelistedMilestone()
//validating the values received
require.Equal(t, doExist, true, "expected true as milestone exist at this point")
require.Equal(t, number, uint64(11), "expected number to be 11 but got", number)
require.Equal(t, hash, common.Hash{1}, "expected the 1 hash but got", hash)
s.PurgeWhitelistedMilestone()
doExist, number, hash = s.GetWhitelistedMilestone()
//Validating the values received from the db, not memory
require.Equal(t, doExist, true, "expected true as milestone exist at this point")
require.Equal(t, number, uint64(11), "expected number to be 11 but got", number)
require.Equal(t, hash, common.Hash{1}, "expected the 1 hash but got", hash)
milestoneNumber, milestoneHash, err := rawdb.ReadFinality[*rawdb.Milestone](db)
require.Nil(t, err, "Error should be nil while reading from the db")
require.Equal(t, milestoneHash, common.Hash{1}, "expected the 1 hash but got", hash)
require.Equal(t, milestoneNumber, uint64(11), "expected number to be 11 but got", number)
_, _, err = rawdb.ReadFutureMilestoneList(db)
require.NotNil(t, err, "Error should be not nil")
s.ProcessFutureMilestone(16, common.Hash{16})
require.Equal(t, len(milestone.FutureMilestoneOrder), 1, "expected length is 1 as we added only 1 future milestone")
require.Equal(t, milestone.FutureMilestoneOrder[0], uint64(16), "expected value is 16 but got", milestone.FutureMilestoneOrder[0])
require.Equal(t, milestone.FutureMilestoneList[16], common.Hash{16}, "expected value is", common.Hash{16}.String()[2:], "but got", milestone.FutureMilestoneList[16])
order, list, err := rawdb.ReadFutureMilestoneList(db)
require.Nil(t, err, "Error should be nil while reading from the db")
require.Equal(t, len(order), 1, "expected the 1 hash but got", len(order))
require.Equal(t, order[0], uint64(16), "expected number to be 16 but got", order[0])
require.Equal(t, list[order[0]], common.Hash{16}, "expected value is", common.Hash{16}.String()[2:], "but got", list[order[0]])
capicity := milestone.MaxCapacity
for i := 16; i <= 16*(capicity+1); i = i + 16 {
s.ProcessFutureMilestone(uint64(i), common.Hash{16})
}
require.Equal(t, len(milestone.FutureMilestoneOrder), capicity, "expected length is", capicity)
require.Equal(t, milestone.FutureMilestoneOrder[capicity-1], uint64(16*capicity), "expected value is", uint64(16*capicity), "but got", milestone.FutureMilestoneOrder[capicity-1])
}
// TestIsValidPeer checks the IsValidPeer function in isolation
@ -45,18 +254,26 @@ func TestWhitelistCheckpoint(t *testing.T) {
func TestIsValidPeer(t *testing.T) {
t.Parallel()
s := NewMockService(10, 10)
db := rawdb.NewMemoryDatabase()
s := NewMockService(db)
// case1: no checkpoint whitelist, should consider the chain as valid
res, err := s.IsValidPeer(nil, nil)
res, err := s.IsValidPeer(nil)
require.NoError(t, err, "expected no error")
require.Equal(t, res, true, "expected chain to be valid")
// add checkpoint entries and mock fetchHeadersByNumber function
s.ProcessCheckpoint(uint64(0), common.Hash{})
// add checkpoint entry and mock fetchHeadersByNumber function
s.ProcessCheckpoint(uint64(1), common.Hash{})
require.Equal(t, s.length(), 2, "expected 2 items in whitelist")
// add milestone entry and mock fetchHeadersByNumber function
s.ProcessMilestone(uint64(1), common.Hash{})
checkpoint := s.checkpointService.(*checkpoint)
milestone := s.milestoneService.(*milestone)
//Check whether the milestone and checkpoint exist
require.Equal(t, checkpoint.doExist, true, "expected true as checkpoint exists")
require.Equal(t, milestone.doExist, true, "expected true as milestone exists")
// create a false function, returning absolutely nothing
falseFetchHeadersByNumber := func(number uint64, amount int, skip int, reverse bool) ([]*types.Header, []common.Hash, error) {
@ -64,20 +281,19 @@ func TestIsValidPeer(t *testing.T) {
}
// case2: false fetchHeadersByNumber function provided, should consider the chain as invalid
// and throw `ErrNoRemoteCheckpoint` error
res, err = s.IsValidPeer(nil, falseFetchHeadersByNumber)
// and throw `ErrNoRemoteCheckoint` error
res, err = s.IsValidPeer(falseFetchHeadersByNumber)
if err == nil {
t.Fatal("expected error, got nil")
}
if !errors.Is(err, ErrNoRemoteCheckpoint) {
t.Fatalf("expected error ErrNoRemoteCheckpoint, got %v", err)
if !errors.Is(err, ErrNoRemote) {
t.Fatalf("expected error ErrNoRemote, got %v", err)
}
require.Equal(t, res, false, "expected chain to be invalid")
require.Equal(t, res, false, "expected peer chain to be invalid")
// case3: correct fetchHeadersByNumber function provided, should consider the chain as valid
// create a mock function, returning a the required header
// create a mock function, returning the required header
fetchHeadersByNumber := func(number uint64, _ int, _ int, _ bool) ([]*types.Header, []common.Hash, error) {
hash := common.Hash{}
header := types.Header{Number: big.NewInt(0)}
@ -96,18 +312,124 @@ func TestIsValidPeer(t *testing.T) {
}
}
res, err = s.IsValidPeer(nil, fetchHeadersByNumber)
// case3: correct fetchHeadersByNumber function provided, should consider the chain as valid
res, err = s.IsValidPeer(fetchHeadersByNumber)
require.NoError(t, err, "expected no error")
require.Equal(t, res, true, "expected chain to be valid")
// add one more checkpoint whitelist entry
// add checkpoint whitelist entry
s.ProcessCheckpoint(uint64(2), common.Hash{})
require.Equal(t, s.length(), 3, "expected 3 items in whitelist")
require.Equal(t, checkpoint.doExist, true, "expected true as checkpoint exists")
// case4: correct fetchHeadersByNumber function provided with wrong header
// for block number 2. Should consider the chain as invalid and throw an error
res, err = s.IsValidPeer(nil, fetchHeadersByNumber)
require.Equal(t, err, ErrCheckpointMismatch, "expected checkpoint mismatch error")
res, err = s.IsValidPeer(fetchHeadersByNumber)
require.Equal(t, err, ErrMismatch, "expected mismatch error")
require.Equal(t, res, false, "expected chain to be invalid")
// create a mock function, returning the required header
fetchHeadersByNumber = func(number uint64, _ int, _ int, _ bool) ([]*types.Header, []common.Hash, error) {
hash := common.Hash{}
header := types.Header{Number: big.NewInt(0)}
switch number {
case 0:
return []*types.Header{&header}, []common.Hash{hash}, nil
case 1:
header.Number = big.NewInt(1)
return []*types.Header{&header}, []common.Hash{hash}, nil
case 2:
header.Number = big.NewInt(2)
return []*types.Header{&header}, []common.Hash{hash}, nil
case 3:
header.Number = big.NewInt(3)
hash3 := common.Hash{3}
return []*types.Header{&header}, []common.Hash{hash3}, nil
default:
return nil, nil, errors.New("invalid number")
}
}
s.ProcessMilestone(uint64(3), common.Hash{})
//Case5: correct fetchHeadersByNumber function provided with hash mismatch, should consider the chain as invalid
res, err = s.IsValidPeer(fetchHeadersByNumber)
require.Equal(t, err, ErrMismatch, "expected milestone mismatch error")
require.Equal(t, res, false, "expected chain to be invalid")
s.ProcessMilestone(uint64(2), common.Hash{})
// create a mock function, returning the required header
fetchHeadersByNumber = func(number uint64, _ int, _ int, _ bool) ([]*types.Header, []common.Hash, error) {
hash := common.Hash{}
header := types.Header{Number: big.NewInt(0)}
switch number {
case 0:
return []*types.Header{&header}, []common.Hash{hash}, nil
case 1:
header.Number = big.NewInt(1)
return []*types.Header{&header}, []common.Hash{hash}, nil
case 2:
header.Number = big.NewInt(2)
return []*types.Header{&header}, []common.Hash{hash}, nil
default:
return nil, nil, errors.New("invalid number")
}
}
// case6: correct fetchHeadersByNumber function provided, should consider the chain as valid
res, err = s.IsValidPeer(fetchHeadersByNumber)
require.NoError(t, err, "expected no error")
require.Equal(t, res, true, "expected chain to be valid")
// create a mock function, returning the required header
fetchHeadersByNumber = func(number uint64, _ int, _ int, _ bool) ([]*types.Header, []common.Hash, error) {
hash := common.Hash{}
hash3 := common.Hash{3}
header := types.Header{Number: big.NewInt(0)}
switch number {
case 0:
return []*types.Header{&header}, []common.Hash{hash}, nil
case 1:
header.Number = big.NewInt(1)
return []*types.Header{&header}, []common.Hash{hash}, nil
case 2:
header.Number = big.NewInt(2)
return []*types.Header{&header}, []common.Hash{hash}, nil
case 3:
header.Number = big.NewInt(2) // sending wrong header for misamatch
return []*types.Header{&header}, []common.Hash{hash}, nil
case 4:
header.Number = big.NewInt(4) // sending wrong header for misamatch
return []*types.Header{&header}, []common.Hash{hash3}, nil
default:
return nil, nil, errors.New("invalid number")
}
}
//Add one more milestone in the list
s.ProcessMilestone(uint64(3), common.Hash{})
// case7: correct fetchHeadersByNumber function provided with wrong header for block 3, should consider the chain as invalid
res, err = s.IsValidPeer(fetchHeadersByNumber)
require.Equal(t, err, ErrMismatch, "expected milestone mismatch error")
require.Equal(t, res, false, "expected chain to be invalid")
//require.Equal(t, milestone.length(), 3, "expected 3 items in milestoneList")
//Add one more milestone in the list
s.ProcessMilestone(uint64(4), common.Hash{})
// case8: correct fetchHeadersByNumber function provided with wrong hash for block 3, should consider the chain as valid
res, err = s.IsValidPeer(fetchHeadersByNumber)
require.Equal(t, err, ErrMismatch, "expected milestone mismatch error")
require.Equal(t, res, false, "expected chain to be invalid")
}
@ -116,71 +438,479 @@ func TestIsValidPeer(t *testing.T) {
func TestIsValidChain(t *testing.T) {
t.Parallel()
s := NewMockService(10, 10)
db := rawdb.NewMemoryDatabase()
s := NewMockService(db)
chainA := createMockChain(1, 20) // A1->A2...A19->A20
// case1: no checkpoint whitelist, should consider the chain as valid
//Case1: no checkpoint whitelist and no milestone and no locking, should consider the chain as valid
res, err := s.IsValidChain(nil, chainA)
require.Equal(t, res, true, "expected chain to be valid")
require.Equal(t, err, nil, "expected error to be nil")
require.Nil(t, err)
require.Equal(t, res, true, "Expected chain to be valid")
tempChain := createMockChain(21, 22) // A21->A22
// add mock checkpoint entries
s.ProcessCheckpoint(tempChain[0].Number.Uint64(), tempChain[0].Hash())
// add mock checkpoint entry
s.ProcessCheckpoint(tempChain[1].Number.Uint64(), tempChain[1].Hash())
require.Equal(t, s.length(), 2, "expected 2 items in whitelist")
//Make the mock chain with zero blocks
zeroChain := make([]*types.Header, 0)
// case2: We're behind the oldest whitelisted block entry, should consider
// the chain as valid as we're still far behind the latest blocks
//Case2: As input chain is of zero length,should consider the chain as invalid
res, err = s.IsValidChain(nil, zeroChain)
require.Nil(t, err)
require.Equal(t, res, false, "expected chain to be invalid", len(zeroChain))
//Case3A: As the received chain and current tip of local chain is behind the oldest whitelisted block entry, should consider
// the chain as valid
res, err = s.IsValidChain(chainA[len(chainA)-1], chainA)
require.Nil(t, err)
require.Equal(t, res, true, "expected chain to be valid")
require.Equal(t, err, nil, "expected error to be nil")
// Clear checkpoint whitelist and add blocks A5 and A15 in whitelist
s.PurgeCheckpointWhitelist()
s.ProcessCheckpoint(chainA[5].Number.Uint64(), chainA[5].Hash())
//Case3B: As the received chain is behind the oldest whitelisted block entry,but current tip is at par with whitelisted checkpoint, should consider
// the chain as invalid
res, err = s.IsValidChain(tempChain[1], chainA)
require.Nil(t, err)
require.Equal(t, res, false, "expected chain to be invalid ")
// add mock milestone entry
s.ProcessMilestone(tempChain[1].Number.Uint64(), tempChain[1].Hash())
//Case4A: As the received chain and current tip of local chain is behind the oldest whitelisted block entry, should consider
// the chain as valid
res, err = s.IsValidChain(chainA[len(chainA)-1], chainA)
require.Nil(t, err)
require.Equal(t, res, true, "expected chain to be valid")
//Case4B: As the received chain is behind the oldest whitelisted block entry and but current tip is at par with whitelisted milestine, should consider
// the chain as invalid
res, err = s.IsValidChain(tempChain[1], chainA)
require.Nil(t, err)
require.Equal(t, res, false, "expected chain to be invalid")
//Remove the whitelisted checkpoint
s.PurgeWhitelistedCheckpoint()
//Case5: As the received chain is still invalid after removing the checkpoint as it is
//still behind the whitelisted milestone
res, err = s.IsValidChain(tempChain[1], chainA)
require.Nil(t, err)
require.Equal(t, res, false, "expected chain to be invalid")
//Remove the whitelisted milestone
s.PurgeWhitelistedMilestone()
//At this stage there is no whitelisted milestone and checkpoint
checkpoint := s.checkpointService.(*checkpoint)
milestone := s.milestoneService.(*milestone)
//Locking for sprintNumber 15
milestone.LockMutex(chainA[len(chainA)-5].Number.Uint64())
milestone.UnlockMutex(true, "MilestoneID1", chainA[len(chainA)-5].Number.Uint64(), chainA[len(chainA)-5].Hash())
//Case6: As the received chain is valid as the locked sprintHash matches with the incoming chain.
res, err = s.IsValidChain(chainA[len(chainA)-1], chainA)
require.Nil(t, err)
require.Equal(t, res, true, "expected chain to be valid as incoming chain matches with the locked value ")
hash3 := common.Hash{3}
//Locking for sprintNumber 16 with different hash
milestone.LockMutex(chainA[len(chainA)-4].Number.Uint64())
milestone.UnlockMutex(true, "MilestoneID2", chainA[len(chainA)-4].Number.Uint64(), hash3)
res, err = s.IsValidChain(chainA[len(chainA)-1], chainA)
require.Nil(t, err)
require.Equal(t, res, false, "expected chain to be invalid as incoming chain does match with the locked value hash ")
//Locking for sprintNumber 19
milestone.LockMutex(chainA[len(chainA)-1].Number.Uint64())
milestone.UnlockMutex(true, "MilestoneID1", chainA[len(chainA)-1].Number.Uint64(), chainA[len(chainA)-1].Hash())
//Case7: As the received chain is valid as the locked sprintHash matches with the incoming chain.
res, err = s.IsValidChain(chainA[len(chainA)-1], chainA)
require.Nil(t, err)
require.Equal(t, res, false, "expected chain to be invalid as incoming chain is less than the locked value ")
//Locking for sprintNumber 19
milestone.LockMutex(uint64(21))
milestone.UnlockMutex(true, "MilestoneID1", uint64(21), hash3)
//Case8: As the received chain is invalid as the locked sprintHash matches is ahead of incoming chain.
res, err = s.IsValidChain(chainA[len(chainA)-1], chainA)
require.Nil(t, err)
require.Equal(t, res, false, "expected chain to be invalid as incoming chain is less than the locked value ")
//Unlocking the sprint
milestone.UnlockSprint(uint64(21))
// Clear checkpoint whitelist and add block A15 in whitelist
s.PurgeWhitelistedCheckpoint()
s.ProcessCheckpoint(chainA[15].Number.Uint64(), chainA[15].Hash())
require.Equal(t, s.length(), 2, "expected 2 items in whitelist")
require.Equal(t, checkpoint.doExist, true, "expected true as checkpoint exists.")
// case3: Try importing a past chain having valid checkpoint, should
// consider the chain as valid
// case9: As the received chain is having valid checkpoint,should consider the chain as valid.
res, err = s.IsValidChain(chainA[len(chainA)-1], chainA)
require.Nil(t, err)
require.Equal(t, res, true, "expected chain to be valid")
// add mock milestone entries
s.ProcessMilestone(tempChain[1].Number.Uint64(), tempChain[1].Hash())
// case10: Try importing a past chain having valid checkpoint, should
// consider the chain as invalid as still lastest milestone is ahead of the chain.
res, err = s.IsValidChain(tempChain[1], chainA)
require.Nil(t, err)
require.Equal(t, res, false, "expected chain to be invalid")
// add mock milestone entries
s.ProcessMilestone(chainA[19].Number.Uint64(), chainA[19].Hash())
// case12: Try importing a chain having valid checkpoint and milestone, should
// consider the chain as valid
res, err = s.IsValidChain(tempChain[1], chainA)
require.Nil(t, err)
require.Equal(t, res, true, "expected chain to be invalid")
// add mock milestone entries
s.ProcessMilestone(chainA[19].Number.Uint64(), chainA[19].Hash())
// case13: Try importing a past chain having valid checkpoint and milestone, should
// consider the chain as valid
res, err = s.IsValidChain(tempChain[1], chainA)
require.Nil(t, err)
require.Equal(t, res, true, "expected chain to be valid")
// add mock milestone entries with wrong hash
s.ProcessMilestone(chainA[19].Number.Uint64(), chainA[18].Hash())
// case14: Try importing a past chain having valid checkpoint and milestone with wrong hash, should
// consider the chain as invalid
res, err = s.IsValidChain(chainA[len(chainA)-1], chainA)
require.Nil(t, err)
require.Equal(t, res, false, "expected chain to be invalid as hash mismatches")
// Clear milestone and add blocks A15 in whitelist
s.ProcessMilestone(chainA[15].Number.Uint64(), chainA[15].Hash())
// case16: Try importing a past chain having valid checkpoint, should
// consider the chain as valid
res, err = s.IsValidChain(tempChain[1], chainA)
require.Nil(t, err)
require.Equal(t, res, true, "expected chain to be valid")
require.Equal(t, err, nil, "expected error to be nil")
// Clear checkpoint whitelist and mock blocks in whitelist
tempChain = createMockChain(20, 20) // A20
s.PurgeCheckpointWhitelist()
s.PurgeWhitelistedCheckpoint()
s.ProcessCheckpoint(tempChain[0].Number.Uint64(), tempChain[0].Hash())
require.Equal(t, s.length(), 1, "expected 1 items in whitelist")
require.Equal(t, checkpoint.doExist, true, "expected true")
// case4: Try importing a past chain having invalid checkpoint
res, _ = s.IsValidChain(chainA[len(chainA)-1], chainA)
// case17: Try importing a past chain having invalid checkpoint,should consider the chain as invalid
res, err = s.IsValidChain(tempChain[0], chainA)
require.Nil(t, err)
require.Equal(t, res, false, "expected chain to be invalid")
// Not checking error here because we return nil in case of checkpoint mismatch
// case18: Try importing a future chain but within interval, should consider the chain as valid
res, err = s.IsValidChain(tempChain[len(tempChain)-1], tempChain)
require.Nil(t, err)
require.Equal(t, res, true, "expected chain to be invalid")
// create a future chain to be imported of length <= `checkpointInterval`
chainB := createMockChain(21, 30) // B21->B22...B29->B30
// case5: Try importing a future chain (1)
res, err = s.IsValidChain(chainA[len(chainA)-1], chainB)
// case19: Try importing a future chain of acceptable length,should consider the chain as valid
res, err = s.IsValidChain(tempChain[0], chainB)
require.Nil(t, err)
require.Equal(t, res, true, "expected chain to be valid")
require.Equal(t, err, nil, "expected error to be nil")
// create a future chain to be imported of length > `checkpointInterval`
chainB = createMockChain(21, 40) // C21->C22...C39->C40
s.PurgeWhitelistedCheckpoint()
s.PurgeWhitelistedMilestone()
// Note: Earlier, it used to reject future chains longer than some threshold.
// That check is removed for now.
chainB = createMockChain(21, 29) // C21->C22....C29
// case6: Try importing a future chain (2)
res, err = s.IsValidChain(chainA[len(chainA)-1], chainB)
s.milestoneService.ProcessFutureMilestone(29, chainB[8].Hash())
// case20: Try importing a future chain which match the future milestone should the chain as valid
res, err = s.IsValidChain(tempChain[0], chainB)
require.Nil(t, err)
require.Equal(t, res, true, "expected chain to be valid")
require.Equal(t, err, nil, "expected error to be nil")
chainB = createMockChain(21, 27) // C21->C22...C39->C40...C->256
// case21: Try importing a chain whose end point is less than future milestone
res, err = s.IsValidChain(tempChain[0], chainB)
require.Nil(t, err)
require.Equal(t, res, true, "expected chain to be valid")
chainB = createMockChain(30, 39) // C21->C22...C39->C40...C->256
//Processing wrong hash
s.milestoneService.ProcessFutureMilestone(38, chainB[9].Hash())
// case22: Try importing a future chain with mismatch future milestone
res, err = s.IsValidChain(tempChain[0], chainB)
require.Nil(t, err)
require.Equal(t, res, false, "expected chain to be invalid")
chainB = createMockChain(40, 49) // C40->C41...C48->C49
// case23: Try importing a future chain whose starting point is ahead of latest future milestone
res, err = s.IsValidChain(tempChain[0], chainB)
require.Nil(t, err)
require.Equal(t, res, true, "expected chain to be invalid")
}
func TestPropertyBasedTestingMilestone(t *testing.T) {
rapid.Check(t, func(t *rapid.T) {
db := rawdb.NewMemoryDatabase()
milestone := milestone{
finality: finality[*rawdb.Milestone]{
doExist: false,
Number: 0,
Hash: common.Hash{},
interval: 256,
db: db,
},
Locked: false,
LockedMilestoneNumber: 0,
LockedMilestoneHash: common.Hash{},
LockedMilestoneIDs: make(map[string]struct{}),
FutureMilestoneList: make(map[uint64]common.Hash),
FutureMilestoneOrder: make([]uint64, 0),
MaxCapacity: 10,
}
var (
milestoneEndNum = rapid.Uint64().Draw(t, "endBlock")
milestoneID = rapid.String().Draw(t, "MilestoneID")
doLock = rapid.Bool().Draw(t, "Voted")
)
val := milestone.LockMutex(milestoneEndNum.(uint64))
if !val {
t.Error("LockMutex need to return true when there is no whitelisted milestone and locked milestone")
}
milestone.UnlockMutex(doLock.(bool), milestoneID.(string), milestoneEndNum.(uint64), common.Hash{})
if doLock.(bool) {
//Milestone should not be whitelisted
if milestone.doExist {
t.Error("Milestone is not expected to be whitelisted")
}
//Local chain should be locked
if !milestone.Locked {
t.Error("Milestone is expected to be locked at", milestoneEndNum.(uint64))
}
if milestone.LockedMilestoneNumber != milestoneEndNum.(uint64) {
t.Error("Locked milestone number is expected to be", milestoneEndNum.(uint64))
}
if len(milestone.LockedMilestoneIDs) != 1 {
t.Error("List should contain 1 milestone")
}
_, ok := milestone.LockedMilestoneIDs[milestoneID.(string)]
if !ok {
t.Error("List doesn't contain correct milestoneID")
}
}
if !doLock.(bool) {
if milestone.doExist {
t.Error("Milestone is not expected to be whitelisted")
}
if milestone.Locked {
t.Error("Milestone is expected not to be locked")
}
if milestone.LockedMilestoneNumber != 0 {
t.Error("Locked milestone number is expected to be", 0)
}
if len(milestone.LockedMilestoneIDs) != 0 {
t.Error("List should not contain milestone")
}
_, ok := milestone.LockedMilestoneIDs[milestoneID.(string)]
if ok {
t.Error("List shouldn't contain any milestoneID")
}
}
fitlerFn := func(i uint64) bool {
if i <= uint64(1000) {
return true
}
return false
}
var (
start = rapid.Uint64Max(milestoneEndNum.(uint64)).Draw(t, "start for mock chain")
end = rapid.Uint64Min(start.(uint64)).Filter(fitlerFn).Draw(t, "end for mock chain")
)
chainTemp := createMockChain(start.(uint64), end.(uint64))
val, err := milestone.IsValidChain(chainTemp[0], chainTemp)
if err != nil {
t.Error("Error", err)
}
if doLock.(bool) && val {
t.Error("When the chain is locked at milestone, it should not pass IsValidChain for incompatible incoming chain")
}
if !doLock.(bool) && !val {
t.Error("When the chain is not locked at milestone, it should pass IsValidChain for incoming chain")
}
var (
milestoneEndNum2 = rapid.Uint64().Draw(t, "endBlockNum 2")
milestoneID2 = rapid.String().Draw(t, "MilestoneID 2")
doLock2 = rapid.Bool().Draw(t, "Voted 2")
)
val = milestone.LockMutex(milestoneEndNum2.(uint64))
if doLock.(bool) && milestoneEndNum.(uint64) > milestoneEndNum2.(uint64) && val {
t.Error("LockMutex need to return false as previous locked milestone is greater")
}
if doLock.(bool) && milestoneEndNum.(uint64) <= milestoneEndNum2.(uint64) && !val {
t.Error("LockMutex need to return true as previous locked milestone is less")
}
milestone.UnlockMutex(doLock2.(bool), milestoneID2.(string), milestoneEndNum2.(uint64), common.Hash{})
if doLock2.(bool) {
if milestone.doExist {
t.Error("Milestone is not expected to be whitelisted")
}
if !milestone.Locked {
t.Error("Milestone is expected to be locked at", milestoneEndNum2.(uint64))
}
if milestone.LockedMilestoneNumber != milestoneEndNum2.(uint64) {
t.Error("Locked milestone number is expected to be", milestoneEndNum.(uint64))
}
if len(milestone.LockedMilestoneIDs) != 1 {
t.Error("List should contain 1 milestone")
}
_, ok := milestone.LockedMilestoneIDs[milestoneID2.(string)]
if !ok {
t.Error("List doesn't contain correct milestoneID")
}
}
if !doLock2.(bool) {
if milestone.doExist {
t.Error("Milestone is not expected to be whitelisted")
}
if !doLock.(bool) && milestone.Locked {
t.Error("Milestone is expected not to be locked")
}
if doLock.(bool) && !milestone.Locked {
t.Error("Milestone is expected to be locked at", milestoneEndNum.(uint64))
}
if !doLock.(bool) && milestone.LockedMilestoneNumber != 0 {
t.Error("Locked milestone number is expected to be", 0)
}
if doLock.(bool) && milestone.LockedMilestoneNumber != milestoneEndNum.(uint64) {
t.Error("Locked milestone number is expected to be", milestoneEndNum.(uint64))
}
if !doLock.(bool) && len(milestone.LockedMilestoneIDs) != 0 {
t.Error("List should not contain milestone")
}
if doLock.(bool) && len(milestone.LockedMilestoneIDs) != 1 {
t.Error("List should not contain milestone")
}
_, ok := milestone.LockedMilestoneIDs[milestoneID.(string)]
if !doLock.(bool) && ok {
t.Error("List shouldn't contain any milestoneID")
}
if doLock.(bool) && !ok {
t.Error("List should contain milestoneID")
}
}
var (
milestoneNum = rapid.Uint64().Draw(t, "milestone Number")
)
lockedValue := milestone.LockedMilestoneNumber
milestone.Process(milestoneNum.(uint64), common.Hash{})
isChainLocked := doLock.(bool) || doLock2.(bool)
if !milestone.doExist {
t.Error("Should have the whitelisted milestone")
}
if milestone.finality.Number != milestoneNum.(uint64) {
t.Error("Should have the whitelisted milestone", milestoneNum.(uint64))
}
if isChainLocked {
if milestoneNum.(uint64) < lockedValue {
if !milestone.Locked {
t.Error("Milestone is expected to be locked")
}
} else {
if milestone.Locked {
t.Error("Milestone is expected not to be locked")
}
}
}
var (
futureMilestoneNum = rapid.Uint64Min(milestoneNum.(uint64)).Draw(t, "future milestone Number")
)
isChainLocked = milestone.Locked
milestone.ProcessFutureMilestone(futureMilestoneNum.(uint64), common.Hash{})
if isChainLocked {
if futureMilestoneNum.(uint64) < lockedValue {
if !milestone.Locked {
t.Error("Milestone is expected to be locked")
}
} else {
if milestone.Locked {
t.Error("Milestone is expected not to be locked")
}
}
}
})
}
func TestSplitChain(t *testing.T) {
@ -384,11 +1114,12 @@ func TestSplitChainProperties(t *testing.T) {
// starting from `start` to `end` (inclusive)
func createMockChain(start, end uint64) []*types.Header {
var (
i uint64
idx uint64
chain []*types.Header = make([]*types.Header, end-start+1)
i uint64
idx uint64
)
chain := make([]*types.Header, end-start+1)
for i = start; i <= end; i++ {
header := &types.Header{
Number: big.NewInt(int64(i)),

View file

@ -174,6 +174,21 @@ func (mr *MockBackendMockRecorder) GetReceipts(arg0, arg1 interface{}) *gomock.C
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetReceipts", reflect.TypeOf((*MockBackend)(nil).GetReceipts), arg0, arg1)
}
// GetVoteOnHash mocks base method.
func (m *MockBackend) GetVoteOnHash(arg0 context.Context, arg1, arg2 uint64, arg3, arg4 string) (bool, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetVoteOnHash", arg0, arg1, arg2, arg3, arg4)
ret0, _ := ret[0].(bool)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetVoteOnHash indicates an expected call of GetVoteOnHash.
func (mr *MockBackendMockRecorder) GetVoteOnHash(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVoteOnHash", reflect.TypeOf((*MockBackend)(nil).GetVoteOnHash), arg0, arg1, arg2, arg3, arg4)
}
// HeaderByHash mocks base method.
func (m *MockBackend) HeaderByHash(arg0 context.Context, arg1 common.Hash) (*types.Header, error) {
m.ctrl.T.Helper()

View file

@ -103,6 +103,10 @@ func (b *TestBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.
return nil, nil
}
func (b *TestBackend) GetVoteOnHash(ctx context.Context, starBlockNr uint64, endBlockNr uint64, hash string, milestoneId string) (bool, error) {
return false, nil
}
func (b *TestBackend) GetLogs(ctx context.Context, hash common.Hash, number uint64) ([][]*types.Log, error) {
receipts := rawdb.ReadReceipts(b.DB, hash, number, params.TestChainConfig)

View file

@ -10,94 +10,112 @@ import (
)
var (
// errCheckpointCount is returned when we are unable to fetch
// the checkpoint count from local heimdall.
errCheckpointCount = errors.New("failed to fetch checkpoint count")
// errNoCheckpoint is returned when there is not checkpoint proposed
// by heimdall yet or heimdall is not in sync
errNoCheckpoint = errors.New("no checkpoint proposed")
// errCheckpoint is returned when we are unable to fetch the
// latest checkpoint from the local heimdall.
errCheckpoint = errors.New("failed to fetch latest checkpoint")
// errMissingCheckpoint is returned when we don't have the
// checkpoint blocks locally, yet.
errMissingCheckpoint = errors.New("missing checkpoint blocks")
// errMilestone is returned when we are unable to fetch the
// latest milestone from the local heimdall.
errMilestone = errors.New("failed to fetch latest milestone")
// errRootHash is returned when we aren't able to calculate the root hash
// locally for a range of blocks.
errRootHash = errors.New("failed to get local root hash")
// errCheckpointRootHashMismatch is returned when the local root hash
// doesn't match with the root hash in checkpoint.
errCheckpointRootHashMismatch = errors.New("checkpoint roothash mismatch")
// errEndBlock is returned when we're unable to fetch a block locally.
errEndBlock = errors.New("failed to get end block")
ErrNotInRejectedList = errors.New("MilestoneID not in rejected list")
)
// fetchWhitelistCheckpoints fetches the latest checkpoint/s from it's local heimdall
// fetchWhitelistCheckpoint fetches the latest checkpoint from it's local heimdall
// and verifies the data against bor data.
func (h *ethHandler) fetchWhitelistCheckpoints(ctx context.Context, bor *bor.Bor, checkpointVerifier *checkpointVerifier, first bool) ([]uint64, []common.Hash, error) {
// Create an array for block number and block hashes
//nolint:prealloc
func (h *ethHandler) fetchWhitelistCheckpoint(ctx context.Context, bor *bor.Bor, eth *Ethereum, verifier *borVerifier) (uint64, common.Hash, error) {
var (
blockNums []uint64 = make([]uint64, 0)
blockHashes []common.Hash = make([]common.Hash, 0)
blockNum uint64
blockHash common.Hash
)
// Fetch the checkpoint count from heimdall
count, err := bor.HeimdallClient.FetchCheckpointCount(ctx)
// fetch the latest checkpoint from Heimdall
checkpoint, err := bor.HeimdallClient.FetchCheckpoint(ctx, -1)
if err != nil {
log.Debug("Failed to fetch checkpoint count for whitelisting", "err", err)
return blockNums, blockHashes, errCheckpointCount
log.Debug("Failed to fetch latest checkpoint for whitelisting", "err", err)
return blockNum, blockHash, errCheckpoint
}
if count == 0 {
return blockNums, blockHashes, errNoCheckpoint
log.Info("Got new checkpoint from heimdall", "start", checkpoint.StartBlock.Uint64(), "end", checkpoint.EndBlock.Uint64(), "rootHash", checkpoint.RootHash.String())
// Verify if the checkpoint fetched can be added to the local whitelist entry or not
// If verified, it returns the hash of the end block of the checkpoint. If not,
// it will return appropriate error.
hash, err := verifier.verify(ctx, eth, h, checkpoint.StartBlock.Uint64(), checkpoint.EndBlock.Uint64(), checkpoint.RootHash.String()[2:], true)
if err != nil {
log.Warn("Failed to whitelist checkpoint", "err", err)
return blockNum, blockHash, err
}
blockNum = checkpoint.EndBlock.Uint64()
blockHash = common.HexToHash(hash)
return blockNum, blockHash, nil
}
// fetchWhitelistMilestone fetches the latest milestone from it's local heimdall
// and verifies the data against bor data.
func (h *ethHandler) fetchWhitelistMilestone(ctx context.Context, bor *bor.Bor, eth *Ethereum, verifier *borVerifier) (uint64, common.Hash, error) {
var (
start int64
end int64
num uint64
hash common.Hash
)
// Prepare the checkpoint range to fetch
if count <= 10 {
start = 1
} else {
start = count - 10 + 1 // 10 is the max number of checkpoints to fetch
// fetch latest milestone
milestone, err := bor.HeimdallClient.FetchMilestone(ctx)
if err != nil {
log.Error("Failed to fetch latest milestone for whitelisting", "err", err)
return num, hash, errMilestone
}
end = count
num = milestone.EndBlock.Uint64()
hash = milestone.Hash
// If we're in not in the first iteration, only fetch the latest checkpoint
if !first {
start = count
log.Info("Got new milestone from heimdall", "start", milestone.StartBlock.Uint64(), "end", milestone.EndBlock.Uint64(), "hash", milestone.Hash.String())
// Verify if the milestone fetched can be added to the local whitelist entry or not
// If verified, it returns the hash of the end block of the milestone. If not,
// it will return appropriate error.
_, err = verifier.verify(ctx, eth, h, milestone.StartBlock.Uint64(), milestone.EndBlock.Uint64(), milestone.Hash.String()[2:], false)
if err != nil {
h.downloader.UnlockSprint(milestone.EndBlock.Uint64())
return num, hash, err
}
for i := start; i <= end; i++ {
// fetch `i` indexed checkpoint from heimdall
checkpoint, err := bor.HeimdallClient.FetchCheckpoint(ctx, i)
if err != nil {
log.Debug("Failed to fetch latest checkpoint for whitelisting", "err", err)
return blockNums, blockHashes, errCheckpoint
}
// Verify if the checkpoint fetched can be added to the local whitelist entry or not
// If verified, it returns the hash of the end block of the checkpoint. If not,
// it will return appropriate error.
hash, err := checkpointVerifier.verify(ctx, h, checkpoint)
if err != nil {
return blockNums, blockHashes, err
}
blockNums = append(blockNums, checkpoint.EndBlock.Uint64())
blockHashes = append(blockHashes, common.HexToHash(hash))
}
return blockNums, blockHashes, nil
return num, hash, nil
}
func (h *ethHandler) fetchNoAckMilestone(ctx context.Context, bor *bor.Bor) (string, error) {
var (
milestoneID string
)
// fetch latest milestone
milestoneID, err := bor.HeimdallClient.FetchLastNoAckMilestone(ctx)
if err != nil {
log.Error("Failed to fetch latest no-ack milestone", "err", err)
return milestoneID, errMilestone
}
return milestoneID, nil
}
func (h *ethHandler) fetchNoAckMilestoneByID(ctx context.Context, bor *bor.Bor, milestoneID string) error {
// fetch latest milestone
err := bor.HeimdallClient.FetchNoAckMilestone(ctx, milestoneID)
// fixme: handle different types of errors
if errors.Is(err, ErrNotInRejectedList) {
log.Warn("MilestoneID not in rejected list", "milestoneID", milestoneID, "err", err)
return err
}
if err != nil {
log.Error("Failed to fetch no-ack milestone by ID ", "milestoneID", milestoneID, "err", err)
return errMilestone
}
return nil
}

View file

@ -12,12 +12,17 @@ import (
"github.com/ethereum/go-ethereum/consensus/bor"
"github.com/ethereum/go-ethereum/consensus/bor/clerk"
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/checkpoint"
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/milestone"
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/span"
)
type mockHeimdall struct {
fetchCheckpoint func(ctx context.Context, number int64) (*checkpoint.Checkpoint, error)
fetchCheckpointCount func(ctx context.Context) (int64, error)
fetchCheckpoint func(ctx context.Context, number int64) (*checkpoint.Checkpoint, error)
fetchCheckpointCount func(ctx context.Context) (int64, error)
fetchMilestone func(ctx context.Context) (*milestone.Milestone, error)
fetchMilestoneCount func(ctx context.Context) (int64, error)
fetchNoAckMilestone func(ctx context.Context, milestoneID string) error
fetchLastNoAckMilestone func(ctx context.Context) (string, error)
}
func (m *mockHeimdall) StateSyncEvents(ctx context.Context, fromID uint64, to int64) ([]*clerk.EventRecordWithTime, error) {
@ -33,78 +38,112 @@ func (m *mockHeimdall) FetchCheckpoint(ctx context.Context, number int64) (*chec
func (m *mockHeimdall) FetchCheckpointCount(ctx context.Context) (int64, error) {
return m.fetchCheckpointCount(ctx)
}
func (m *mockHeimdall) FetchMilestone(ctx context.Context) (*milestone.Milestone, error) {
return m.fetchMilestone(ctx)
}
func (m *mockHeimdall) FetchMilestoneCount(ctx context.Context) (int64, error) {
return m.fetchMilestoneCount(ctx)
}
func (m *mockHeimdall) FetchNoAckMilestone(ctx context.Context, milestoneID string) error {
return m.fetchNoAckMilestone(ctx, milestoneID)
}
func (m *mockHeimdall) FetchLastNoAckMilestone(ctx context.Context) (string, error) {
return m.fetchLastNoAckMilestone(ctx)
}
func (m *mockHeimdall) FetchMilestoneID(ctx context.Context, milestoneID string) error {
return m.fetchNoAckMilestone(ctx, milestoneID)
}
func (m *mockHeimdall) Close() {}
func TestFetchWhitelistCheckpoints(t *testing.T) {
func TestFetchWhitelistCheckpointAndMilestone(t *testing.T) {
t.Parallel()
// create an empty ethHandler
handler := &ethHandler{}
// create a mock checkpoint verification function and use it to create a verifier
verify := func(ctx context.Context, handler *ethHandler, checkpoint *checkpoint.Checkpoint) (string, error) {
verify := func(ctx context.Context, eth *Ethereum, handler *ethHandler, start uint64, end uint64, hash string, isCheckpoint bool) (string, error) {
return "", nil
}
verifier := newCheckpointVerifier(verify)
verifier := newBorVerifier()
verifier.setVerify(verify)
// Create a mock heimdall instance and use it for creating a bor instance
var heimdall mockHeimdall
bor := &bor.Bor{HeimdallClient: &heimdall}
// create 20 mock checkpoints
checkpoints := createMockCheckpoints(20)
fetchCheckpointTest(t, &heimdall, bor, handler, verifier)
fetchMilestoneTest(t, &heimdall, bor, handler, verifier)
}
func (b *borVerifier) setVerify(verifyFn func(ctx context.Context, eth *Ethereum, handler *ethHandler, start uint64, end uint64, hash string, isCheckpoint bool) (string, error)) {
b.verify = verifyFn
}
func fetchCheckpointTest(t *testing.T, heimdall *mockHeimdall, bor *bor.Bor, handler *ethHandler, verifier *borVerifier) {
t.Helper()
var checkpoints []*checkpoint.Checkpoint
// create a mock fetch checkpoint function
heimdall.fetchCheckpoint = func(_ context.Context, number int64) (*checkpoint.Checkpoint, error) {
return checkpoints[number-1], nil // we're sure that number won't exceed 20
if len(checkpoints) == 0 {
return nil, errCheckpoint
} else if number == -1 {
return checkpoints[len(checkpoints)-1], nil
} else {
return checkpoints[number-1], nil
}
}
// create a background context
ctx := context.Background()
testCases := []struct {
name string
first bool
count int64
length int
start uint64
end uint64
fetchErr error
expectedErr error
}{
{"fail to fetch checkpoint count", false, 0, 0, 0, 0, errCheckpointCount, errCheckpointCount},
{"no checkpoints available", false, 0, 0, 0, 0, nil, errNoCheckpoint},
{"fetch multiple checkpoints (count < 10)", true, 6, 6, 0, 6, nil, nil},
{"fetch multiple checkpoints (count = 10)", true, 10, 10, 0, 10, nil, nil},
{"fetch multiple checkpoints (count > 10)", true, 16, 10, 6, 16, nil, nil},
{"fetch single checkpoint", false, 18, 1, 17, 18, nil, nil},
}
_, _, err := handler.fetchWhitelistCheckpoint(ctx, bor, nil, verifier)
require.Equal(t, err, errCheckpoint)
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
// create 4 mock checkpoints
checkpoints = createMockCheckpoints(4)
heimdall.fetchCheckpointCount = getMockFetchCheckpointFn(tc.count, tc.fetchErr)
blockNums, blockHashes, err := handler.fetchWhitelistCheckpoints(ctx, bor, verifier, tc.first)
blockNum, blockHash, err := handler.fetchWhitelistCheckpoint(ctx, bor, nil, verifier)
// Check if we have expected result
require.Equal(t, tc.expectedErr, err)
require.Equal(t, tc.length, len(blockNums))
require.Equal(t, tc.length, len(blockHashes))
validateBlockNumber(t, blockNums, checkpoints[tc.start:tc.end])
})
}
// Check if we have expected result
require.Equal(t, err, nil)
require.Equal(t, checkpoints[len(checkpoints)-1].EndBlock.Uint64(), blockNum)
require.Equal(t, checkpoints[len(checkpoints)-1].RootHash, blockHash)
}
func validateBlockNumber(t *testing.T, blockNums []uint64, checkpoints []*checkpoint.Checkpoint) {
func fetchMilestoneTest(t *testing.T, heimdall *mockHeimdall, bor *bor.Bor, handler *ethHandler, verifier *borVerifier) {
t.Helper()
for i, blockNum := range blockNums {
require.Equal(t, blockNum, checkpoints[i].EndBlock.Uint64(), "expect block number in array to match with checkpoint")
var milestones []*milestone.Milestone
// create a mock fetch checkpoint function
heimdall.fetchMilestone = func(_ context.Context) (*milestone.Milestone, error) {
if len(milestones) == 0 {
return nil, errMilestone
} else {
return milestones[len(milestones)-1], nil
}
}
// create a background context
ctx := context.Background()
_, _, err := handler.fetchWhitelistMilestone(ctx, bor, nil, verifier)
require.Equal(t, err, errMilestone)
// create 4 mock checkpoints
milestones = createMockMilestones(4)
num, hash, err := handler.fetchWhitelistMilestone(ctx, bor, nil, verifier)
// Check if we have expected result
require.Equal(t, err, nil)
require.Equal(t, milestones[len(milestones)-1].EndBlock.Uint64(), num)
require.Equal(t, milestones[len(milestones)-1].Hash, hash)
}
func getMockFetchCheckpointFn(number int64, err error) func(ctx context.Context) (int64, error) {
@ -133,3 +172,24 @@ func createMockCheckpoints(count int) []*checkpoint.Checkpoint {
return checkpoints
}
func createMockMilestones(count int) []*milestone.Milestone {
var (
milestones []*milestone.Milestone = make([]*milestone.Milestone, count)
startBlock int64 = 257 // any number can be used
)
for i := 0; i < count; i++ {
milestones[i] = &milestone.Milestone{
Proposer: common.Address{},
StartBlock: big.NewInt(startBlock),
EndBlock: big.NewInt(startBlock + 255),
Hash: common.Hash{},
BorChainID: "137",
Timestamp: uint64(time.Now().Unix()),
}
startBlock += 256
}
return milestones
}

View file

@ -18,6 +18,16 @@ func (ec *Client) GetRootHash(ctx context.Context, startBlockNumber uint64, endB
return rootHash, nil
}
// GetRootHash returns the merkle root of the block headers
func (ec *Client) GetVoteOnHash(ctx context.Context, startBlockNumber uint64, endBlockNumber uint64, hash string, milestoneID string) (bool, error) {
var value bool
if err := ec.c.CallContext(ctx, &value, "bor_getVoteOnHash", startBlockNumber, endBlockNumber, hash, milestoneID); err != nil {
return false, err
}
return value, nil
}
// GetBorBlockReceipt returns bor block receipt
func (ec *Client) GetBorBlockReceipt(ctx context.Context, hash common.Hash) (*types.Receipt, error) {
var r *types.Receipt

59
go.mod
View file

@ -17,14 +17,14 @@ require (
github.com/cloudflare/cloudflare-go v0.14.0
github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811
github.com/consensys/gnark-crypto v0.9.1-0.20230105202408-1a7a29904a7c
github.com/cosmos/cosmos-sdk v0.37.4
github.com/cosmos/cosmos-sdk v0.46.2
github.com/davecgh/go-spew v1.1.1
github.com/deckarep/golang-set/v2 v2.1.0
github.com/docker/docker v1.6.2
github.com/dop251/goja v0.0.0-20230122112309-96b1610dd4f7
github.com/edsrzf/mmap-go v1.0.0
github.com/emirpasic/gods v1.18.1
github.com/fatih/color v1.9.0
github.com/fatih/color v1.13.0
github.com/fjl/gencodec v0.0.0-20220412091415-8bb9e558978c
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5
github.com/fsnotify/fsnotify v1.6.0
@ -53,12 +53,13 @@ require (
github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c
github.com/jackpal/go-nat-pmp v1.0.2
github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e
github.com/json-iterator/go v1.1.12
github.com/julienschmidt/httprouter v1.3.0
github.com/karalabe/usb v0.0.2
github.com/kylelemons/godebug v1.1.0
github.com/maticnetwork/crand v1.0.2
github.com/maticnetwork/heimdall v0.3.1-0.20230105132832-d0063f71e3f0
github.com/maticnetwork/polyproto v0.0.2
github.com/maticnetwork/heimdall v0.3.1-0.20230227104835-81bd1055b0bc
github.com/maticnetwork/polyproto v0.0.3-0.20230216113155-340ea926ca53
github.com/mattn/go-colorable v0.1.13
github.com/mattn/go-isatty v0.0.16
github.com/mitchellh/cli v1.1.2
@ -88,10 +89,28 @@ require (
)
require (
cloud.google.com/go/compute v1.7.0 // indirect
cloud.google.com/go/iam v0.5.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.1.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pborman/uuid v1.2.1 // indirect
github.com/pelletier/go-toml/v2 v2.0.5 // indirect
github.com/subosito/gotenv v1.4.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
)
require (
cloud.google.com/go v0.104.0 // indirect
cloud.google.com/go/pubsub v1.3.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.21.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.3 // indirect
github.com/DataDog/zstd v1.5.2 // indirect
github.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46 // indirect
github.com/agext/levenshtein v1.2.1 // indirect
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 // indirect
github.com/aws/aws-sdk-go v1.34.28 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.0.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.0.2 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.1.1 // indirect
@ -119,7 +138,7 @@ require (
github.com/kr/text v0.2.0 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mitchellh/mapstructure v1.4.1 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/pointerstructure v1.2.0 // indirect
github.com/mmcloughlin/addchain v0.4.0 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
@ -141,7 +160,6 @@ require (
go.uber.org/goleak v1.1.12
golang.org/x/mod v0.9.0 // indirect
golang.org/x/net v0.8.0 // indirect
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect
gonum.org/v1/gonum v0.11.0
google.golang.org/grpc v1.51.0
google.golang.org/protobuf v1.28.1
@ -168,10 +186,10 @@ require (
github.com/go-kit/kit v0.10.0 // indirect
github.com/go-logfmt/logfmt v0.5.1 // indirect
github.com/go-redis/redis v6.15.7+incompatible // indirect
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/gomodule/redigo v2.0.0+incompatible // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/googleapis/gax-go/v2 v2.0.5 // indirect
github.com/googleapis/gax-go/v2 v2.5.1 // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
@ -181,25 +199,21 @@ require (
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/jmhodges/levigo v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/jstemmer/go-junit-report v0.9.1 // indirect
github.com/kelseyhightower/envconfig v1.4.0 // indirect
github.com/libp2p/go-buffer-pool v0.0.2 // indirect
github.com/magiconair/properties v1.8.1 // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/mitchellh/copystructure v1.0.0 // indirect
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect
github.com/mitchellh/reflectwalk v1.0.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/posener/complete v1.1.1 // indirect
github.com/rakyll/statik v0.1.7 // indirect
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
github.com/spf13/afero v1.2.2 // indirect
github.com/spf13/cast v1.4.1 // indirect
github.com/spf13/afero v1.8.2 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/cobra v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.4.0 // indirect
github.com/spf13/viper v1.13.0 // indirect
github.com/streadway/amqp v0.0.0-20200108173154-1c71cc93ed71 // indirect
github.com/stumble/gorocksdb v0.0.3 // indirect
github.com/tendermint/btcd v0.1.1 // indirect
@ -211,33 +225,26 @@ require (
github.com/xdg-go/scram v1.0.2 // indirect
github.com/xdg-go/stringprep v1.0.2 // indirect
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect
golang.org/x/oauth2 v0.3.0 // indirect
)
require (
cloud.google.com/go v0.65.0 // indirect
cloud.google.com/go/pubsub v1.3.1 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver v1.5.0 // indirect
github.com/Masterminds/sprig v2.22.0+incompatible // indirect
github.com/RichardKnop/logging v0.0.0-20190827224416-1a693bdd4fae // indirect
github.com/RichardKnop/machinery v1.7.4 // indirect
github.com/RichardKnop/redsync v1.2.0 // indirect
github.com/agext/levenshtein v1.2.1 // indirect
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 // indirect
github.com/aws/aws-sdk-go v1.34.28 // indirect
github.com/zclconf/go-cty v1.8.0 // indirect
github.com/zondax/hid v0.9.0 // indirect
go.mongodb.org/mongo-driver v1.3.0 // indirect
go.opencensus.io v0.22.6 // indirect
go.opencensus.io v0.23.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.2.0 // indirect
go.opentelemetry.io/otel/trace v1.2.0
go.opentelemetry.io/proto/otlp v0.10.0 // indirect
google.golang.org/api v0.34.0 // indirect
google.golang.org/api v0.97.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20220725144611-272f38e5d71b // indirect
google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce // indirect
)
replace github.com/cosmos/cosmos-sdk => github.com/maticnetwork/cosmos-sdk v0.37.5-0.20220311095845-81690c6a53e7

263
go.sum
View file

@ -4,6 +4,7 @@ cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSR
cloud.google.com/go v0.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
@ -14,8 +15,25 @@ cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bP
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0 h1:Dg9iHVQfrhq82rUNu9ZxUDrJLaxFUe/HlCVaLyRruq8=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=
cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=
cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=
cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A=
cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc=
cloud.google.com/go v0.104.0 h1:gSmWO7DY1vOm0MVU6DNXM11BWHHsTUmsC5cv1fuW5X8=
cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
@ -23,8 +41,19 @@ cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUM
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/bigtable v1.2.0/go.mod h1:JcVAOl45lrTmQfLj7T6TxyMzIN/3FGGcFm+2xVAli2o=
cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow=
cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM=
cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M=
cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s=
cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU=
cloud.google.com/go/compute v1.7.0 h1:v/k9Eueb8aAJ0vZuxKMrgm6kPhCLZU9HxFU+AFDs9Uk=
cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY=
cloud.google.com/go/iam v0.5.0 h1:fz9X5zyTWBmamZsqvqZqD7khbifcZF/q+Z1J8pfhIUg=
cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc=
cloud.google.com/go/kms v1.4.0 h1:iElbfoE61VeLhnZcGOltqL8HIly8Nhbe5t6JlH9GXjo=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
@ -35,6 +64,8 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y=
collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8=
@ -204,6 +235,7 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
github.com/cloudflare/cloudflare-go v0.14.0 h1:gFqGlGl/5f9UGXAaKapCGUfaTCgRKKnzu2VvzMZlOFA=
github.com/cloudflare/cloudflare-go v0.14.0/go.mod h1:EnwdgGMaFOruiPZRFSgn+TsQ3hQ7C/YWzIGLeu5c304=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
@ -306,6 +338,7 @@ github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4s
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
@ -322,8 +355,9 @@ github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 h1:E2s37DuLxFhQD
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0=
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/fjl/gencodec v0.0.0-20220412091415-8bb9e558978c h1:CndMRAH4JIwxbW8KYq6Q+cGWcGHz0FjGR3QqcInWcW0=
github.com/fjl/gencodec v0.0.0-20220412091415-8bb9e558978c/go.mod h1:AzA8Lj6YtixmJWL+wkKoBGsLWy9gFrAzi4g+5bCKwpY=
@ -335,6 +369,7 @@ github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
@ -454,8 +489,9 @@ github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4er
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
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=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1-0.20190508161146-9fa652df1129/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
@ -464,6 +500,7 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@ -483,6 +520,7 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
@ -521,6 +559,8 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.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/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
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=
@ -528,6 +568,14 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@ -536,9 +584,20 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
github.com/googleapis/enterprise-certificate-proxy v0.1.0 h1:zO8WHNx/MYiAKJ3d5spxZXZE6KHmIQGQcAzwUzV7qQw=
github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM=
github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM=
github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c=
github.com/googleapis/gax-go/v2 v2.5.1 h1:kBRZU0PSuI7PspsSb/ChWoVResUcwNVIdpB049pKTiw=
github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo=
github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
@ -609,6 +668,7 @@ github.com/huin/goupnp v1.0.3/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixH
github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o=
github.com/hydrogen18/memlistener v0.0.0-20200120041712-dcc25e7acd91/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
@ -661,7 +721,6 @@ github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jsternberg/zap-logfmt v1.0.0/go.mod h1:uvPs/4X51zdkcm5jXl5SYoN+4RK21K8mysFmDaM/h+o=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
@ -703,6 +762,7 @@ github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH6
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.2/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/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
@ -728,8 +788,9 @@ github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-b
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
@ -739,10 +800,12 @@ github.com/maticnetwork/cosmos-sdk v0.37.5-0.20220311095845-81690c6a53e7 h1:8NoE
github.com/maticnetwork/cosmos-sdk v0.37.5-0.20220311095845-81690c6a53e7/go.mod h1:uW55Ru86N5o3L8SVkVL1TPE+mV/WRM2la8sC3TR/Ajc=
github.com/maticnetwork/crand v1.0.2 h1:Af0tAivC8zrxXDpGWNWVT/0s1fOz8w0eRbahZgURS8I=
github.com/maticnetwork/crand v1.0.2/go.mod h1:/NRNL3bj2eYdqpWmoIP5puxndTpi0XRxpj5ZKxfHjyg=
github.com/maticnetwork/heimdall v0.3.1-0.20230105132832-d0063f71e3f0 h1:MYTAcBs4y88GEzesT8eAU472ZfQSLluW0qgkZTzNgwM=
github.com/maticnetwork/heimdall v0.3.1-0.20230105132832-d0063f71e3f0/go.mod h1:A3bUSe9jjMQHEPPOUW1JytM3x2g3t+jvsbPY66KBPIs=
github.com/maticnetwork/polyproto v0.0.2 h1:cPxuxbIDItdwGnucc3lZB58U8Zfe1mH73PWTGd15554=
github.com/maticnetwork/heimdall v0.3.1-0.20230227104835-81bd1055b0bc h1:7wEWQbYs6ESGsVBhjiVp7fZZyfaN4lg4XYLOLfRIpmY=
github.com/maticnetwork/heimdall v0.3.1-0.20230227104835-81bd1055b0bc/go.mod h1:P2DoKhovYP9G9Kj2EH/zHaiRJF1jNU7ZJOyelG4UCa8=
github.com/maticnetwork/polyproto v0.0.2/go.mod h1:e1mU2EXSwEpn5jM7GfNwu3AupsV6WAGoPFFfswXOF0o=
github.com/maticnetwork/polyproto v0.0.3-0.20230216113155-340ea926ca53 h1:PjYV+lghs106JKkrYgOnrsfDLoTc11BxZd4rUa4Rus4=
github.com/maticnetwork/polyproto v0.0.3-0.20230216113155-340ea926ca53/go.mod h1:e1mU2EXSwEpn5jM7GfNwu3AupsV6WAGoPFFfswXOF0o=
github.com/maticnetwork/tendermint v0.26.0-dev0.0.20220923185258-3e7c7f86ce9f h1:iV69PJUEdwJJFXQvbADYVEMxDrkKAsPdHTg4U3F510I=
github.com/maticnetwork/tendermint v0.26.0-dev0.0.20220923185258-3e7c7f86ce9f/go.mod h1:90S74348uYSGfWwNIgvzQiRRakSH/c7VVt1TR5mzIuY=
github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=
@ -751,6 +814,7 @@ github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVc
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
@ -793,8 +857,9 @@ github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS4
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A=
github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4=
github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
@ -859,10 +924,14 @@ github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIw
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw=
github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg=
github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas=
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
github.com/peterh/liner v1.0.1-0.20180619022028-8c1271fcf47f/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
github.com/peterh/liner v1.2.0 h1:w/UPXyl5GfahFxcTOz2j9wCIHNI+pUPr2laqpojKNCg=
@ -881,6 +950,7 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
github.com/pkg/term v0.0.0-20180730021639-bffc007b7fd5/go.mod h1:eCbImbZ95eXtAUIbLAuAVnBnwf83mjf6QIVH8SHYwqQ=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@ -980,11 +1050,13 @@ github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJ
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo=
github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA=
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
@ -998,8 +1070,9 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/spf13/viper v1.13.0 h1:BWSJ/M+f+3nmdz9bxB+bWX28kkALN2ok11D0rSo8EJU=
github.com/spf13/viper v1.13.0/go.mod h1:Icm2xNL3/8uyh/wFuB1jI7TiTNKp8632Nwegu+zgdYw=
github.com/status-im/keycard-go v0.0.0-20211109104530-b0e0482ba91d/go.mod h1:97vT0Rym0wCnK4B++hNA3nCetr0Mh1KXaVxzSt1arjg=
github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA=
github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg=
@ -1026,6 +1099,8 @@ github.com/stumble/gorocksdb v0.0.3 h1:9UU+QA1pqFYJuf9+5p7z1IqdE5k0mma4UAeu2wmX8
github.com/stumble/gorocksdb v0.0.3/go.mod h1:v6IHdFBXk5DJ1K4FZ0xi+eY737quiiBxYtSWXadLybY=
github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203 h1:QVqDTf3h2WHt08YuiTGPZLls0Wq99X9bWd0Q5ZSBesM=
github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203/go.mod h1:oqN97ltKNihBbwlX8dLpwxCl3+HnXKV/R0e+sRLd9C8=
github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/supranational/blst v0.3.8-0.20220526154634-513d2456b344 h1:m+8fKfQwCAy1QjzINvKe/pYtLjo2dl59x2w9YSEJxuY=
github.com/supranational/blst v0.3.8-0.20220526154634-513d2456b344/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA=
@ -1127,8 +1202,10 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.6 h1:BdkrbWrzDlV9dnbzoP7sfN+dHheJ4J9JOaYxcUDL+ok=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.22.6/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opentelemetry.io/otel v1.2.0 h1:YOQDvxO1FayUcT9MIhJhgMyNO1WqoduiyvQHzGN0kUQ=
go.opentelemetry.io/otel v1.2.0/go.mod h1:aT17Fk0Z1Nor9e0uisf98LrntPGMnk4frBO9+dkf69I=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.2.0 h1:xzbcGykysUh776gzD1LUPsNNHKWN0kQWDnJhn1ddUuk=
@ -1174,7 +1251,9 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@ -1218,6 +1297,7 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
@ -1228,6 +1308,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
@ -1278,18 +1360,31 @@ golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81R
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/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=
golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
@ -1300,7 +1395,21 @@ golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4Iltr
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
golang.org/x/oauth2 v0.3.0 h1:6l90koy8/LaBLmLu8jpHeHexzMwEita0zFfYlggy2F8=
golang.org/x/oauth2 v0.3.0/go.mod h1:rQrIauxkUhJ6CuwEXwymO2/eh4xz2ZWF1nBkcxS+tGk=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -1315,6 +1424,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
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/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -1378,10 +1488,17 @@ golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/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-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -1389,19 +1506,36 @@ golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211020174200-9d6173849985/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220818161305-2296e01440c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -1498,10 +1632,17 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc
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-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.6/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
@ -1515,8 +1656,9 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df h1:5Pf6pFKu98ODmgnpvkJ3kFUOQGGLIzLIkbzUHp47618=
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f h1:uF6paiQQebLeSXkrTqHqz0MXhXXS1KgF41eUdBNvxK0=
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
gonum.org/v1/gonum v0.6.0/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU=
@ -1546,8 +1688,32 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/api v0.34.0 h1:k40adF3uR+6x/+hO5Dh4ZFUqFp67vxvbpafFiJxl10A=
google.golang.org/api v0.34.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=
google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=
google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo=
google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g=
google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA=
google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8=
google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs=
google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA=
google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw=
google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg=
google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o=
google.golang.org/api v0.97.0 h1:x/vEL1XDF/2V4xzdNgFPaKHluRESo2aTsL7QzHnBtGQ=
google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@ -1596,10 +1762,60 @@ google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210921142501-181ce0d877f6/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20220725144611-272f38e5d71b h1:SfSkJugek6xm7lWywqth4r2iTrYLpD8lOj1nMIIhMNM=
google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
google.golang.org/genproto v0.0.0-20220725144611-272f38e5d71b/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc=
google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce h1:+2ye9vAK4F9F/LCex8dT2cDk0VnTAwUL8uRgX/6nAMU=
google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI=
google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
@ -1622,15 +1838,28 @@ google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k=
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U=
google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0/go.mod h1:DNq5QpG7LJqD2AamLZ7zvKE0DEpVl2BSEVjFycAAjRY=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
@ -1661,6 +1890,8 @@ gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=

View file

@ -250,9 +250,19 @@ type StateSyncFilter struct {
// interface for whitelist service
type ChainValidator interface {
IsValidPeer(remoteHeader *types.Header, fetchHeadersByNumber func(number uint64, amount int, skip int, reverse bool) ([]*types.Header, []common.Hash, error)) (bool, error)
IsValidPeer(fetchHeadersByNumber func(number uint64, amount int, skip int, reverse bool) ([]*types.Header, []common.Hash, error)) (bool, error)
IsValidChain(currentHeader *types.Header, chain []*types.Header) (bool, error)
GetWhitelistedCheckpoint() (bool, uint64, common.Hash)
GetWhitelistedMilestone() (bool, uint64, common.Hash)
ProcessCheckpoint(endBlockNum uint64, endBlockHash common.Hash)
GetCheckpointWhitelist() map[uint64]common.Hash
PurgeCheckpointWhitelist()
ProcessMilestone(endBlockNum uint64, endBlockHash common.Hash)
ProcessFutureMilestone(num uint64, hash common.Hash)
PurgeWhitelistedCheckpoint()
PurgeWhitelistedMilestone()
LockMutex(endBlockNum uint64) bool
UnlockMutex(doLock bool, milestoneId string, endBlockNum uint64, endBlockHash common.Hash)
UnlockSprint(endBlockNum uint64)
RemoveMilestoneID(milestoneId string)
GetMilestoneIDsList() []string
}

View file

@ -2558,17 +2558,6 @@ func (api *DebugAPI) SetHead(number hexutil.Uint64) {
api.b.SetHead(uint64(number))
}
// GetCheckpointWhitelist retrieves the current checkpoint whitelist
// entries (of the form block number -> block hash)
func (api *DebugAPI) GetCheckpointWhitelist() map[uint64]common.Hash {
return api.b.GetCheckpointWhitelist()
}
// PurgeCheckpointWhitelist purges the current checkpoint whitelist entries
func (api *DebugAPI) PurgeCheckpointWhitelist() {
api.b.PurgeCheckpointWhitelist()
}
// GetTraceStack returns the current trace stack
func (api *DebugAPI) GetTraceStack() string {
buf := make([]byte, 1024)

View file

@ -102,13 +102,16 @@ type Backend interface {
// Bor related APIs
SubscribeStateSyncEvent(ch chan<- core.StateSyncEvent) event.Subscription
GetRootHash(ctx context.Context, starBlockNr uint64, endBlockNr uint64) (string, error)
GetVoteOnHash(ctx context.Context, startBlockNumber uint64, endBlockNumber uint64, hash string, milestoneID string) (bool, error)
GetBorBlockReceipt(ctx context.Context, hash common.Hash) (*types.Receipt, error)
GetBorBlockLogs(ctx context.Context, hash common.Hash) ([]*types.Log, error)
GetBorBlockTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error)
GetBorBlockTransactionWithBlockHash(ctx context.Context, txHash common.Hash, blockHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error)
SubscribeChain2HeadEvent(ch chan<- core.Chain2HeadEvent) event.Subscription
GetCheckpointWhitelist() map[uint64]common.Hash
PurgeCheckpointWhitelist()
GetWhitelistedCheckpoint() (bool, uint64, common.Hash)
PurgeWhitelistedCheckpoint()
GetWhitelistedMilestone() (bool, uint64, common.Hash)
PurgeWhitelistedMilestone()
}
func GetAPIs(apiBackend Backend) []rpc.API {

View file

@ -21,6 +21,10 @@ func (s *BlockChainAPI) GetBorBlockReceipt(ctx context.Context, hash common.Hash
return s.b.GetBorBlockReceipt(ctx, hash)
}
func (s *BlockChainAPI) GetVoteOnHash(ctx context.Context, starBlockNr uint64, endBlockNr uint64, hash string, milestoneId string) (bool, error) {
return s.b.GetVoteOnHash(ctx, starBlockNr, endBlockNr, hash, milestoneId)
}
//
// Bor transaction utils
//

View file

@ -364,51 +364,51 @@ func (b *backendMock) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent)
func (b *backendMock) Engine() consensus.Engine { return nil }
func (b *backendMock) RPCRpcReturnDataLimit() uint64 {
//nolint: staticcheck
return b.RPCRpcReturnDataLimit()
return 0
}
func (b *backendMock) SubscribeStateSyncEvent(ch chan<- core.StateSyncEvent) event.Subscription {
//nolint: staticcheck
return b.SubscribeStateSyncEvent(ch)
return nil
}
func (b *backendMock) GetRootHash(ctx context.Context, starBlockNr uint64, endBlockNr uint64) (string, error) {
//nolint: staticcheck
return b.GetRootHash(ctx, starBlockNr, endBlockNr)
return "", nil
}
func (b *backendMock) GetVoteOnHash(ctx context.Context, starBlockNr uint64, endBlockNr uint64, hash string, milestoneId string) (bool, error) {
return false, nil
}
func (b *backendMock) GetBorBlockReceipt(ctx context.Context, hash common.Hash) (*types.Receipt, error) {
//nolint: staticcheck
return b.GetBorBlockReceipt(ctx, hash)
//nolint: nilnil
return nil, nil
}
func (b *backendMock) GetBorBlockLogs(ctx context.Context, hash common.Hash) ([]*types.Log, error) {
//nolint: staticcheck
return b.GetBorBlockLogs(ctx, hash)
// nolint: nilnil
return nil, nil
}
func (b *backendMock) GetBorBlockTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) {
//nolint: staticcheck
return b.GetBorBlockTransaction(ctx, txHash)
return nil, common.Hash{}, 0, 0, nil
}
func (b *backendMock) GetBorBlockTransactionWithBlockHash(ctx context.Context, txHash common.Hash, blockHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) {
//nolint: staticcheck
return b.GetBorBlockTransactionWithBlockHash(ctx, txHash, blockHash)
return nil, common.Hash{}, 0, 0, nil
}
func (b *backendMock) SubscribeChain2HeadEvent(ch chan<- core.Chain2HeadEvent) event.Subscription {
//nolint: staticcheck
return b.SubscribeChain2HeadEvent(ch)
return nil
}
func (b *backendMock) GetCheckpointWhitelist() map[uint64]common.Hash {
//nolint: staticcheck
return b.GetCheckpointWhitelist()
func (b *backendMock) GetWhitelistedCheckpoint() (bool, uint64, common.Hash) {
return false, 0, common.Hash{}
}
func (b *backendMock) PurgeCheckpointWhitelist() {
//nolint: staticcheck
b.PurgeCheckpointWhitelist()
func (b *backendMock) GetWhitelistedMilestone() (bool, uint64, common.Hash) {
return false, 0, common.Hash{}
}
func (b *backendMock) PurgeWhitelistedCheckpoint() {}
func (b *backendMock) PurgeWhitelistedMilestone() {}

View file

@ -60,6 +60,11 @@ web3._extend({
call: 'bor_getRootHash',
params: 2,
}),
new web3._extend.Method({
name: 'getVoteOnHash',
call: 'bor_getVoteOnHash',
params: 4,
}),
]
});
`

View file

@ -515,13 +515,23 @@ web3._extend({
inputFormatter:[web3._extend.formatters.inputBlockNumberFormatter, web3._extend.formatters.inputBlockNumberFormatter],
}),
new web3._extend.Method({
name: 'getCheckpointWhitelist',
call: 'debug_getCheckpointWhitelist',
name: 'getWhitelistedCheckpoint',
call: 'debug_getWhitelistedCheckpoint',
params: 0,
}),
new web3._extend.Method({
name: 'purgeCheckpointWhitelist',
call: 'debug_purgeCheckpointWhitelist',
name: 'purgeWhitelistedCheckpoint',
call: 'debug_purgeWhitelistedCheckpoint',
params: 0,
}),
new web3._extend.Method({
name: 'getWhitelistedMilestone',
call: 'debug_getWhitelistedMilestone',
params: 0,
}),
new web3._extend.Method({
name: 'purgeWhitelistedMilestone',
call: 'debug_purgeWhitelistedMilestone',
params: 0,
}),
new web3._extend.Method({

View file

@ -370,6 +370,9 @@ func (b *LesApiBackend) StateAtTransaction(ctx context.Context, block *types.Blo
func (b *LesApiBackend) GetBorBlockReceipt(ctx context.Context, hash common.Hash) (*types.Receipt, error) {
return nil, errors.New("not implemented")
}
func (b *LesApiBackend) GetVoteOnHash(ctx context.Context, starBlockNr uint64, endBlockNr uint64, hash string, milestoneId string) (bool, error) {
return false, errors.New("not implemented")
}
func (b *LesApiBackend) GetBorBlockLogs(ctx context.Context, hash common.Hash) ([]*types.Log, error) {
return nil, errors.New("not implemented")
@ -383,9 +386,16 @@ func (b *LesApiBackend) GetBorBlockTransactionWithBlockHash(ctx context.Context,
return nil, common.Hash{}, 0, 0, errors.New("not implemented")
}
func (b *LesApiBackend) GetCheckpointWhitelist() map[uint64]common.Hash {
return nil
func (b *LesApiBackend) GetWhitelistedCheckpoint() (bool, uint64, common.Hash) {
return false, 0, common.Hash{}
}
func (b *LesApiBackend) PurgeCheckpointWhitelist() {
func (b *LesApiBackend) PurgeWhitelistedCheckpoint() {
}
func (b *LesApiBackend) GetWhitelistedMilestone() (bool, uint64, common.Hash) {
return false, 0, common.Hash{}
}
func (b *LesApiBackend) PurgeWhitelistedMilestone() {
}

View file

@ -165,7 +165,10 @@ func New(stack *node.Node, config *ethconfig.Config) (*LightEthereum, error) {
}
// Note: NewLightChain adds the trusted checkpoint so it needs an ODR with
// indexers already set but not started yet
if leth.blockchain, err = light.NewLightChain(leth.odr, leth.chainConfig, leth.engine, checkpoint, whitelist.NewService(10)); err != nil {
checker := whitelist.NewService(chainDb)
if leth.blockchain, err = light.NewLightChain(leth.odr, leth.chainConfig, leth.engine, checkpoint, checker); err != nil {
return nil, err
}

View file

@ -80,7 +80,7 @@ type Miner struct {
engine consensus.Engine
exitCh chan struct{}
startCh chan struct{}
stopCh chan struct{}
stopCh chan chan struct{}
worker *worker
wg sync.WaitGroup
@ -92,8 +92,8 @@ func New(eth Backend, config *Config, chainConfig *params.ChainConfig, mux *even
eth: eth,
engine: engine,
exitCh: make(chan struct{}),
stopCh: make(chan chan struct{}),
startCh: make(chan struct{}),
stopCh: make(chan struct{}),
worker: newWorker(config, chainConfig, engine, eth, mux, isLocalBlock, true),
}
miner.wg.Add(1)
@ -164,10 +164,11 @@ func (miner *Miner) update() {
}
shouldStart = true
case <-miner.stopCh:
case ch := <-miner.stopCh:
shouldStart = false
miner.worker.stop()
close(ch)
case <-miner.exitCh:
miner.worker.close()
return
@ -179,8 +180,8 @@ func (miner *Miner) Start() {
miner.startCh <- struct{}{}
}
func (miner *Miner) Stop() {
miner.stopCh <- struct{}{}
func (miner *Miner) Stop(ch chan struct{}) {
miner.stopCh <- ch
}
func (miner *Miner) Close() {

View file

@ -137,13 +137,15 @@ func TestMinerStartStopAfterDownloaderEvents(t *testing.T) {
mux.Post(downloader.DoneEvent{})
waitForMiningState(t, miner, true)
miner.Stop()
ch := make(chan struct{})
miner.Stop(ch)
waitForMiningState(t, miner, false)
miner.Start()
waitForMiningState(t, miner, true)
miner.Stop()
ch = make(chan struct{})
miner.Stop(ch)
waitForMiningState(t, miner, false)
}
@ -187,7 +189,8 @@ func TestStartStopMiner(t *testing.T) {
miner.Start()
waitForMiningState(t, miner, true)
miner.Stop()
ch := make(chan struct{})
miner.Stop(ch)
waitForMiningState(t, miner, false)
}

View file

@ -68,6 +68,11 @@ const (
PendingBlockNumber = BlockNumber(-2)
LatestBlockNumber = BlockNumber(-1)
EarliestBlockNumber = BlockNumber(0)
latest = "latest"
earliest = "earliest"
pending = "pending"
finalized = "finalized"
)
// UnmarshalJSON parses the given JSON fragment into a BlockNumber. It supports:
@ -84,13 +89,13 @@ func (bn *BlockNumber) UnmarshalJSON(data []byte) error {
}
switch input {
case "earliest":
case earliest:
*bn = EarliestBlockNumber
return nil
case "latest":
case latest:
*bn = LatestBlockNumber
return nil
case "pending":
case pending:
*bn = PendingBlockNumber
return nil
case "finalized":
@ -121,9 +126,9 @@ func (bn *BlockNumber) UnmarshalJSON(data []byte) error {
func (bn BlockNumber) MarshalText() ([]byte, error) {
switch bn {
case EarliestBlockNumber:
return []byte("earliest"), nil
return []byte(earliest), nil
case LatestBlockNumber:
return []byte("latest"), nil
return []byte(latest), nil
case PendingBlockNumber:
return []byte("pending"), nil
case FinalizedBlockNumber:
@ -171,23 +176,20 @@ func (bnh *BlockNumberOrHash) UnmarshalJSON(data []byte) error {
}
switch input {
case "earliest":
case earliest:
bn := EarliestBlockNumber
bnh.BlockNumber = &bn
return nil
case "latest":
case latest:
bn := LatestBlockNumber
bnh.BlockNumber = &bn
return nil
case "pending":
bn := PendingBlockNumber
bnh.BlockNumber = &bn
return nil
case "finalized":
bn := FinalizedBlockNumber
case pending:
bn := PendingBlockNumber
bnh.BlockNumber = &bn
return nil
@ -196,6 +198,13 @@ func (bnh *BlockNumberOrHash) UnmarshalJSON(data []byte) error {
bnh.BlockNumber = &bn
return nil
case finalized:
bn := FinalizedBlockNumber
bnh.BlockNumber = &bn
return nil
default:
if len(input) == 66 {
hash := common.Hash{}

View file

@ -19,7 +19,7 @@ import (
)
func TestBorFilters(t *testing.T) {
t.Parallel()
// t.Parallel()
var (
db = rawdb.NewMemoryDatabase()

File diff suppressed because it is too large Load diff

View file

@ -49,7 +49,7 @@ const (
// Sprint length change tests
func TestValidatorsBlockProduction(t *testing.T) {
t.Parallel()
// t.Parallel()
log.Root().SetHandler(log.LvlFilterHandler(3, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
@ -201,7 +201,7 @@ func TestValidatorsBlockProduction(t *testing.T) {
}
func TestSprintLengths(t *testing.T) {
t.Parallel()
// t.Parallel()
testBorConfig := params.TestChainConfig.Bor
testBorConfig.Sprint = map[string]uint64{
@ -214,7 +214,7 @@ func TestSprintLengths(t *testing.T) {
}
func TestProducerDelay(t *testing.T) {
t.Parallel()
// t.Parallel()
testBorConfig := params.TestChainConfig.Bor
testBorConfig.ProducerDelay = map[string]uint64{
@ -376,7 +376,7 @@ func SprintLengthReorgIndividual2Nodes(t *testing.T, index int, tt map[string]in
func TestSprintLengthReorg2Nodes(t *testing.T) {
t.Skip()
t.Parallel()
// t.Parallel()
log.Root().SetHandler(log.LvlFilterHandler(3, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
@ -424,7 +424,7 @@ func TestSprintLengthReorg2Nodes(t *testing.T) {
func TestSprintLengthReorg(t *testing.T) {
t.Skip()
t.Parallel()
// t.Parallel()
reorgsLengthTests := getTestSprintLengthReorgCases()
f, err := os.Create("sprintReorg.csv")

View file

@ -0,0 +1,703 @@
//go:build integration
// nolint
package bor
import (
"crypto/ecdsa"
"encoding/csv"
"fmt" // nolint: staticcheck
_log "log"
"math"
"os"
"sync"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/fdlimit"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p/enode"
)
var (
// Only this account is a validator for the 0th span
keySprintLength_Milestone, _ = crypto.HexToECDSA(privKeySprintLength_Milestone)
// This account is one the validators for 1st span (0-indexed)
keySprintLength_Milestone2, _ = crypto.HexToECDSA(privKeySprintLength_Milestone2)
keysSprintLength_Milestone = []*ecdsa.PrivateKey{keySprintLength_Milestone, keySprintLength_Milestone2}
)
const (
privKeySprintLength_Milestone = "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291"
privKeySprintLength_Milestone2 = "9b28f36fbd67381120752d6172ecdcf10e06ab2d9a1367aac00cdcd6ac7855d3"
)
var keys_21validator = []map[string]string{
{
"address": "0x5C3E1B893B9315a968fcC6bce9EB9F7d8E22edB3",
"priv_key": "c19fac8e538447124ad2408d9fbaeda2bb686fee763dca7a6bab58ea12442413",
"pub_key": "0x0495421933eda03dcc37f9186c24e255b569513aefae71e96d55d0db3df17502e24e86297b01a167fab9ce1174f06ee3110510ac242e39218bd964de5b345edbd6",
},
{
"address": "0x73E033779C9030D4528d86FbceF5B02e97488921",
"priv_key": "61eb51cf8936309151ab7b931841ea033b6a09931f6a100b464fbbd74f3e0bd7",
"pub_key": "0x04f9a5e9bf76b45ac58f1b018ccba4b83b3531010cdadf42174c18a9db9879ef1dcb5d1254ce834bc108b110cd8d0186ed69a0387528a142bdb5936faf58bf98c9",
},
{
"address": "0x751eC4877450B8a4D652d0D70197337FC38a42e6",
"priv_key": "6e7f48d012c9c0baadbdc88af32521e2e477fd6898a9b65e6abe19fd6652cb2e",
"pub_key": "0x0479db4c0b757bf0e5d9b8954b078ab7c0e91d6c19697904d23d07ea4853c8584382de91174929ba5c598214b8a991471ae051458ea787cdc15a4e435a55ef8059",
},
{
"address": "0xA464DC4810Bc79B956810759e314d85BcE35cD1c",
"priv_key": "3efcf3f7014a6257f4a443119851414111820c681b27525dab3f35e72e28e51e",
"pub_key": "0x040180920306bf598ea050e258f2c7e50804a77a64f5a11705e08d18ee71eb0a80fafc95d0a42b92371ded042edda16c1f0b5f2fef7c4113ad66c59a71c29d977e",
},
{
"address": "0xb005bc07015170266Bd430f3EC1322938603be20",
"priv_key": "17cd9b38c2b3a639c7d97ccbf2bb6c7140ab8f625aec4c249bc8e4cfd3bf9a96",
"pub_key": "0x04435a70d343aa569e6f3386c73e39a1aa6f88c30e5943baedda9618b55cc944a2de1d114aff6d0e9fa002bebc780b04ef6c1b8a06bbf0d41c10d1efa55390f198",
},
{
"address": "0xE8d02Da3dFeeB3e755472D95D666BD6821D92129",
"priv_key": "45c9ef66361a2283cef14184f128c41949103b791aa622ead3c0bc844648b835",
"pub_key": "0x04a14651ddc80467eb589d72d95153fa695e4cb2e4bb99edeb912e840d309d61313b6f4676081b099f29e6598ecf98cb7b44bb862d019920718b558f27ba94ca51",
},
{
"address": "0xF93B54Cf36E917f625B48e1e3C9F93BC2344Fb06",
"priv_key": "93788a1305605808df1f9a96b5e1157da191680cf08bc15e077138f517563cd5",
"pub_key": "0x045eee11dceccd9cccc371ca3d96d74c848e785223f1e5df4d1a7f08efdfeb90bd8f0035342a9c26068cf6c7ab395ca3ceea555541325067fc187c375390efa57d",
},
}
func getTestSprintLengthMilestoneReorgCases() []map[string]uint64 {
faultyNodes := []int64{2, 4}
milestoneMark := []int64{48, 72}
sprintLen := int64(16)
milestoneLen := int64(24)
reorgsLengthTests := make([]map[string]uint64, 0)
for i := int64(0); i < int64(len(faultyNodes)); i++ {
for j := int64(1); j < int64(14); j = j + 2 {
startBlock := faultyNodes[i]*sprintLen + j
diff := milestoneMark[i] - startBlock
if diff <= 0 {
break
}
a := diff*2 - 2
for k := diff + 1; k < a; k = k + 4 {
for l := 0; l < 2; l++ {
reorgsLengthTest := map[string]uint64{
"reorgLength": uint64(k),
"startBlock": uint64(startBlock),
"sprintSize": uint64(sprintLen),
"faultyNode": uint64(faultyNodes[i]), // node 1(index) is primary validator of the first sprint
"milestoneLength": uint64(milestoneLen),
"milestoneFlag": uint64(l),
}
reorgsLengthTests = append(reorgsLengthTests, reorgsLengthTest)
}
}
}
}
return reorgsLengthTests
}
func getTestSprintLengthMilestoneReorgCases2Nodes() []map[string]interface{} {
sprintSizes := []uint64{16, 32, 64}
faultyNodes := [][]uint64{{0, 1}, {1, 2}, {0, 2}}
milestoneLength := []uint64{16, 32, 64}
reorgsLengthTests := make([]map[string]interface{}, 0)
for i := uint64(0); i < uint64(len(sprintSizes)); i++ {
for m := uint64(0); m < uint64(len(milestoneLength)); m++ {
maxReorgLength := sprintSizes[i] * 4
for j := uint64(20); j <= maxReorgLength; j = j + 8 {
maxStartBlock := sprintSizes[i] - 1
for k := sprintSizes[i] / 2; k <= maxStartBlock; k = k + 8 {
for l := uint64(0); l < uint64(len(faultyNodes)); l++ {
if j+k < sprintSizes[i] {
continue
}
reorgsLengthTest := map[string]interface{}{
"reorgLength": j,
"startBlock": k,
"sprintSize": sprintSizes[i],
"faultyNodes": faultyNodes[l], // node 1(index) is primary validator of the first sprint
"milestoneLength": milestoneLength[m],
}
reorgsLengthTests = append(reorgsLengthTests, reorgsLengthTest)
}
}
}
}
}
// reorgsLengthTests := []map[string]uint64{
// {
// "reorgLength": 3,
// "startBlock": 7,
// "sprintSize": 8,
// "faultyNode": 1,
// "milestoneLength": 32
// },
// }
return reorgsLengthTests
}
func SprintLengthMilestoneReorgIndividual(t *testing.T, index int, tt map[string]uint64) (uint64, uint64, uint64, uint64, uint64, uint64, uint64, uint64) {
t.Helper()
log.Warn("Case ----- ", "Index", index, "InducedReorgLength", tt["reorgLength"], "BlockStart", tt["startBlock"], "SprintSize", tt["startBlock"], "MilestoneFlag", tt["milestoneFlag"], "MilestoneLength", tt["milestoneLength"], "DisconnectedNode", tt["faultyNode"])
observerOldChainLength, faultyOldChainLength := SetupValidatorsAndTestSprintLengthMilestone(t, tt)
if observerOldChainLength > 0 {
log.Warn("Observer", "Old Chain length", observerOldChainLength)
}
if faultyOldChainLength > 0 {
log.Warn("Faulty", "Old Chain length", faultyOldChainLength)
}
return tt["reorgLength"], tt["startBlock"], tt["sprintSize"], tt["milestoneFlag"], tt["milestoneLength"], tt["faultyNode"], faultyOldChainLength, observerOldChainLength
}
func SprintLengthMilestoneReorgIndividual2Nodes(t *testing.T, index int, tt map[string]interface{}) (uint64, uint64, uint64, uint64, []uint64, uint64, uint64) {
t.Helper()
log.Warn("Case ----- ", "Index", index, "InducedReorgLength", tt["reorgLength"], "BlockStart", tt["startBlock"], "SprintSize", tt["sprintSize"], "DisconnectedNode", tt["faultyNodes"])
observerOldChainLength, faultyOldChainLength := SetupValidatorsAndTest2NodesSprintLengthMilestone(t, tt)
if observerOldChainLength > 0 {
log.Warn("Observer", "Old Chain length", observerOldChainLength)
}
if faultyOldChainLength > 0 {
log.Warn("Faulty", "Old Chain length", faultyOldChainLength)
}
fNodes, _ := tt["faultyNodes"].([]uint64)
return tt["reorgLength"].(uint64), tt["startBlock"].(uint64), tt["sprintSize"].(uint64), tt["milestoneLength"].(uint64), fNodes, faultyOldChainLength, observerOldChainLength
}
func TestSprintLengthMilestoneReorg2Nodes(t *testing.T) {
t.Skip()
// t.Parallel()
log.Root().SetHandler(log.LvlFilterHandler(3, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
_, err := fdlimit.Raise(2048)
if err != nil {
panic(err)
}
reorgsLengthTests := getTestSprintLengthMilestoneReorgCases2Nodes()
f, err := os.Create("sprintReorgMilestone2Nodes.csv")
defer func() {
err = f.Close()
if err != nil {
panic(err)
}
}()
if err != nil {
_log.Fatalln("failed to open file", err)
}
w := csv.NewWriter(f)
err = w.Write([]string{"Induced Reorg Length", "Start Block", "Sprint Size", "MilestoneLength", "Disconnected Node Ids", "Disconnected Node Id's Rewind Length", "Observer Node Id's Reorg Length"})
w.Flush()
if err != nil {
panic(err)
}
var wg sync.WaitGroup
for index, tt := range reorgsLengthTests {
if index%4 == 0 {
wg.Wait()
}
wg.Add(1)
go SprintLengthMilestoneReorgIndividual2NodesHelper(t, index, tt, w, &wg)
}
}
func TestSprintLengthMilestoneReorg(t *testing.T) {
t.Skip()
// t.Parallel()
log.Root().SetHandler(log.LvlFilterHandler(3, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
_, err := fdlimit.Raise(2048)
if err != nil {
panic(err)
}
reorgsLengthTests := getTestSprintLengthMilestoneReorgCases()
f, err := os.Create("sprintMilestoneReorg.csv")
defer func() {
err = f.Close()
if err != nil {
panic(err)
}
}()
if err != nil {
_log.Fatalln("failed to open file", err)
}
w := csv.NewWriter(f)
err = w.Write([]string{"Induced Reorg Length", "Start Block", "Sprint Size", "MilestoneFlag", "Milestone Length", "Disconnected Node Id", "Disconnected Node Id's Rewind Length", "Observer Node Id's Reorg Length"})
w.Flush()
if err != nil {
panic(err)
}
err = w.Write([]string{fmt.Sprint(len(reorgsLengthTests))})
var wg sync.WaitGroup
for index, tt := range reorgsLengthTests {
if index%4 == 0 {
wg.Wait()
}
wg.Add(1)
go SprintLengthMilestoneReorgIndividualHelper(t, index, tt, w, &wg)
}
}
func SprintLengthMilestoneReorgIndividualHelper(t *testing.T, index int, tt map[string]uint64, w *csv.Writer, wg *sync.WaitGroup) {
t.Helper()
r1, r2, r3, r4, r5, r6, r7, r8 := SprintLengthMilestoneReorgIndividual(t, index, tt)
err := w.Write([]string{fmt.Sprint(r1), fmt.Sprint(r2), fmt.Sprint(r3), fmt.Sprint(r4), fmt.Sprint(r5), fmt.Sprint(r6), fmt.Sprint(r7), fmt.Sprint(r8)})
if err != nil {
panic(err)
}
w.Flush()
(*wg).Done()
}
func SprintLengthMilestoneReorgIndividual2NodesHelper(t *testing.T, index int, tt map[string]interface{}, w *csv.Writer, wg *sync.WaitGroup) {
t.Helper()
r1, r2, r3, r4, r5, r6, r7 := SprintLengthMilestoneReorgIndividual2Nodes(t, index, tt)
err := w.Write([]string{fmt.Sprint(r1), fmt.Sprint(r2), fmt.Sprint(r3), fmt.Sprint(r4), fmt.Sprint(r5), fmt.Sprint(r6), fmt.Sprint(r7)})
if err != nil {
panic(err)
}
w.Flush()
(*wg).Done()
}
// nolint: gocognit
func SetupValidatorsAndTest2NodesSprintLengthMilestone(t *testing.T, tt map[string]interface{}) (uint64, uint64) {
t.Helper()
log.Root().SetHandler(log.LvlFilterHandler(3, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
_, err := fdlimit.Raise(2048)
if err != nil {
panic(err)
}
// Generate a batch of accounts to seal and fund with
faucets := make([]*ecdsa.PrivateKey, 128)
for i := 0; i < len(faucets); i++ {
faucets[i], _ = crypto.GenerateKey()
}
// Create an Ethash network based off of the Ropsten config
// Generate a batch of accounts to seal and fund with
genesis := InitGenesis(t, faucets, "./testdata/genesis_7val.json", tt["sprintSize"].(uint64))
nodes := make([]*eth.Ethereum, len(keys_21val))
enodes := make([]*enode.Node, len(keys_21val))
stacks := make([]*node.Node, len(keys_21val))
pkeys_21val := make([]*ecdsa.PrivateKey, len(keys_21val))
for index, signerdata := range keys_21val {
pkeys_21val[index], _ = crypto.HexToECDSA(signerdata["priv_key"])
}
for i := 0; i < len(keys_21val); i++ {
// Start the node and wait until it's up
stack, ethBackend, err := InitMiner(genesis, pkeys_21val[i], true)
if err != nil {
panic(err)
}
defer stack.Close()
for stack.Server().NodeInfo().Ports.Listener == 0 {
time.Sleep(250 * time.Millisecond)
}
// Connect the node to all the previous ones
for j, n := range enodes {
if j < i {
stack.Server().AddPeer(n)
}
}
// Start tracking the node and its enode
stacks[i] = stack
nodes[i] = ethBackend
enodes[i] = stack.Server().Self()
}
// Iterate over all the nodes and start mining
time.Sleep(3 * time.Second)
for _, node := range nodes {
if err := node.StartMining(1); err != nil {
panic(err)
}
}
milestoneLength := tt["milestoneLength"].(uint64)
chain2HeadChObserver := make(chan core.Chain2HeadEvent, 64)
chain2HeadChFaulty := make(chan core.Chain2HeadEvent, 64)
var observerOldChainLength, faultyOldChainLength uint64
faultyProducerIndex := tt["faultyNodes"].([]uint64)[0] // node causing reorg :: faulty ::
subscribedNodeIndex := 6 // node on different partition, produces 7th sprint but our testcase does not run till 7th sprint. :: observer ::
nodes[subscribedNodeIndex].BlockChain().SubscribeChain2HeadEvent(chain2HeadChObserver)
nodes[faultyProducerIndex].BlockChain().SubscribeChain2HeadEvent(chain2HeadChFaulty)
stacks[faultyProducerIndex].Server().NoDiscovery = true
for {
blockHeaderObserver := nodes[subscribedNodeIndex].BlockChain().CurrentHeader()
blockHeaderFaulty := nodes[faultyProducerIndex].BlockChain().CurrentHeader()
log.Warn("Current Observer block", "number", blockHeaderObserver.Number, "hash", blockHeaderObserver.Hash())
log.Warn("Current Faulty block", "number", blockHeaderFaulty.Number, "hash", blockHeaderFaulty.Hash())
if blockHeaderObserver.Number.Uint64() >= tt["startBlock"].(uint64) && blockHeaderObserver.Number.Uint64() < tt["startBlock"].(uint64)+tt["reorgLength"].(uint64) {
for _, n := range tt["faultyNodes"].([]uint64) {
stacks[n].Server().MaxPeers = 1
for _, enode := range enodes {
stacks[n].Server().RemovePeer(enode)
}
for _, m := range tt["faultyNodes"].([]uint64) {
stacks[m].Server().AddPeer(enodes[n])
}
}
}
if math.Mod(float64(blockHeaderObserver.Number.Uint64()), float64(milestoneLength)) == 0 {
blockHash := blockHeaderObserver.Hash()
nodes[subscribedNodeIndex].Downloader().ChainValidator.ProcessMilestone(blockHeaderObserver.Number.Uint64(), blockHash)
}
if blockHeaderObserver.Number.Uint64() == tt["startBlock"].(uint64)+tt["reorgLength"].(uint64) {
stacks[faultyProducerIndex].Server().NoDiscovery = false
stacks[faultyProducerIndex].Server().MaxPeers = 100
for _, enode := range enodes {
stacks[faultyProducerIndex].Server().AddPeer(enode)
}
}
if blockHeaderObserver.Number.Uint64() >= 255 {
break
}
select {
case ev := <-chain2HeadChObserver:
if ev.Type == core.Chain2HeadReorgEvent {
if len(ev.OldChain) > 1 {
observerOldChainLength = uint64(len(ev.OldChain))
return observerOldChainLength, 0
}
}
case ev := <-chain2HeadChFaulty:
if ev.Type == core.Chain2HeadReorgEvent {
if len(ev.OldChain) > 1 {
faultyOldChainLength = uint64(len(ev.OldChain))
return 0, faultyOldChainLength
}
}
default:
time.Sleep(500 * time.Millisecond)
}
}
return 0, 0
}
func SetupValidatorsAndTestSprintLengthMilestone(t *testing.T, tt map[string]uint64) (uint64, uint64) {
t.Helper()
log.Root().SetHandler(log.LvlFilterHandler(3, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
_, err := fdlimit.Raise(2048)
if err != nil {
panic(err)
}
// Generate a batch of accounts to seal and fund with
faucets := make([]*ecdsa.PrivateKey, 128)
for i := 0; i < len(faucets); i++ {
faucets[i], _ = crypto.GenerateKey()
}
// Create an Ethash network based off of the Ropsten config
// Generate a batch of accounts to seal and fund with
genesis := InitGenesis(t, faucets, "./testdata/genesis_7val.json", tt["sprintSize"])
nodes := make([]*eth.Ethereum, len(keys_21val))
enodes := make([]*enode.Node, len(keys_21val))
stacks := make([]*node.Node, len(keys_21val))
pkeys_21val := make([]*ecdsa.PrivateKey, len(keys_21val))
for index, signerdata := range keys_21val {
pkeys_21val[index], _ = crypto.HexToECDSA(signerdata["priv_key"])
}
for i := 0; i < len(keys_21val); i++ {
// Start the node and wait until it's up
stack, ethBackend, err := InitMiner(genesis, pkeys_21val[i], true)
if err != nil {
panic(err)
}
defer stack.Close()
for stack.Server().NodeInfo().Ports.Listener == 0 {
time.Sleep(250 * time.Millisecond)
}
// Connect the node to all the previous ones
for j, n := range enodes {
if j < i {
stack.Server().AddPeer(n)
}
}
// Start tracking the node and its enode
stacks[i] = stack
nodes[i] = ethBackend
enodes[i] = stack.Server().Self()
}
// Iterate over all the nodes and start mining
time.Sleep(3 * time.Second)
for _, node := range nodes {
if err := node.StartMining(1); err != nil {
panic(err)
}
}
chain2HeadChObserver := make(chan core.Chain2HeadEvent, 64)
chain2HeadChFaulty := make(chan core.Chain2HeadEvent, 64)
var observerOldChainLength, faultyOldChainLength uint64
faultyProducerIndex := tt["faultyNode"] // node causing reorg :: faulty ::
subscribedNodeIndex := 6 // node on different partition, produces 7th sprint but our testcase does not run till 7th sprint. :: observer ::
milestoneLength := tt["milestoneLength"]
milestoneFlag := tt["milestoneFlag"]
nodes[subscribedNodeIndex].BlockChain().SubscribeChain2HeadEvent(chain2HeadChObserver)
nodes[faultyProducerIndex].BlockChain().SubscribeChain2HeadEvent(chain2HeadChFaulty)
stacks[faultyProducerIndex].Server().NoDiscovery = true
var milestoneNum uint64 = 0
var milestoneHash common.Hash
var lastRun uint64 = 0
for {
blockHeaderObserver := nodes[subscribedNodeIndex].BlockChain().CurrentHeader()
blockHeaderFaulty := nodes[faultyProducerIndex].BlockChain().CurrentHeader()
log.Warn("Current Observer block", "number", blockHeaderObserver.Number, "hash", blockHeaderObserver.Hash())
if blockHeaderFaulty != nil {
log.Warn("Current Faulty block", "number", blockHeaderFaulty.Number, "hash", blockHeaderFaulty.Hash())
}
if blockHeaderFaulty.Number.Uint64() == tt["startBlock"] {
stacks[faultyProducerIndex].Server().MaxPeers = 0
for _, enode := range enodes {
stacks[faultyProducerIndex].Server().RemovePeer(enode)
}
}
if blockHeaderFaulty.Number.Uint64() >= tt["startBlock"] && blockHeaderFaulty.Number.Uint64() < tt["startBlock"]+tt["reorgLength"] {
for _, enode := range enodes {
stacks[faultyProducerIndex].Server().RemovePeer(enode)
}
}
if milestoneFlag == 1 {
if blockHeaderObserver.Number.Uint64() >= milestoneLength && math.Mod(float64(blockHeaderObserver.Number.Uint64()), float64(milestoneLength)) == 0 && blockHeaderObserver.Number.Uint64() > milestoneNum {
milestoneNum = blockHeaderObserver.Number.Uint64()
milestoneHash = blockHeaderObserver.Hash()
}
if blockHeaderObserver.Number.Uint64() > lastRun {
for _, nodeTemp := range nodes {
_, _, err := borVerifyTemP(nodeTemp, milestoneNum-milestoneLength+1, milestoneNum, milestoneHash.String())
if err == nil {
nodeTemp.Downloader().ChainValidator.ProcessMilestone(milestoneNum, milestoneHash)
} else {
nodeTemp.Downloader().ChainValidator.ProcessFutureMilestone(milestoneNum, milestoneHash)
}
}
}
}
if blockHeaderFaulty.Number.Uint64() == tt["startBlock"]+tt["reorgLength"] {
stacks[faultyProducerIndex].Server().NoDiscovery = false
stacks[faultyProducerIndex].Server().MaxPeers = 100
for _, enode := range enodes {
stacks[faultyProducerIndex].Server().AddPeer(enode)
}
}
if blockHeaderFaulty.Number.Uint64() >= 150 {
break
}
select {
case ev := <-chain2HeadChObserver:
if ev.Type == core.Chain2HeadReorgEvent {
if len(ev.OldChain) > 1 {
observerOldChainLength = uint64(len(ev.OldChain))
return observerOldChainLength, 0
}
}
case ev := <-chain2HeadChFaulty:
if ev.Type == core.Chain2HeadReorgEvent {
if len(ev.OldChain) > 1 {
faultyOldChainLength = uint64(len(ev.OldChain))
return 0, faultyOldChainLength
}
}
default:
time.Sleep(500 * time.Millisecond)
}
}
return 0, 0
}
func borVerifyTemP(eth *eth.Ethereum, start uint64, end uint64, hash string) (string, uint64, error) {
// check if we have the given blocks
currentBlock := eth.BlockChain().CurrentBlock()
if currentBlock == nil {
log.Debug("Failed to fetch current block from blockchain while verifying incoming milestone")
return hash, 0, errMissingBlocks
}
head := currentBlock.Number.Uint64()
if head < end {
log.Debug("Current head block behind incoming milestone block", "head", head, "end block", end)
return hash, 0, errMissingBlocks
}
var localHash string
block := eth.BlockChain().GetBlockByNumber(end)
if block == nil {
log.Debug("Failed to get end block hash while whitelisting milestone", "number", end)
return hash, 0, errEndBlock
}
localHash = block.Hash().String()
//nolint
if localHash != hash {
log.Warn("End block hash mismatch while whitelisting milestone", "expected", localHash, "got", hash)
var (
rewindTo uint64
doExist bool
)
if doExist, rewindTo, _ = eth.Downloader().GetWhitelistedMilestone(); doExist {
} else if doExist, rewindTo, _ = eth.Downloader().GetWhitelistedCheckpoint(); doExist {
} else {
if start <= 0 {
rewindTo = 0
} else {
rewindTo = start - 1
}
}
if head-rewindTo > 255 {
rewindTo = head - 255
}
rewindBackTemp(eth, rewindTo)
return hash, rewindTo, errHashMismatch
}
return block.Hash().String(), 0, nil
}
// Stop the miner if the mining process is running and rewind back the chain
func rewindBackTemp(eth *eth.Ethereum, rewindTo uint64) {
if eth.Miner().Mining() {
ch := make(chan struct{})
eth.Miner().Stop(ch)
<-ch
rewindTemp(eth, rewindTo)
eth.StartMining(1)
} else {
rewindTemp(eth, rewindTo)
}
}
func rewindTemp(eth *eth.Ethereum, rewindTo uint64) {
log.Warn("Rewinding chain to :", rewindTo, "block number")
err := eth.BlockChain().SetHead(rewindTo)
if err != nil {
log.Error("Error while rewinding the chain to", "Block Number", rewindTo, "Error", err)
}
}

View file

@ -26,6 +26,7 @@ import (
"github.com/ethereum/go-ethereum/consensus/bor"
"github.com/ethereum/go-ethereum/consensus/bor/clerk"
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/checkpoint"
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/milestone"
"github.com/ethereum/go-ethereum/consensus/bor/valset"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
@ -370,20 +371,32 @@ func TestInsertingSpanSizeBlocks(t *testing.T) {
engine := init.ethereum.Engine()
_bor := engine.(*bor.Bor)
defer _bor.Close()
_, currentSpan := loadSpanFromFile(t)
h, ctrl := getMockedHeimdallClient(t, currentSpan)
defer ctrl.Finish()
defer func() {
_bor.Close()
ctrl.Finish()
}()
h.EXPECT().Close().AnyTimes()
h.EXPECT().FetchCheckpoint(gomock.Any(), int64(-1)).Return(&checkpoint.Checkpoint{
Proposer: currentSpan.SelectedProducers[0].Address,
StartBlock: big.NewInt(0),
EndBlock: big.NewInt(int64(spanSize)),
}, nil).AnyTimes()
h.EXPECT().FetchMilestone(gomock.Any()).Return(&milestone.Milestone{
Proposer: currentSpan.SelectedProducers[0].Address,
StartBlock: big.NewInt(0),
EndBlock: big.NewInt(int64(spanSize)),
}, nil).AnyTimes()
h.EXPECT().FetchLastNoAckMilestone(gomock.Any()).Return("", nil).AnyTimes()
h.EXPECT().FetchNoAckMilestone(gomock.Any(), string("test")).Return(nil).AnyTimes()
_bor.SetHeimdallClient(h)
block := init.genesis.ToBlock()
@ -450,8 +463,17 @@ func TestFetchStateSyncEvents(t *testing.T) {
h := mocks.NewMockIHeimdallClient(ctrl)
h.EXPECT().Close().AnyTimes()
h.EXPECT().Span(gomock.Any(), uint64(1)).Return(&res.Result, nil).AnyTimes()
h.EXPECT().FetchCheckpoint(gomock.Any(), int64(-1)).Return(&checkpoint.Checkpoint{}, nil).AnyTimes()
h.EXPECT().FetchMilestone(gomock.Any()).Return(&milestone.Milestone{}, nil).AnyTimes()
h.EXPECT().FetchLastNoAckMilestone(gomock.Any()).Return("", nil).AnyTimes()
h.EXPECT().FetchNoAckMilestone(gomock.Any(), string("test")).Return(nil).AnyTimes()
// B.2 Mock State Sync events
fromID := uint64(1)
// at # sprintSize, events are fetched for [fromID, (block-sprint).Time)
@ -500,8 +522,17 @@ func TestFetchStateSyncEvents_2(t *testing.T) {
h := mocks.NewMockIHeimdallClient(ctrl)
h.EXPECT().Close().AnyTimes()
h.EXPECT().Span(gomock.Any(), uint64(1)).Return(&res.Result, nil).AnyTimes()
h.EXPECT().FetchCheckpoint(gomock.Any(), int64(-1)).Return(&checkpoint.Checkpoint{}, nil).AnyTimes()
h.EXPECT().FetchMilestone(gomock.Any()).Return(&milestone.Milestone{}, nil).AnyTimes()
h.EXPECT().FetchLastNoAckMilestone(gomock.Any()).Return("", nil).AnyTimes()
h.EXPECT().FetchNoAckMilestone(gomock.Any(), string("test")).Return(nil).AnyTimes()
// Mock State Sync events
// at # sprintSize, events are fetched for [fromID, (block-sprint).Time)
fromID := uint64(1)
@ -592,8 +623,17 @@ func TestOutOfTurnSigning(t *testing.T) {
h.EXPECT().Close().AnyTimes()
h.EXPECT().FetchCheckpoint(gomock.Any(), int64(-1)).Return(&checkpoint.Checkpoint{}, nil).AnyTimes()
h.EXPECT().FetchMilestone(gomock.Any()).Return(&milestone.Milestone{}, nil).AnyTimes()
h.EXPECT().FetchLastNoAckMilestone(gomock.Any()).Return("", nil).AnyTimes()
h.EXPECT().FetchNoAckMilestone(gomock.Any(), string("test")).Return(nil).AnyTimes()
spanner := getMockedSpanner(t, heimdallSpan.ValidatorSet.Validators)
_bor.SetSpanner(spanner)
_bor.SetHeimdallClient(h)
block := init.genesis.ToBlock()
@ -673,6 +713,13 @@ func TestSignerNotFound(t *testing.T) {
defer ctrl.Finish()
h.EXPECT().Close().AnyTimes()
h.EXPECT().FetchCheckpoint(gomock.Any(), int64(-1)).Return(&checkpoint.Checkpoint{}, nil).AnyTimes()
h.EXPECT().FetchMilestone(gomock.Any()).Return(&milestone.Milestone{}, nil).AnyTimes()
h.EXPECT().FetchLastNoAckMilestone(gomock.Any()).Return("", nil).AnyTimes()
h.EXPECT().FetchNoAckMilestone(gomock.Any(), string("test")).Return(nil).AnyTimes()
_bor.SetHeimdallClient(h)

View file

@ -10,6 +10,7 @@ import (
clerk "github.com/ethereum/go-ethereum/consensus/bor/clerk"
checkpoint "github.com/ethereum/go-ethereum/consensus/bor/heimdall/checkpoint"
milestone "github.com/ethereum/go-ethereum/consensus/bor/heimdall/milestone"
span "github.com/ethereum/go-ethereum/consensus/bor/heimdall/span"
gomock "github.com/golang/mock/gomock"
)
@ -79,6 +80,79 @@ func (mr *MockIHeimdallClientMockRecorder) FetchCheckpointCount(arg0 interface{}
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchCheckpointCount", reflect.TypeOf((*MockIHeimdallClient)(nil).FetchCheckpointCount), arg0)
}
// FetchLastNoAckMilestone mocks base method.
func (m *MockIHeimdallClient) FetchLastNoAckMilestone(arg0 context.Context) (string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "FetchLastNoAckMilestone", arg0)
ret0, _ := ret[0].(string)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// FetchLastNoAckMilestone indicates an expected call of FetchLastNoAckMilestone.
func (mr *MockIHeimdallClientMockRecorder) FetchLastNoAckMilestone(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchLastNoAckMilestone", reflect.TypeOf((*MockIHeimdallClient)(nil).FetchLastNoAckMilestone), arg0)
}
// FetchMilestone mocks base method.
func (m *MockIHeimdallClient) FetchMilestone(arg0 context.Context) (*milestone.Milestone, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "FetchMilestone", arg0)
ret0, _ := ret[0].(*milestone.Milestone)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// FetchMilestone indicates an expected call of FetchMilestone.
func (mr *MockIHeimdallClientMockRecorder) FetchMilestone(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchMilestone", reflect.TypeOf((*MockIHeimdallClient)(nil).FetchMilestone), arg0)
}
// FetchMilestoneCount mocks base method.
func (m *MockIHeimdallClient) FetchMilestoneCount(arg0 context.Context) (int64, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "FetchMilestoneCount", arg0)
ret0, _ := ret[0].(int64)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// FetchMilestoneCount indicates an expected call of FetchMilestoneCount.
func (mr *MockIHeimdallClientMockRecorder) FetchMilestoneCount(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchMilestoneCount", reflect.TypeOf((*MockIHeimdallClient)(nil).FetchMilestoneCount), arg0)
}
// FetchMilestoneID mocks base method.
func (m *MockIHeimdallClient) FetchMilestoneID(arg0 context.Context, arg1 string) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "FetchMilestoneID", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// FetchMilestoneID indicates an expected call of FetchMilestoneID.
func (mr *MockIHeimdallClientMockRecorder) FetchMilestoneID(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchMilestoneID", reflect.TypeOf((*MockIHeimdallClient)(nil).FetchMilestoneID), arg0, arg1)
}
// FetchNoAckMilestone mocks base method.
func (m *MockIHeimdallClient) FetchNoAckMilestone(arg0 context.Context, arg1 string) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "FetchNoAckMilestone", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// FetchNoAckMilestone indicates an expected call of FetchNoAckMilestone.
func (mr *MockIHeimdallClientMockRecorder) FetchNoAckMilestone(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchNoAckMilestone", reflect.TypeOf((*MockIHeimdallClient)(nil).FetchNoAckMilestone), arg0, arg1)
}
// Span mocks base method.
func (m *MockIHeimdallClient) Span(arg0 context.Context, arg1 uint64) (*span.HeimdallSpan, error) {
m.ctrl.T.Helper()