2019-11-08 15:40:21 +00:00
|
|
|
package consensus
|
|
|
|
|
2019-11-15 10:32:40 +00:00
|
|
|
import (
|
|
|
|
"errors"
|
2020-11-17 12:57:50 +00:00
|
|
|
"fmt"
|
2019-11-15 10:32:40 +00:00
|
|
|
"sort"
|
2023-10-11 10:24:16 +00:00
|
|
|
"sync/atomic"
|
2019-11-15 10:32:40 +00:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/nspcc-dev/dbft"
|
|
|
|
"github.com/nspcc-dev/dbft/block"
|
|
|
|
"github.com/nspcc-dev/dbft/crypto"
|
|
|
|
"github.com/nspcc-dev/dbft/payload"
|
2020-06-26 14:23:10 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
2020-06-18 09:00:51 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
2020-03-03 14:21:42 +00:00
|
|
|
coreb "github.com/nspcc-dev/neo-go/pkg/core/block"
|
2022-01-13 00:19:10 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
2020-08-19 16:27:15 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/mempool"
|
2022-07-22 20:14:02 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
2020-03-03 14:21:42 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
2020-06-25 08:16:42 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
2020-03-03 14:21:42 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
2020-08-19 13:55:01 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
2020-04-21 13:45:48 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
2021-01-14 11:17:00 +00:00
|
|
|
npayload "github.com/nspcc-dev/neo-go/pkg/network/payload"
|
2020-03-03 14:21:42 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
2020-04-21 13:45:48 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
2020-03-03 14:21:42 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
2019-11-15 10:32:40 +00:00
|
|
|
"go.uber.org/zap"
|
|
|
|
)
|
2019-11-08 15:40:21 +00:00
|
|
|
|
|
|
|
// cacheMaxCapacity is the default cache capacity taken
|
|
|
|
// from C# implementation https://github.com/neo-project/neo/blob/master/neo/Ledger/Blockchain.cs#L64
|
|
|
|
const cacheMaxCapacity = 100
|
|
|
|
|
2022-12-07 13:51:03 +00:00
|
|
|
// defaultTimePerBlock is a period between blocks which is used in Neo.
|
2019-11-15 10:32:40 +00:00
|
|
|
const defaultTimePerBlock = 15 * time.Second
|
|
|
|
|
2020-07-11 12:22:14 +00:00
|
|
|
// Number of nanoseconds in millisecond.
|
|
|
|
const nsInMs = 1000000
|
|
|
|
|
2022-01-13 00:19:10 +00:00
|
|
|
// Ledger is the interface to Blockchain sufficient for Service.
|
|
|
|
type Ledger interface {
|
|
|
|
ApplyPolicyToTxSet([]*transaction.Transaction) []*transaction.Transaction
|
2022-12-06 13:34:38 +00:00
|
|
|
GetConfig() config.Blockchain
|
2022-01-13 00:19:10 +00:00
|
|
|
GetMemPool() *mempool.Pool
|
|
|
|
GetNextBlockValidators() ([]*keys.PublicKey, error)
|
2022-07-22 20:14:02 +00:00
|
|
|
GetStateRoot(height uint32) (*state.MPTRoot, error)
|
2022-01-13 00:19:10 +00:00
|
|
|
GetTransaction(util.Uint256) (*transaction.Transaction, uint32, error)
|
2023-08-30 16:48:20 +00:00
|
|
|
ComputeNextBlockValidators() []*keys.PublicKey
|
2022-01-13 00:19:10 +00:00
|
|
|
PoolTx(t *transaction.Transaction, pools ...*mempool.Pool) error
|
2022-08-19 17:47:55 +00:00
|
|
|
SubscribeForBlocks(ch chan *coreb.Block)
|
|
|
|
UnsubscribeFromBlocks(ch chan *coreb.Block)
|
2022-04-08 09:31:55 +00:00
|
|
|
GetBaseExecFee() int64
|
2023-09-20 13:33:04 +00:00
|
|
|
CalculateAttributesFee(tx *transaction.Transaction) int64
|
2022-01-13 00:19:10 +00:00
|
|
|
interop.Ledger
|
|
|
|
mempool.Feer
|
|
|
|
}
|
|
|
|
|
2023-03-07 09:06:53 +00:00
|
|
|
// BlockQueuer is an interface to the block queue manager sufficient for Service.
|
|
|
|
type BlockQueuer interface {
|
|
|
|
PutBlock(block *coreb.Block) error
|
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// Service represents a consensus instance.
|
2019-11-08 15:40:21 +00:00
|
|
|
type Service interface {
|
2022-04-22 08:33:56 +00:00
|
|
|
// Name returns service name.
|
|
|
|
Name() string
|
2019-11-15 10:32:40 +00:00
|
|
|
// Start initializes dBFT and starts event loop for consensus service.
|
2022-04-20 18:30:09 +00:00
|
|
|
// It must be called only when the sufficient amount of peers are connected.
|
2022-07-04 20:03:50 +00:00
|
|
|
// The service only starts once, subsequent calls to Start are no-op.
|
2019-11-15 10:32:40 +00:00
|
|
|
Start()
|
2022-07-04 20:03:50 +00:00
|
|
|
// Shutdown stops dBFT event loop. It can only be called once, subsequent calls
|
|
|
|
// to Shutdown on the same instance are no-op. The instance that was stopped can
|
|
|
|
// not be started again by calling Start (use a new instance if needed).
|
2020-09-01 16:58:51 +00:00
|
|
|
Shutdown()
|
2019-11-15 10:32:40 +00:00
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// OnPayload is a callback to notify the Service about a newly received payload.
|
2022-01-12 18:09:37 +00:00
|
|
|
OnPayload(p *npayload.Extensible) error
|
2022-04-20 18:30:09 +00:00
|
|
|
// OnTransaction is a callback to notify the Service about a newly received transaction.
|
2019-11-15 10:32:40 +00:00
|
|
|
OnTransaction(tx *transaction.Transaction)
|
2019-11-08 15:40:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type service struct {
|
2019-11-15 10:32:40 +00:00
|
|
|
Config
|
|
|
|
|
2020-01-09 14:46:08 +00:00
|
|
|
log *zap.Logger
|
2019-11-15 10:32:40 +00:00
|
|
|
// txx is a fifo cache which stores miner transactions.
|
|
|
|
txx *relayCache
|
|
|
|
dbft *dbft.DBFT
|
|
|
|
// messages and transactions are channels needed to process
|
|
|
|
// everything in single thread.
|
|
|
|
messages chan Payload
|
|
|
|
transactions chan *transaction.Transaction
|
2020-02-17 13:20:04 +00:00
|
|
|
// blockEvents is used to pass a new block event to the consensus
|
2023-03-02 13:44:18 +00:00
|
|
|
// process. It has a tiny buffer in order to avoid Blockchain blocking
|
|
|
|
// on block addition under the high load.
|
2020-05-07 19:45:06 +00:00
|
|
|
blockEvents chan *coreb.Block
|
2020-01-14 11:34:09 +00:00
|
|
|
lastProposal []util.Uint256
|
2020-01-15 14:05:47 +00:00
|
|
|
wallet *wallet.Wallet
|
consensus: prevent synchronization stalls
When CN is not up to date with the network is synchonizes blocks first and
only then starts consensus process. But while synchronizing it receives
consensus payloads and tries to process them even though messages reader
routine is not started yet. This leads to lots of goroutines waiting to send
their messages:
Jun 25 23:55:53 nodoka neo-go[32733]: goroutine 1639919 [chan send, 4 minutes]:
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/consensus.(*service).OnPayload(0xc0000ecb40, 0xc005bd7680)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/consensus/consensus.go:329 +0x31b
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleConsensusCmd(...)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:687
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleMessage(0xc0000ba160, 0x1053260, 0xc00507d170, 0xc005bdd560, 0x0, 0x0)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:806 +0xd58
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*TCPPeer).handleConn(0xc00507d170)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_peer.go:160 +0x294
Jun 25 23:55:53 nodoka neo-go[32733]: created by github.com/nspcc-dev/neo-go/pkg/network.(*TCPTransport).Dial
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_transport.go:38 +0x1ad
Jun 25 23:55:53 nodoka neo-go[32733]: goroutine 1639181 [chan send, 10 minutes]:
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/consensus.(*service).OnPayload(0xc0000ecb40, 0xc013bb6600)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/consensus/consensus.go:329 +0x31b
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleConsensusCmd(...)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:687
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleMessage(0xc0000ba160, 0x1053260, 0xc01361ee10, 0xc01342c780, 0x0, 0x0)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:806 +0xd58
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*TCPPeer).handleConn(0xc01361ee10)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_peer.go:160 +0x294
Jun 25 23:55:53 nodoka neo-go[32733]: created by github.com/nspcc-dev/neo-go/pkg/network.(*TCPTransport).Dial
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_transport.go:38 +0x1ad
Jun 25 23:55:53 nodoka neo-go[32733]: goroutine 39454 [chan send, 32 minutes]:
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/consensus.(*service).OnPayload(0xc0000ecb40, 0xc014fea680)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/consensus/consensus.go:329 +0x31b
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleConsensusCmd(...)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:687
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleMessage(0xc0000ba160, 0x1053260, 0xc0140b2ea0, 0xc014fe0ed0, 0x0, 0x0)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:806 +0xd58
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*TCPPeer).handleConn(0xc0140b2ea0)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_peer.go:160 +0x294
Jun 25 23:55:53 nodoka neo-go[32733]: created by github.com/nspcc-dev/neo-go/pkg/network.(*TCPTransport).Dial
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_transport.go:38 +0x1ad
Luckily it doesn't break synchronization completely as eventually connection
timers fire, the node breaks all connections, create new ones and these new
ones request blocks successfully until another consensus payload stalls them
too. In the end the node reaches synchronization, message processing loop
starts and releases all of these waiting goroutines, but it's better for us to
avoid this happening at all.
This also makes double-starting a no-op which is a nice property.
2020-06-26 08:19:01 +00:00
|
|
|
// started is a flag set with Start method that runs an event handling
|
|
|
|
// goroutine.
|
2023-10-11 10:24:16 +00:00
|
|
|
started atomic.Bool
|
consensus: wait goroutine to finish on Shutdown
Fixes:
panic: assignment to entry in nil map
goroutine 227 [running]:
github.com/nspcc-dev/neo-go/pkg/core/storage.(*MemoryStore).put(...)
/home/rik/dev/neo-go/pkg/core/storage/memory_store.go:53
github.com/nspcc-dev/neo-go/pkg/core/storage.(*MemoryStore).PutBatch(0xc00035f580, 0x110a680, 0xc000336750, 0x0, 0x0)
/home/rik/dev/neo-go/pkg/core/storage/memory_store.go:93 +0x286
github.com/nspcc-dev/neo-go/pkg/core.(*Blockchain).addHeaders(0xc0000a2340, 0xc0001be301, 0xc00036d428, 0x1, 0x1, 0x0, 0x0)
/home/rik/dev/neo-go/pkg/core/blockchain.go:516 +0xd5a
github.com/nspcc-dev/neo-go/pkg/core.(*Blockchain).AddBlock(0xc0000a2340, 0xc0000bc2c0, 0x0, 0x0)
/home/rik/dev/neo-go/pkg/core/blockchain.go:407 +0x9ca
github.com/nspcc-dev/neo-go/pkg/consensus.(*service).processBlock(0xc000152160, 0x1122320, 0xc0000bc2c0)
/home/rik/dev/neo-go/pkg/consensus/consensus.go:440 +0xbf
github.com/nspcc-dev/dbft.(*DBFT).checkCommit(0xc0000d3400)
/home/rik/go/pkg/mod/github.com/nspcc-dev/dbft@v0.0.0-20200911152629-be965ee4d449/check.go:71 +0x918
github.com/nspcc-dev/dbft.(*DBFT).checkPrepare(0xc0000d3400)
/home/rik/go/pkg/mod/github.com/nspcc-dev/dbft@v0.0.0-20200911152629-be965ee4d449/check.go:36 +0x465
github.com/nspcc-dev/dbft.(*DBFT).sendPrepareRequest(0xc0000d3400)
/home/rik/go/pkg/mod/github.com/nspcc-dev/dbft@v0.0.0-20200911152629-be965ee4d449/send.go:42 +0x2f8
github.com/nspcc-dev/dbft.(*DBFT).start(0xc0000d3400)
/home/rik/go/pkg/mod/github.com/nspcc-dev/dbft@v0.0.0-20200911152629-be965ee4d449/dbft.go:269 +0x26f
github.com/nspcc-dev/dbft.(*DBFT).Start(0xc0000d3400)
/home/rik/go/pkg/mod/github.com/nspcc-dev/dbft@v0.0.0-20200911152629-be965ee4d449/dbft.go:82 +0x59
github.com/nspcc-dev/neo-go/pkg/consensus.(*service).Start(0xc000152160)
/home/rik/dev/neo-go/pkg/consensus/consensus.go:191 +0x56
github.com/nspcc-dev/neo-go/pkg/network.(*Server).tryStartConsensus(0xc000235040)
/home/rik/dev/neo-go/pkg/network/server.go:311 +0xda
github.com/nspcc-dev/neo-go/pkg/network.(*Server).Start(0xc000235040, 0xc0000faba0)
/home/rik/dev/neo-go/pkg/network/server.go:173 +0x202
created by github.com/nspcc-dev/neo-go/cli.newTestChain
/home/rik/dev/neo-go/cli/executor_test.go:77 +0x47d
FAIL github.com/nspcc-dev/neo-go/cli 14.479s
2020-09-19 18:49:05 +00:00
|
|
|
quit chan struct{}
|
|
|
|
finished chan struct{}
|
2020-12-16 10:24:49 +00:00
|
|
|
// lastTimestamp contains timestamp for the last processed block.
|
|
|
|
// We can't rely on timestamp from dbft context because it is changed
|
2022-04-20 18:30:09 +00:00
|
|
|
// before the block is accepted. So, in case of change view, it will contain
|
|
|
|
// an updated value.
|
2020-12-16 10:24:49 +00:00
|
|
|
lastTimestamp uint64
|
2019-11-15 10:32:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Config is a configuration for consensus services.
|
|
|
|
type Config struct {
|
2019-12-30 07:43:05 +00:00
|
|
|
// Logger is a logger instance.
|
|
|
|
Logger *zap.Logger
|
2022-04-20 18:30:09 +00:00
|
|
|
// Broadcast is a callback which is called to notify the server
|
|
|
|
// about a new consensus payload to be sent.
|
2021-01-14 13:38:40 +00:00
|
|
|
Broadcast func(p *npayload.Extensible)
|
2022-01-13 00:19:10 +00:00
|
|
|
// Chain is a Ledger instance.
|
|
|
|
Chain Ledger
|
2023-03-07 09:06:53 +00:00
|
|
|
// BlockQueue is a BlockQueuer instance.
|
|
|
|
BlockQueue BlockQueuer
|
2021-03-15 09:25:52 +00:00
|
|
|
// ProtocolConfiguration contains protocol settings.
|
|
|
|
ProtocolConfiguration config.ProtocolConfiguration
|
2019-11-15 10:32:40 +00:00
|
|
|
// RequestTx is a callback to which will be called
|
2022-04-20 18:30:09 +00:00
|
|
|
// when a node lacks transactions present in the block.
|
2019-11-15 10:32:40 +00:00
|
|
|
RequestTx func(h ...util.Uint256)
|
2022-10-14 18:00:26 +00:00
|
|
|
// StopTxFlow is a callback that is called after the consensus
|
|
|
|
// process stops accepting incoming transactions.
|
|
|
|
StopTxFlow func()
|
2022-04-20 18:30:09 +00:00
|
|
|
// TimePerBlock is minimal time that should pass before the next block is accepted.
|
2019-11-15 10:32:40 +00:00
|
|
|
TimePerBlock time.Duration
|
2022-12-05 15:11:18 +00:00
|
|
|
// Wallet is a local-node wallet configuration. If the path is empty, then
|
|
|
|
// no wallet will be initialized and the service will be in watch-only mode.
|
|
|
|
Wallet config.Wallet
|
2019-11-08 15:40:21 +00:00
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// NewService returns a new consensus.Service instance.
|
2019-11-15 10:32:40 +00:00
|
|
|
func NewService(cfg Config) (Service, error) {
|
|
|
|
if cfg.TimePerBlock <= 0 {
|
|
|
|
cfg.TimePerBlock = defaultTimePerBlock
|
|
|
|
}
|
|
|
|
|
2019-12-30 07:43:05 +00:00
|
|
|
if cfg.Logger == nil {
|
|
|
|
return nil, errors.New("empty logger")
|
|
|
|
}
|
|
|
|
|
2019-11-15 10:32:40 +00:00
|
|
|
srv := &service{
|
|
|
|
Config: cfg,
|
|
|
|
|
2020-01-09 14:46:08 +00:00
|
|
|
log: cfg.Logger,
|
2019-11-15 10:32:40 +00:00
|
|
|
txx: newFIFOCache(cacheMaxCapacity),
|
|
|
|
messages: make(chan Payload, 100),
|
2019-11-29 12:40:11 +00:00
|
|
|
|
2021-03-15 09:25:52 +00:00
|
|
|
transactions: make(chan *transaction.Transaction, 100),
|
|
|
|
blockEvents: make(chan *coreb.Block, 1),
|
|
|
|
quit: make(chan struct{}),
|
|
|
|
finished: make(chan struct{}),
|
2019-11-15 10:32:40 +00:00
|
|
|
}
|
|
|
|
|
2020-01-15 14:05:47 +00:00
|
|
|
var err error
|
|
|
|
|
2022-12-05 15:11:18 +00:00
|
|
|
if len(cfg.Wallet.Path) > 0 {
|
|
|
|
if srv.wallet, err = wallet.NewWalletFromFile(cfg.Wallet.Path); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-01-15 14:05:47 +00:00
|
|
|
|
2022-12-05 15:11:18 +00:00
|
|
|
// Check that the wallet password is correct for at least one account.
|
|
|
|
var ok bool
|
|
|
|
for _, acc := range srv.wallet.Accounts {
|
|
|
|
err := acc.Decrypt(srv.Config.Wallet.Password, srv.wallet.Scrypt)
|
|
|
|
if err == nil {
|
|
|
|
ok = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("no account with provided password was found")
|
2020-08-13 09:01:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-15 10:32:40 +00:00
|
|
|
srv.dbft = dbft.New(
|
2020-01-09 14:46:08 +00:00
|
|
|
dbft.WithLogger(srv.log),
|
2019-11-15 10:32:40 +00:00
|
|
|
dbft.WithSecondsPerBlock(cfg.TimePerBlock),
|
2020-01-17 14:30:45 +00:00
|
|
|
dbft.WithGetKeyPair(srv.getKeyPair),
|
2019-11-15 10:32:40 +00:00
|
|
|
dbft.WithRequestTx(cfg.RequestTx),
|
2022-10-14 18:00:26 +00:00
|
|
|
dbft.WithStopTxFlow(cfg.StopTxFlow),
|
2019-11-15 10:32:40 +00:00
|
|
|
dbft.WithGetTx(srv.getTx),
|
|
|
|
dbft.WithGetVerified(srv.getVerifiedTx),
|
|
|
|
dbft.WithBroadcast(srv.broadcast),
|
|
|
|
dbft.WithProcessBlock(srv.processBlock),
|
|
|
|
dbft.WithVerifyBlock(srv.verifyBlock),
|
|
|
|
dbft.WithGetBlock(srv.getBlock),
|
|
|
|
dbft.WithWatchOnly(func() bool { return false }),
|
2020-06-18 09:00:51 +00:00
|
|
|
dbft.WithNewBlockFromContext(srv.newBlockFromContext),
|
2019-11-15 10:32:40 +00:00
|
|
|
dbft.WithCurrentHeight(cfg.Chain.BlockHeight),
|
|
|
|
dbft.WithCurrentBlockHash(cfg.Chain.CurrentBlockHash),
|
|
|
|
dbft.WithGetValidators(srv.getValidators),
|
|
|
|
dbft.WithGetConsensusAddress(srv.getConsensusAddress),
|
|
|
|
|
2020-06-26 15:27:18 +00:00
|
|
|
dbft.WithNewConsensusPayload(srv.newPayload),
|
2020-11-17 12:57:50 +00:00
|
|
|
dbft.WithNewPrepareRequest(srv.newPrepareRequest),
|
2019-11-15 10:32:40 +00:00
|
|
|
dbft.WithNewPrepareResponse(func() payload.PrepareResponse { return new(prepareResponse) }),
|
|
|
|
dbft.WithNewChangeView(func() payload.ChangeView { return new(changeView) }),
|
|
|
|
dbft.WithNewCommit(func() payload.Commit { return new(commit) }),
|
2020-01-29 14:56:21 +00:00
|
|
|
dbft.WithNewRecoveryRequest(func() payload.RecoveryRequest { return new(recoveryRequest) }),
|
2020-11-17 12:57:50 +00:00
|
|
|
dbft.WithNewRecoveryMessage(func() payload.RecoveryMessage {
|
2021-03-15 09:25:52 +00:00
|
|
|
return &recoveryMessage{stateRootEnabled: srv.ProtocolConfiguration.StateRootInHeader}
|
2020-11-17 12:57:50 +00:00
|
|
|
}),
|
consensus: prevent synchronization stalls
When CN is not up to date with the network is synchonizes blocks first and
only then starts consensus process. But while synchronizing it receives
consensus payloads and tries to process them even though messages reader
routine is not started yet. This leads to lots of goroutines waiting to send
their messages:
Jun 25 23:55:53 nodoka neo-go[32733]: goroutine 1639919 [chan send, 4 minutes]:
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/consensus.(*service).OnPayload(0xc0000ecb40, 0xc005bd7680)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/consensus/consensus.go:329 +0x31b
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleConsensusCmd(...)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:687
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleMessage(0xc0000ba160, 0x1053260, 0xc00507d170, 0xc005bdd560, 0x0, 0x0)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:806 +0xd58
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*TCPPeer).handleConn(0xc00507d170)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_peer.go:160 +0x294
Jun 25 23:55:53 nodoka neo-go[32733]: created by github.com/nspcc-dev/neo-go/pkg/network.(*TCPTransport).Dial
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_transport.go:38 +0x1ad
Jun 25 23:55:53 nodoka neo-go[32733]: goroutine 1639181 [chan send, 10 minutes]:
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/consensus.(*service).OnPayload(0xc0000ecb40, 0xc013bb6600)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/consensus/consensus.go:329 +0x31b
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleConsensusCmd(...)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:687
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleMessage(0xc0000ba160, 0x1053260, 0xc01361ee10, 0xc01342c780, 0x0, 0x0)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:806 +0xd58
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*TCPPeer).handleConn(0xc01361ee10)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_peer.go:160 +0x294
Jun 25 23:55:53 nodoka neo-go[32733]: created by github.com/nspcc-dev/neo-go/pkg/network.(*TCPTransport).Dial
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_transport.go:38 +0x1ad
Jun 25 23:55:53 nodoka neo-go[32733]: goroutine 39454 [chan send, 32 minutes]:
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/consensus.(*service).OnPayload(0xc0000ecb40, 0xc014fea680)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/consensus/consensus.go:329 +0x31b
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleConsensusCmd(...)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:687
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleMessage(0xc0000ba160, 0x1053260, 0xc0140b2ea0, 0xc014fe0ed0, 0x0, 0x0)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:806 +0xd58
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*TCPPeer).handleConn(0xc0140b2ea0)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_peer.go:160 +0x294
Jun 25 23:55:53 nodoka neo-go[32733]: created by github.com/nspcc-dev/neo-go/pkg/network.(*TCPTransport).Dial
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_transport.go:38 +0x1ad
Luckily it doesn't break synchronization completely as eventually connection
timers fire, the node breaks all connections, create new ones and these new
ones request blocks successfully until another consensus payload stalls them
too. In the end the node reaches synchronization, message processing loop
starts and releases all of these waiting goroutines, but it's better for us to
avoid this happening at all.
This also makes double-starting a no-op which is a nice property.
2020-06-26 08:19:01 +00:00
|
|
|
dbft.WithVerifyPrepareRequest(srv.verifyRequest),
|
2020-09-03 19:02:42 +00:00
|
|
|
dbft.WithVerifyPrepareResponse(func(_ payload.ConsensusPayload) error { return nil }),
|
2019-11-15 10:32:40 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
if srv.dbft == nil {
|
|
|
|
return nil, errors.New("can't initialize dBFT")
|
|
|
|
}
|
|
|
|
|
|
|
|
return srv, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
_ block.Transaction = (*transaction.Transaction)(nil)
|
|
|
|
_ block.Block = (*neoBlock)(nil)
|
|
|
|
)
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// NewPayload creates a new consensus payload for the provided network.
|
2020-11-17 12:57:50 +00:00
|
|
|
func NewPayload(m netmode.Magic, stateRootEnabled bool) *Payload {
|
2020-06-26 15:27:18 +00:00
|
|
|
return &Payload{
|
2021-01-14 11:17:00 +00:00
|
|
|
Extensible: npayload.Extensible{
|
2022-07-28 15:30:14 +00:00
|
|
|
Category: npayload.ConsensusCategory,
|
2021-01-14 11:17:00 +00:00
|
|
|
},
|
|
|
|
message: message{
|
2020-11-17 12:57:50 +00:00
|
|
|
stateRootEnabled: stateRootEnabled,
|
|
|
|
},
|
2021-03-25 18:59:54 +00:00
|
|
|
network: m,
|
2020-06-26 15:27:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-03 10:34:24 +00:00
|
|
|
func (s *service) newPayload(c *dbft.Context, t payload.MessageType, msg any) payload.ConsensusPayload {
|
2021-03-15 09:25:52 +00:00
|
|
|
cp := NewPayload(s.ProtocolConfiguration.Magic, s.ProtocolConfiguration.StateRootInHeader)
|
2021-01-14 11:17:00 +00:00
|
|
|
cp.SetHeight(c.BlockIndex)
|
|
|
|
cp.SetValidatorIndex(uint16(c.MyIndex))
|
|
|
|
cp.SetViewNumber(c.ViewNumber)
|
|
|
|
cp.SetType(t)
|
|
|
|
if pr, ok := msg.(*prepareRequest); ok {
|
|
|
|
pr.SetPrevHash(s.dbft.PrevHash)
|
|
|
|
pr.SetVersion(s.dbft.Version)
|
|
|
|
}
|
|
|
|
cp.SetPayload(msg)
|
|
|
|
|
|
|
|
cp.Extensible.ValidBlockStart = 0
|
|
|
|
cp.Extensible.ValidBlockEnd = c.BlockIndex
|
|
|
|
cp.Extensible.Sender = c.Validators[c.MyIndex].(*publicKey).GetScriptHash()
|
|
|
|
|
|
|
|
return cp
|
2020-11-17 12:57:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *service) newPrepareRequest() payload.PrepareRequest {
|
|
|
|
r := new(prepareRequest)
|
2021-03-15 09:25:52 +00:00
|
|
|
if s.ProtocolConfiguration.StateRootInHeader {
|
2020-11-17 12:57:50 +00:00
|
|
|
r.stateRootEnabled = true
|
2022-07-22 20:14:02 +00:00
|
|
|
if sr, err := s.Chain.GetStateRoot(s.dbft.BlockIndex - 1); err == nil {
|
2020-11-17 12:57:50 +00:00
|
|
|
r.stateRoot = sr.Root
|
|
|
|
} else {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return r
|
2020-06-26 15:27:18 +00:00
|
|
|
}
|
|
|
|
|
2022-04-22 08:33:56 +00:00
|
|
|
// Name returns service name.
|
|
|
|
func (s *service) Name() string {
|
|
|
|
return "consensus"
|
|
|
|
}
|
|
|
|
|
2019-11-15 10:32:40 +00:00
|
|
|
func (s *service) Start() {
|
2023-04-27 15:49:19 +00:00
|
|
|
if s.started.CompareAndSwap(false, true) {
|
2021-04-02 10:13:26 +00:00
|
|
|
s.log.Info("starting consensus service")
|
2022-10-19 15:43:23 +00:00
|
|
|
b, _ := s.Chain.GetBlock(s.Chain.CurrentBlockHash()) // Can't fail, we have some current block!
|
|
|
|
s.lastTimestamp = b.Timestamp
|
|
|
|
s.dbft.Start(s.lastTimestamp * nsInMs)
|
consensus: prevent synchronization stalls
When CN is not up to date with the network is synchonizes blocks first and
only then starts consensus process. But while synchronizing it receives
consensus payloads and tries to process them even though messages reader
routine is not started yet. This leads to lots of goroutines waiting to send
their messages:
Jun 25 23:55:53 nodoka neo-go[32733]: goroutine 1639919 [chan send, 4 minutes]:
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/consensus.(*service).OnPayload(0xc0000ecb40, 0xc005bd7680)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/consensus/consensus.go:329 +0x31b
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleConsensusCmd(...)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:687
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleMessage(0xc0000ba160, 0x1053260, 0xc00507d170, 0xc005bdd560, 0x0, 0x0)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:806 +0xd58
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*TCPPeer).handleConn(0xc00507d170)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_peer.go:160 +0x294
Jun 25 23:55:53 nodoka neo-go[32733]: created by github.com/nspcc-dev/neo-go/pkg/network.(*TCPTransport).Dial
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_transport.go:38 +0x1ad
Jun 25 23:55:53 nodoka neo-go[32733]: goroutine 1639181 [chan send, 10 minutes]:
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/consensus.(*service).OnPayload(0xc0000ecb40, 0xc013bb6600)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/consensus/consensus.go:329 +0x31b
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleConsensusCmd(...)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:687
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleMessage(0xc0000ba160, 0x1053260, 0xc01361ee10, 0xc01342c780, 0x0, 0x0)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:806 +0xd58
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*TCPPeer).handleConn(0xc01361ee10)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_peer.go:160 +0x294
Jun 25 23:55:53 nodoka neo-go[32733]: created by github.com/nspcc-dev/neo-go/pkg/network.(*TCPTransport).Dial
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_transport.go:38 +0x1ad
Jun 25 23:55:53 nodoka neo-go[32733]: goroutine 39454 [chan send, 32 minutes]:
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/consensus.(*service).OnPayload(0xc0000ecb40, 0xc014fea680)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/consensus/consensus.go:329 +0x31b
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleConsensusCmd(...)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:687
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleMessage(0xc0000ba160, 0x1053260, 0xc0140b2ea0, 0xc014fe0ed0, 0x0, 0x0)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:806 +0xd58
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*TCPPeer).handleConn(0xc0140b2ea0)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_peer.go:160 +0x294
Jun 25 23:55:53 nodoka neo-go[32733]: created by github.com/nspcc-dev/neo-go/pkg/network.(*TCPTransport).Dial
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_transport.go:38 +0x1ad
Luckily it doesn't break synchronization completely as eventually connection
timers fire, the node breaks all connections, create new ones and these new
ones request blocks successfully until another consensus payload stalls them
too. In the end the node reaches synchronization, message processing loop
starts and releases all of these waiting goroutines, but it's better for us to
avoid this happening at all.
This also makes double-starting a no-op which is a nice property.
2020-06-26 08:19:01 +00:00
|
|
|
s.Chain.SubscribeForBlocks(s.blockEvents)
|
|
|
|
go s.eventLoop()
|
|
|
|
}
|
2019-11-15 10:32:40 +00:00
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// Shutdown implements the Service interface.
|
2020-09-01 16:58:51 +00:00
|
|
|
func (s *service) Shutdown() {
|
2023-04-27 15:49:19 +00:00
|
|
|
if s.started.CompareAndSwap(true, false) {
|
2022-07-26 19:41:52 +00:00
|
|
|
s.log.Info("stopping consensus service")
|
2021-04-02 09:50:46 +00:00
|
|
|
close(s.quit)
|
|
|
|
<-s.finished
|
2022-12-05 15:11:18 +00:00
|
|
|
if s.wallet != nil {
|
|
|
|
s.wallet.Close()
|
|
|
|
}
|
2021-04-02 09:50:46 +00:00
|
|
|
}
|
2020-09-01 16:58:51 +00:00
|
|
|
}
|
|
|
|
|
2019-11-15 10:32:40 +00:00
|
|
|
func (s *service) eventLoop() {
|
consensus: wait goroutine to finish on Shutdown
Fixes:
panic: assignment to entry in nil map
goroutine 227 [running]:
github.com/nspcc-dev/neo-go/pkg/core/storage.(*MemoryStore).put(...)
/home/rik/dev/neo-go/pkg/core/storage/memory_store.go:53
github.com/nspcc-dev/neo-go/pkg/core/storage.(*MemoryStore).PutBatch(0xc00035f580, 0x110a680, 0xc000336750, 0x0, 0x0)
/home/rik/dev/neo-go/pkg/core/storage/memory_store.go:93 +0x286
github.com/nspcc-dev/neo-go/pkg/core.(*Blockchain).addHeaders(0xc0000a2340, 0xc0001be301, 0xc00036d428, 0x1, 0x1, 0x0, 0x0)
/home/rik/dev/neo-go/pkg/core/blockchain.go:516 +0xd5a
github.com/nspcc-dev/neo-go/pkg/core.(*Blockchain).AddBlock(0xc0000a2340, 0xc0000bc2c0, 0x0, 0x0)
/home/rik/dev/neo-go/pkg/core/blockchain.go:407 +0x9ca
github.com/nspcc-dev/neo-go/pkg/consensus.(*service).processBlock(0xc000152160, 0x1122320, 0xc0000bc2c0)
/home/rik/dev/neo-go/pkg/consensus/consensus.go:440 +0xbf
github.com/nspcc-dev/dbft.(*DBFT).checkCommit(0xc0000d3400)
/home/rik/go/pkg/mod/github.com/nspcc-dev/dbft@v0.0.0-20200911152629-be965ee4d449/check.go:71 +0x918
github.com/nspcc-dev/dbft.(*DBFT).checkPrepare(0xc0000d3400)
/home/rik/go/pkg/mod/github.com/nspcc-dev/dbft@v0.0.0-20200911152629-be965ee4d449/check.go:36 +0x465
github.com/nspcc-dev/dbft.(*DBFT).sendPrepareRequest(0xc0000d3400)
/home/rik/go/pkg/mod/github.com/nspcc-dev/dbft@v0.0.0-20200911152629-be965ee4d449/send.go:42 +0x2f8
github.com/nspcc-dev/dbft.(*DBFT).start(0xc0000d3400)
/home/rik/go/pkg/mod/github.com/nspcc-dev/dbft@v0.0.0-20200911152629-be965ee4d449/dbft.go:269 +0x26f
github.com/nspcc-dev/dbft.(*DBFT).Start(0xc0000d3400)
/home/rik/go/pkg/mod/github.com/nspcc-dev/dbft@v0.0.0-20200911152629-be965ee4d449/dbft.go:82 +0x59
github.com/nspcc-dev/neo-go/pkg/consensus.(*service).Start(0xc000152160)
/home/rik/dev/neo-go/pkg/consensus/consensus.go:191 +0x56
github.com/nspcc-dev/neo-go/pkg/network.(*Server).tryStartConsensus(0xc000235040)
/home/rik/dev/neo-go/pkg/network/server.go:311 +0xda
github.com/nspcc-dev/neo-go/pkg/network.(*Server).Start(0xc000235040, 0xc0000faba0)
/home/rik/dev/neo-go/pkg/network/server.go:173 +0x202
created by github.com/nspcc-dev/neo-go/cli.newTestChain
/home/rik/dev/neo-go/cli/executor_test.go:77 +0x47d
FAIL github.com/nspcc-dev/neo-go/cli 14.479s
2020-09-19 18:49:05 +00:00
|
|
|
events:
|
2019-11-15 10:32:40 +00:00
|
|
|
for {
|
|
|
|
select {
|
2020-09-01 16:58:51 +00:00
|
|
|
case <-s.quit:
|
|
|
|
s.dbft.Timer.Stop()
|
2022-06-28 15:07:22 +00:00
|
|
|
s.Chain.UnsubscribeFromBlocks(s.blockEvents)
|
consensus: wait goroutine to finish on Shutdown
Fixes:
panic: assignment to entry in nil map
goroutine 227 [running]:
github.com/nspcc-dev/neo-go/pkg/core/storage.(*MemoryStore).put(...)
/home/rik/dev/neo-go/pkg/core/storage/memory_store.go:53
github.com/nspcc-dev/neo-go/pkg/core/storage.(*MemoryStore).PutBatch(0xc00035f580, 0x110a680, 0xc000336750, 0x0, 0x0)
/home/rik/dev/neo-go/pkg/core/storage/memory_store.go:93 +0x286
github.com/nspcc-dev/neo-go/pkg/core.(*Blockchain).addHeaders(0xc0000a2340, 0xc0001be301, 0xc00036d428, 0x1, 0x1, 0x0, 0x0)
/home/rik/dev/neo-go/pkg/core/blockchain.go:516 +0xd5a
github.com/nspcc-dev/neo-go/pkg/core.(*Blockchain).AddBlock(0xc0000a2340, 0xc0000bc2c0, 0x0, 0x0)
/home/rik/dev/neo-go/pkg/core/blockchain.go:407 +0x9ca
github.com/nspcc-dev/neo-go/pkg/consensus.(*service).processBlock(0xc000152160, 0x1122320, 0xc0000bc2c0)
/home/rik/dev/neo-go/pkg/consensus/consensus.go:440 +0xbf
github.com/nspcc-dev/dbft.(*DBFT).checkCommit(0xc0000d3400)
/home/rik/go/pkg/mod/github.com/nspcc-dev/dbft@v0.0.0-20200911152629-be965ee4d449/check.go:71 +0x918
github.com/nspcc-dev/dbft.(*DBFT).checkPrepare(0xc0000d3400)
/home/rik/go/pkg/mod/github.com/nspcc-dev/dbft@v0.0.0-20200911152629-be965ee4d449/check.go:36 +0x465
github.com/nspcc-dev/dbft.(*DBFT).sendPrepareRequest(0xc0000d3400)
/home/rik/go/pkg/mod/github.com/nspcc-dev/dbft@v0.0.0-20200911152629-be965ee4d449/send.go:42 +0x2f8
github.com/nspcc-dev/dbft.(*DBFT).start(0xc0000d3400)
/home/rik/go/pkg/mod/github.com/nspcc-dev/dbft@v0.0.0-20200911152629-be965ee4d449/dbft.go:269 +0x26f
github.com/nspcc-dev/dbft.(*DBFT).Start(0xc0000d3400)
/home/rik/go/pkg/mod/github.com/nspcc-dev/dbft@v0.0.0-20200911152629-be965ee4d449/dbft.go:82 +0x59
github.com/nspcc-dev/neo-go/pkg/consensus.(*service).Start(0xc000152160)
/home/rik/dev/neo-go/pkg/consensus/consensus.go:191 +0x56
github.com/nspcc-dev/neo-go/pkg/network.(*Server).tryStartConsensus(0xc000235040)
/home/rik/dev/neo-go/pkg/network/server.go:311 +0xda
github.com/nspcc-dev/neo-go/pkg/network.(*Server).Start(0xc000235040, 0xc0000faba0)
/home/rik/dev/neo-go/pkg/network/server.go:173 +0x202
created by github.com/nspcc-dev/neo-go/cli.newTestChain
/home/rik/dev/neo-go/cli/executor_test.go:77 +0x47d
FAIL github.com/nspcc-dev/neo-go/cli 14.479s
2020-09-19 18:49:05 +00:00
|
|
|
break events
|
2020-09-10 13:34:33 +00:00
|
|
|
case <-s.dbft.Timer.C():
|
|
|
|
hv := s.dbft.Timer.HV()
|
2020-01-09 14:46:08 +00:00
|
|
|
s.log.Debug("timer fired",
|
|
|
|
zap.Uint32("height", hv.Height),
|
|
|
|
zap.Uint("view", uint(hv.View)))
|
2020-02-17 13:20:04 +00:00
|
|
|
s.dbft.OnTimeout(hv)
|
2019-11-15 10:32:40 +00:00
|
|
|
case msg := <-s.messages:
|
2020-01-31 11:31:19 +00:00
|
|
|
fields := []zap.Field{
|
2021-01-14 11:17:00 +00:00
|
|
|
zap.Uint8("from", msg.message.ValidatorIndex),
|
2020-01-31 11:31:19 +00:00
|
|
|
zap.Stringer("type", msg.Type()),
|
|
|
|
}
|
|
|
|
|
|
|
|
if msg.Type() == payload.RecoveryMessageType {
|
|
|
|
rec := msg.GetRecoveryMessage().(*recoveryMessage)
|
2020-01-31 12:02:03 +00:00
|
|
|
if rec.preparationHash == nil {
|
|
|
|
req := rec.GetPrepareRequest(&msg, s.dbft.Validators, uint16(s.dbft.PrimaryIndex))
|
|
|
|
if req != nil {
|
|
|
|
h := req.Hash()
|
|
|
|
rec.preparationHash = &h
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-31 11:31:19 +00:00
|
|
|
fields = append(fields,
|
|
|
|
zap.Int("#preparation", len(rec.preparationPayloads)),
|
|
|
|
zap.Int("#commit", len(rec.commitPayloads)),
|
|
|
|
zap.Int("#changeview", len(rec.changeViewPayloads)),
|
2020-01-31 11:49:15 +00:00
|
|
|
zap.Bool("#request", rec.prepareRequest != nil),
|
|
|
|
zap.Bool("#hash", rec.preparationHash != nil))
|
2020-01-31 11:31:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
s.log.Debug("received message", fields...)
|
2019-11-15 10:32:40 +00:00
|
|
|
s.dbft.OnReceive(&msg)
|
|
|
|
case tx := <-s.transactions:
|
|
|
|
s.dbft.OnTransaction(tx)
|
2020-05-07 19:45:06 +00:00
|
|
|
case b := <-s.blockEvents:
|
2020-09-17 15:27:27 +00:00
|
|
|
s.handleChainBlock(b)
|
|
|
|
}
|
2023-03-06 15:27:09 +00:00
|
|
|
// Always process block event if there is any, we can add one above or external
|
|
|
|
// services can add several blocks during message processing.
|
2023-03-15 14:45:56 +00:00
|
|
|
var latestBlock *coreb.Block
|
2023-03-06 15:27:09 +00:00
|
|
|
syncLoop:
|
|
|
|
for {
|
|
|
|
select {
|
2023-03-15 14:45:56 +00:00
|
|
|
case latestBlock = <-s.blockEvents:
|
2023-03-06 15:27:09 +00:00
|
|
|
default:
|
|
|
|
break syncLoop
|
|
|
|
}
|
2019-11-15 10:32:40 +00:00
|
|
|
}
|
2023-03-15 14:45:56 +00:00
|
|
|
if latestBlock != nil {
|
|
|
|
s.handleChainBlock(latestBlock)
|
|
|
|
}
|
2020-09-17 15:27:27 +00:00
|
|
|
}
|
2022-07-01 18:55:41 +00:00
|
|
|
drainLoop:
|
2022-06-28 15:07:22 +00:00
|
|
|
for {
|
|
|
|
select {
|
2022-07-01 18:55:41 +00:00
|
|
|
case <-s.messages:
|
|
|
|
case <-s.transactions:
|
2022-06-28 15:07:22 +00:00
|
|
|
case <-s.blockEvents:
|
|
|
|
default:
|
2022-07-01 18:55:41 +00:00
|
|
|
break drainLoop
|
2022-06-28 15:07:22 +00:00
|
|
|
}
|
|
|
|
}
|
2022-07-01 18:55:41 +00:00
|
|
|
close(s.messages)
|
|
|
|
close(s.transactions)
|
2022-06-28 15:07:22 +00:00
|
|
|
close(s.blockEvents)
|
consensus: wait goroutine to finish on Shutdown
Fixes:
panic: assignment to entry in nil map
goroutine 227 [running]:
github.com/nspcc-dev/neo-go/pkg/core/storage.(*MemoryStore).put(...)
/home/rik/dev/neo-go/pkg/core/storage/memory_store.go:53
github.com/nspcc-dev/neo-go/pkg/core/storage.(*MemoryStore).PutBatch(0xc00035f580, 0x110a680, 0xc000336750, 0x0, 0x0)
/home/rik/dev/neo-go/pkg/core/storage/memory_store.go:93 +0x286
github.com/nspcc-dev/neo-go/pkg/core.(*Blockchain).addHeaders(0xc0000a2340, 0xc0001be301, 0xc00036d428, 0x1, 0x1, 0x0, 0x0)
/home/rik/dev/neo-go/pkg/core/blockchain.go:516 +0xd5a
github.com/nspcc-dev/neo-go/pkg/core.(*Blockchain).AddBlock(0xc0000a2340, 0xc0000bc2c0, 0x0, 0x0)
/home/rik/dev/neo-go/pkg/core/blockchain.go:407 +0x9ca
github.com/nspcc-dev/neo-go/pkg/consensus.(*service).processBlock(0xc000152160, 0x1122320, 0xc0000bc2c0)
/home/rik/dev/neo-go/pkg/consensus/consensus.go:440 +0xbf
github.com/nspcc-dev/dbft.(*DBFT).checkCommit(0xc0000d3400)
/home/rik/go/pkg/mod/github.com/nspcc-dev/dbft@v0.0.0-20200911152629-be965ee4d449/check.go:71 +0x918
github.com/nspcc-dev/dbft.(*DBFT).checkPrepare(0xc0000d3400)
/home/rik/go/pkg/mod/github.com/nspcc-dev/dbft@v0.0.0-20200911152629-be965ee4d449/check.go:36 +0x465
github.com/nspcc-dev/dbft.(*DBFT).sendPrepareRequest(0xc0000d3400)
/home/rik/go/pkg/mod/github.com/nspcc-dev/dbft@v0.0.0-20200911152629-be965ee4d449/send.go:42 +0x2f8
github.com/nspcc-dev/dbft.(*DBFT).start(0xc0000d3400)
/home/rik/go/pkg/mod/github.com/nspcc-dev/dbft@v0.0.0-20200911152629-be965ee4d449/dbft.go:269 +0x26f
github.com/nspcc-dev/dbft.(*DBFT).Start(0xc0000d3400)
/home/rik/go/pkg/mod/github.com/nspcc-dev/dbft@v0.0.0-20200911152629-be965ee4d449/dbft.go:82 +0x59
github.com/nspcc-dev/neo-go/pkg/consensus.(*service).Start(0xc000152160)
/home/rik/dev/neo-go/pkg/consensus/consensus.go:191 +0x56
github.com/nspcc-dev/neo-go/pkg/network.(*Server).tryStartConsensus(0xc000235040)
/home/rik/dev/neo-go/pkg/network/server.go:311 +0xda
github.com/nspcc-dev/neo-go/pkg/network.(*Server).Start(0xc000235040, 0xc0000faba0)
/home/rik/dev/neo-go/pkg/network/server.go:173 +0x202
created by github.com/nspcc-dev/neo-go/cli.newTestChain
/home/rik/dev/neo-go/cli/executor_test.go:77 +0x47d
FAIL github.com/nspcc-dev/neo-go/cli 14.479s
2020-09-19 18:49:05 +00:00
|
|
|
close(s.finished)
|
2020-09-17 15:27:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *service) handleChainBlock(b *coreb.Block) {
|
|
|
|
// We can get our own block here, so check for index.
|
|
|
|
if b.Index >= s.dbft.BlockIndex {
|
|
|
|
s.log.Debug("new block in the chain",
|
|
|
|
zap.Uint32("dbft index", s.dbft.BlockIndex),
|
|
|
|
zap.Uint32("chain index", s.Chain.BlockHeight()))
|
2021-02-04 15:54:01 +00:00
|
|
|
s.postBlock(b)
|
2022-10-19 15:43:23 +00:00
|
|
|
s.dbft.InitializeConsensus(0, b.Timestamp*nsInMs)
|
2019-11-08 15:40:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-16 08:57:49 +00:00
|
|
|
func (s *service) validatePayload(p *Payload) bool {
|
|
|
|
validators := s.getValidators()
|
2021-01-14 11:17:00 +00:00
|
|
|
if int(p.message.ValidatorIndex) >= len(validators) {
|
2019-12-16 08:57:49 +00:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2021-01-14 11:17:00 +00:00
|
|
|
pub := validators[p.message.ValidatorIndex]
|
2020-02-19 09:10:36 +00:00
|
|
|
h := pub.(*publicKey).GetScriptHash()
|
2021-01-14 11:17:00 +00:00
|
|
|
return p.Sender == h
|
2019-12-16 08:57:49 +00:00
|
|
|
}
|
|
|
|
|
2020-01-15 14:05:47 +00:00
|
|
|
func (s *service) getKeyPair(pubs []crypto.PublicKey) (int, crypto.PrivateKey, crypto.PublicKey) {
|
2022-12-05 15:11:18 +00:00
|
|
|
if s.wallet != nil {
|
|
|
|
for i := range pubs {
|
|
|
|
sh := pubs[i].(*publicKey).GetScriptHash()
|
|
|
|
acc := s.wallet.GetAccount(sh)
|
|
|
|
if acc == nil {
|
|
|
|
continue
|
|
|
|
}
|
2020-01-15 14:05:47 +00:00
|
|
|
|
2022-12-05 15:11:18 +00:00
|
|
|
if !acc.CanSign() {
|
|
|
|
err := acc.Decrypt(s.Config.Wallet.Password, s.wallet.Scrypt)
|
|
|
|
if err != nil {
|
|
|
|
s.log.Fatal("can't unlock account", zap.String("address", address.Uint160ToString(sh)))
|
|
|
|
break
|
|
|
|
}
|
2020-08-19 13:55:01 +00:00
|
|
|
}
|
2020-01-15 14:05:47 +00:00
|
|
|
|
2022-12-05 15:11:18 +00:00
|
|
|
return i, &privateKey{PrivateKey: acc.PrivateKey()}, &publicKey{PublicKey: acc.PublicKey()}
|
|
|
|
}
|
2019-11-15 10:32:40 +00:00
|
|
|
}
|
2020-01-15 14:05:47 +00:00
|
|
|
return -1, nil, nil
|
2019-11-15 10:32:40 +00:00
|
|
|
}
|
|
|
|
|
2021-01-14 13:38:40 +00:00
|
|
|
func (s *service) payloadFromExtensible(ep *npayload.Extensible) *Payload {
|
|
|
|
return &Payload{
|
|
|
|
Extensible: *ep,
|
|
|
|
message: message{
|
2021-03-15 09:25:52 +00:00
|
|
|
stateRootEnabled: s.ProtocolConfiguration.StateRootInHeader,
|
2021-01-14 13:38:40 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-08 15:40:21 +00:00
|
|
|
// OnPayload handles Payload receive.
|
2022-01-12 18:09:37 +00:00
|
|
|
func (s *service) OnPayload(cp *npayload.Extensible) error {
|
2020-04-15 15:56:45 +00:00
|
|
|
log := s.log.With(zap.Stringer("hash", cp.Hash()))
|
2021-01-14 13:38:40 +00:00
|
|
|
p := s.payloadFromExtensible(cp)
|
2021-05-12 17:14:52 +00:00
|
|
|
// decode payload data into message
|
|
|
|
if err := p.decodeData(); err != nil {
|
|
|
|
log.Info("can't decode payload data", zap.Error(err))
|
2022-01-12 18:09:37 +00:00
|
|
|
return nil
|
2021-05-12 17:14:52 +00:00
|
|
|
}
|
|
|
|
|
2021-01-14 13:38:40 +00:00
|
|
|
if !s.validatePayload(p) {
|
2021-01-14 11:17:00 +00:00
|
|
|
log.Info("can't validate payload")
|
2022-01-12 18:09:37 +00:00
|
|
|
return nil
|
2019-11-15 10:32:40 +00:00
|
|
|
}
|
|
|
|
|
consensus: prevent synchronization stalls
When CN is not up to date with the network is synchonizes blocks first and
only then starts consensus process. But while synchronizing it receives
consensus payloads and tries to process them even though messages reader
routine is not started yet. This leads to lots of goroutines waiting to send
their messages:
Jun 25 23:55:53 nodoka neo-go[32733]: goroutine 1639919 [chan send, 4 minutes]:
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/consensus.(*service).OnPayload(0xc0000ecb40, 0xc005bd7680)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/consensus/consensus.go:329 +0x31b
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleConsensusCmd(...)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:687
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleMessage(0xc0000ba160, 0x1053260, 0xc00507d170, 0xc005bdd560, 0x0, 0x0)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:806 +0xd58
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*TCPPeer).handleConn(0xc00507d170)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_peer.go:160 +0x294
Jun 25 23:55:53 nodoka neo-go[32733]: created by github.com/nspcc-dev/neo-go/pkg/network.(*TCPTransport).Dial
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_transport.go:38 +0x1ad
Jun 25 23:55:53 nodoka neo-go[32733]: goroutine 1639181 [chan send, 10 minutes]:
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/consensus.(*service).OnPayload(0xc0000ecb40, 0xc013bb6600)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/consensus/consensus.go:329 +0x31b
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleConsensusCmd(...)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:687
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleMessage(0xc0000ba160, 0x1053260, 0xc01361ee10, 0xc01342c780, 0x0, 0x0)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:806 +0xd58
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*TCPPeer).handleConn(0xc01361ee10)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_peer.go:160 +0x294
Jun 25 23:55:53 nodoka neo-go[32733]: created by github.com/nspcc-dev/neo-go/pkg/network.(*TCPTransport).Dial
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_transport.go:38 +0x1ad
Jun 25 23:55:53 nodoka neo-go[32733]: goroutine 39454 [chan send, 32 minutes]:
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/consensus.(*service).OnPayload(0xc0000ecb40, 0xc014fea680)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/consensus/consensus.go:329 +0x31b
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleConsensusCmd(...)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:687
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleMessage(0xc0000ba160, 0x1053260, 0xc0140b2ea0, 0xc014fe0ed0, 0x0, 0x0)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:806 +0xd58
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*TCPPeer).handleConn(0xc0140b2ea0)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_peer.go:160 +0x294
Jun 25 23:55:53 nodoka neo-go[32733]: created by github.com/nspcc-dev/neo-go/pkg/network.(*TCPTransport).Dial
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_transport.go:38 +0x1ad
Luckily it doesn't break synchronization completely as eventually connection
timers fire, the node breaks all connections, create new ones and these new
ones request blocks successfully until another consensus payload stalls them
too. In the end the node reaches synchronization, message processing loop
starts and releases all of these waiting goroutines, but it's better for us to
avoid this happening at all.
This also makes double-starting a no-op which is a nice property.
2020-06-26 08:19:01 +00:00
|
|
|
if s.dbft == nil || !s.started.Load() {
|
|
|
|
log.Debug("dbft is inactive or not started yet")
|
2022-01-12 18:09:37 +00:00
|
|
|
return nil
|
2019-11-15 10:32:40 +00:00
|
|
|
}
|
|
|
|
|
2021-01-14 13:38:40 +00:00
|
|
|
s.messages <- *p
|
2022-01-12 18:09:37 +00:00
|
|
|
return nil
|
2019-11-15 10:32:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *service) OnTransaction(tx *transaction.Transaction) {
|
2022-06-28 15:39:47 +00:00
|
|
|
if s.dbft != nil && s.started.Load() {
|
2019-11-15 10:32:40 +00:00
|
|
|
s.transactions <- tx
|
|
|
|
}
|
2019-11-08 15:40:21 +00:00
|
|
|
}
|
|
|
|
|
2019-11-15 10:32:40 +00:00
|
|
|
func (s *service) broadcast(p payload.ConsensusPayload) {
|
2019-12-05 09:07:09 +00:00
|
|
|
if err := p.(*Payload).Sign(s.dbft.Priv.(*privateKey)); err != nil {
|
2020-01-09 14:46:08 +00:00
|
|
|
s.log.Warn("can't sign consensus payload", zap.Error(err))
|
2019-12-05 09:07:09 +00:00
|
|
|
}
|
|
|
|
|
2021-01-14 13:38:40 +00:00
|
|
|
ep := &p.(*Payload).Extensible
|
|
|
|
s.Config.Broadcast(ep)
|
2019-11-15 10:32:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *service) getTx(h util.Uint256) block.Transaction {
|
|
|
|
if tx := s.txx.Get(h); tx != nil {
|
|
|
|
return tx.(*transaction.Transaction)
|
|
|
|
}
|
|
|
|
|
|
|
|
tx, _, _ := s.Config.Chain.GetTransaction(h)
|
|
|
|
|
2019-12-27 10:52:07 +00:00
|
|
|
// this is needed because in case of absent tx dBFT expects to
|
|
|
|
// get nil interface, not a nil pointer to any concrete type
|
|
|
|
if tx != nil {
|
|
|
|
return tx
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
2019-11-15 10:32:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *service) verifyBlock(b block.Block) bool {
|
|
|
|
coreb := &b.(*neoBlock).Block
|
2020-08-03 16:27:32 +00:00
|
|
|
|
2020-08-19 16:38:50 +00:00
|
|
|
if s.Chain.BlockHeight() >= coreb.Index {
|
|
|
|
s.log.Warn("proposed block has already outdated")
|
|
|
|
return false
|
|
|
|
}
|
2020-12-16 10:24:49 +00:00
|
|
|
if s.lastTimestamp >= coreb.Timestamp {
|
|
|
|
s.log.Warn("proposed block has small timestamp",
|
|
|
|
zap.Uint64("ts", coreb.Timestamp),
|
|
|
|
zap.Uint64("last", s.lastTimestamp))
|
|
|
|
return false
|
|
|
|
}
|
2020-08-03 16:27:32 +00:00
|
|
|
|
2021-03-15 10:00:04 +00:00
|
|
|
size := coreb.GetExpectedBlockSize()
|
|
|
|
if size > int(s.ProtocolConfiguration.MaxBlockSize) {
|
|
|
|
s.log.Warn("proposed block size exceeds config MaxBlockSize",
|
|
|
|
zap.Uint32("max size allowed", s.ProtocolConfiguration.MaxBlockSize),
|
|
|
|
zap.Int("block size", size))
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2021-03-15 10:51:07 +00:00
|
|
|
var fee int64
|
2023-04-13 11:03:02 +00:00
|
|
|
var pool = mempool.New(len(coreb.Transactions), 0, false, nil)
|
2020-08-19 16:27:15 +00:00
|
|
|
var mainPool = s.Chain.GetMemPool()
|
2019-11-15 10:32:40 +00:00
|
|
|
for _, tx := range coreb.Transactions {
|
2020-08-19 16:27:15 +00:00
|
|
|
var err error
|
|
|
|
|
2021-03-15 10:51:07 +00:00
|
|
|
fee += tx.SystemFee
|
2020-08-19 16:27:15 +00:00
|
|
|
if mainPool.ContainsKey(tx.Hash()) {
|
|
|
|
err = pool.Add(tx, s.Chain)
|
|
|
|
if err == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
err = s.Chain.PoolTx(tx, pool)
|
|
|
|
}
|
|
|
|
if err != nil {
|
2020-04-20 07:57:09 +00:00
|
|
|
s.log.Warn("invalid transaction in proposed block",
|
|
|
|
zap.Stringer("hash", tx.Hash()),
|
|
|
|
zap.Error(err))
|
2019-11-15 10:32:40 +00:00
|
|
|
return false
|
|
|
|
}
|
2020-08-19 16:38:50 +00:00
|
|
|
if s.Chain.BlockHeight() >= coreb.Index {
|
|
|
|
s.log.Warn("proposed block has already outdated")
|
|
|
|
return false
|
|
|
|
}
|
2019-11-15 10:32:40 +00:00
|
|
|
}
|
|
|
|
|
2021-03-15 10:51:07 +00:00
|
|
|
maxBlockSysFee := s.ProtocolConfiguration.MaxBlockSystemFee
|
|
|
|
if fee > maxBlockSysFee {
|
|
|
|
s.log.Warn("proposed block system fee exceeds config MaxBlockSystemFee",
|
|
|
|
zap.Int("max system fee allowed", int(maxBlockSysFee)),
|
|
|
|
zap.Int("block system fee", int(fee)))
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2019-11-15 10:32:40 +00:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2021-01-14 11:17:00 +00:00
|
|
|
var (
|
2021-03-09 18:30:09 +00:00
|
|
|
errInvalidPrevHash = errors.New("invalid PrevHash")
|
|
|
|
errInvalidVersion = errors.New("invalid Version")
|
|
|
|
errInvalidStateRoot = errors.New("state root mismatch")
|
|
|
|
errInvalidTransactionsCount = errors.New("invalid transactions count")
|
2021-01-14 11:17:00 +00:00
|
|
|
)
|
|
|
|
|
consensus: prevent synchronization stalls
When CN is not up to date with the network is synchonizes blocks first and
only then starts consensus process. But while synchronizing it receives
consensus payloads and tries to process them even though messages reader
routine is not started yet. This leads to lots of goroutines waiting to send
their messages:
Jun 25 23:55:53 nodoka neo-go[32733]: goroutine 1639919 [chan send, 4 minutes]:
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/consensus.(*service).OnPayload(0xc0000ecb40, 0xc005bd7680)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/consensus/consensus.go:329 +0x31b
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleConsensusCmd(...)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:687
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleMessage(0xc0000ba160, 0x1053260, 0xc00507d170, 0xc005bdd560, 0x0, 0x0)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:806 +0xd58
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*TCPPeer).handleConn(0xc00507d170)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_peer.go:160 +0x294
Jun 25 23:55:53 nodoka neo-go[32733]: created by github.com/nspcc-dev/neo-go/pkg/network.(*TCPTransport).Dial
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_transport.go:38 +0x1ad
Jun 25 23:55:53 nodoka neo-go[32733]: goroutine 1639181 [chan send, 10 minutes]:
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/consensus.(*service).OnPayload(0xc0000ecb40, 0xc013bb6600)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/consensus/consensus.go:329 +0x31b
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleConsensusCmd(...)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:687
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleMessage(0xc0000ba160, 0x1053260, 0xc01361ee10, 0xc01342c780, 0x0, 0x0)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:806 +0xd58
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*TCPPeer).handleConn(0xc01361ee10)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_peer.go:160 +0x294
Jun 25 23:55:53 nodoka neo-go[32733]: created by github.com/nspcc-dev/neo-go/pkg/network.(*TCPTransport).Dial
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_transport.go:38 +0x1ad
Jun 25 23:55:53 nodoka neo-go[32733]: goroutine 39454 [chan send, 32 minutes]:
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/consensus.(*service).OnPayload(0xc0000ecb40, 0xc014fea680)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/consensus/consensus.go:329 +0x31b
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleConsensusCmd(...)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:687
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleMessage(0xc0000ba160, 0x1053260, 0xc0140b2ea0, 0xc014fe0ed0, 0x0, 0x0)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:806 +0xd58
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*TCPPeer).handleConn(0xc0140b2ea0)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_peer.go:160 +0x294
Jun 25 23:55:53 nodoka neo-go[32733]: created by github.com/nspcc-dev/neo-go/pkg/network.(*TCPTransport).Dial
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_transport.go:38 +0x1ad
Luckily it doesn't break synchronization completely as eventually connection
timers fire, the node breaks all connections, create new ones and these new
ones request blocks successfully until another consensus payload stalls them
too. In the end the node reaches synchronization, message processing loop
starts and releases all of these waiting goroutines, but it's better for us to
avoid this happening at all.
This also makes double-starting a no-op which is a nice property.
2020-06-26 08:19:01 +00:00
|
|
|
func (s *service) verifyRequest(p payload.ConsensusPayload) error {
|
|
|
|
req := p.GetPrepareRequest().(*prepareRequest)
|
2021-01-14 11:17:00 +00:00
|
|
|
if req.prevHash != s.dbft.PrevHash {
|
|
|
|
return errInvalidPrevHash
|
|
|
|
}
|
|
|
|
if req.version != s.dbft.Version {
|
|
|
|
return errInvalidVersion
|
|
|
|
}
|
2021-03-15 09:25:52 +00:00
|
|
|
if s.ProtocolConfiguration.StateRootInHeader {
|
2022-07-22 20:14:02 +00:00
|
|
|
sr, err := s.Chain.GetStateRoot(s.dbft.BlockIndex - 1)
|
2020-11-17 12:57:50 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
} else if sr.Root != req.stateRoot {
|
2021-01-14 11:17:00 +00:00
|
|
|
return fmt.Errorf("%w: %s != %s", errInvalidStateRoot, sr.Root, req.stateRoot)
|
2020-11-17 12:57:50 +00:00
|
|
|
}
|
|
|
|
}
|
2021-03-09 18:30:09 +00:00
|
|
|
if len(req.TransactionHashes()) > int(s.ProtocolConfiguration.MaxTransactionsPerBlock) {
|
|
|
|
return fmt.Errorf("%w: max = %d, got %d", errInvalidTransactionsCount, s.ProtocolConfiguration.MaxTransactionsPerBlock, len(req.TransactionHashes()))
|
|
|
|
}
|
consensus: prevent synchronization stalls
When CN is not up to date with the network is synchonizes blocks first and
only then starts consensus process. But while synchronizing it receives
consensus payloads and tries to process them even though messages reader
routine is not started yet. This leads to lots of goroutines waiting to send
their messages:
Jun 25 23:55:53 nodoka neo-go[32733]: goroutine 1639919 [chan send, 4 minutes]:
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/consensus.(*service).OnPayload(0xc0000ecb40, 0xc005bd7680)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/consensus/consensus.go:329 +0x31b
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleConsensusCmd(...)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:687
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleMessage(0xc0000ba160, 0x1053260, 0xc00507d170, 0xc005bdd560, 0x0, 0x0)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:806 +0xd58
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*TCPPeer).handleConn(0xc00507d170)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_peer.go:160 +0x294
Jun 25 23:55:53 nodoka neo-go[32733]: created by github.com/nspcc-dev/neo-go/pkg/network.(*TCPTransport).Dial
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_transport.go:38 +0x1ad
Jun 25 23:55:53 nodoka neo-go[32733]: goroutine 1639181 [chan send, 10 minutes]:
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/consensus.(*service).OnPayload(0xc0000ecb40, 0xc013bb6600)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/consensus/consensus.go:329 +0x31b
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleConsensusCmd(...)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:687
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleMessage(0xc0000ba160, 0x1053260, 0xc01361ee10, 0xc01342c780, 0x0, 0x0)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:806 +0xd58
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*TCPPeer).handleConn(0xc01361ee10)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_peer.go:160 +0x294
Jun 25 23:55:53 nodoka neo-go[32733]: created by github.com/nspcc-dev/neo-go/pkg/network.(*TCPTransport).Dial
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_transport.go:38 +0x1ad
Jun 25 23:55:53 nodoka neo-go[32733]: goroutine 39454 [chan send, 32 minutes]:
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/consensus.(*service).OnPayload(0xc0000ecb40, 0xc014fea680)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/consensus/consensus.go:329 +0x31b
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleConsensusCmd(...)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:687
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleMessage(0xc0000ba160, 0x1053260, 0xc0140b2ea0, 0xc014fe0ed0, 0x0, 0x0)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:806 +0xd58
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*TCPPeer).handleConn(0xc0140b2ea0)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_peer.go:160 +0x294
Jun 25 23:55:53 nodoka neo-go[32733]: created by github.com/nspcc-dev/neo-go/pkg/network.(*TCPTransport).Dial
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_transport.go:38 +0x1ad
Luckily it doesn't break synchronization completely as eventually connection
timers fire, the node breaks all connections, create new ones and these new
ones request blocks successfully until another consensus payload stalls them
too. In the end the node reaches synchronization, message processing loop
starts and releases all of these waiting goroutines, but it's better for us to
avoid this happening at all.
This also makes double-starting a no-op which is a nice property.
2020-06-26 08:19:01 +00:00
|
|
|
// Save lastProposal for getVerified().
|
|
|
|
s.lastProposal = req.transactionHashes
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-11-15 10:32:40 +00:00
|
|
|
func (s *service) processBlock(b block.Block) {
|
|
|
|
bb := &b.(*neoBlock).Block
|
2019-12-09 14:14:10 +00:00
|
|
|
bb.Script = *(s.getBlockWitness(bb))
|
2019-11-15 10:32:40 +00:00
|
|
|
|
2023-03-07 09:06:53 +00:00
|
|
|
if err := s.BlockQueue.PutBlock(bb); err != nil {
|
2020-02-04 16:32:29 +00:00
|
|
|
// The block might already be added via the regular network
|
|
|
|
// interaction.
|
|
|
|
if _, errget := s.Chain.GetBlock(bb.Hash()); errget != nil {
|
2023-03-07 09:06:53 +00:00
|
|
|
s.log.Warn("error on enqueue block", zap.Error(err))
|
2020-02-04 16:32:29 +00:00
|
|
|
}
|
2019-11-15 10:32:40 +00:00
|
|
|
}
|
2021-02-04 15:54:01 +00:00
|
|
|
s.postBlock(bb)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *service) postBlock(b *coreb.Block) {
|
|
|
|
if s.lastTimestamp < b.Timestamp {
|
|
|
|
s.lastTimestamp = b.Timestamp
|
2020-12-16 10:24:49 +00:00
|
|
|
}
|
2021-02-04 15:54:01 +00:00
|
|
|
s.lastProposal = nil
|
2019-11-15 10:32:40 +00:00
|
|
|
}
|
|
|
|
|
2020-01-14 12:32:07 +00:00
|
|
|
func (s *service) getBlockWitness(b *coreb.Block) *transaction.Witness {
|
2019-11-15 10:32:40 +00:00
|
|
|
dctx := s.dbft.Context
|
|
|
|
pubs := convertKeys(dctx.Validators)
|
|
|
|
sigs := make(map[*keys.PublicKey][]byte)
|
|
|
|
|
|
|
|
for i := range pubs {
|
|
|
|
if p := dctx.CommitPayloads[i]; p != nil && p.ViewNumber() == dctx.ViewNumber {
|
|
|
|
sigs[pubs[i]] = p.GetCommit().Signature()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m := s.dbft.Context.M()
|
|
|
|
verif, err := smartcontract.CreateMultiSigRedeemScript(m, pubs)
|
|
|
|
if err != nil {
|
2020-01-09 14:46:08 +00:00
|
|
|
s.log.Warn("can't create multisig redeem script", zap.Error(err))
|
2019-11-15 10:32:40 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
sort.Sort(keys.PublicKeys(pubs))
|
|
|
|
|
2020-04-21 13:45:48 +00:00
|
|
|
buf := io.NewBufBinWriter()
|
2019-11-15 10:32:40 +00:00
|
|
|
for i, j := 0, 0; i < len(pubs) && j < m; i++ {
|
|
|
|
if sig, ok := sigs[pubs[i]]; ok {
|
2020-04-21 13:45:48 +00:00
|
|
|
emit.Bytes(buf.BinWriter, sig)
|
2019-11-15 10:32:40 +00:00
|
|
|
j++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return &transaction.Witness{
|
2020-04-21 13:45:48 +00:00
|
|
|
InvocationScript: buf.Bytes(),
|
2019-11-15 10:32:40 +00:00
|
|
|
VerificationScript: verif,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *service) getBlock(h util.Uint256) block.Block {
|
|
|
|
b, err := s.Chain.GetBlock(h)
|
|
|
|
if err != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-03-25 18:46:52 +00:00
|
|
|
return &neoBlock{network: s.ProtocolConfiguration.Magic, Block: *b}
|
2019-11-15 10:32:40 +00:00
|
|
|
}
|
|
|
|
|
2020-06-16 11:59:09 +00:00
|
|
|
func (s *service) getVerifiedTx() []block.Transaction {
|
2019-11-15 10:32:40 +00:00
|
|
|
pool := s.Config.Chain.GetMemPool()
|
2020-01-14 11:34:09 +00:00
|
|
|
|
2020-06-05 16:01:10 +00:00
|
|
|
var txx []*transaction.Transaction
|
2020-01-14 11:34:09 +00:00
|
|
|
|
2021-02-04 15:48:09 +00:00
|
|
|
if s.dbft.ViewNumber > 0 && len(s.lastProposal) > 0 {
|
2020-06-05 16:01:10 +00:00
|
|
|
txx = make([]*transaction.Transaction, 0, len(s.lastProposal))
|
2020-01-14 11:34:09 +00:00
|
|
|
for i := range s.lastProposal {
|
2020-06-05 16:01:10 +00:00
|
|
|
if tx, ok := pool.TryGetValue(s.lastProposal[i]); ok {
|
|
|
|
txx = append(txx, tx)
|
2020-01-14 11:34:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(txx) < len(s.lastProposal)/2 {
|
|
|
|
txx = pool.GetVerifiedTransactions()
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
txx = pool.GetVerifiedTransactions()
|
|
|
|
}
|
2019-11-15 10:32:40 +00:00
|
|
|
|
2020-02-18 17:16:38 +00:00
|
|
|
if len(txx) > 0 {
|
|
|
|
txx = s.Config.Chain.ApplyPolicyToTxSet(txx)
|
|
|
|
}
|
|
|
|
|
2020-04-22 17:42:38 +00:00
|
|
|
res := make([]block.Transaction, len(txx))
|
2019-11-29 12:40:21 +00:00
|
|
|
for i := range txx {
|
2020-06-05 16:01:10 +00:00
|
|
|
res[i] = txx[i]
|
2020-02-14 16:21:24 +00:00
|
|
|
}
|
2019-11-15 10:32:40 +00:00
|
|
|
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
|
2020-07-11 10:10:57 +00:00
|
|
|
func (s *service) getValidators(txes ...block.Transaction) []crypto.PublicKey {
|
2020-02-28 08:10:01 +00:00
|
|
|
var (
|
|
|
|
pKeys []*keys.PublicKey
|
|
|
|
err error
|
|
|
|
)
|
2020-07-11 10:10:57 +00:00
|
|
|
if txes == nil {
|
2023-09-01 12:10:42 +00:00
|
|
|
// getValidators with empty args is used by dbft to fill the list of
|
|
|
|
// block's validators, thus should return validators from the current
|
|
|
|
// epoch without recalculation.
|
2020-07-11 10:10:57 +00:00
|
|
|
pKeys, err = s.Chain.GetNextBlockValidators()
|
|
|
|
}
|
2023-12-21 15:03:22 +00:00
|
|
|
// getValidators with non-empty args is used by dbft to fill block's
|
|
|
|
// NextConsensus field, but NeoGo doesn't provide WithGetConsensusAddress
|
|
|
|
// callback and fills NextConsensus by itself via WithNewBlockFromContext
|
|
|
|
// callback. Thus, leave pKeys empty if txes != nil.
|
|
|
|
|
2020-02-28 08:10:01 +00:00
|
|
|
if err != nil {
|
|
|
|
s.log.Error("error while trying to get validators", zap.Error(err))
|
2019-11-15 10:32:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pubs := make([]crypto.PublicKey, len(pKeys))
|
|
|
|
for i := range pKeys {
|
|
|
|
pubs[i] = &publicKey{PublicKey: pKeys[i]}
|
|
|
|
}
|
|
|
|
|
|
|
|
return pubs
|
|
|
|
}
|
|
|
|
|
2020-07-11 09:32:53 +00:00
|
|
|
func (s *service) getConsensusAddress(validators ...crypto.PublicKey) util.Uint160 {
|
|
|
|
return util.Uint160{}
|
2019-11-15 10:32:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func convertKeys(validators []crypto.PublicKey) (pubs []*keys.PublicKey) {
|
|
|
|
pubs = make([]*keys.PublicKey, len(validators))
|
|
|
|
for i, k := range validators {
|
|
|
|
pubs[i] = k.(*publicKey).PublicKey
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
2019-11-08 15:40:21 +00:00
|
|
|
}
|
2020-04-23 08:52:31 +00:00
|
|
|
|
2020-06-18 09:00:51 +00:00
|
|
|
func (s *service) newBlockFromContext(ctx *dbft.Context) block.Block {
|
2021-03-25 18:46:52 +00:00
|
|
|
block := &neoBlock{network: s.ProtocolConfiguration.Magic}
|
2020-04-23 08:52:31 +00:00
|
|
|
|
2020-07-11 12:22:14 +00:00
|
|
|
block.Block.Timestamp = ctx.Timestamp / nsInMs
|
2021-07-21 15:39:57 +00:00
|
|
|
block.Block.Nonce = ctx.Nonce
|
2020-04-23 08:52:31 +00:00
|
|
|
block.Block.Index = ctx.BlockIndex
|
2021-03-15 09:25:52 +00:00
|
|
|
if s.ProtocolConfiguration.StateRootInHeader {
|
2022-07-22 20:14:02 +00:00
|
|
|
sr, err := s.Chain.GetStateRoot(ctx.BlockIndex - 1)
|
2020-11-17 12:57:50 +00:00
|
|
|
if err != nil {
|
consensus: fix panic during verifyBlock
Issue: panic during mixed 4-nodes consensus setup:
```
2021-03-18T12:01:50.715Z INFO skip change view {"nc": 0, "nf": 3}
2021-03-18T12:01:52.786Z INFO received ChangeView {"validator": 0, "reason": "Timeout", "new view": 1}
2021-03-18T12:01:53.602Z INFO received ChangeView {"validator": 2, "reason": "Timeout", "new view": 1}
2021-03-18T12:01:56.736Z INFO received ChangeView {"validator": 1, "reason": "Timeout", "new view": 1}
2021-03-18T12:01:56.736Z INFO changing dbft view {"height": 3, "view": 1, "index": 3, "role": "Backup"}
2021-03-18T12:02:01.758Z INFO received PrepareRequest {"validator": 2, "tx": 0}
panic: interface conversion: block.Block is nil, not *consensus.neoBlock
goroutine 315 [running]:
github.com/nspcc-dev/neo-go/pkg/consensus.(*service).verifyBlock(0xc000419540, 0x0, 0x0, 0x4)
github.com/nspcc-dev/neo-go/pkg/consensus/consensus.go:427 +0x1306
github.com/nspcc-dev/dbft.(*DBFT).createAndCheckBlock(0xc0001f8840, 0x13f0002)
github.com/nspcc-dev/dbft@v0.0.0-20210302103605-cc75991b7cfb/dbft.go:373 +0x27e
github.com/nspcc-dev/dbft.(*DBFT).onPrepareRequest(0xc0001f8840, 0x13f4378, 0xc0003b8500)
github.com/nspcc-dev/dbft@v0.0.0-20210302103605-cc75991b7cfb/dbft.go:329 +0xdf1
github.com/nspcc-dev/dbft.(*DBFT).OnReceive(0xc0001f8840, 0x13f4378, 0xc0003b8500)
github.com/nspcc-dev/dbft@v0.0.0-20210302103605-cc75991b7cfb/dbft.go:247 +0xe25
github.com/nspcc-dev/neo-go/pkg/consensus.(*service).eventLoop(0xc000419540)
github.com/nspcc-dev/neo-go/pkg/consensus/consensus.go:297 +0x79d
created by github.com/nspcc-dev/neo-go/pkg/consensus.(*service).Start
github.com/nspcc-dev/neo-go/pkg/consensus/consensus.go:249 +0xa5
```
So (*service).verifyBlock is unable to work with nil block.
2021-03-18 15:05:24 +00:00
|
|
|
s.log.Fatal(fmt.Sprintf("failed to get state root: %s", err.Error()))
|
2020-11-17 12:57:50 +00:00
|
|
|
}
|
|
|
|
block.StateRootEnabled = true
|
|
|
|
block.PrevStateRoot = sr.Root
|
|
|
|
}
|
2020-07-11 09:32:53 +00:00
|
|
|
|
2023-12-21 15:03:22 +00:00
|
|
|
// ComputeNextBlockValidators returns proper set of validators wrt dBFT epochs
|
|
|
|
// boundary. I.e. for the last block in the dBFT epoch this method returns the
|
|
|
|
// list of validators recalculated from the latest relevant information about
|
|
|
|
// NEO votes; in this case list of validators may differ from the one returned
|
|
|
|
// by GetNextBlockValidators. For the not-last block of dBFT epoch this method
|
|
|
|
// returns the same list as GetNextBlockValidators. Note, that by this moment
|
|
|
|
// we must be sure that previous block was successfully persisted to chain
|
|
|
|
// (i.e. PostPersist was completed for native Neo contract and PostPersist
|
|
|
|
// execution cache was persisted to s.Chain's DAO), otherwise the wrong
|
|
|
|
// (outdated, relevant for the previous dBFT epoch) value will be returned.
|
2023-09-01 15:15:36 +00:00
|
|
|
var validators = s.Chain.ComputeNextBlockValidators()
|
2022-01-21 02:33:06 +00:00
|
|
|
script, err := smartcontract.CreateDefaultMultiSigRedeemScript(validators)
|
2020-07-11 09:32:53 +00:00
|
|
|
if err != nil {
|
consensus: fix panic during verifyBlock
Issue: panic during mixed 4-nodes consensus setup:
```
2021-03-18T12:01:50.715Z INFO skip change view {"nc": 0, "nf": 3}
2021-03-18T12:01:52.786Z INFO received ChangeView {"validator": 0, "reason": "Timeout", "new view": 1}
2021-03-18T12:01:53.602Z INFO received ChangeView {"validator": 2, "reason": "Timeout", "new view": 1}
2021-03-18T12:01:56.736Z INFO received ChangeView {"validator": 1, "reason": "Timeout", "new view": 1}
2021-03-18T12:01:56.736Z INFO changing dbft view {"height": 3, "view": 1, "index": 3, "role": "Backup"}
2021-03-18T12:02:01.758Z INFO received PrepareRequest {"validator": 2, "tx": 0}
panic: interface conversion: block.Block is nil, not *consensus.neoBlock
goroutine 315 [running]:
github.com/nspcc-dev/neo-go/pkg/consensus.(*service).verifyBlock(0xc000419540, 0x0, 0x0, 0x4)
github.com/nspcc-dev/neo-go/pkg/consensus/consensus.go:427 +0x1306
github.com/nspcc-dev/dbft.(*DBFT).createAndCheckBlock(0xc0001f8840, 0x13f0002)
github.com/nspcc-dev/dbft@v0.0.0-20210302103605-cc75991b7cfb/dbft.go:373 +0x27e
github.com/nspcc-dev/dbft.(*DBFT).onPrepareRequest(0xc0001f8840, 0x13f4378, 0xc0003b8500)
github.com/nspcc-dev/dbft@v0.0.0-20210302103605-cc75991b7cfb/dbft.go:329 +0xdf1
github.com/nspcc-dev/dbft.(*DBFT).OnReceive(0xc0001f8840, 0x13f4378, 0xc0003b8500)
github.com/nspcc-dev/dbft@v0.0.0-20210302103605-cc75991b7cfb/dbft.go:247 +0xe25
github.com/nspcc-dev/neo-go/pkg/consensus.(*service).eventLoop(0xc000419540)
github.com/nspcc-dev/neo-go/pkg/consensus/consensus.go:297 +0x79d
created by github.com/nspcc-dev/neo-go/pkg/consensus.(*service).Start
github.com/nspcc-dev/neo-go/pkg/consensus/consensus.go:249 +0xa5
```
So (*service).verifyBlock is unable to work with nil block.
2021-03-18 15:05:24 +00:00
|
|
|
s.log.Fatal(fmt.Sprintf("failed to create multisignature script: %s", err.Error()))
|
2020-07-11 09:32:53 +00:00
|
|
|
}
|
|
|
|
block.Block.NextConsensus = crypto.Hash160(script)
|
2020-04-23 08:52:31 +00:00
|
|
|
block.Block.PrevHash = ctx.PrevHash
|
|
|
|
block.Block.Version = ctx.Version
|
|
|
|
|
2021-03-01 12:20:27 +00:00
|
|
|
primaryIndex := byte(ctx.PrimaryIndex)
|
|
|
|
block.Block.PrimaryIndex = primaryIndex
|
2020-04-23 08:52:31 +00:00
|
|
|
|
consensus: fix panic during verifyBlock
Issue: panic during mixed 4-nodes consensus setup:
```
2021-03-18T12:01:50.715Z INFO skip change view {"nc": 0, "nf": 3}
2021-03-18T12:01:52.786Z INFO received ChangeView {"validator": 0, "reason": "Timeout", "new view": 1}
2021-03-18T12:01:53.602Z INFO received ChangeView {"validator": 2, "reason": "Timeout", "new view": 1}
2021-03-18T12:01:56.736Z INFO received ChangeView {"validator": 1, "reason": "Timeout", "new view": 1}
2021-03-18T12:01:56.736Z INFO changing dbft view {"height": 3, "view": 1, "index": 3, "role": "Backup"}
2021-03-18T12:02:01.758Z INFO received PrepareRequest {"validator": 2, "tx": 0}
panic: interface conversion: block.Block is nil, not *consensus.neoBlock
goroutine 315 [running]:
github.com/nspcc-dev/neo-go/pkg/consensus.(*service).verifyBlock(0xc000419540, 0x0, 0x0, 0x4)
github.com/nspcc-dev/neo-go/pkg/consensus/consensus.go:427 +0x1306
github.com/nspcc-dev/dbft.(*DBFT).createAndCheckBlock(0xc0001f8840, 0x13f0002)
github.com/nspcc-dev/dbft@v0.0.0-20210302103605-cc75991b7cfb/dbft.go:373 +0x27e
github.com/nspcc-dev/dbft.(*DBFT).onPrepareRequest(0xc0001f8840, 0x13f4378, 0xc0003b8500)
github.com/nspcc-dev/dbft@v0.0.0-20210302103605-cc75991b7cfb/dbft.go:329 +0xdf1
github.com/nspcc-dev/dbft.(*DBFT).OnReceive(0xc0001f8840, 0x13f4378, 0xc0003b8500)
github.com/nspcc-dev/dbft@v0.0.0-20210302103605-cc75991b7cfb/dbft.go:247 +0xe25
github.com/nspcc-dev/neo-go/pkg/consensus.(*service).eventLoop(0xc000419540)
github.com/nspcc-dev/neo-go/pkg/consensus/consensus.go:297 +0x79d
created by github.com/nspcc-dev/neo-go/pkg/consensus.(*service).Start
github.com/nspcc-dev/neo-go/pkg/consensus/consensus.go:249 +0xa5
```
So (*service).verifyBlock is unable to work with nil block.
2021-03-18 15:05:24 +00:00
|
|
|
// it's OK to have ctx.TransactionsHashes == nil here
|
2021-03-01 12:20:27 +00:00
|
|
|
hashes := make([]util.Uint256, len(ctx.TransactionHashes))
|
|
|
|
copy(hashes, ctx.TransactionHashes)
|
2020-09-15 15:38:15 +00:00
|
|
|
block.Block.MerkleRoot = hash.CalcMerkleRoot(hashes)
|
2020-06-25 08:16:42 +00:00
|
|
|
|
2020-04-23 08:52:31 +00:00
|
|
|
return block
|
|
|
|
}
|