From dcddbbabb41cffc2ee209ffc19090acf6b04a8c0 Mon Sep 17 00:00:00 2001 From: Donald Adu-Poku Date: Thu, 23 Jul 2020 14:35:42 +0000 Subject: [PATCH] multi: add SanityChecker interface. This adds the sanity checker interface and update the function signature of CheckBlockSanity. Associated tests and mocks updated/added. --- internal/rpcserver/interface.go | 10 +++ internal/rpcserver/rpcserver.go | 16 ++--- internal/rpcserver/rpcserverhandlers_test.go | 75 +++++++++++++------- rpcadaptors.go | 20 ++++++ server.go | 27 +++---- 5 files changed, 102 insertions(+), 46 deletions(-) diff --git a/internal/rpcserver/interface.go b/internal/rpcserver/interface.go index 364dc095..18f6ec69 100644 --- a/internal/rpcserver/interface.go +++ b/internal/rpcserver/interface.go @@ -455,3 +455,13 @@ type LogManager interface { // is invalid. ParseAndSetDebugLevels(debugLevel string) error } + +// SanityChecker represents a block sanity checker for use with the RPC server. +// +// The interface contract requires that all of these methods are safe for +// concurrent access. +type SanityChecker interface { + // CheckBlockSanity checks the correctness of the provided block + // per consensus. + CheckBlockSanity(block *dcrutil.Block) error +} diff --git a/internal/rpcserver/rpcserver.go b/internal/rpcserver/rpcserver.go index 58873597..99a8dc8b 100644 --- a/internal/rpcserver/rpcserver.go +++ b/internal/rpcserver/rpcserver.go @@ -4786,8 +4786,7 @@ func verifyChain(_ context.Context, s *Server, level, depth int64) error { // Level 1 does basic chain sanity checks. if level > 0 { - err := blockchain.CheckBlockSanity(block, s.cfg.TimeSource, - s.cfg.ChainParams) + err := s.cfg.SanityChecker.CheckBlockSanity(block) if err != nil { log.Errorf("Verify is unable to validate "+ "block at hash %v height %d: %v", @@ -5593,12 +5592,13 @@ type Config struct { // These fields allow the RPC server to interface with the local block // chain data and state. - TimeSource blockchain.MedianTimeSource - Chain Chain - ChainParams *chaincfg.Params - DB database.DB - FeeEstimator FeeEstimator - Services wire.ServiceFlag + TimeSource blockchain.MedianTimeSource + Chain Chain + SanityChecker SanityChecker + ChainParams *chaincfg.Params + DB database.DB + FeeEstimator FeeEstimator + Services wire.ServiceFlag // SubsidyCache defines a cache for efficient access to consensus-critical // subsidy calculations. diff --git a/internal/rpcserver/rpcserverhandlers_test.go b/internal/rpcserver/rpcserverhandlers_test.go index aa719742..25559891 100644 --- a/internal/rpcserver/rpcserverhandlers_test.go +++ b/internal/rpcserver/rpcserverhandlers_test.go @@ -654,6 +654,17 @@ func (l *testLogManager) SupportedSubsystems() []string { return l.supportedSubsystems } +// testSanityChecker provides a mock implementation that checks the sanity +// state of a block. +type testSanityChecker struct { + checkBlockSanityErr error +} + +// testSanityChecker returns mock sanity state of the provided block. +func (s *testSanityChecker) CheckBlockSanity(block *dcrutil.Block) error { + return s.checkBlockSanityErr +} + // ParseAndSetDebugLevels provides a mock implementation for parsing the // specified debug level and setting the levels accordingly. func (l *testLogManager) ParseAndSetDebugLevels(debugLevel string) error { @@ -719,20 +730,21 @@ var block432100 = func() wire.MsgBlock { }() type rpcTest struct { - name string - handler commandHandler - cmd interface{} - mockChainParams *chaincfg.Params - mockChain *testRPCChain - mockAddrManager *testAddrManager - mockFeeEstimator *testFeeEstimator - mockSyncManager *testSyncManager - mockConnManager *testConnManager - mockClock *testClock - mockLogManager *testLogManager - result interface{} - wantErr bool - errCode dcrjson.RPCErrorCode + name string + handler commandHandler + cmd interface{} + mockChainParams *chaincfg.Params + mockChain *testRPCChain + mockSanityChecker *testSanityChecker + mockAddrManager *testAddrManager + mockFeeEstimator *testFeeEstimator + mockSyncManager *testSyncManager + mockConnManager *testConnManager + mockClock *testClock + mockLogManager *testLogManager + result interface{} + wantErr bool + errCode dcrjson.RPCErrorCode } // defaultChainParams provides a default chaincfg.Params to be used throughout @@ -878,6 +890,15 @@ func defaultMockRPCChain() *testRPCChain { } } +// defaultMockSanityChecker provides a default mock sanity checker to be used +// throughout the tests. Tests can override these defaults by calling +// defaultMockSanityChecker, updating fields as necessary on the returned +// *testSanityChecker, and then setting rpcTest.mockSanityChecker as that +// *testSanityChecker. +func defaultMockSanityChecker() *testSanityChecker { + return &testSanityChecker{} +} + // defaultMockAddrManager provides a default mock address manager to be used // throughout the tests. Tests can override these defaults by calling // defaultMockAddrManager, updating fields as necessary on the returned @@ -990,17 +1011,18 @@ func defaultMockLogManager() *testLogManager { // the tests. Defaults can be overridden by tests through the rpcTest struct. func defaultMockConfig(chainParams *chaincfg.Params) *Config { return &Config{ - ChainParams: chainParams, - Chain: defaultMockRPCChain(), - AddrManager: defaultMockAddrManager(), - FeeEstimator: defaultMockFeeEstimator(), - SyncMgr: defaultMockSyncManager(), - ConnMgr: defaultMockConnManager(), - Clock: &testClock{}, - LogManager: defaultMockLogManager(), - TimeSource: blockchain.NewMedianTime(), - Services: wire.SFNodeNetwork | wire.SFNodeCF, - SubsidyCache: standalone.NewSubsidyCache(chainParams), + ChainParams: chainParams, + Chain: defaultMockRPCChain(), + SanityChecker: defaultMockSanityChecker(), + AddrManager: defaultMockAddrManager(), + FeeEstimator: defaultMockFeeEstimator(), + SyncMgr: defaultMockSyncManager(), + ConnMgr: defaultMockConnManager(), + Clock: &testClock{}, + LogManager: defaultMockLogManager(), + TimeSource: blockchain.NewMedianTime(), + Services: wire.SFNodeNetwork | wire.SFNodeCF, + SubsidyCache: standalone.NewSubsidyCache(chainParams), NetInfo: []types.NetworksResult{{ Name: "IPV4", Limited: false, @@ -3913,6 +3935,9 @@ func testRPCServerHandler(t *testing.T, tests []rpcTest) { if test.mockLogManager != nil { rpcserverConfig.LogManager = test.mockLogManager } + if test.mockSanityChecker != nil { + rpcserverConfig.SanityChecker = test.mockSanityChecker + } testServer := &Server{cfg: *rpcserverConfig} result, err := test.handler(nil, testServer, test.cmd) diff --git a/rpcadaptors.go b/rpcadaptors.go index 501f1d79..84f0ea27 100644 --- a/rpcadaptors.go +++ b/rpcadaptors.go @@ -14,6 +14,7 @@ import ( "github.com/decred/dcrd/blockchain/v3" "github.com/decred/dcrd/blockchain/v3/indexers" "github.com/decred/dcrd/chaincfg/chainhash" + "github.com/decred/dcrd/chaincfg/v3" "github.com/decred/dcrd/dcrutil/v3" "github.com/decred/dcrd/internal/fees" "github.com/decred/dcrd/internal/mempool" @@ -496,3 +497,22 @@ func (*rpcLogManager) SupportedSubsystems() []string { func (*rpcLogManager) ParseAndSetDebugLevels(debugLevel string) error { return parseAndSetDebugLevels(debugLevel) } + +// rpcSanityChecker provides a block sanity checker for use with the RPC and +// implements the rpcserver.SanityChecker interface. +type rpcSanityChecker struct { + timeSource blockchain.MedianTimeSource + chainParams *chaincfg.Params +} + +// Ensure rpcSanityChecker implements the rpcserver.SanityChecker interface. +var _ rpcserver.SanityChecker = (*rpcSanityChecker)(nil) + +// CheckBlockSanity checks the correctness of the provided block +// per consensus. An appropriate error is returned if anything is +// invalid. +// +// This function is part of the rpcserver.SanityChecker interface implementation. +func (s *rpcSanityChecker) CheckBlockSanity(block *dcrutil.Block) error { + return blockchain.CheckBlockSanity(block, s.timeSource, s.chainParams) +} diff --git a/server.go b/server.go index 2091edf9..50c8652a 100644 --- a/server.go +++ b/server.go @@ -3265,19 +3265,20 @@ func newServer(ctx context.Context, listenAddrs []string, db database.DB, chainP } s.rpcServer, err = rpcserver.New(&rpcserver.Config{ - Listeners: rpcListeners, - ConnMgr: &rpcConnManager{&s}, - SyncMgr: &rpcSyncMgr{&s, s.blockManager}, - FeeEstimator: &rpcFeeEstimator{s.feeEstimator}, - TimeSource: s.timeSource, - Services: s.services, - AddrManager: &rpcAddrManager{s.addrManager}, - Clock: &rpcClock{}, - SubsidyCache: s.subsidyCache, - Chain: &rpcChain{s.chain}, - ChainParams: chainParams, - DB: db, - TxMemPool: s.txMemPool, + Listeners: rpcListeners, + ConnMgr: &rpcConnManager{&s}, + SyncMgr: &rpcSyncMgr{&s, s.blockManager}, + FeeEstimator: &rpcFeeEstimator{s.feeEstimator}, + TimeSource: s.timeSource, + Services: s.services, + AddrManager: &rpcAddrManager{s.addrManager}, + Clock: &rpcClock{}, + SubsidyCache: s.subsidyCache, + Chain: &rpcChain{s.chain}, + ChainParams: chainParams, + SanityChecker: &rpcSanityChecker{s.timeSource, chainParams}, + DB: db, + TxMemPool: s.txMemPool, BgBlkTmplGenerator: func() *mining.BgBlkTmplGenerator { return s.bg },