From b48b89f8529c61f32ad5be6d26baf0df883006e5 Mon Sep 17 00:00:00 2001 From: Arpit Temani Date: Mon, 28 Aug 2023 18:42:21 +0530 Subject: [PATCH] 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 Co-authored-by: VaibhavJindal <74560896+VAIBHAVJINDAL3012@users.noreply.github.com> Co-authored-by: Manav Darji Co-authored-by: Evgeny Danienko <6655321@bk.ru> Co-authored-by: Shivam Sharma Co-authored-by: Anshal Shukla --- accounts/abi/bind/backends/bor_simulated.go | 4 + common/flags/milestone.go | 3 + common/gererics/empty.go | 5 + consensus/bor/api.go | 4 + consensus/bor/heimdall.go | 6 + consensus/bor/heimdall/client.go | 134 +- consensus/bor/heimdall/client_test.go | 99 +- consensus/bor/heimdall/metrics.go | 48 +- consensus/bor/heimdall/milestone/milestone.go | 58 + consensus/bor/heimdallapp/checkpoint.go | 7 - consensus/bor/heimdallapp/client.go | 8 + consensus/bor/heimdallapp/milestone.go | 82 + consensus/bor/heimdallapp/state_sync.go | 8 +- consensus/bor/heimdallgrpc/milestone.go | 103 ++ core/blockchain.go | 10 +- core/blockchain_reader.go | 11 + core/blockchain_test.go | 30 +- core/forkchoice_test.go | 28 +- core/rawdb/checkpoint.go | 33 + core/rawdb/milestone.go | 221 +++ eth/api.go | 49 +- eth/api_backend.go | 34 +- eth/backend.go | 176 ++- eth/bor_api_backend.go | 70 +- eth/bor_checkpoint_verifier.go | 178 ++- eth/downloader/downloader.go | 21 +- eth/downloader/downloader_test.go | 36 +- eth/downloader/whitelist/checkpoint.go | 67 + eth/downloader/whitelist/finality.go | 96 ++ eth/downloader/whitelist/milestone.go | 315 ++++ eth/downloader/whitelist/service.go | 308 ++-- eth/downloader/whitelist/service_test.go | 863 +++++++++- eth/filters/IBackend.go | 15 + eth/filters/test_backend.go | 4 + eth/handler_bor.go | 150 +- eth/handler_bor_test.go | 142 +- ethclient/bor_ethclient.go | 10 + go.mod | 59 +- go.sum | 263 +++- interfaces.go | 16 +- internal/ethapi/api.go | 11 - internal/ethapi/backend.go | 7 +- internal/ethapi/bor_api.go | 4 + internal/ethapi/transaction_args_test.go | 44 +- internal/jsre/deps/web3.js | 78 +- internal/web3ext/bor_ext.go | 5 + internal/web3ext/web3ext.go | 18 +- les/api_backend.go | 16 +- les/client.go | 5 +- miner/miner.go | 11 +- miner/miner_test.go | 9 +- rpc/types.go | 35 +- tests/bor/bor_filter_test.go | 2 +- tests/bor/bor_milestone_test.go | 1392 +++++++++++++++++ tests/bor/bor_sprint_length_change_test.go | 10 +- .../bor_sprint_length_milestone_merge_test.go | 703 +++++++++ tests/bor/bor_test.go | 53 +- tests/bor/mocks/IHeimdallClient.go | 74 + 58 files changed, 5639 insertions(+), 612 deletions(-) create mode 100644 common/flags/milestone.go create mode 100644 common/gererics/empty.go create mode 100644 consensus/bor/heimdall/milestone/milestone.go create mode 100644 consensus/bor/heimdallapp/milestone.go create mode 100644 consensus/bor/heimdallgrpc/milestone.go create mode 100644 core/rawdb/checkpoint.go create mode 100644 core/rawdb/milestone.go create mode 100644 eth/downloader/whitelist/checkpoint.go create mode 100644 eth/downloader/whitelist/finality.go create mode 100644 eth/downloader/whitelist/milestone.go create mode 100644 tests/bor/bor_milestone_test.go create mode 100644 tests/bor/bor_sprint_length_milestone_merge_test.go diff --git a/accounts/abi/bind/backends/bor_simulated.go b/accounts/abi/bind/backends/bor_simulated.go index 03130d2969..18d2bc0f53 100644 --- a/accounts/abi/bind/backends/bor_simulated.go +++ b/accounts/abi/bind/backends/bor_simulated.go @@ -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 { diff --git a/common/flags/milestone.go b/common/flags/milestone.go new file mode 100644 index 0000000000..d858bc29dd --- /dev/null +++ b/common/flags/milestone.go @@ -0,0 +1,3 @@ +package flags + +const Milestone = true diff --git a/common/gererics/empty.go b/common/gererics/empty.go new file mode 100644 index 0000000000..7a41df3a26 --- /dev/null +++ b/common/gererics/empty.go @@ -0,0 +1,5 @@ +package gererics + +func Empty[T any]() (t T) { + return +} diff --git a/consensus/bor/api.go b/consensus/bor/api.go index 6d72e309e3..7dd1e8b071 100644 --- a/consensus/bor/api.go +++ b/consensus/bor/api.go @@ -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 { diff --git a/consensus/bor/heimdall.go b/consensus/bor/heimdall.go index 8934e07cb4..a093cc95da 100644 --- a/consensus/bor/heimdall.go +++ b/consensus/bor/heimdall.go @@ -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() } diff --git a/consensus/bor/heimdall/client.go b/consensus/bor/heimdall/client.go index b6daa7e8d8..6f696a3ac2 100644 --- a/consensus/bor/heimdall/client.go +++ b/consensus/bor/heimdall/client.go @@ -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 { diff --git a/consensus/bor/heimdall/client_test.go b/consensus/bor/heimdall/client_test.go index 22265c1975..5023b847a5 100644 --- a/consensus/bor/heimdall/client_test.go +++ b/consensus/bor/heimdall/client_test.go @@ -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) { diff --git a/consensus/bor/heimdall/metrics.go b/consensus/bor/heimdall/metrics.go index 99d7ca65ac..8ad686f373 100644 --- a/consensus/bor/heimdall/metrics.go +++ b/consensus/bor/heimdall/metrics.go @@ -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), + }, } ) diff --git a/consensus/bor/heimdall/milestone/milestone.go b/consensus/bor/heimdall/milestone/milestone.go new file mode 100644 index 0000000000..a0dd04742a --- /dev/null +++ b/consensus/bor/heimdall/milestone/milestone.go @@ -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"` +} diff --git a/consensus/bor/heimdallapp/checkpoint.go b/consensus/bor/heimdallapp/checkpoint.go index 226dbb35c4..288a1a51d1 100644 --- a/consensus/bor/heimdallapp/checkpoint.go +++ b/consensus/bor/heimdallapp/checkpoint.go @@ -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(), diff --git a/consensus/bor/heimdallapp/client.go b/consensus/bor/heimdallapp/client.go index d64d0f7a13..18a030fcf3 100644 --- a/consensus/bor/heimdallapp/client.go +++ b/consensus/bor/heimdallapp/client.go @@ -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()}) +} diff --git a/consensus/bor/heimdallapp/milestone.go b/consensus/bor/heimdallapp/milestone.go new file mode 100644 index 0000000000..b7183b6498 --- /dev/null +++ b/consensus/bor/heimdallapp/milestone.go @@ -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, + } +} diff --git a/consensus/bor/heimdallapp/state_sync.go b/consensus/bor/heimdallapp/state_sync.go index 09af97afc4..b61c886436 100644 --- a/consensus/bor/heimdallapp/state_sync.go +++ b/consensus/bor/heimdallapp/state_sync.go @@ -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 } diff --git a/consensus/bor/heimdallgrpc/milestone.go b/consensus/bor/heimdallgrpc/milestone.go new file mode 100644 index 0000000000..917e33771a --- /dev/null +++ b/consensus/bor/heimdallgrpc/milestone.go @@ -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 +} diff --git a/core/blockchain.go b/core/blockchain.go index d3699e992d..fd50d19042 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -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. diff --git a/core/blockchain_reader.go b/core/blockchain_reader.go index 52ae339d94..852f3141bc 100644 --- a/core/blockchain_reader.go +++ b/core/blockchain_reader.go @@ -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 { diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 09d15e1150..733c3faa38 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -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 { diff --git a/core/forkchoice_test.go b/core/forkchoice_test.go index 2457f86fdd..857081e578 100644 --- a/core/forkchoice_test.go +++ b/core/forkchoice_test.go @@ -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 +} diff --git a/core/rawdb/checkpoint.go b/core/rawdb/checkpoint.go new file mode 100644 index 0000000000..af1658b4fc --- /dev/null +++ b/core/rawdb/checkpoint.go @@ -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 +} diff --git a/core/rawdb/milestone.go b/core/rawdb/milestone.go new file mode 100644 index 0000000000..ca3064050e --- /dev/null +++ b/core/rawdb/milestone.go @@ -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 +} diff --git a/eth/api.go b/eth/api.go index d3ff788c15..9336880221 100644 --- a/eth/api.go +++ b/eth/api.go @@ -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") +} diff --git a/eth/api_backend.go b/eth/api_backend.go index 564aab59c8..080ff0dc3e 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -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() } diff --git a/eth/backend.go b/eth/backend.go index 5a4c78046b..37cd23f366 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -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 diff --git a/eth/bor_api_backend.go b/eth/bor_api_backend.go index dd07d96edd..dbc6844d8a 100644 --- a/eth/bor_api_backend.go +++ b/eth/bor_api_backend.go @@ -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) diff --git a/eth/bor_checkpoint_verifier.go b/eth/bor_checkpoint_verifier.go index ad81eb6116..f8ac341e74 100644 --- a/eth/bor_checkpoint_verifier.go +++ b/eth/bor_checkpoint_verifier.go @@ -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} } diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index b1a4d88976..35455596dd 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -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. diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go index fad44c1312..575b4fcd57 100644 --- a/eth/downloader/downloader_test.go +++ b/eth/downloader/downloader_test.go @@ -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 diff --git a/eth/downloader/whitelist/checkpoint.go b/eth/downloader/whitelist/checkpoint.go new file mode 100644 index 0000000000..b0eaae8c49 --- /dev/null +++ b/eth/downloader/whitelist/checkpoint.go @@ -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)) +} diff --git a/eth/downloader/whitelist/finality.go b/eth/downloader/whitelist/finality.go new file mode 100644 index 0000000000..7ab684d88a --- /dev/null +++ b/eth/downloader/whitelist/finality.go @@ -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 +} diff --git a/eth/downloader/whitelist/milestone.go b/eth/downloader/whitelist/milestone.go new file mode 100644 index 0000000000..d2ea7f7bed --- /dev/null +++ b/eth/downloader/whitelist/milestone.go @@ -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) + } +} diff --git a/eth/downloader/whitelist/service.go b/eth/downloader/whitelist/service.go index 5d3fe477d3..08b92c536e 100644 --- a/eth/downloader/whitelist/service.go +++ b/eth/downloader/whitelist/service.go @@ -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 } diff --git a/eth/downloader/whitelist/service_test.go b/eth/downloader/whitelist/service_test.go index b3c1f8b010..f37af30f0c 100644 --- a/eth/downloader/whitelist/service_test.go +++ b/eth/downloader/whitelist/service_test.go @@ -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)), diff --git a/eth/filters/IBackend.go b/eth/filters/IBackend.go index da2c51617a..0d83cbf341 100644 --- a/eth/filters/IBackend.go +++ b/eth/filters/IBackend.go @@ -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() diff --git a/eth/filters/test_backend.go b/eth/filters/test_backend.go index 197b4432db..1187004702 100644 --- a/eth/filters/test_backend.go +++ b/eth/filters/test_backend.go @@ -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) diff --git a/eth/handler_bor.go b/eth/handler_bor.go index 604deef282..63b59c0375 100644 --- a/eth/handler_bor.go +++ b/eth/handler_bor.go @@ -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 } diff --git a/eth/handler_bor_test.go b/eth/handler_bor_test.go index 7bf15b0650..ac04ad26f8 100644 --- a/eth/handler_bor_test.go +++ b/eth/handler_bor_test.go @@ -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 := ðHandler{} // 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 +} diff --git a/ethclient/bor_ethclient.go b/ethclient/bor_ethclient.go index b56356592c..d40cdc6d8e 100644 --- a/ethclient/bor_ethclient.go +++ b/ethclient/bor_ethclient.go @@ -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 diff --git a/go.mod b/go.mod index 151f11eb60..0cbaa83c5a 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index 49a3c4cdf2..3d26678946 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/interfaces.go b/interfaces.go index b88456c415..40b614a14d 100644 --- a/interfaces.go +++ b/interfaces.go @@ -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 } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index a393326dba..4c5b9293a1 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -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) diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index 57399dde8b..2add3b8def 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -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 { diff --git a/internal/ethapi/bor_api.go b/internal/ethapi/bor_api.go index aaf5733f61..6a96480504 100644 --- a/internal/ethapi/bor_api.go +++ b/internal/ethapi/bor_api.go @@ -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 // diff --git a/internal/ethapi/transaction_args_test.go b/internal/ethapi/transaction_args_test.go index b8785a6773..39e14a06af 100644 --- a/internal/ethapi/transaction_args_test.go +++ b/internal/ethapi/transaction_args_test.go @@ -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() {} diff --git a/internal/jsre/deps/web3.js b/internal/jsre/deps/web3.js index 2786368e06..7d3d6d163d 100644 --- a/internal/jsre/deps/web3.js +++ b/internal/jsre/deps/web3.js @@ -1192,7 +1192,7 @@ module.exports = SolidityTypeInt; You should have received a copy of the GNU Lesser General Public License along with web3.js. If not, see . */ -/** +/** * @file param.js * @author Marek Kotewicz * @date 2015 @@ -1211,7 +1211,7 @@ var SolidityParam = function (value, offset) { /** * This method should be used to get length of params's dynamic part - * + * * @method dynamicPartLength * @returns {Number} length of dynamic part (in bytes) */ @@ -1239,7 +1239,7 @@ SolidityParam.prototype.withOffset = function (offset) { * @param {SolidityParam} result of combination */ SolidityParam.prototype.combine = function (param) { - return new SolidityParam(this.value + param.value); + return new SolidityParam(this.value + param.value); }; /** @@ -1271,8 +1271,8 @@ SolidityParam.prototype.offsetAsBytes = function () { */ SolidityParam.prototype.staticPart = function () { if (!this.isDynamic()) { - return this.value; - } + return this.value; + } return this.offsetAsBytes(); }; @@ -1304,7 +1304,7 @@ SolidityParam.prototype.encode = function () { * @returns {String} */ SolidityParam.encodeList = function (params) { - + // updating offsets var totalOffset = params.length * 32; var offsetParams = params.map(function (param) { @@ -1746,13 +1746,13 @@ if (typeof XMLHttpRequest === 'undefined') { /** * Utils - * + * * @module utils */ /** * Utility functions - * + * * @class [utils] config * @constructor */ @@ -1819,7 +1819,7 @@ module.exports = { You should have received a copy of the GNU Lesser General Public License along with web3.js. If not, see . */ -/** +/** * @file sha3.js * @author Marek Kotewicz * @date 2015 @@ -2739,7 +2739,7 @@ module.exports = AllSolidityEvents; You should have received a copy of the GNU Lesser General Public License along with web3.js. If not, see . */ -/** +/** * @file batch.js * @author Marek Kotewicz * @date 2015 @@ -2784,7 +2784,7 @@ Batch.prototype.execute = function () { requests[index].callback(null, (requests[index].format ? requests[index].format(result.result) : result.result)); } }); - }); + }); }; module.exports = Batch; @@ -2971,7 +2971,7 @@ var ContractFactory = function (eth, abi) { */ this.new = function () { /*jshint maxcomplexity: 7 */ - + var contract = new Contract(this.eth, this.abi); // parse arguments @@ -3119,7 +3119,7 @@ module.exports = ContractFactory; You should have received a copy of the GNU Lesser General Public License along with web3.js. If not, see . */ -/** +/** * @file errors.js * @author Marek Kotewicz * @date 2015 @@ -3394,7 +3394,7 @@ var extend = function (web3) { } }; - ex.formatters = formatters; + ex.formatters = formatters; ex.utils = utils; ex.Method = Method; ex.Property = Property; @@ -4443,7 +4443,7 @@ module.exports = HttpProvider; You should have received a copy of the GNU Lesser General Public License along with web3.js. If not, see . */ -/** +/** * @file iban.js * @author Marek Kotewicz * @date 2015 @@ -4643,7 +4643,7 @@ Iban.prototype.address = function () { var base36 = this._iban.substr(4); var asBn = new BigNumber(base36, 36); return padLeft(asBn.toString(16), 20); - } + } return ''; }; @@ -4688,7 +4688,7 @@ var IpcProvider = function (path, net) { var _this = this; this.responseCallbacks = {}; this.path = path; - + this.connection = net.connect({path: this.path}); this.connection.on('error', function(e){ @@ -4698,7 +4698,7 @@ var IpcProvider = function (path, net) { this.connection.on('end', function(){ _this._timeout(); - }); + }); // LISTEN FOR CONNECTION RESPONSES @@ -4737,7 +4737,7 @@ Will parse the response and make an array out of it. IpcProvider.prototype._parseResponse = function(data) { var _this = this, returnValues = []; - + // DE-CHUNKER var dechunkedData = data .replace(/\}[\n\r]?\{/g,'}|--|{') // }{ @@ -4841,7 +4841,7 @@ IpcProvider.prototype.send = function (payload) { try { result = JSON.parse(data); } catch(e) { - throw errors.InvalidResponse(data); + throw errors.InvalidResponse(data); } return result; @@ -5016,7 +5016,7 @@ Method.prototype.extractCallback = function (args) { /** * Should be called to check if the number of arguments is correct - * + * * @method validateArgs * @param {Array} arguments * @throws {Error} if it is not @@ -5029,7 +5029,7 @@ Method.prototype.validateArgs = function (args) { /** * Should be called to format input args of method - * + * * @method formatInput * @param {Array} * @return {Array} @@ -5083,7 +5083,7 @@ Method.prototype.attachToObject = function (obj) { obj[name[0]] = obj[name[0]] || {}; obj[name[0]][name[1]] = func; } else { - obj[name[0]] = func; + obj[name[0]] = func; } }; @@ -5146,8 +5146,8 @@ var DB = function (web3) { this._requestManager = web3._requestManager; var self = this; - - methods().forEach(function(method) { + + methods().forEach(function(method) { method.attachToObject(self); method.setRequestManager(web3._requestManager); }); @@ -5579,7 +5579,7 @@ var Net = function (web3) { var self = this; - properties().forEach(function(p) { + properties().forEach(function(p) { p.attachToObject(self); p.setRequestManager(web3._requestManager); }); @@ -6138,7 +6138,7 @@ module.exports = { You should have received a copy of the GNU Lesser General Public License along with web3.js. If not, see . */ -/** +/** * @file namereg.js * @author Marek Kotewicz * @date 2015 @@ -6325,7 +6325,7 @@ module.exports = Property; You should have received a copy of the GNU Lesser General Public License along with web3.js. If not, see . */ -/** +/** * @file requestmanager.js * @author Jeffrey Wilcke * @author Marek Kotewicz @@ -6392,7 +6392,7 @@ RequestManager.prototype.sendAsync = function (data, callback) { if (err) { return callback(err); } - + if (!Jsonrpc.isValidResponse(result)) { return callback(errors.InvalidResponse(result)); } @@ -6425,7 +6425,7 @@ RequestManager.prototype.sendBatch = function (data, callback) { } callback(err, results); - }); + }); }; /** @@ -6529,7 +6529,7 @@ RequestManager.prototype.poll = function () { } var payload = Jsonrpc.toBatchPayload(pollsData); - + // map the request id to they poll id var pollsIdMap = {}; payload.forEach(function(load, index){ @@ -6559,7 +6559,7 @@ RequestManager.prototype.poll = function () { } else return false; }).filter(function (result) { - return !!result; + return !!result; }).filter(function (result) { var valid = Jsonrpc.isValidResponse(result); if (!valid) { @@ -6634,16 +6634,16 @@ var pollSyncing = function(self) { self.callbacks.forEach(function (callback) { if (self.lastSyncState !== sync) { - + // call the callback with true first so the app can stop anything, before receiving the sync data if(!self.lastSyncState && utils.isObject(sync)) callback(null, true); - + // call on the next CPU cycle, so the actions of the sync stop can be processes first setTimeout(function() { callback(null, sync); }, 0); - + self.lastSyncState = sync; } }); @@ -6698,7 +6698,7 @@ module.exports = IsSyncing; You should have received a copy of the GNU Lesser General Public License along with web3.js. If not, see . */ -/** +/** * @file transfer.js * @author Marek Kotewicz * @date 2015 @@ -6717,7 +6717,7 @@ var exchangeAbi = require('../contracts/SmartExchange.json'); * @param {Function} callback, callback */ var transfer = function (eth, from, to, value, callback) { - var iban = new Iban(to); + var iban = new Iban(to); if (!iban.isValid()) { throw new Error('invalid iban address'); } @@ -6725,7 +6725,7 @@ var transfer = function (eth, from, to, value, callback) { if (iban.isDirect()) { return transferToAddress(eth, from, iban.address(), value, callback); } - + if (!callback) { var address = eth.icapNamereg().addr(iban.institution()); return deposit(eth, from, address, value, iban.client()); @@ -6734,7 +6734,7 @@ var transfer = function (eth, from, to, value, callback) { eth.icapNamereg().addr(iban.institution(), function (err, address) { return deposit(eth, from, address, value, iban.client(), callback); }); - + }; /** diff --git a/internal/web3ext/bor_ext.go b/internal/web3ext/bor_ext.go index fe8b01da19..2f2f2acdbc 100644 --- a/internal/web3ext/bor_ext.go +++ b/internal/web3ext/bor_ext.go @@ -60,6 +60,11 @@ web3._extend({ call: 'bor_getRootHash', params: 2, }), + new web3._extend.Method({ + name: 'getVoteOnHash', + call: 'bor_getVoteOnHash', + params: 4, + }), ] }); ` diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index a058f5b805..dcd6bf1f76 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -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({ diff --git a/les/api_backend.go b/les/api_backend.go index b27df0498c..c5c097ab42 100644 --- a/les/api_backend.go +++ b/les/api_backend.go @@ -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() { } diff --git a/les/client.go b/les/client.go index 40bdef6a02..9c31b2dd9e 100644 --- a/les/client.go +++ b/les/client.go @@ -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 } diff --git a/miner/miner.go b/miner/miner.go index 349b08d583..dbec7a4717 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -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() { diff --git a/miner/miner_test.go b/miner/miner_test.go index f0f1d96182..680c1b1382 100644 --- a/miner/miner_test.go +++ b/miner/miner_test.go @@ -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) } diff --git a/rpc/types.go b/rpc/types.go index 6150bba8fc..40ec7f84ac 100644 --- a/rpc/types.go +++ b/rpc/types.go @@ -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{} diff --git a/tests/bor/bor_filter_test.go b/tests/bor/bor_filter_test.go index 8a22d99a44..9252de4a57 100644 --- a/tests/bor/bor_filter_test.go +++ b/tests/bor/bor_filter_test.go @@ -19,7 +19,7 @@ import ( ) func TestBorFilters(t *testing.T) { - t.Parallel() + // t.Parallel() var ( db = rawdb.NewMemoryDatabase() diff --git a/tests/bor/bor_milestone_test.go b/tests/bor/bor_milestone_test.go new file mode 100644 index 0000000000..5fe10f55df --- /dev/null +++ b/tests/bor/bor_milestone_test.go @@ -0,0 +1,1392 @@ +//go:build integration + +// nolint +package bor + +import ( + "crypto/ecdsa" + "errors" + + "os" + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "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 + keyMilestone, _ = crypto.HexToECDSA(privKeyMilestone) + addrMilestone = crypto.PubkeyToAddress(keyMilestone.PublicKey) // 0x71562b71999873DB5b286dF957af199Ec94617F7 + + // This account is one the validators for 1st span (0-indexed) + key2Milestone, _ = crypto.HexToECDSA(privKey2Milestone) + addr2Milestone = crypto.PubkeyToAddress(key2Milestone.PublicKey) // 0x9fB29AAc15b9A4B7F17c3385939b007540f4d791 + + keysMilestone = []*ecdsa.PrivateKey{keyMilestone, key2Milestone} +) + +const ( + privKeyMilestone = "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291" + privKey2Milestone = "9b28f36fbd67381120752d6172ecdcf10e06ab2d9a1367aac00cdcd6ac7855d3" +) + +func TestMiningAfterLocking(t *testing.T) { + log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, 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 + genesis := InitGenesis(t, faucets, "./testdata/genesis_2val.json", 8) + + var ( + stacks []*node.Node + nodes []*eth.Ethereum + enodes []*enode.Node + ) + + for i := 0; i < 2; i++ { + // Start the node and wait until it's up + stack, ethBackend, err := InitMiner(genesis, keysMilestone[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 _, n := range enodes { + stack.Server().AddPeer(n) + } + // Start tracking the node and its enode + stacks = append(stacks, stack) + nodes = append(nodes, ethBackend) + enodes = append(enodes, 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) + } + } + + for { + + // for block 0 to 7, the primary validator is node0 + // for block 8 to 15, the primary validator is node1 + // for block 16 to 23, the primary validator is node0 + // for block 24 to 31, the primary validator is node1 + blockHeaderVal0 := nodes[0].BlockChain().CurrentHeader() + blockHeaderVal1 := nodes[1].BlockChain().CurrentHeader() + + //Lock the sprint at 8th block + if blockHeaderVal0.Number.Uint64() == 8 { + block8Hash := blockHeaderVal0.Hash() + + nodes[0].Downloader().ChainValidator.LockMutex(uint64(8)) + nodes[0].Downloader().ChainValidator.UnlockMutex(true, "MilestoneID1", uint64(8), block8Hash) + } + + //Unlock the locked sprint + if blockHeaderVal0.Number.Uint64() == 12 { + nodes[0].Downloader().ChainValidator.UnlockSprint(8) + } + + if blockHeaderVal1.Number.Uint64() == 16 { + block16Hash := blockHeaderVal1.Hash() + nodes[1].Downloader().ChainValidator.LockMutex(uint64(16)) + nodes[1].Downloader().ChainValidator.UnlockMutex(true, "MilestoneID2", uint64(16), block16Hash) + } + + if blockHeaderVal1.Number.Uint64() == 20 { + nodes[1].Downloader().ChainValidator.UnlockSprint(16) + } + + if blockHeaderVal0.Number.Uint64() == 30 { + break + } + } + + blockHeaderVal0 := nodes[0].BlockChain().GetHeaderByNumber(29) + blockHeaderVal1 := nodes[1].BlockChain().GetHeaderByNumber(29) + + //Both nodes should have same blockheader at 29th block + assert.Equal(t, blockHeaderVal0, blockHeaderVal1) + + milestoneListVal0 := nodes[0].Downloader().ChainValidator.GetMilestoneIDsList() + + assert.Equal(t, len(milestoneListVal0), int(0)) + + milestoneListVal1 := nodes[1].Downloader().ChainValidator.GetMilestoneIDsList() + + assert.Equal(t, len(milestoneListVal1), int(0)) +} + +func TestReorgingAfterLockingSprint(t *testing.T) { + t.Skip() + // t.Parallel() + + log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, 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 + genesis := InitGenesis(t, faucets, "./testdata/genesis_2val.json", 8) + + var ( + stacks []*node.Node + nodes []*eth.Ethereum + enodes []*enode.Node + ) + + for i := 0; i < 2; i++ { + // Start the node and wait until it's up + stack, ethBackend, err := InitMiner(genesis, keysMilestone[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 _, n := range enodes { + stack.Server().AddPeer(n) + } + // Start tracking the node and its enode + stacks = append(stacks, stack) + nodes = append(nodes, ethBackend) + enodes = append(enodes, 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) + } + } + + for { + + // for block 0 to 7, the primary validator is node0 + // for block 8 to 15, the primary validator is node1 + // for block 16 to 23, the primary validator is node0 + // for block 24 to 31, the primary validator is node1 + blockHeaderVal0 := nodes[0].BlockChain().CurrentHeader() + + //Disconnect the peers at block 9 + if blockHeaderVal0.Number.Uint64() == 9 { + + stacks[0].Server().RemovePeer(enodes[1]) + stacks[1].Server().RemovePeer(enodes[0]) + } + + //Node0 will be sealing out of turn and lock it till 12th block + if blockHeaderVal0.Number.Uint64() == 12 { + block12Hash := blockHeaderVal0.Hash() + nodes[0].Downloader().ChainValidator.LockMutex(uint64(12)) + nodes[0].Downloader().ChainValidator.UnlockMutex(true, "MilestoneID1", uint64(12), block12Hash) + } + + //Connect both the nodes + if blockHeaderVal0.Number.Uint64() == 14 { + stacks[0].Server().AddPeer(enodes[1]) + stacks[1].Server().AddPeer(enodes[0]) + } + + authorVal0, err := nodes[0].Engine().Author(blockHeaderVal0) + + //This will be true only when Node 0 has received the block from node 1 after 12th block. + if err == nil && blockHeaderVal0.Number.Uint64() > 12 && authorVal0 == nodes[1].AccountManager().Accounts()[0] { + break + } + + if blockHeaderVal0.Number.Uint64() == 30 { + break + } + } + + // check block 10 block ; expected author is node1 signer + blockHeader10Val0 := nodes[0].BlockChain().GetHeaderByNumber(10) + + author10Val0, err := nodes[0].Engine().Author(blockHeader10Val0) + + if err == nil { + assert.Equal(t, author10Val0, nodes[0].AccountManager().Accounts()[0]) + } + + blockHeader12Val0 := nodes[0].BlockChain().GetHeaderByNumber(12) + + author12Val0, err := nodes[0].Engine().Author(blockHeader12Val0) + + if err == nil { + assert.Equal(t, author12Val0, nodes[0].AccountManager().Accounts()[0]) + } + + //milestoneIDList should contain only one milestoneID + milestoneListVal1 := nodes[0].Downloader().ChainValidator.GetMilestoneIDsList() + + assert.Equal(t, len(milestoneListVal1), int(1)) +} + +func TestReorgingAfterWhitelisting(t *testing.T) { + t.Skip() + // t.Parallel() + + log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, 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 + genesis := InitGenesis(t, faucets, "./testdata/genesis_2val.json", 8) + + var ( + stacks []*node.Node + nodes []*eth.Ethereum + enodes []*enode.Node + ) + + for i := 0; i < 2; i++ { + // Start the node and wait until it's up + stack, ethBackend, err := InitMiner(genesis, keysMilestone[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 _, n := range enodes { + stack.Server().AddPeer(n) + } + // Start tracking the node and its enode + stacks = append(stacks, stack) + nodes = append(nodes, ethBackend) + enodes = append(enodes, 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) + } + } + + for { + // for block 0 to 7, the primary validator is node0 + // for block 8 to 15, the primary validator is node1 + // for block 16 to 23, the primary validator is node0 + // for block 24 to 31, the primary validator is node1 + blockHeaderVal0 := nodes[0].BlockChain().CurrentHeader() + + //Disconnect the peers at block 9 + if blockHeaderVal0.Number.Uint64() == 9 { + stacks[0].Server().RemovePeer(enodes[1]) + stacks[1].Server().RemovePeer(enodes[0]) + } + + //Node0 will be sealing out of turn and lock it till 12th block + if blockHeaderVal0.Number.Uint64() == 12 { + block12Hash := blockHeaderVal0.Hash() + + nodes[0].Downloader().ChainValidator.LockMutex(uint64(12)) + nodes[0].Downloader().ChainValidator.UnlockMutex(true, "MilestoneID1", uint64(12), block12Hash) + } + + if blockHeaderVal0.Number.Uint64() == 13 { + block13Hash := blockHeaderVal0.Hash() + nodes[0].Downloader().ChainValidator.ProcessMilestone(13, block13Hash) + } + + if blockHeaderVal0.Number.Uint64() == 14 { + stacks[0].Server().AddPeer(enodes[1]) + stacks[1].Server().AddPeer(enodes[0]) + } + + authorVal0, err := nodes[0].Engine().Author(blockHeaderVal0) + + //This condition is true when Node 0 has received the block from node 1 after block 12 + if err == nil && blockHeaderVal0.Number.Uint64() > 12 && authorVal0 == nodes[1].AccountManager().Accounts()[0] { + break + } + + if blockHeaderVal0.Number.Uint64() == 30 { + break + } + } + + arr := []uint64{10, 12, 13} + + for _, val := range arr { + blockHeaderVal := nodes[0].BlockChain().GetHeaderByNumber(val) + + authorVal, err := nodes[0].Engine().Author(blockHeaderVal) + + if err == nil { + assert.Equal(t, authorVal, nodes[0].AccountManager().Accounts()[0]) + } + } +} + +func TestPeerConnectionAfterWhitelisting(t *testing.T) { + t.Skip() + // t.Parallel() + + log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, 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 + genesis := InitGenesis(t, faucets, "./testdata/genesis_2val.json", 8) + + var ( + stacks []*node.Node + nodes []*eth.Ethereum + enodes []*enode.Node + ) + + for i := 0; i < 2; i++ { + // Start the node and wait until it's up + stack, ethBackend, err := InitMiner(genesis, keysMilestone[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 _, n := range enodes { + stack.Server().AddPeer(n) + } + // Start tracking the node and its enode + stacks = append(stacks, stack) + nodes = append(nodes, ethBackend) + enodes = append(enodes, 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) + } + } + + disconnectFlag := false + + for { + // for block 0 to 7, the primary validator is node0 + // for block 8 to 15, the primary validator is node1 + // for block 16 to 23, the primary validator is node0 + // for block 24 to 31, the primary validator is node1 + blockHeaderVal0 := nodes[0].BlockChain().CurrentHeader() + blockHeaderVal1 := nodes[1].BlockChain().CurrentHeader() + + //Disconnect the peers at block 8 + if blockHeaderVal0.Number.Uint64() == 8 && blockHeaderVal1.Number.Uint64() < 12 { + disconnectFlag = true + stacks[0].Server().RemovePeer(enodes[1]) + stacks[1].Server().RemovePeer(enodes[0]) + } + + //Whitelist the validator0 with milestone at 12 + if blockHeaderVal0.Number.Uint64() == 12 { + block12Hash := blockHeaderVal0.Hash() + nodes[0].Downloader().ChainValidator.ProcessMilestone(uint64(12), block12Hash) + } + + ///Whitelist the validator1 with milestone at 12 + if blockHeaderVal1.Number.Uint64() == 12 { + block12Hash := blockHeaderVal1.Hash() + nodes[1].Downloader().ChainValidator.ProcessMilestone(uint64(12), block12Hash) + } + + if blockHeaderVal0.Number.Uint64() > 12 && blockHeaderVal0.Number.Uint64() > 12 { + stacks[0].Server().AddPeer(enodes[1]) + stacks[1].Server().AddPeer(enodes[0]) + } + + if blockHeaderVal0.Number.Uint64() == 30 { + break + } + } + + // validator one peer count + val0PeerCount := stacks[0].Server().PeerCount() + val1PeerCount := stacks[1].Server().PeerCount() + + if disconnectFlag { + assert.Equal(t, val0PeerCount, 0) + assert.Equal(t, val1PeerCount, 0) + + blockHeader13Val0 := nodes[0].BlockChain().GetHeaderByNumber(13) + + author13Val0, err := nodes[0].Engine().Author(blockHeader13Val0) + + if err == nil { + assert.Equal(t, author13Val0, nodes[0].AccountManager().Accounts()[0]) + } + + blockHeader13Val1 := nodes[1].BlockChain().GetHeaderByNumber(13) + + author13Val1, err := nodes[1].Engine().Author(blockHeader13Val1) + + if err == nil { + assert.Equal(t, author13Val1, nodes[1].AccountManager().Accounts()[0]) + } + } +} + +func TestReorgingFutureSprintAfterLocking(t *testing.T) { + t.Skip() + // t.Parallel() + + log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, 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 + genesis := InitGenesis(t, faucets, "./testdata/genesis_2val.json", 8) + + var ( + stacks []*node.Node + nodes []*eth.Ethereum + enodes []*enode.Node + ) + for i := 0; i < 2; i++ { + // Start the node and wait until it's up + stack, ethBackend, err := InitMiner(genesis, keysMilestone[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 _, n := range enodes { + stack.Server().AddPeer(n) + } + // Start tracking the node and its enode + stacks = append(stacks, stack) + nodes = append(nodes, ethBackend) + enodes = append(enodes, 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) + } + } + + for { + // for block 0 to 7, the primary validator is node0 + // for block 8 to 15, the primary validator is node1 + // for block 16 to 23, the primary validator is node0 + // for block 24 to 31, the primary validator is node1 + blockHeaderVal0 := nodes[0].BlockChain().CurrentHeader() + + //Locking sprint at height 8 + if blockHeaderVal0.Number.Uint64() == 8 { + block8Hash := blockHeaderVal0.Hash() + nodes[0].Downloader().ChainValidator.LockMutex(uint64(8)) + nodes[0].Downloader().ChainValidator.UnlockMutex(true, "milestoneID1", uint64(8), block8Hash) + } + + if blockHeaderVal0.Number.Uint64() == 30 { + break + } + } + + node1Arr := []uint64{8, 15, 24} + + for _, val := range node1Arr { + blockHeader := nodes[0].BlockChain().GetHeaderByNumber(val) + + authorVal, err := nodes[0].Engine().Author(blockHeader) + + if err == nil { + assert.Equal(t, authorVal, nodes[1].AccountManager().Accounts()[0]) + } + } +} + +func TestReorgingFutureSprintAfterLockingOnSameHash(t *testing.T) { + t.Skip() + // t.Parallel() + + log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, 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 + genesis := InitGenesis(t, faucets, "./testdata/genesis_2val.json", 8) + + var ( + stacks []*node.Node + nodes []*eth.Ethereum + enodes []*enode.Node + ) + for i := 0; i < 2; i++ { + // Start the node and wait until it's up + stack, ethBackend, err := InitMiner(genesis, keysMilestone[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 _, n := range enodes { + stack.Server().AddPeer(n) + } + // Start tracking the node and its enode + stacks = append(stacks, stack) + nodes = append(nodes, ethBackend) + enodes = append(enodes, 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) + } + } + + for { + // for block 0 to 7, the primary validator is node0 + // for block 8 to 15, the primary validator is node1 + // for block 16 to 23, the primary validator is node0 + // for block 24 to 31, the primary validator is node1 + blockHeaderVal0 := nodes[0].BlockChain().CurrentHeader() + + //Locking sprint at height 8 + if blockHeaderVal0.Number.Uint64() == 8 { + block8Hash := blockHeaderVal0.Hash() + nodes[0].Downloader().ChainValidator.LockMutex(uint64(8)) + nodes[0].Downloader().ChainValidator.UnlockMutex(true, "milestoneID1", uint64(8), block8Hash) + } + + if blockHeaderVal0.Number.Uint64() == 30 { + break + } + + } + + node1Arr := []uint64{8, 15, 24} + + for _, val := range node1Arr { + blockHeader := nodes[0].BlockChain().GetHeaderByNumber(val) + + authorVal, err := nodes[0].Engine().Author(blockHeader) + + if err == nil { + assert.Equal(t, authorVal, nodes[1].AccountManager().Accounts()[0]) + } + } +} + +func TestReorgingAfterLockingOnDifferentHash(t *testing.T) { + t.Skip() + // t.Parallel() + + log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, 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 + genesis := InitGenesis(t, faucets, "./testdata/genesis_2val.json", 8) + + var ( + stacks []*node.Node + nodes []*eth.Ethereum + enodes []*enode.Node + ) + for i := 0; i < 2; i++ { + // Start the node and wait until it's up + stack, ethBackend, err := InitMiner(genesis, keysMilestone[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 _, n := range enodes { + stack.Server().AddPeer(n) + } + + // Start tracking the node and its enode + stacks = append(stacks, stack) + nodes = append(nodes, ethBackend) + enodes = append(enodes, 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) + } + } + + //Peers are disconnected as we have not connected them + + stacks[0].Server().RemovePeer(enodes[1]) + stacks[1].Server().RemovePeer(enodes[0]) + + chain2HeadChNode0 := make(chan core.Chain2HeadEvent, 64) + chain2HeadChNode1 := make(chan core.Chain2HeadEvent, 64) + + nodes[0].BlockChain().SubscribeChain2HeadEvent(chain2HeadChNode0) + nodes[1].BlockChain().SubscribeChain2HeadEvent(chain2HeadChNode1) + + for { + // for block 0 to 7, the primary validator is node0 + // for block 8 to 15, the primary validator is node1 + // for block 16 to 23, the primary validator is node0 + // for block 24 to 31, the primary validator is node1 + blockHeaderVal0 := nodes[0].BlockChain().CurrentHeader() + blockHeaderVal1 := nodes[1].BlockChain().CurrentHeader() + + //Locking sprint at height 7 + if blockHeaderVal0.Number.Uint64() == 7 { + block7Hash := blockHeaderVal0.Hash() + nodes[0].Downloader().ChainValidator.LockMutex(uint64(7)) + nodes[0].Downloader().ChainValidator.UnlockMutex(true, "milestoneID1", uint64(7), block7Hash) + } + + if blockHeaderVal1.Number.Uint64() == 7 { + block7Hash := blockHeaderVal1.Hash() + nodes[1].Downloader().ChainValidator.LockMutex(uint64(7)) + nodes[1].Downloader().ChainValidator.UnlockMutex(true, "milestoneID1", uint64(7), block7Hash) + } + + if blockHeaderVal0.Number.Uint64() > 15 && blockHeaderVal1.Number.Uint64() > 15 { + stacks[0].Server().AddPeer(enodes[1]) + stacks[1].Server().AddPeer(enodes[0]) + } + + select { + case ev := <-chain2HeadChNode0: + if ev.Type == core.Chain2HeadReorgEvent { + assert.Fail(t, "Node 0 should not get reorged") + break + + } + + case ev := <-chain2HeadChNode1: + if ev.Type == core.Chain2HeadReorgEvent { + assert.Fail(t, "Node 1 should not get reorged") + break + } + + default: + time.Sleep(1 * time.Millisecond) + + } + + if blockHeaderVal0.Number.Uint64() == 30 { + break + } + } +} + +func TestReorgingAfterWhitelistingOnDifferentHash(t *testing.T) { + t.Skip() + // t.Parallel() + + log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, 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 + genesis := InitGenesis(t, faucets, "./testdata/genesis_2val.json", 8) + + var ( + stacks []*node.Node + nodes []*eth.Ethereum + enodes []*enode.Node + ) + + for i := 0; i < 2; i++ { + // Start the node and wait until it's up + stack, ethBackend, err := InitMiner(genesis, keysMilestone[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 _, n := range enodes { + stack.Server().AddPeer(n) + } + + // Start tracking the node and its enode + stacks = append(stacks, stack) + nodes = append(nodes, ethBackend) + enodes = append(enodes, 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) + } + } + + //Peers are disconnected as we have not connected them + + stacks[0].Server().RemovePeer(enodes[1]) + stacks[1].Server().RemovePeer(enodes[0]) + + chain2HeadChNode0 := make(chan core.Chain2HeadEvent, 64) + chain2HeadChNode1 := make(chan core.Chain2HeadEvent, 64) + + nodes[0].BlockChain().SubscribeChain2HeadEvent(chain2HeadChNode0) + nodes[1].BlockChain().SubscribeChain2HeadEvent(chain2HeadChNode1) + + for { + // for block 0 to 7, the primary validator is node0 + // for block 8 to 15, the primary validator is node1 + // for block 16 to 23, the primary validator is node0 + // for block 24 to 31, the primary validator is node1 + blockHeaderVal0 := nodes[0].BlockChain().CurrentHeader() + blockHeaderVal1 := nodes[1].BlockChain().CurrentHeader() + + if blockHeaderVal0.Number.Uint64() == 20 { + break + } + + //whitelisting at height + if blockHeaderVal0.Number.Uint64() == 1 { + block1Hash := blockHeaderVal0.Hash() + nodes[0].Downloader().ChainValidator.ProcessMilestone(uint64(1), block1Hash) + } + + if blockHeaderVal1.Number.Uint64() == 1 { + block1Hash := blockHeaderVal1.Hash() + nodes[1].Downloader().ChainValidator.ProcessMilestone(uint64(1), block1Hash) + } + + if blockHeaderVal0.Number.Uint64() > 1 && blockHeaderVal1.Number.Uint64() > 1 { + stacks[0].Server().AddPeer(enodes[1]) + stacks[1].Server().AddPeer(enodes[0]) + } + + select { + case ev := <-chain2HeadChNode0: + if ev.Type == core.Chain2HeadReorgEvent { + assert.Fail(t, "Node 0 should not get reorged as it was whitelisted on different hash") + break + } + + case ev := <-chain2HeadChNode1: + + if ev.Type == core.Chain2HeadReorgEvent { + assert.Fail(t, "Node 1 should not get reorged as it was whiteliseted on different hash") + break + } + + default: + time.Sleep(1 * time.Millisecond) + } + + if blockHeaderVal0.Number.Uint64() == 30 { + break + } + } +} + +func TestNonMinerNodeWithWhitelisting(t *testing.T) { + t.Skip() + // t.Parallel() + + log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, 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 + genesis := InitGenesis(t, faucets, "./testdata/genesis_2val.json", 8) + + var ( + stacks []*node.Node + nodes []*eth.Ethereum + enodes []*enode.Node + ) + + for i := 0; i < 2; i++ { + // Start the node and wait until it's up + stack, ethBackend, err := InitMiner(genesis, keysMilestone[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 _, n := range enodes { + stack.Server().AddPeer(n) + } + + // Start tracking the node and its enode + stacks = append(stacks, stack) + nodes = append(nodes, ethBackend) + enodes = append(enodes, stack.Server().Self()) + } + + // Iterate over all the nodes and start mining + time.Sleep(3 * time.Second) + + //Only started the node 0 and keep the node 1 as non mining + err = nodes[0].StartMining(1) + if err != nil { + panic(err) + } + + for { + blockHeaderVal0 := nodes[0].BlockChain().CurrentHeader() + blockHeaderVal1 := nodes[1].BlockChain().CurrentHeader() + + //Whitelisting milestone + if blockHeaderVal1.Number.Uint64() == 7 { + blockHash := blockHeaderVal1.Hash() + nodes[0].Downloader().ChainValidator.ProcessMilestone(blockHeaderVal1.Number.Uint64(), blockHash) + } + + //Whitelisting milestone + if blockHeaderVal1.Number.Uint64() == 15 { + blockHash := blockHeaderVal1.Hash() + nodes[0].Downloader().ChainValidator.ProcessMilestone(blockHeaderVal1.Number.Uint64(), blockHash) + } + + //Whitelisting milestone + if blockHeaderVal1.Number.Uint64() == 23 { + blockHash := blockHeaderVal1.Hash() + nodes[0].Downloader().ChainValidator.ProcessMilestone(blockHeaderVal1.Number.Uint64(), blockHash) + } + + if blockHeaderVal0.Number.Uint64() == 30 { + break + } + } + + for i := uint64(0); i < nodes[1].BlockChain().CurrentBlock().Number.Uint64(); i++ { + blockHeader := nodes[1].BlockChain().GetHeaderByNumber(i) + + authorVal, err := nodes[1].Engine().Author(blockHeader) + + if err == nil { + assert.Equal(t, authorVal, nodes[0].AccountManager().Accounts()[0]) + } + } +} + +func TestNonMinerNodeWithTryToLock(t *testing.T) { + t.Skip() + // t.Parallel() + + log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, 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 + genesis := InitGenesis(t, faucets, "./testdata/genesis_2val.json", 8) + + var ( + stacks []*node.Node + nodes []*eth.Ethereum + enodes []*enode.Node + ) + for i := 0; i < 2; i++ { + // Start the node and wait until it's up + stack, ethBackend, err := InitMiner(genesis, keysMilestone[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 _, n := range enodes { + stack.Server().AddPeer(n) + } + + // Start tracking the node and its enode + stacks = append(stacks, stack) + nodes = append(nodes, ethBackend) + enodes = append(enodes, stack.Server().Self()) + } + + // Iterate over all the nodes and start mining + time.Sleep(3 * time.Second) + + //Only started the node 0 and keep the node 1 as non mining + err = nodes[0].StartMining(1) + if err != nil { + panic(err) + } + + for { + blockHeaderVal0 := nodes[0].BlockChain().CurrentHeader() + blockHeaderVal1 := nodes[1].BlockChain().CurrentHeader() + + //Asking for the vote + if blockHeaderVal1.Number.Uint64() == 7 { + blockHash := blockHeaderVal1.Hash() + _, _ = nodes[1].APIBackend.GetVoteOnHash(nil, 0, 7, "0x"+blockHash.String(), "MilestoneID1") + + } + + //Asking for the vote + if blockHeaderVal1.Number.Uint64() == 15 { + blockHash := blockHeaderVal1.Hash() + _, _ = nodes[1].APIBackend.GetVoteOnHash(nil, 0, 7, "0x"+blockHash.String(), "MilestoneID2") + } + + //Asking for the vote + if blockHeaderVal1.Number.Uint64() == 23 { + blockHash := blockHeaderVal1.Hash() + _, _ = nodes[1].APIBackend.GetVoteOnHash(nil, 0, 7, "0x"+blockHash.String(), "MilestoneID3") + } + + milestoneList := nodes[0].Downloader().ChainValidator.GetMilestoneIDsList() + if len(milestoneList) > 0 { + assert.Fail(t, "MilestoneList should be of zero length") + } + + if blockHeaderVal0.Number.Uint64() == 30 { + break + } + } +} + +func TestRewind(t *testing.T) { + t.Skip() + // t.Parallel() + + log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, 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 + genesis := InitGenesis(t, faucets, "./testdata/genesis_2val.json", 8) + + var ( + stacks []*node.Node + nodes []*eth.Ethereum + enodes []*enode.Node + ) + for i := 0; i < 2; i++ { + // Start the node and wait until it's up + stack, ethBackend, err := InitMiner(genesis, keysMilestone[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 _, n := range enodes { + stack.Server().AddPeer(n) + } + + // Start tracking the node and its enode + stacks = append(stacks, stack) + nodes = append(nodes, ethBackend) + enodes = append(enodes, 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) + } + } + + // step1 := false + // step2 := false + // step3 := false + step4 := false + + for { + blockHeaderVal0 := nodes[0].BlockChain().CurrentHeader() + blockHeaderVal1 := nodes[1].BlockChain().CurrentHeader() + + // if blockHeaderVal1.Number.Uint64() == 20 && !step1 { + // nodes[1].BlockChain().SetHead(2) + + // step1 = true + // } + + // if blockHeaderVal1.Number.Uint64() == 40 && !step2 { + // nodes[1].BlockChain().SetHead(2) + + // step2 = true + // } + + // if blockHeaderVal1.Number.Uint64() == 80 && !step3 { + // nodes[1].BlockChain().SetHead(2) + + // step3 = true + // } + + // if blockHeaderVal1.Number.Uint64() == 120 && !step4 { + // nodes[1].BlockChain().SetHead(2) + + // step4 = true + // } + + if blockHeaderVal1.Number.Uint64() == 180 && !step4 { + ch := make(chan struct{}) + nodes[1].Miner().Stop(ch) + <-ch + err := nodes[1].BlockChain().SetHead(2) + + if err != nil { + panic(err) + } + + err = nodes[1].StartMining(1) + + if err != nil { + panic(err) + } + + step4 = true + } + + if blockHeaderVal0.Number.Uint64() == 200 { + break + } + } +} + +func TestRewinding(t *testing.T) { + t.Skip() + // t.Parallel() + + log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, 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 + genesis := InitGenesis(t, faucets, "./testdata/genesis_2val.json", 8) + + var ( + stacks []*node.Node + nodes []*eth.Ethereum + enodes []*enode.Node + ) + for i := 0; i < 2; i++ { + // Start the node and wait until it's up + stack, ethBackend, err := InitMiner(genesis, keysMilestone[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 _, n := range enodes { + stack.Server().AddPeer(n) + } + + // Start tracking the node and its enode + stacks = append(stacks, stack) + nodes = append(nodes, ethBackend) + enodes = append(enodes, stack.Server().Self()) + } + + // Iterate over all the nodes and start mining + time.Sleep(3 * time.Second) + + //Start mining + for _, node := range nodes { + if err := node.StartMining(1); err != nil { + panic(err) + } + } + + var step1 = false + var step2 = false + + for { + blockHeaderVal0 := nodes[0].BlockChain().CurrentHeader() + blockHeaderVal1 := nodes[1].BlockChain().CurrentHeader() + + //Processing the milestone + if blockHeaderVal0.Number.Uint64() == 7 { + blockHash := blockHeaderVal1.Hash() + nodes[0].Downloader().ChainValidator.ProcessMilestone(blockHeaderVal1.Number.Uint64(), blockHash) + } + + //Verify the wrong hash to rewind back + if blockHeaderVal0.Number.Uint64() == 15 && !step1 { + _, _ = borVerify(nodes[0], 8, 15, "LocalHash", "RootHash", 15, 7) + step1 = true + } + + //Check for the rewind + if step1 && blockHeaderVal0.Number.Uint64() <= 6 { + assert.Fail(t, "Node1 chain rewound to more than expected number") + } + + //Verify the wrong hash + if blockHeaderVal0.Number.Uint64() == 270 && !step2 { + _, _ = borVerify(nodes[0], 8, 15, "LocalHash", "RootHash", 270, 7) + step2 = true + } + + //Check for the rewind + if step2 && blockHeaderVal0.Number.Uint64() <= 14 { + assert.Fail(t, "Node1 chain rewound to more than expected number") + } + + if blockHeaderVal0.Number.Uint64() == 300 { + break + } + } +} + +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") + + // errBlockNumberConversion is returned when we get err in parsing hexautil block number + errBlockNumberConversion = errors.New("failed to parse the block number") +) + +func borVerify(eth *eth.Ethereum, start uint64, end uint64, rootHash string, localHash string, head uint64, lastMilestone uint64) (string, error) { + //nolint + if localHash != rootHash { + var rewindTo uint64 + rewindTo = lastMilestone + + if head-rewindTo > 255 { + rewindTo = head - 254 + } + + rewindBack(eth, rewindTo) + + return "", errHashMismatch + } + + return "", nil +} + +// Stop the miner if the mining process is running and rewind back the chain +func rewindBack(eth *eth.Ethereum, rewindTo uint64) { + if eth.Miner().Mining() { + ch := make(chan struct{}) + eth.Miner().Stop(ch) + <-ch + rewind(eth, rewindTo) + + err := eth.StartMining(1) + + if err != nil { + panic(err) + } + } else { + rewind(eth, rewindTo) + } +} + +func rewind(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) + } +} diff --git a/tests/bor/bor_sprint_length_change_test.go b/tests/bor/bor_sprint_length_change_test.go index 05302cd069..9ec8593d38 100644 --- a/tests/bor/bor_sprint_length_change_test.go +++ b/tests/bor/bor_sprint_length_change_test.go @@ -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") diff --git a/tests/bor/bor_sprint_length_milestone_merge_test.go b/tests/bor/bor_sprint_length_milestone_merge_test.go new file mode 100644 index 0000000000..2cfbc13ba6 --- /dev/null +++ b/tests/bor/bor_sprint_length_milestone_merge_test.go @@ -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) + } +} diff --git a/tests/bor/bor_test.go b/tests/bor/bor_test.go index 0bd50c5592..9981283fe4 100644 --- a/tests/bor/bor_test.go +++ b/tests/bor/bor_test.go @@ -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) diff --git a/tests/bor/mocks/IHeimdallClient.go b/tests/bor/mocks/IHeimdallClient.go index 15a09712f1..28697f5ce6 100644 --- a/tests/bor/mocks/IHeimdallClient.go +++ b/tests/bor/mocks/IHeimdallClient.go @@ -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()