2019-11-08 15:40:21 +00:00
|
|
|
package consensus
|
|
|
|
|
2019-11-15 10:32:40 +00:00
|
|
|
import (
|
|
|
|
"errors"
|
2020-06-10 16:00:19 +00:00
|
|
|
"fmt"
|
2019-11-15 10:32:40 +00:00
|
|
|
"math/rand"
|
|
|
|
"sort"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/nspcc-dev/dbft"
|
|
|
|
"github.com/nspcc-dev/dbft/block"
|
|
|
|
"github.com/nspcc-dev/dbft/crypto"
|
2020-06-01 15:41:31 +00:00
|
|
|
"github.com/nspcc-dev/dbft/merkle"
|
2019-11-15 10:32:40 +00:00
|
|
|
"github.com/nspcc-dev/dbft/payload"
|
2020-03-03 14:21:42 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core"
|
|
|
|
coreb "github.com/nspcc-dev/neo-go/pkg/core/block"
|
2020-06-05 09:11:22 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/cache"
|
2020-03-03 14:21:42 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/mempool"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/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
|
|
|
"go.uber.org/atomic"
|
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
|
|
|
|
|
2019-11-15 10:32:40 +00:00
|
|
|
// defaultTimePerBlock is a period between blocks which is used in NEO.
|
|
|
|
const defaultTimePerBlock = 15 * time.Second
|
|
|
|
|
2019-11-08 15:40:21 +00:00
|
|
|
// Service represents consensus instance.
|
|
|
|
type Service interface {
|
2019-11-15 10:32:40 +00:00
|
|
|
// Start initializes dBFT and starts event loop for consensus service.
|
|
|
|
// It must be called only when sufficient amount of peers are connected.
|
|
|
|
Start()
|
|
|
|
|
|
|
|
// OnPayload is a callback to notify Service about new received payload.
|
2019-11-08 15:40:21 +00:00
|
|
|
OnPayload(p *Payload)
|
2019-11-15 10:32:40 +00:00
|
|
|
// OnTransaction is a callback to notify Service about new received transaction.
|
|
|
|
OnTransaction(tx *transaction.Transaction)
|
|
|
|
// GetPayload returns Payload with specified hash if it is present in the local cache.
|
2019-11-08 15:40:21 +00:00
|
|
|
GetPayload(h util.Uint256) *Payload
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
// cache is a fifo cache which stores recent payloads.
|
2020-06-05 09:11:22 +00:00
|
|
|
cache *cache.HashCache
|
2019-11-15 10:32:40 +00:00
|
|
|
// txx is a fifo cache which stores miner transactions.
|
2020-06-05 09:11:22 +00:00
|
|
|
txx *cache.HashCache
|
2019-11-15 10:32:40 +00:00
|
|
|
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
|
|
|
|
// process.
|
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.
|
|
|
|
started *atomic.Bool
|
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
|
2019-11-15 10:32:40 +00:00
|
|
|
// Broadcast is a callback which is called to notify server
|
|
|
|
// about new consensus payload to sent.
|
2020-06-05 09:11:22 +00:00
|
|
|
Broadcast func(cache.Hashable)
|
2019-11-15 10:32:40 +00:00
|
|
|
// Chain is a core.Blockchainer instance.
|
|
|
|
Chain core.Blockchainer
|
|
|
|
// RequestTx is a callback to which will be called
|
|
|
|
// when a node lacks transactions present in a block.
|
|
|
|
RequestTx func(h ...util.Uint256)
|
|
|
|
// TimePerBlock minimal time that should pass before next block is accepted.
|
|
|
|
TimePerBlock time.Duration
|
|
|
|
// Wallet is a local-node wallet configuration.
|
2020-03-25 15:30:21 +00:00
|
|
|
Wallet *wallet.Config
|
2019-11-08 15:40:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewService returns 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,
|
2020-06-05 09:11:22 +00:00
|
|
|
cache: cache.NewFIFOCache(cacheMaxCapacity),
|
|
|
|
txx: cache.NewFIFOCache(cacheMaxCapacity),
|
2019-11-15 10:32:40 +00:00
|
|
|
messages: make(chan Payload, 100),
|
2019-11-29 12:40:11 +00:00
|
|
|
|
|
|
|
transactions: make(chan *transaction.Transaction, 100),
|
2020-05-07 19:45:06 +00:00
|
|
|
blockEvents: make(chan *coreb.Block, 1),
|
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: atomic.NewBool(false),
|
2019-11-15 10:32:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if cfg.Wallet == nil {
|
|
|
|
return srv, nil
|
|
|
|
}
|
|
|
|
|
2020-01-15 14:05:47 +00:00
|
|
|
var err error
|
|
|
|
|
|
|
|
if srv.wallet, err = wallet.NewWalletFromFile(cfg.Wallet.Path); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
defer srv.wallet.Close()
|
|
|
|
|
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),
|
|
|
|
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-08-09 20:31:34 +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-22 12:51:32 +00:00
|
|
|
dbft.WithNewConsensusPayload(srv.newPayload),
|
2020-05-29 14:54:41 +00:00
|
|
|
dbft.WithNewPrepareRequest(srv.newPrepareRequest),
|
2020-08-09 20:31:34 +00:00
|
|
|
dbft.WithNewPrepareResponse(srv.newPrepareResponse),
|
2019-11-15 10:32:40 +00:00
|
|
|
dbft.WithNewChangeView(func() payload.ChangeView { return new(changeView) }),
|
2020-08-13 13:57:59 +00:00
|
|
|
dbft.WithNewCommit(srv.newCommit),
|
2020-01-29 14:56:21 +00:00
|
|
|
dbft.WithNewRecoveryRequest(func() payload.RecoveryRequest { return new(recoveryRequest) }),
|
2020-08-05 14:08:24 +00:00
|
|
|
dbft.WithNewRecoveryMessage(srv.newRecoveryMessage),
|
2020-06-10 16:00:19 +00:00
|
|
|
dbft.WithVerifyPrepareRequest(srv.verifyRequest),
|
2020-08-09 20:31:34 +00:00
|
|
|
dbft.WithVerifyPrepareResponse(srv.verifyResponse),
|
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)
|
|
|
|
)
|
|
|
|
|
|
|
|
func (s *service) Start() {
|
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.started.CAS(false, true) {
|
|
|
|
s.dbft.Start()
|
|
|
|
s.Chain.SubscribeForBlocks(s.blockEvents)
|
|
|
|
go s.eventLoop()
|
|
|
|
}
|
2019-11-15 10:32:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *service) eventLoop() {
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case hv := <-s.dbft.Timer.C():
|
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{
|
|
|
|
zap.Uint16("from", msg.validatorIndex),
|
|
|
|
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:
|
|
|
|
// We also receive our own blocks 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()))
|
|
|
|
s.dbft.InitializeConsensus(0)
|
|
|
|
}
|
2019-11-15 10:32:40 +00:00
|
|
|
}
|
2019-11-08 15:40:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-22 12:51:32 +00:00
|
|
|
func (s *service) newPayload() payload.ConsensusPayload {
|
|
|
|
return &Payload{
|
2020-06-23 06:41:53 +00:00
|
|
|
message: &message{
|
|
|
|
stateRootEnabled: s.stateRootEnabled(),
|
|
|
|
},
|
2020-06-22 12:51:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-23 06:41:53 +00:00
|
|
|
// stateRootEnabled checks if state root feature is enabled on current height.
|
|
|
|
// It should be called only from dbft callbacks and is not protected by any mutex.
|
|
|
|
func (s *service) stateRootEnabled() bool {
|
|
|
|
return s.Chain.GetConfig().EnableStateRoot
|
|
|
|
}
|
|
|
|
|
2020-05-29 14:54:41 +00:00
|
|
|
func (s *service) newPrepareRequest() payload.PrepareRequest {
|
2020-08-09 20:31:34 +00:00
|
|
|
res := &prepareRequest{
|
|
|
|
stateRootEnabled: s.stateRootEnabled(),
|
|
|
|
}
|
2020-06-23 06:41:53 +00:00
|
|
|
if !s.stateRootEnabled() {
|
2020-08-09 20:31:34 +00:00
|
|
|
return res
|
|
|
|
}
|
|
|
|
sig := s.getStateRootSig()
|
|
|
|
if sig != nil {
|
|
|
|
copy(res.stateRootSig[:], sig)
|
2020-05-29 14:54:41 +00:00
|
|
|
}
|
2020-08-09 20:31:34 +00:00
|
|
|
return res
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *service) getStateRootSig() []byte {
|
|
|
|
var sig []byte
|
|
|
|
|
|
|
|
sr, err := s.Chain.GetStateRoot(s.dbft.BlockIndex - 1)
|
2020-06-23 06:41:53 +00:00
|
|
|
if err == nil {
|
2020-08-09 20:31:34 +00:00
|
|
|
data := sr.GetSignedPart()
|
|
|
|
sig, _ = s.dbft.Priv.Sign(data)
|
2020-05-29 14:54:41 +00:00
|
|
|
}
|
2020-08-09 20:31:34 +00:00
|
|
|
return sig
|
2020-05-29 14:54:41 +00:00
|
|
|
}
|
|
|
|
|
2020-08-13 13:57:59 +00:00
|
|
|
func (s *service) newCommit() payload.Commit {
|
|
|
|
if s.stateRootEnabled() {
|
|
|
|
// This is being called when we're ready to commit, so we can safely
|
|
|
|
// relay stateroot here.
|
|
|
|
stateRoot, err := s.Chain.GetStateRoot(s.dbft.Context.BlockIndex - 1)
|
|
|
|
if err != nil {
|
|
|
|
s.log.Warn("can't get stateroot", zap.Uint32("block", s.dbft.Context.BlockIndex-1))
|
|
|
|
}
|
|
|
|
r := stateRoot.MPTRoot
|
|
|
|
r.Witness = s.getWitness(func(ctx dbft.Context, i int) []byte {
|
|
|
|
if p := ctx.PreparationPayloads[i]; p != nil && p.ViewNumber() == ctx.ViewNumber {
|
|
|
|
if int(ctx.PrimaryIndex) == i {
|
|
|
|
return p.GetPrepareRequest().(*prepareRequest).stateRootSig[:]
|
|
|
|
}
|
|
|
|
return p.GetPrepareResponse().(*prepareResponse).stateRootSig[:]
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err := s.Chain.AddStateRoot(&r); err != nil {
|
|
|
|
s.log.Warn("errors while adding state root", zap.Error(err))
|
|
|
|
}
|
|
|
|
s.Broadcast(&r)
|
|
|
|
}
|
|
|
|
return new(commit)
|
|
|
|
}
|
|
|
|
|
2020-08-09 20:31:34 +00:00
|
|
|
func (s *service) newPrepareResponse() payload.PrepareResponse {
|
|
|
|
res := &prepareResponse{
|
|
|
|
stateRootEnabled: s.stateRootEnabled(),
|
|
|
|
}
|
2020-06-23 06:41:53 +00:00
|
|
|
if !s.stateRootEnabled() {
|
2020-08-09 20:31:34 +00:00
|
|
|
return res
|
2020-05-29 14:54:41 +00:00
|
|
|
}
|
2020-08-09 20:31:34 +00:00
|
|
|
sig := s.getStateRootSig()
|
|
|
|
if sig != nil {
|
|
|
|
copy(res.stateRootSig[:], sig)
|
|
|
|
}
|
|
|
|
return res
|
2020-05-29 14:54:41 +00:00
|
|
|
}
|
|
|
|
|
2020-08-05 14:08:24 +00:00
|
|
|
func (s *service) newRecoveryMessage() payload.RecoveryMessage {
|
|
|
|
return &recoveryMessage{stateRootEnabled: s.stateRootEnabled()}
|
|
|
|
}
|
|
|
|
|
2019-12-16 08:57:49 +00:00
|
|
|
func (s *service) validatePayload(p *Payload) bool {
|
|
|
|
validators := s.getValidators()
|
|
|
|
if int(p.validatorIndex) >= len(validators) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
pub := validators[p.validatorIndex]
|
2020-02-19 09:10:36 +00:00
|
|
|
h := pub.(*publicKey).GetScriptHash()
|
2019-12-16 08:57:49 +00:00
|
|
|
|
|
|
|
return p.Verify(h)
|
|
|
|
}
|
|
|
|
|
2020-01-15 14:05:47 +00:00
|
|
|
func (s *service) getKeyPair(pubs []crypto.PublicKey) (int, crypto.PrivateKey, crypto.PublicKey) {
|
|
|
|
for i := range pubs {
|
2020-02-19 09:10:36 +00:00
|
|
|
sh := pubs[i].(*publicKey).GetScriptHash()
|
|
|
|
acc := s.wallet.GetAccount(sh)
|
2020-01-15 14:05:47 +00:00
|
|
|
if acc == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
key, err := keys.NEP2Decrypt(acc.EncryptedWIF, s.Config.Wallet.Password)
|
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
return i, &privateKey{PrivateKey: key}, &publicKey{PublicKey: key.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
|
|
|
}
|
|
|
|
|
2019-11-08 15:40:21 +00:00
|
|
|
// OnPayload handles Payload receive.
|
2019-11-15 10:32:40 +00:00
|
|
|
func (s *service) OnPayload(cp *Payload) {
|
2020-04-15 15:56:45 +00:00
|
|
|
log := s.log.With(zap.Stringer("hash", cp.Hash()))
|
2020-01-31 11:31:19 +00:00
|
|
|
if !s.validatePayload(cp) {
|
|
|
|
log.Debug("can't validate payload")
|
|
|
|
return
|
|
|
|
} else if s.cache.Has(cp.Hash()) {
|
|
|
|
log.Debug("payload is already in cache")
|
2019-11-15 10:32:40 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
s.Config.Broadcast(cp)
|
|
|
|
s.cache.Add(cp)
|
|
|
|
|
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")
|
2019-11-15 10:32:40 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-04-15 15:56:45 +00:00
|
|
|
// decode payload data into message
|
|
|
|
if cp.message == nil {
|
2020-06-23 06:41:53 +00:00
|
|
|
if err := cp.decodeData(s.stateRootEnabled()); err != nil {
|
|
|
|
log.Debug("can't decode payload data", zap.Error(err))
|
2020-04-15 15:56:45 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-15 10:32:40 +00:00
|
|
|
s.messages <- *cp
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *service) OnTransaction(tx *transaction.Transaction) {
|
|
|
|
if s.dbft != nil {
|
|
|
|
s.transactions <- tx
|
|
|
|
}
|
2019-11-08 15:40:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetPayload returns payload stored in cache.
|
|
|
|
func (s *service) GetPayload(h util.Uint256) *Payload {
|
2019-11-15 10:32:40 +00:00
|
|
|
p := s.cache.Get(h)
|
|
|
|
if p == nil {
|
|
|
|
return (*Payload)(nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
cp := *p.(*Payload)
|
|
|
|
|
|
|
|
return &cp
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *service) broadcast(p payload.ConsensusPayload) {
|
|
|
|
switch p.Type() {
|
|
|
|
case payload.PrepareRequestType:
|
|
|
|
pr := p.GetPrepareRequest().(*prepareRequest)
|
|
|
|
pr.minerTx = *s.txx.Get(pr.transactionHashes[0]).(*transaction.Transaction)
|
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2019-11-15 10:32:40 +00:00
|
|
|
s.cache.Add(p)
|
|
|
|
s.Config.Broadcast(p.(*Payload))
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
for _, tx := range coreb.Transactions {
|
|
|
|
if err := s.Chain.VerifyTx(tx, coreb); err != nil {
|
2020-02-11 15:02:13 +00:00
|
|
|
s.log.Warn("invalid transaction in proposed block", zap.Stringer("hash", tx.Hash()))
|
2019-11-15 10:32:40 +00:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2020-08-09 20:31:34 +00:00
|
|
|
func (s *service) verifyStateRootSig(index int, sig []byte) error {
|
|
|
|
r, err := s.Chain.GetStateRoot(s.dbft.BlockIndex - 1)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("can't get local state root: %v", err)
|
|
|
|
}
|
|
|
|
validators := s.getValidators()
|
|
|
|
if index >= len(validators) {
|
|
|
|
return errors.New("bad validator index")
|
|
|
|
}
|
|
|
|
|
|
|
|
pub := validators[index]
|
|
|
|
if pub.Verify(r.GetSignedPart(), sig) != nil {
|
|
|
|
return errors.New("bad state root signature")
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-06-10 16:00:19 +00:00
|
|
|
func (s *service) verifyRequest(p payload.ConsensusPayload) error {
|
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
|
|
|
req := p.GetPrepareRequest().(*prepareRequest)
|
|
|
|
if s.stateRootEnabled() {
|
2020-08-09 20:31:34 +00:00
|
|
|
err := s.verifyStateRootSig(int(p.ValidatorIndex()), req.stateRootSig[:])
|
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 err != nil {
|
2020-08-09 20:31:34 +00:00
|
|
|
return err
|
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
|
|
|
}
|
2020-06-10 16:00:19 +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
|
|
|
// Save lastProposal for getVerified().
|
|
|
|
s.txx.Add(&req.minerTx)
|
|
|
|
s.lastProposal = req.transactionHashes
|
|
|
|
|
2020-06-10 16:00:19 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-08-09 20:31:34 +00:00
|
|
|
func (s *service) verifyResponse(p payload.ConsensusPayload) error {
|
|
|
|
if !s.stateRootEnabled() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
resp := p.GetPrepareResponse().(*prepareResponse)
|
|
|
|
return s.verifyStateRootSig(int(p.ValidatorIndex()), resp.stateRootSig[:])
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
|
|
|
if err := s.Chain.AddBlock(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 {
|
|
|
|
s.log.Warn("error on add block", zap.Error(err))
|
|
|
|
}
|
2019-11-15 10:32:40 +00:00
|
|
|
}
|
2020-05-29 14:54:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *service) getBlockWitness(_ *coreb.Block) *transaction.Witness {
|
2020-08-09 20:31:34 +00:00
|
|
|
return s.getWitness(func(ctx dbft.Context, i int) []byte {
|
|
|
|
if p := ctx.CommitPayloads[i]; p != nil && p.ViewNumber() == ctx.ViewNumber {
|
|
|
|
return p.GetCommit().Signature()
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
2019-11-15 10:32:40 +00:00
|
|
|
}
|
|
|
|
|
2020-08-09 20:31:34 +00:00
|
|
|
func (s *service) getWitness(f func(dbft.Context, int) []byte) *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 {
|
2020-08-09 20:31:34 +00:00
|
|
|
sig := f(dctx, i)
|
|
|
|
if sig != nil {
|
|
|
|
sigs[pubs[i]] = sig
|
2019-11-15 10:32:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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))
|
|
|
|
|
|
|
|
var invoc []byte
|
|
|
|
for i, j := 0, 0; i < len(pubs) && j < m; i++ {
|
|
|
|
if sig, ok := sigs[pubs[i]]; ok {
|
2019-12-03 14:05:06 +00:00
|
|
|
invoc = append(invoc, byte(opcode.PUSHBYTES64))
|
2019-11-15 10:32:40 +00:00
|
|
|
invoc = append(invoc, sig...)
|
|
|
|
j++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return &transaction.Witness{
|
|
|
|
InvocationScript: invoc,
|
|
|
|
VerificationScript: verif,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *service) getBlock(h util.Uint256) block.Block {
|
|
|
|
b, err := s.Chain.GetBlock(h)
|
|
|
|
if err != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return &neoBlock{Block: *b}
|
|
|
|
}
|
|
|
|
|
2020-06-24 10:39:12 +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-02-18 15:56:41 +00:00
|
|
|
var txx []mempool.TxWithFee
|
2020-01-14 11:34:09 +00:00
|
|
|
|
|
|
|
if s.dbft.ViewNumber > 0 {
|
2020-02-18 15:56:41 +00:00
|
|
|
txx = make([]mempool.TxWithFee, 0, len(s.lastProposal))
|
2020-01-14 11:34:09 +00:00
|
|
|
for i := range s.lastProposal {
|
2020-02-18 15:56:41 +00:00
|
|
|
if tx, fee, ok := pool.TryGetValue(s.lastProposal[i]); ok {
|
|
|
|
txx = append(txx, mempool.TxWithFee{Tx: tx, Fee: fee})
|
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)
|
|
|
|
}
|
|
|
|
|
2019-11-15 10:32:40 +00:00
|
|
|
res := make([]block.Transaction, len(txx)+1)
|
2020-02-14 16:21:24 +00:00
|
|
|
var netFee util.Fixed8
|
2019-11-29 12:40:21 +00:00
|
|
|
for i := range txx {
|
2020-02-18 15:56:41 +00:00
|
|
|
res[i+1] = txx[i].Tx
|
|
|
|
netFee += txx[i].Fee
|
2019-11-15 10:32:40 +00:00
|
|
|
}
|
|
|
|
|
2020-02-14 16:21:24 +00:00
|
|
|
var txOuts []transaction.Output
|
|
|
|
if netFee != 0 {
|
|
|
|
sh := s.wallet.GetChangeAddress()
|
|
|
|
if sh.Equals(util.Uint160{}) {
|
|
|
|
pk := s.dbft.Pub.(*publicKey)
|
2020-02-19 09:10:36 +00:00
|
|
|
sh = pk.GetScriptHash()
|
2020-02-14 16:21:24 +00:00
|
|
|
}
|
2020-03-03 14:22:15 +00:00
|
|
|
txOuts = []transaction.Output{{
|
2020-02-14 16:21:24 +00:00
|
|
|
AssetID: core.UtilityTokenID(),
|
|
|
|
Amount: netFee,
|
|
|
|
ScriptHash: sh,
|
|
|
|
}}
|
|
|
|
}
|
2019-11-15 10:32:40 +00:00
|
|
|
for {
|
|
|
|
nonce := rand.Uint32()
|
|
|
|
res[0] = &transaction.Transaction{
|
|
|
|
Type: transaction.MinerType,
|
|
|
|
Version: 0,
|
|
|
|
Data: &transaction.MinerTX{Nonce: nonce},
|
|
|
|
Attributes: nil,
|
|
|
|
Inputs: nil,
|
2020-02-14 16:21:24 +00:00
|
|
|
Outputs: txOuts,
|
2019-11-15 10:32:40 +00:00
|
|
|
Scripts: nil,
|
|
|
|
Trimmed: false,
|
|
|
|
}
|
|
|
|
|
|
|
|
if tx, _, _ := s.Chain.GetTransaction(res[0].Hash()); tx == nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
s.txx.Add(res[0])
|
|
|
|
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *service) getValidators(txx ...block.Transaction) []crypto.PublicKey {
|
2020-02-28 08:10:01 +00:00
|
|
|
var (
|
|
|
|
pKeys []*keys.PublicKey
|
|
|
|
err error
|
|
|
|
)
|
2019-11-15 10:32:40 +00:00
|
|
|
if len(txx) == 0 {
|
2020-02-28 08:10:01 +00:00
|
|
|
pKeys, err = s.Chain.GetValidators()
|
2019-11-15 10:32:40 +00:00
|
|
|
} else {
|
|
|
|
ntxx := make([]*transaction.Transaction, len(txx))
|
|
|
|
for i := range ntxx {
|
|
|
|
ntxx[i] = txx[i].(*transaction.Transaction)
|
|
|
|
}
|
|
|
|
|
2020-02-28 08:10:01 +00:00
|
|
|
pKeys, err = s.Chain.GetValidators(ntxx...)
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *service) getConsensusAddress(validators ...crypto.PublicKey) (h util.Uint160) {
|
|
|
|
pubs := convertKeys(validators)
|
|
|
|
|
|
|
|
script, err := smartcontract.CreateMultiSigRedeemScript(s.dbft.M(), pubs)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
return crypto.Hash160(script)
|
|
|
|
}
|
|
|
|
|
|
|
|
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-06-01 15:41:31 +00:00
|
|
|
|
2020-08-09 20:31:34 +00:00
|
|
|
func (s *service) newBlockFromContext(ctx *dbft.Context) block.Block {
|
2020-06-01 15:41:31 +00:00
|
|
|
block := new(neoBlock)
|
|
|
|
if len(ctx.TransactionHashes) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
block.Block.Timestamp = uint32(ctx.Timestamp / 1000000000)
|
|
|
|
block.Block.Index = ctx.BlockIndex
|
|
|
|
block.Block.NextConsensus = ctx.NextConsensus
|
|
|
|
block.Block.PrevHash = ctx.PrevHash
|
|
|
|
block.Block.Version = ctx.Version
|
|
|
|
block.Block.ConsensusData = ctx.Nonce
|
|
|
|
|
|
|
|
mt := merkle.NewMerkleTree(ctx.TransactionHashes...)
|
|
|
|
block.Block.MerkleRoot = mt.Root().Hash
|
|
|
|
return block
|
|
|
|
}
|