diff --git a/internal/blockchain/chain.go b/internal/blockchain/chain.go index 163047c312..d6764d5f00 100644 --- a/internal/blockchain/chain.go +++ b/internal/blockchain/chain.go @@ -11,7 +11,6 @@ import ( "context" "errors" "fmt" - "math/big" "sync" "time" @@ -463,13 +462,13 @@ func (b *BlockChain) HaveBlock(hash *chainhash.Hash) bool { // ChainWork returns the total work up to and including the block of the // provided block hash. -func (b *BlockChain) ChainWork(hash *chainhash.Hash) (*big.Int, error) { +func (b *BlockChain) ChainWork(hash *chainhash.Hash) (uint256.Uint256, error) { node := b.index.LookupNode(hash) if node == nil { - return nil, unknownBlockError(hash) + return uint256.Uint256{}, unknownBlockError(hash) } - return node.workSum.ToBig(), nil + return node.workSum, nil } // TipGeneration returns the entire generation of blocks stemming from the diff --git a/internal/netsync/manager.go b/internal/netsync/manager.go index aae2fd256d..cc50135434 100644 --- a/internal/netsync/manager.go +++ b/internal/netsync/manager.go @@ -23,6 +23,7 @@ import ( "github.com/decred/dcrd/internal/blockchain" "github.com/decred/dcrd/internal/mempool" "github.com/decred/dcrd/internal/progresslog" + "github.com/decred/dcrd/math/uint256" peerpkg "github.com/decred/dcrd/peer/v3" "github.com/decred/dcrd/wire" ) @@ -272,6 +273,14 @@ type SyncManager struct { // creation time and treated as immutable after that. cfg Config + // minKnownWork houses the minimum known work from the associated network + // params converted to a uint256 so the conversion only needs to be + // performed once when the sync manager is initialized. Ideally, the chain + // params should be updated to use the new type, but that will be a major + // version bump, so a one-time conversion is a good tradeoff in the mean + // time. + minKnownWork *uint256.Uint256 + rejectedTxns *apbf.Filter requestedTxns map[chainhash.Hash]struct{} requestedBlocks map[chainhash.Hash]struct{} @@ -1094,10 +1103,9 @@ func (m *SyncManager) handleHeadersMsg(hmsg *headersMsg) { isChainCurrent := chain.IsCurrent() receivedMaxHeaders := len(headers) == wire.MaxBlockHeadersPerMsg if !isChainCurrent && !peer.Inbound() && !receivedMaxHeaders { - minKnownWork := m.cfg.ChainParams.MinKnownChainWork - if minKnownWork != nil { + if m.minKnownWork != nil { workSum, err := chain.ChainWork(finalReceivedHash) - if err == nil && workSum.Cmp(minKnownWork) < 0 { + if err == nil && workSum.Lt(m.minKnownWork) { log.Debugf("Best known chain for peer %s has too little "+ "cumulative work -- disconnecting", peer) peer.Disconnect() @@ -1771,12 +1779,23 @@ type Config struct { // New returns a new network chain synchronization manager. Use Run to begin // processing asynchronous events. func New(config *Config) *SyncManager { + // Convert the minimum known work to a uint256 when it exists. Ideally, the + // chain params should be updated to use the new type, but that will be a + // major version bump, so a one-time conversion is a good tradeoff in the + // mean time. + var minKnownWork *uint256.Uint256 + minKnownWorkBig := config.ChainParams.MinKnownChainWork + if minKnownWorkBig != nil { + minKnownWork = new(uint256.Uint256).SetBig(minKnownWorkBig) + } + return &SyncManager{ cfg: *config, rejectedTxns: apbf.NewFilter(maxRejectedTxns, rejectedTxnsFPRate), requestedTxns: make(map[chainhash.Hash]struct{}), requestedBlocks: make(map[chainhash.Hash]struct{}), peers: make(map[*peerpkg.Peer]*syncMgrPeer), + minKnownWork: minKnownWork, hdrSyncState: makeHeaderSyncState(), progressLogger: progresslog.New("Processed", log), msgChan: make(chan interface{}, config.MaxPeers*3), diff --git a/internal/rpcserver/interface.go b/internal/rpcserver/interface.go index 5ad40189b6..2ba39600c5 100644 --- a/internal/rpcserver/interface.go +++ b/internal/rpcserver/interface.go @@ -6,7 +6,6 @@ package rpcserver import ( "context" - "math/big" "net" "time" @@ -19,6 +18,7 @@ import ( "github.com/decred/dcrd/internal/blockchain/indexers" "github.com/decred/dcrd/internal/mempool" "github.com/decred/dcrd/internal/mining" + "github.com/decred/dcrd/math/uint256" "github.com/decred/dcrd/peer/v3" "github.com/decred/dcrd/rpc/jsonrpc/types/v4" "github.com/decred/dcrd/txscript/v4/stdaddr" @@ -246,7 +246,7 @@ type Chain interface { // ChainWork returns the total work up to and including the block of the // provided block hash. - ChainWork(hash *chainhash.Hash) (*big.Int, error) + ChainWork(hash *chainhash.Hash) (uint256.Uint256, error) // CheckLiveTicket returns whether or not a ticket exists in the live ticket // treap of the best node. diff --git a/internal/rpcserver/rpcserverhandlers_test.go b/internal/rpcserver/rpcserverhandlers_test.go index 16db729407..ce91efeb5c 100644 --- a/internal/rpcserver/rpcserverhandlers_test.go +++ b/internal/rpcserver/rpcserverhandlers_test.go @@ -40,6 +40,7 @@ import ( "github.com/decred/dcrd/internal/mempool" "github.com/decred/dcrd/internal/mining" "github.com/decred/dcrd/internal/version" + "github.com/decred/dcrd/math/uint256" "github.com/decred/dcrd/peer/v3" "github.com/decred/dcrd/rpc/jsonrpc/types/v4" "github.com/decred/dcrd/txscript/v4" @@ -144,7 +145,7 @@ type testRPCChain struct { blockHeightByHashErr error calcWantHeight int64 chainTips []blockchain.ChainTipInfo - chainWork *big.Int + chainWork uint256.Uint256 chainWorkErr error checkLiveTicket bool checkLiveTickets []bool @@ -241,7 +242,7 @@ func (c *testRPCChain) ChainTips() []blockchain.ChainTipInfo { // ChainWork returns returns a mocked total work up to and including the block // of the provided block hash. -func (c *testRPCChain) ChainWork(hash *chainhash.Hash) (*big.Int, error) { +func (c *testRPCChain) ChainWork(hash *chainhash.Hash) (uint256.Uint256, error) { return c.chainWork, c.chainWorkErr } @@ -1272,6 +1273,19 @@ func hexToBytes(s string) []byte { return b } +// hexToUint256 interprets the passed hex string as a 256-bit big-endian +// unsigned integer and returns the resulting uint256. It will panic if there +// is an error. This is only provided for the hard-coded constants so errors in +// the source code can be detected. It will only (and must only) be called with +// hard-coded values. +func hexToUint256(s string) uint256.Uint256 { + b, err := hex.DecodeString(s) + if err != nil { + panic("invalid hex in source file: " + s) + } + return *new(uint256.Uint256).SetByteSlice(b) +} + // hexToMsgTx converts the passed hex string into a wire.MsgTx and will panic if // there is an error. This is only provided for hard-coded constants so errors // in the source code can be detected. It will only (and must only) be called @@ -1416,7 +1430,7 @@ func defaultMockRPCChain() *testRPCChain { headerByHashFn := func() wire.BlockHeader { return blkHeader } blkHash := blk.Hash() blkHeight := blk.Height() - chainWork, _ := new(big.Int).SetString("0e805fb85284503581c57c", 16) + chainWork := hexToUint256("0e805fb85284503581c57c") return &testRPCChain{ bestSnapshot: &blockchain.BestState{ Hash: *blkHash, @@ -3579,8 +3593,7 @@ func TestHandleGetBlockchainInfo(t *testing.T) { } chain.bestHeaderHash = *hash chain.bestHeaderHeight = 463073 - chain.chainWork = big.NewInt(0).SetBytes([]byte{0x11, 0x5d, 0x28, 0x33, 0x84, - 0x90, 0x90, 0xb0, 0x02, 0x65, 0x06}) + chain.chainWork = hexToUint256("115d2833849090b0026506") chain.isCurrent = false chain.maxBlockSize = 393216 chain.stateLastChangedHeight = int64(149248) @@ -3619,8 +3632,7 @@ func TestHandleGetBlockchainInfo(t *testing.T) { Hash: *genesisHash, PrevHash: *genesisPrevHash, } - chain.chainWork = big.NewInt(0).SetBytes([]byte{0x80, 0x00, 0x40, 0x00, 0x20, - 0x00}) + chain.chainWork = hexToUint256("800040002000") chain.isCurrent = false chain.maxBlockSize = 393216 chain.nextThresholdState = blockchain.ThresholdStateTuple{ @@ -3681,8 +3693,7 @@ func TestHandleGetBlockchainInfo(t *testing.T) { Hash: *hash, PrevHash: *prevHash, } - chain.chainWork = big.NewInt(0).SetBytes([]byte{0x11, 0x5d, 0x28, 0x33, 0x84, - 0x90, 0x90, 0xb0, 0x02, 0x65, 0x06}) + chain.chainWork = hexToUint256("115d2833849090b0026506") chain.maxBlockSizeErr = errors.New("could not fetch max block size") return chain }(), @@ -3700,8 +3711,7 @@ func TestHandleGetBlockchainInfo(t *testing.T) { Hash: *hash, PrevHash: *prevHash, } - chain.chainWork = big.NewInt(0).SetBytes([]byte{0x11, 0x5d, 0x28, 0x33, 0x84, - 0x90, 0x90, 0xb0, 0x02, 0x65, 0x06}) + chain.chainWork = hexToUint256("115d2833849090b0026506") chain.maxBlockSize = 393216 chain.nextThresholdStateErr = errors.New("could not fetch threshold state") return chain @@ -3720,8 +3730,7 @@ func TestHandleGetBlockchainInfo(t *testing.T) { Hash: *hash, PrevHash: *prevHash, } - chain.chainWork = big.NewInt(0).SetBytes([]byte{0x11, 0x5d, 0x28, 0x33, 0x84, - 0x90, 0x90, 0xb0, 0x02, 0x65, 0x06}) + chain.chainWork = hexToUint256("115d2833849090b0026506") chain.maxBlockSize = 393216 chain.stateLastChangedHeightErr = errors.New("could not fetch state last changed") return chain