forked from TrueCloudLab/neoneo-go
Merge pull request #2930 from nspcc-dev/addblock-lock
consensus: prevent AddBlock lock caused by consensus service
This commit is contained in:
commit
a8aa29727b
12 changed files with 159 additions and 88 deletions
|
@ -384,6 +384,7 @@ func mkConsensus(config config.Consensus, tpb time.Duration, chain *core.Blockch
|
||||||
Logger: log,
|
Logger: log,
|
||||||
Broadcast: serv.BroadcastExtensible,
|
Broadcast: serv.BroadcastExtensible,
|
||||||
Chain: chain,
|
Chain: chain,
|
||||||
|
BlockQueue: serv.GetBlockQueue(),
|
||||||
ProtocolConfiguration: chain.GetConfig().ProtocolConfiguration,
|
ProtocolConfiguration: chain.GetConfig().ProtocolConfiguration,
|
||||||
RequestTx: serv.RequestTx,
|
RequestTx: serv.RequestTx,
|
||||||
StopTxFlow: serv.StopTxFlow,
|
StopTxFlow: serv.StopTxFlow,
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -10,7 +10,7 @@ require (
|
||||||
github.com/holiman/uint256 v1.2.0
|
github.com/holiman/uint256 v1.2.0
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||||
github.com/mr-tron/base58 v1.2.0
|
github.com/mr-tron/base58 v1.2.0
|
||||||
github.com/nspcc-dev/dbft v0.0.0-20221020093431-31c1bbdc74f2
|
github.com/nspcc-dev/dbft v0.0.0-20230314095711-114b11c42cf6
|
||||||
github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22
|
github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb
|
||||||
github.com/nspcc-dev/neofs-sdk-go v0.0.0-20220113123743-7f3162110659
|
github.com/nspcc-dev/neofs-sdk-go v0.0.0-20220113123743-7f3162110659
|
||||||
|
|
9
go.sum
9
go.sum
|
@ -251,8 +251,8 @@ github.com/nspcc-dev/dbft v0.0.0-20191209120240-0d6b7568d9ae/go.mod h1:3FjXOoHmA
|
||||||
github.com/nspcc-dev/dbft v0.0.0-20200117124306-478e5cfbf03a/go.mod h1:/YFK+XOxxg0Bfm6P92lY5eDSLYfp06XOdL8KAVgXjVk=
|
github.com/nspcc-dev/dbft v0.0.0-20200117124306-478e5cfbf03a/go.mod h1:/YFK+XOxxg0Bfm6P92lY5eDSLYfp06XOdL8KAVgXjVk=
|
||||||
github.com/nspcc-dev/dbft v0.0.0-20200219114139-199d286ed6c1/go.mod h1:O0qtn62prQSqizzoagHmuuKoz8QMkU3SzBoKdEvm3aQ=
|
github.com/nspcc-dev/dbft v0.0.0-20200219114139-199d286ed6c1/go.mod h1:O0qtn62prQSqizzoagHmuuKoz8QMkU3SzBoKdEvm3aQ=
|
||||||
github.com/nspcc-dev/dbft v0.0.0-20210721160347-1b03241391ac/go.mod h1:U8MSnEShH+o5hexfWJdze6uMFJteP0ko7J2frO7Yu1Y=
|
github.com/nspcc-dev/dbft v0.0.0-20210721160347-1b03241391ac/go.mod h1:U8MSnEShH+o5hexfWJdze6uMFJteP0ko7J2frO7Yu1Y=
|
||||||
github.com/nspcc-dev/dbft v0.0.0-20221020093431-31c1bbdc74f2 h1:2soBy8en5W4/1Gvbog8RyVpEbarGWZwPxppZjffWzZE=
|
github.com/nspcc-dev/dbft v0.0.0-20230314095711-114b11c42cf6 h1:K4AcGPB0JDLloGwRY2BPJJeWyNQw+n0BnrdgFjSEBIs=
|
||||||
github.com/nspcc-dev/dbft v0.0.0-20221020093431-31c1bbdc74f2/go.mod h1:g9xisXmX9NP9MjioaTe862n9SlZTrP+6PVUWLBYOr98=
|
github.com/nspcc-dev/dbft v0.0.0-20230314095711-114b11c42cf6/go.mod h1:IsUsZqxQkav23pFrCyZEibz0VukpI787XnbcsFkekI8=
|
||||||
github.com/nspcc-dev/go-ordered-json v0.0.0-20210915112629-e1b6cce73d02/go.mod h1:79bEUDEviBHJMFV6Iq6in57FEOCMcRhfQnfaf0ETA5U=
|
github.com/nspcc-dev/go-ordered-json v0.0.0-20210915112629-e1b6cce73d02/go.mod h1:79bEUDEviBHJMFV6Iq6in57FEOCMcRhfQnfaf0ETA5U=
|
||||||
github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22 h1:n4ZaFCKt1pQJd7PXoMJabZWK9ejjbLOVrkl/lOUmshg=
|
github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22 h1:n4ZaFCKt1pQJd7PXoMJabZWK9ejjbLOVrkl/lOUmshg=
|
||||||
github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22/go.mod h1:79bEUDEviBHJMFV6Iq6in57FEOCMcRhfQnfaf0ETA5U=
|
github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22/go.mod h1:79bEUDEviBHJMFV6Iq6in57FEOCMcRhfQnfaf0ETA5U=
|
||||||
|
@ -403,6 +403,7 @@ golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPh
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||||
golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8=
|
golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8=
|
||||||
golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80=
|
golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
@ -477,6 +478,7 @@ golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qx
|
||||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||||
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
||||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
|
@ -557,12 +559,14 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210429154555-c04ba851c2a4/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20210429154555-c04ba851c2a4/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
||||||
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
|
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
|
@ -573,6 +577,7 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
|
|
@ -155,6 +155,7 @@ func NewTestChain(t *testing.T, f func(*config.Config), run bool) (*core.Blockch
|
||||||
Logger: zap.NewNop(),
|
Logger: zap.NewNop(),
|
||||||
Broadcast: netSrv.BroadcastExtensible,
|
Broadcast: netSrv.BroadcastExtensible,
|
||||||
Chain: chain,
|
Chain: chain,
|
||||||
|
BlockQueue: netSrv.GetBlockQueue(),
|
||||||
ProtocolConfiguration: cfg.ProtocolConfiguration,
|
ProtocolConfiguration: cfg.ProtocolConfiguration,
|
||||||
RequestTx: netSrv.RequestTx,
|
RequestTx: netSrv.RequestTx,
|
||||||
StopTxFlow: netSrv.StopTxFlow,
|
StopTxFlow: netSrv.StopTxFlow,
|
||||||
|
|
|
@ -42,7 +42,6 @@ const nsInMs = 1000000
|
||||||
|
|
||||||
// Ledger is the interface to Blockchain sufficient for Service.
|
// Ledger is the interface to Blockchain sufficient for Service.
|
||||||
type Ledger interface {
|
type Ledger interface {
|
||||||
AddBlock(block *coreb.Block) error
|
|
||||||
ApplyPolicyToTxSet([]*transaction.Transaction) []*transaction.Transaction
|
ApplyPolicyToTxSet([]*transaction.Transaction) []*transaction.Transaction
|
||||||
GetConfig() config.Blockchain
|
GetConfig() config.Blockchain
|
||||||
GetMemPool() *mempool.Pool
|
GetMemPool() *mempool.Pool
|
||||||
|
@ -58,6 +57,11 @@ type Ledger interface {
|
||||||
mempool.Feer
|
mempool.Feer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BlockQueuer is an interface to the block queue manager sufficient for Service.
|
||||||
|
type BlockQueuer interface {
|
||||||
|
PutBlock(block *coreb.Block) error
|
||||||
|
}
|
||||||
|
|
||||||
// Service represents a consensus instance.
|
// Service represents a consensus instance.
|
||||||
type Service interface {
|
type Service interface {
|
||||||
// Name returns service name.
|
// Name returns service name.
|
||||||
|
@ -89,7 +93,8 @@ type service struct {
|
||||||
messages chan Payload
|
messages chan Payload
|
||||||
transactions chan *transaction.Transaction
|
transactions chan *transaction.Transaction
|
||||||
// blockEvents is used to pass a new block event to the consensus
|
// blockEvents is used to pass a new block event to the consensus
|
||||||
// process.
|
// process. It has a tiny buffer in order to avoid Blockchain blocking
|
||||||
|
// on block addition under the high load.
|
||||||
blockEvents chan *coreb.Block
|
blockEvents chan *coreb.Block
|
||||||
lastProposal []util.Uint256
|
lastProposal []util.Uint256
|
||||||
wallet *wallet.Wallet
|
wallet *wallet.Wallet
|
||||||
|
@ -114,6 +119,8 @@ type Config struct {
|
||||||
Broadcast func(p *npayload.Extensible)
|
Broadcast func(p *npayload.Extensible)
|
||||||
// Chain is a Ledger instance.
|
// Chain is a Ledger instance.
|
||||||
Chain Ledger
|
Chain Ledger
|
||||||
|
// BlockQueue is a BlockQueuer instance.
|
||||||
|
BlockQueue BlockQueuer
|
||||||
// ProtocolConfiguration contains protocol settings.
|
// ProtocolConfiguration contains protocol settings.
|
||||||
ProtocolConfiguration config.ProtocolConfiguration
|
ProtocolConfiguration config.ProtocolConfiguration
|
||||||
// RequestTx is a callback to which will be called
|
// RequestTx is a callback to which will be called
|
||||||
|
@ -336,11 +343,19 @@ events:
|
||||||
case b := <-s.blockEvents:
|
case b := <-s.blockEvents:
|
||||||
s.handleChainBlock(b)
|
s.handleChainBlock(b)
|
||||||
}
|
}
|
||||||
// Always process block event if there is any, we can add one above.
|
// Always process block event if there is any, we can add one above or external
|
||||||
|
// services can add several blocks during message processing.
|
||||||
|
var latestBlock *coreb.Block
|
||||||
|
syncLoop:
|
||||||
|
for {
|
||||||
select {
|
select {
|
||||||
case b := <-s.blockEvents:
|
case latestBlock = <-s.blockEvents:
|
||||||
s.handleChainBlock(b)
|
|
||||||
default:
|
default:
|
||||||
|
break syncLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if latestBlock != nil {
|
||||||
|
s.handleChainBlock(latestBlock)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
drainLoop:
|
drainLoop:
|
||||||
|
@ -564,11 +579,11 @@ func (s *service) processBlock(b block.Block) {
|
||||||
bb := &b.(*neoBlock).Block
|
bb := &b.(*neoBlock).Block
|
||||||
bb.Script = *(s.getBlockWitness(bb))
|
bb.Script = *(s.getBlockWitness(bb))
|
||||||
|
|
||||||
if err := s.Chain.AddBlock(bb); err != nil {
|
if err := s.BlockQueue.PutBlock(bb); err != nil {
|
||||||
// The block might already be added via the regular network
|
// The block might already be added via the regular network
|
||||||
// interaction.
|
// interaction.
|
||||||
if _, errget := s.Chain.GetBlock(bb.Hash()); errget != nil {
|
if _, errget := s.Chain.GetBlock(bb.Hash()); errget != nil {
|
||||||
s.log.Warn("error on add block", zap.Error(err))
|
s.log.Warn("error on enqueue block", zap.Error(err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s.postBlock(bb)
|
s.postBlock(bb)
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core"
|
"github.com/nspcc-dev/neo-go/pkg/core"
|
||||||
|
coreb "github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/fee"
|
"github.com/nspcc-dev/neo-go/pkg/core/fee"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
||||||
|
@ -50,6 +51,7 @@ func TestNewWatchingService(t *testing.T) {
|
||||||
Logger: zaptest.NewLogger(t),
|
Logger: zaptest.NewLogger(t),
|
||||||
Broadcast: func(*npayload.Extensible) {},
|
Broadcast: func(*npayload.Extensible) {},
|
||||||
Chain: bc,
|
Chain: bc,
|
||||||
|
BlockQueue: testBlockQueuer{bc: bc},
|
||||||
ProtocolConfiguration: bc.GetConfig().ProtocolConfiguration,
|
ProtocolConfiguration: bc.GetConfig().ProtocolConfiguration,
|
||||||
RequestTx: func(...util.Uint256) {},
|
RequestTx: func(...util.Uint256) {},
|
||||||
StopTxFlow: func() {},
|
StopTxFlow: func() {},
|
||||||
|
@ -62,6 +64,14 @@ func TestNewWatchingService(t *testing.T) {
|
||||||
require.NotPanics(t, srv.Shutdown)
|
require.NotPanics(t, srv.Shutdown)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func collectBlock(t *testing.T, bc *core.Blockchain, srv *service) {
|
||||||
|
h := bc.BlockHeight()
|
||||||
|
srv.dbft.OnTimeout(timer.HV{Height: srv.dbft.Context.BlockIndex}) // Collect and add block to the chain.
|
||||||
|
header, err := bc.GetHeader(bc.GetHeaderHash(h + 1))
|
||||||
|
require.NoError(t, err)
|
||||||
|
srv.dbft.InitializeConsensus(0, header.Timestamp*nsInMs) // Init consensus manually at the next height, as we don't run the consensus service.
|
||||||
|
}
|
||||||
|
|
||||||
func initServiceNextConsensus(t *testing.T, newAcc *wallet.Account, offset uint32) (*service, *wallet.Account) {
|
func initServiceNextConsensus(t *testing.T, newAcc *wallet.Account, offset uint32) (*service, *wallet.Account) {
|
||||||
acc, err := wallet.NewAccountFromWIF(testchain.WIF(testchain.IDToOrder(0)))
|
acc, err := wallet.NewAccountFromWIF(testchain.WIF(testchain.IDToOrder(0)))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -89,7 +99,11 @@ func initServiceNextConsensus(t *testing.T, newAcc *wallet.Account, offset uint3
|
||||||
require.NoError(t, bc.PoolTx(tx))
|
require.NoError(t, bc.PoolTx(tx))
|
||||||
|
|
||||||
srv := newTestServiceWithChain(t, bc)
|
srv := newTestServiceWithChain(t, bc)
|
||||||
|
h := bc.BlockHeight()
|
||||||
srv.dbft.Start(0)
|
srv.dbft.Start(0)
|
||||||
|
header, err := bc.GetHeader(bc.GetHeaderHash(h + 1))
|
||||||
|
require.NoError(t, err)
|
||||||
|
srv.dbft.InitializeConsensus(0, header.Timestamp*nsInMs) // Init consensus manually at the next height, as we don't run the consensus service.
|
||||||
|
|
||||||
// Register new candidate.
|
// Register new candidate.
|
||||||
b.Reset()
|
b.Reset()
|
||||||
|
@ -104,11 +118,11 @@ func initServiceNextConsensus(t *testing.T, newAcc *wallet.Account, offset uint3
|
||||||
require.NoError(t, newAcc.SignTx(netmode.UnitTestNet, tx))
|
require.NoError(t, newAcc.SignTx(netmode.UnitTestNet, tx))
|
||||||
|
|
||||||
require.NoError(t, bc.PoolTx(tx))
|
require.NoError(t, bc.PoolTx(tx))
|
||||||
srv.dbft.OnTimeout(timer.HV{Height: srv.dbft.Context.BlockIndex})
|
collectBlock(t, bc, srv)
|
||||||
|
|
||||||
cfg := bc.GetConfig()
|
cfg := bc.GetConfig()
|
||||||
for i := srv.dbft.BlockIndex; !cfg.ShouldUpdateCommitteeAt(i + offset); i++ {
|
for i := srv.dbft.BlockIndex; !cfg.ShouldUpdateCommitteeAt(i + offset); i++ {
|
||||||
srv.dbft.OnTimeout(timer.HV{Height: srv.dbft.Context.BlockIndex})
|
collectBlock(t, bc, srv)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Vote for new candidate.
|
// Vote for new candidate.
|
||||||
|
@ -125,7 +139,7 @@ func initServiceNextConsensus(t *testing.T, newAcc *wallet.Account, offset uint3
|
||||||
require.NoError(t, newAcc.SignTx(netmode.UnitTestNet, tx))
|
require.NoError(t, newAcc.SignTx(netmode.UnitTestNet, tx))
|
||||||
|
|
||||||
require.NoError(t, bc.PoolTx(tx))
|
require.NoError(t, bc.PoolTx(tx))
|
||||||
srv.dbft.OnTimeout(timer.HV{Height: srv.dbft.BlockIndex})
|
collectBlock(t, bc, srv)
|
||||||
|
|
||||||
return srv, acc
|
return srv, acc
|
||||||
}
|
}
|
||||||
|
@ -153,7 +167,7 @@ func TestService_NextConsensus(t *testing.T) {
|
||||||
// OnPersist <- update committee
|
// OnPersist <- update committee
|
||||||
// Block <-
|
// Block <-
|
||||||
|
|
||||||
srv.dbft.OnTimeout(timer.HV{Height: srv.dbft.BlockIndex})
|
collectBlock(t, bc, srv)
|
||||||
checkNextConsensus(t, bc, height+1, hash.Hash160(script))
|
checkNextConsensus(t, bc, height+1, hash.Hash160(script))
|
||||||
})
|
})
|
||||||
/*
|
/*
|
||||||
|
@ -495,6 +509,7 @@ func newTestServiceWithChain(t *testing.T, bc *core.Blockchain) *service {
|
||||||
Logger: zaptest.NewLogger(t),
|
Logger: zaptest.NewLogger(t),
|
||||||
Broadcast: func(*npayload.Extensible) {},
|
Broadcast: func(*npayload.Extensible) {},
|
||||||
Chain: bc,
|
Chain: bc,
|
||||||
|
BlockQueue: testBlockQueuer{bc: bc},
|
||||||
ProtocolConfiguration: bc.GetConfig().ProtocolConfiguration,
|
ProtocolConfiguration: bc.GetConfig().ProtocolConfiguration,
|
||||||
RequestTx: func(...util.Uint256) {},
|
RequestTx: func(...util.Uint256) {},
|
||||||
StopTxFlow: func() {},
|
StopTxFlow: func() {},
|
||||||
|
@ -509,6 +524,17 @@ func newTestServiceWithChain(t *testing.T, bc *core.Blockchain) *service {
|
||||||
return srv.(*service)
|
return srv.(*service)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type testBlockQueuer struct {
|
||||||
|
bc *core.Blockchain
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = BlockQueuer(testBlockQueuer{})
|
||||||
|
|
||||||
|
// PutBlock implements BlockQueuer interface.
|
||||||
|
func (bq testBlockQueuer) PutBlock(b *coreb.Block) error {
|
||||||
|
return bq.bc.AddBlock(b)
|
||||||
|
}
|
||||||
|
|
||||||
func getTestValidator(i int) (*privateKey, *publicKey) {
|
func getTestValidator(i int) (*privateKey, *publicKey) {
|
||||||
key := testchain.PrivateKey(i)
|
key := testchain.PrivateKey(i)
|
||||||
return &privateKey{PrivateKey: key}, &publicKey{PublicKey: key.PublicKey()}
|
return &privateKey{PrivateKey: key}, &publicKey{PublicKey: key.PublicKey()}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package network
|
package bqueue
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -15,7 +15,8 @@ type Blockqueuer interface {
|
||||||
BlockHeight() uint32
|
BlockHeight() uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
type blockQueue struct {
|
// Queue is the block queue.
|
||||||
|
type Queue struct {
|
||||||
log *zap.Logger
|
log *zap.Logger
|
||||||
queueLock sync.RWMutex
|
queueLock sync.RWMutex
|
||||||
queue []*block.Block
|
queue []*block.Block
|
||||||
|
@ -25,34 +26,36 @@ type blockQueue struct {
|
||||||
relayF func(*block.Block)
|
relayF func(*block.Block)
|
||||||
discarded *atomic.Bool
|
discarded *atomic.Bool
|
||||||
len int
|
len int
|
||||||
|
lenUpdateF func(int)
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
// CacheSize is the amount of blocks above the current height
|
||||||
// blockCacheSize is the amount of blocks above the current height
|
// which are stored in the queue.
|
||||||
// which are stored in the queue.
|
const CacheSize = 2000
|
||||||
blockCacheSize = 2000
|
|
||||||
)
|
|
||||||
|
|
||||||
func indexToPosition(i uint32) int {
|
func indexToPosition(i uint32) int {
|
||||||
return int(i) % blockCacheSize
|
return int(i) % CacheSize
|
||||||
}
|
}
|
||||||
|
|
||||||
func newBlockQueue(capacity int, bc Blockqueuer, log *zap.Logger, relayer func(*block.Block)) *blockQueue {
|
// New creates an instance of BlockQueue.
|
||||||
|
func New(bc Blockqueuer, log *zap.Logger, relayer func(*block.Block), lenMetricsUpdater func(l int)) *Queue {
|
||||||
if log == nil {
|
if log == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return &blockQueue{
|
return &Queue{
|
||||||
log: log,
|
log: log,
|
||||||
queue: make([]*block.Block, blockCacheSize),
|
queue: make([]*block.Block, CacheSize),
|
||||||
checkBlocks: make(chan struct{}, 1),
|
checkBlocks: make(chan struct{}, 1),
|
||||||
chain: bc,
|
chain: bc,
|
||||||
relayF: relayer,
|
relayF: relayer,
|
||||||
discarded: atomic.NewBool(false),
|
discarded: atomic.NewBool(false),
|
||||||
|
lenUpdateF: lenMetricsUpdater,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bq *blockQueue) run() {
|
// Run runs the BlockQueue queueing loop. It must be called in a separate routine.
|
||||||
|
func (bq *Queue) Run() {
|
||||||
var lastHeight = bq.chain.BlockHeight()
|
var lastHeight = bq.chain.BlockHeight()
|
||||||
for {
|
for {
|
||||||
_, ok := <-bq.checkBlocks
|
_, ok := <-bq.checkBlocks
|
||||||
|
@ -97,19 +100,22 @@ func (bq *blockQueue) run() {
|
||||||
bq.queue[pos] = nil
|
bq.queue[pos] = nil
|
||||||
}
|
}
|
||||||
bq.queueLock.Unlock()
|
bq.queueLock.Unlock()
|
||||||
updateBlockQueueLenMetric(l)
|
if bq.lenUpdateF != nil {
|
||||||
|
bq.lenUpdateF(l)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bq *blockQueue) putBlock(block *block.Block) error {
|
// PutBlock enqueues block to be added to the chain.
|
||||||
|
func (bq *Queue) PutBlock(block *block.Block) error {
|
||||||
h := bq.chain.BlockHeight()
|
h := bq.chain.BlockHeight()
|
||||||
bq.queueLock.Lock()
|
bq.queueLock.Lock()
|
||||||
defer bq.queueLock.Unlock()
|
defer bq.queueLock.Unlock()
|
||||||
if bq.discarded.Load() {
|
if bq.discarded.Load() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if block.Index <= h || h+blockCacheSize < block.Index {
|
if block.Index <= h || h+CacheSize < block.Index {
|
||||||
// can easily happen when fetching the same blocks from
|
// can easily happen when fetching the same blocks from
|
||||||
// different peers, thus not considered as error
|
// different peers, thus not considered as error
|
||||||
return nil
|
return nil
|
||||||
|
@ -119,14 +125,15 @@ func (bq *blockQueue) putBlock(block *block.Block) error {
|
||||||
if bq.queue[pos] == nil || bq.queue[pos].Index < block.Index {
|
if bq.queue[pos] == nil || bq.queue[pos].Index < block.Index {
|
||||||
bq.len++
|
bq.len++
|
||||||
bq.queue[pos] = block
|
bq.queue[pos] = block
|
||||||
for pos < blockCacheSize && bq.queue[pos] != nil && bq.lastQ+1 == bq.queue[pos].Index {
|
for pos < CacheSize && bq.queue[pos] != nil && bq.lastQ+1 == bq.queue[pos].Index {
|
||||||
bq.lastQ = bq.queue[pos].Index
|
bq.lastQ = bq.queue[pos].Index
|
||||||
pos++
|
pos++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
l := bq.len
|
|
||||||
// update metrics
|
// update metrics
|
||||||
updateBlockQueueLenMetric(l)
|
if bq.lenUpdateF != nil {
|
||||||
|
bq.lenUpdateF(bq.len)
|
||||||
|
}
|
||||||
select {
|
select {
|
||||||
case bq.checkBlocks <- struct{}{}:
|
case bq.checkBlocks <- struct{}{}:
|
||||||
// ok, signalled to goroutine processing queue
|
// ok, signalled to goroutine processing queue
|
||||||
|
@ -136,20 +143,21 @@ func (bq *blockQueue) putBlock(block *block.Block) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// lastQueued returns the index of the last queued block and the queue's capacity
|
// LastQueued returns the index of the last queued block and the queue's capacity
|
||||||
// left.
|
// left.
|
||||||
func (bq *blockQueue) lastQueued() (uint32, int) {
|
func (bq *Queue) LastQueued() (uint32, int) {
|
||||||
bq.queueLock.RLock()
|
bq.queueLock.RLock()
|
||||||
defer bq.queueLock.RUnlock()
|
defer bq.queueLock.RUnlock()
|
||||||
return bq.lastQ, blockCacheSize - bq.len
|
return bq.lastQ, CacheSize - bq.len
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bq *blockQueue) discard() {
|
// Discard stops the queue and prevents it from accepting more blocks to enqueue.
|
||||||
|
func (bq *Queue) Discard() {
|
||||||
if bq.discarded.CAS(false, true) {
|
if bq.discarded.CAS(false, true) {
|
||||||
bq.queueLock.Lock()
|
bq.queueLock.Lock()
|
||||||
close(bq.checkBlocks)
|
close(bq.checkBlocks)
|
||||||
// Technically we could bq.queue = nil, but this would cost
|
// Technically we could bq.queue = nil, but this would cost
|
||||||
// another if in run().
|
// another if in Run().
|
||||||
for i := 0; i < len(bq.queue); i++ {
|
for i := 0; i < len(bq.queue); i++ {
|
||||||
bq.queue[i] = nil
|
bq.queue[i] = nil
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package network
|
package bqueue
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -13,77 +13,77 @@ import (
|
||||||
func TestBlockQueue(t *testing.T) {
|
func TestBlockQueue(t *testing.T) {
|
||||||
chain := fakechain.NewFakeChain()
|
chain := fakechain.NewFakeChain()
|
||||||
// notice, it's not yet running
|
// notice, it's not yet running
|
||||||
bq := newBlockQueue(0, chain, zaptest.NewLogger(t), nil)
|
bq := New(chain, zaptest.NewLogger(t), nil, nil)
|
||||||
blocks := make([]*block.Block, 11)
|
blocks := make([]*block.Block, 11)
|
||||||
for i := 1; i < 11; i++ {
|
for i := 1; i < 11; i++ {
|
||||||
blocks[i] = &block.Block{Header: block.Header{Index: uint32(i)}}
|
blocks[i] = &block.Block{Header: block.Header{Index: uint32(i)}}
|
||||||
}
|
}
|
||||||
// not the ones expected currently
|
// not the ones expected currently
|
||||||
for i := 3; i < 5; i++ {
|
for i := 3; i < 5; i++ {
|
||||||
assert.NoError(t, bq.putBlock(blocks[i]))
|
assert.NoError(t, bq.PutBlock(blocks[i]))
|
||||||
}
|
}
|
||||||
last, capLeft := bq.lastQueued()
|
last, capLeft := bq.LastQueued()
|
||||||
assert.Equal(t, uint32(0), last)
|
assert.Equal(t, uint32(0), last)
|
||||||
assert.Equal(t, blockCacheSize-2, capLeft)
|
assert.Equal(t, CacheSize-2, capLeft)
|
||||||
// nothing should be put into the blockchain
|
// nothing should be put into the blockchain
|
||||||
assert.Equal(t, uint32(0), chain.BlockHeight())
|
assert.Equal(t, uint32(0), chain.BlockHeight())
|
||||||
assert.Equal(t, 2, bq.length())
|
assert.Equal(t, 2, bq.length())
|
||||||
// now added the expected ones (with duplicates)
|
// now added the expected ones (with duplicates)
|
||||||
for i := 1; i < 5; i++ {
|
for i := 1; i < 5; i++ {
|
||||||
assert.NoError(t, bq.putBlock(blocks[i]))
|
assert.NoError(t, bq.PutBlock(blocks[i]))
|
||||||
}
|
}
|
||||||
// but they're still not put into the blockchain, because bq isn't running
|
// but they're still not put into the blockchain, because bq isn't running
|
||||||
last, capLeft = bq.lastQueued()
|
last, capLeft = bq.LastQueued()
|
||||||
assert.Equal(t, uint32(4), last)
|
assert.Equal(t, uint32(4), last)
|
||||||
assert.Equal(t, blockCacheSize-4, capLeft)
|
assert.Equal(t, CacheSize-4, capLeft)
|
||||||
assert.Equal(t, uint32(0), chain.BlockHeight())
|
assert.Equal(t, uint32(0), chain.BlockHeight())
|
||||||
assert.Equal(t, 4, bq.length())
|
assert.Equal(t, 4, bq.length())
|
||||||
// block with too big index is dropped
|
// block with too big index is dropped
|
||||||
assert.NoError(t, bq.putBlock(&block.Block{Header: block.Header{Index: bq.chain.BlockHeight() + blockCacheSize + 1}}))
|
assert.NoError(t, bq.PutBlock(&block.Block{Header: block.Header{Index: bq.chain.BlockHeight() + CacheSize + 1}}))
|
||||||
assert.Equal(t, 4, bq.length())
|
assert.Equal(t, 4, bq.length())
|
||||||
go bq.run()
|
go bq.Run()
|
||||||
// run() is asynchronous, so we need some kind of timeout anyway and this is the simplest one
|
// run() is asynchronous, so we need some kind of timeout anyway and this is the simplest one
|
||||||
assert.Eventually(t, func() bool { return chain.BlockHeight() == 4 }, 4*time.Second, 100*time.Millisecond)
|
assert.Eventually(t, func() bool { return chain.BlockHeight() == 4 }, 4*time.Second, 100*time.Millisecond)
|
||||||
last, capLeft = bq.lastQueued()
|
last, capLeft = bq.LastQueued()
|
||||||
assert.Equal(t, uint32(4), last)
|
assert.Equal(t, uint32(4), last)
|
||||||
assert.Equal(t, blockCacheSize, capLeft)
|
assert.Equal(t, CacheSize, capLeft)
|
||||||
assert.Equal(t, 0, bq.length())
|
assert.Equal(t, 0, bq.length())
|
||||||
assert.Equal(t, uint32(4), chain.BlockHeight())
|
assert.Equal(t, uint32(4), chain.BlockHeight())
|
||||||
// put some old blocks
|
// put some old blocks
|
||||||
for i := 1; i < 5; i++ {
|
for i := 1; i < 5; i++ {
|
||||||
assert.NoError(t, bq.putBlock(blocks[i]))
|
assert.NoError(t, bq.PutBlock(blocks[i]))
|
||||||
}
|
}
|
||||||
last, capLeft = bq.lastQueued()
|
last, capLeft = bq.LastQueued()
|
||||||
assert.Equal(t, uint32(4), last)
|
assert.Equal(t, uint32(4), last)
|
||||||
assert.Equal(t, blockCacheSize, capLeft)
|
assert.Equal(t, CacheSize, capLeft)
|
||||||
assert.Equal(t, 0, bq.length())
|
assert.Equal(t, 0, bq.length())
|
||||||
assert.Equal(t, uint32(4), chain.BlockHeight())
|
assert.Equal(t, uint32(4), chain.BlockHeight())
|
||||||
// unexpected blocks with run() active
|
// unexpected blocks with run() active
|
||||||
assert.NoError(t, bq.putBlock(blocks[8]))
|
assert.NoError(t, bq.PutBlock(blocks[8]))
|
||||||
assert.Equal(t, 1, bq.length())
|
assert.Equal(t, 1, bq.length())
|
||||||
assert.Equal(t, uint32(4), chain.BlockHeight())
|
assert.Equal(t, uint32(4), chain.BlockHeight())
|
||||||
assert.NoError(t, bq.putBlock(blocks[7]))
|
assert.NoError(t, bq.PutBlock(blocks[7]))
|
||||||
assert.Equal(t, 2, bq.length())
|
assert.Equal(t, 2, bq.length())
|
||||||
assert.Equal(t, uint32(4), chain.BlockHeight())
|
assert.Equal(t, uint32(4), chain.BlockHeight())
|
||||||
// sparse put
|
// sparse put
|
||||||
assert.NoError(t, bq.putBlock(blocks[10]))
|
assert.NoError(t, bq.PutBlock(blocks[10]))
|
||||||
assert.Equal(t, 3, bq.length())
|
assert.Equal(t, 3, bq.length())
|
||||||
assert.Equal(t, uint32(4), chain.BlockHeight())
|
assert.Equal(t, uint32(4), chain.BlockHeight())
|
||||||
assert.NoError(t, bq.putBlock(blocks[6]))
|
assert.NoError(t, bq.PutBlock(blocks[6]))
|
||||||
assert.NoError(t, bq.putBlock(blocks[5]))
|
assert.NoError(t, bq.PutBlock(blocks[5]))
|
||||||
// run() is asynchronous, so we need some kind of timeout anyway and this is the simplest one
|
// run() is asynchronous, so we need some kind of timeout anyway and this is the simplest one
|
||||||
assert.Eventually(t, func() bool { return chain.BlockHeight() == 8 }, 4*time.Second, 100*time.Millisecond)
|
assert.Eventually(t, func() bool { return chain.BlockHeight() == 8 }, 4*time.Second, 100*time.Millisecond)
|
||||||
last, capLeft = bq.lastQueued()
|
last, capLeft = bq.LastQueued()
|
||||||
assert.Equal(t, uint32(8), last)
|
assert.Equal(t, uint32(8), last)
|
||||||
assert.Equal(t, blockCacheSize-1, capLeft)
|
assert.Equal(t, CacheSize-1, capLeft)
|
||||||
assert.Equal(t, 1, bq.length())
|
assert.Equal(t, 1, bq.length())
|
||||||
assert.Equal(t, uint32(8), chain.BlockHeight())
|
assert.Equal(t, uint32(8), chain.BlockHeight())
|
||||||
bq.discard()
|
bq.Discard()
|
||||||
assert.Equal(t, 0, bq.length())
|
assert.Equal(t, 0, bq.length())
|
||||||
}
|
}
|
||||||
|
|
||||||
// length wraps len access for tests to make them thread-safe.
|
// length wraps len access for tests to make them thread-safe.
|
||||||
func (bq *blockQueue) length() int {
|
func (bq *Queue) length() int {
|
||||||
bq.queueLock.Lock()
|
bq.queueLock.Lock()
|
||||||
defer bq.queueLock.Unlock()
|
defer bq.queueLock.Unlock()
|
||||||
return bq.len
|
return bq.len
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/network/bqueue"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/network/capability"
|
"github.com/nspcc-dev/neo-go/pkg/network/capability"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/network/extpool"
|
"github.com/nspcc-dev/neo-go/pkg/network/extpool"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/network/payload"
|
"github.com/nspcc-dev/neo-go/pkg/network/payload"
|
||||||
|
@ -57,7 +58,7 @@ type (
|
||||||
Ledger interface {
|
Ledger interface {
|
||||||
extpool.Ledger
|
extpool.Ledger
|
||||||
mempool.Feer
|
mempool.Feer
|
||||||
Blockqueuer
|
bqueue.Blockqueuer
|
||||||
GetBlock(hash util.Uint256) (*block.Block, error)
|
GetBlock(hash util.Uint256) (*block.Block, error)
|
||||||
GetConfig() config.Blockchain
|
GetConfig() config.Blockchain
|
||||||
GetHeader(hash util.Uint256) (*block.Header, error)
|
GetHeader(hash util.Uint256) (*block.Header, error)
|
||||||
|
@ -100,8 +101,8 @@ type (
|
||||||
transports []Transporter
|
transports []Transporter
|
||||||
discovery Discoverer
|
discovery Discoverer
|
||||||
chain Ledger
|
chain Ledger
|
||||||
bQueue *blockQueue
|
bQueue *bqueue.Queue
|
||||||
bSyncQueue *blockQueue
|
bSyncQueue *bqueue.Queue
|
||||||
mempool *mempool.Pool
|
mempool *mempool.Pool
|
||||||
notaryRequestPool *mempool.Pool
|
notaryRequestPool *mempool.Pool
|
||||||
extensiblePool *extpool.Pool
|
extensiblePool *extpool.Pool
|
||||||
|
@ -204,11 +205,11 @@ func newServerFromConstructors(config ServerConfig, chain Ledger, stSync StateSy
|
||||||
}, s.notaryFeer)
|
}, s.notaryFeer)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
s.bQueue = newBlockQueue(maxBlockBatch, chain, log, func(b *block.Block) {
|
s.bQueue = bqueue.New(chain, log, func(b *block.Block) {
|
||||||
s.tryStartServices()
|
s.tryStartServices()
|
||||||
})
|
}, updateBlockQueueLenMetric)
|
||||||
|
|
||||||
s.bSyncQueue = newBlockQueue(maxBlockBatch, s.stateSync, log, nil)
|
s.bSyncQueue = bqueue.New(s.stateSync, log, nil, updateBlockQueueLenMetric)
|
||||||
|
|
||||||
if s.MinPeers < 0 {
|
if s.MinPeers < 0 {
|
||||||
s.log.Info("bad MinPeers configured, using the default value",
|
s.log.Info("bad MinPeers configured, using the default value",
|
||||||
|
@ -278,8 +279,8 @@ func (s *Server) Start(errChan chan error) {
|
||||||
}
|
}
|
||||||
go s.broadcastTxLoop()
|
go s.broadcastTxLoop()
|
||||||
go s.relayBlocksLoop()
|
go s.relayBlocksLoop()
|
||||||
go s.bQueue.run()
|
go s.bQueue.Run()
|
||||||
go s.bSyncQueue.run()
|
go s.bSyncQueue.Run()
|
||||||
for _, tr := range s.transports {
|
for _, tr := range s.transports {
|
||||||
go tr.Accept()
|
go tr.Accept()
|
||||||
}
|
}
|
||||||
|
@ -297,8 +298,8 @@ func (s *Server) Shutdown() {
|
||||||
for _, p := range s.getPeers(nil) {
|
for _, p := range s.getPeers(nil) {
|
||||||
p.Disconnect(errServerShutdown)
|
p.Disconnect(errServerShutdown)
|
||||||
}
|
}
|
||||||
s.bQueue.discard()
|
s.bQueue.Discard()
|
||||||
s.bSyncQueue.discard()
|
s.bSyncQueue.Discard()
|
||||||
s.serviceLock.RLock()
|
s.serviceLock.RLock()
|
||||||
for _, svc := range s.services {
|
for _, svc := range s.services {
|
||||||
svc.Shutdown()
|
svc.Shutdown()
|
||||||
|
@ -323,6 +324,11 @@ func (s *Server) addService(svc Service) {
|
||||||
s.services[svc.Name()] = svc
|
s.services[svc.Name()] = svc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetBlockQueue returns the block queue instance managed by Server.
|
||||||
|
func (s *Server) GetBlockQueue() *bqueue.Queue {
|
||||||
|
return s.bQueue
|
||||||
|
}
|
||||||
|
|
||||||
// AddExtensibleService register a service that handles an extensible payload of some kind.
|
// AddExtensibleService register a service that handles an extensible payload of some kind.
|
||||||
func (s *Server) AddExtensibleService(svc Service, category string, handler func(*payload.Extensible) error) {
|
func (s *Server) AddExtensibleService(svc Service, category string, handler func(*payload.Extensible) error) {
|
||||||
s.serviceLock.Lock()
|
s.serviceLock.Lock()
|
||||||
|
@ -723,9 +729,9 @@ func (s *Server) handleVersionCmd(p Peer, version *payload.Version) error {
|
||||||
// handleBlockCmd processes the block received from its peer.
|
// handleBlockCmd processes the block received from its peer.
|
||||||
func (s *Server) handleBlockCmd(p Peer, block *block.Block) error {
|
func (s *Server) handleBlockCmd(p Peer, block *block.Block) error {
|
||||||
if s.stateSync.IsActive() {
|
if s.stateSync.IsActive() {
|
||||||
return s.bSyncQueue.putBlock(block)
|
return s.bSyncQueue.PutBlock(block)
|
||||||
}
|
}
|
||||||
return s.bQueue.putBlock(block)
|
return s.bQueue.PutBlock(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
// handlePing processes a ping request.
|
// handlePing processes a ping request.
|
||||||
|
@ -749,7 +755,7 @@ func (s *Server) requestBlocksOrHeaders(p Peer) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
bq Blockqueuer = s.chain
|
bq bqueue.Blockqueuer = s.chain
|
||||||
requestMPTNodes bool
|
requestMPTNodes bool
|
||||||
)
|
)
|
||||||
if s.stateSync.IsActive() {
|
if s.stateSync.IsActive() {
|
||||||
|
@ -1247,9 +1253,9 @@ func (s *Server) handleGetAddrCmd(p Peer) error {
|
||||||
// 1. Block range is divided into chunks of payload.MaxHashesCount.
|
// 1. Block range is divided into chunks of payload.MaxHashesCount.
|
||||||
// 2. Send requests for chunk in increasing order.
|
// 2. Send requests for chunk in increasing order.
|
||||||
// 3. After all requests have been sent, request random height.
|
// 3. After all requests have been sent, request random height.
|
||||||
func (s *Server) requestBlocks(bq Blockqueuer, p Peer) error {
|
func (s *Server) requestBlocks(bq bqueue.Blockqueuer, p Peer) error {
|
||||||
pl := getRequestBlocksPayload(p, bq.BlockHeight(), &s.lastRequestedBlock)
|
pl := getRequestBlocksPayload(p, bq.BlockHeight(), &s.lastRequestedBlock)
|
||||||
lq, capLeft := s.bQueue.lastQueued()
|
lq, capLeft := s.bQueue.LastQueued()
|
||||||
if capLeft == 0 {
|
if capLeft == 0 {
|
||||||
// No more blocks will fit into the queue.
|
// No more blocks will fit into the queue.
|
||||||
return nil
|
return nil
|
||||||
|
@ -1274,7 +1280,7 @@ func getRequestBlocksPayload(p Peer, currHeight uint32, lastRequestedHeight *ato
|
||||||
if !lastRequestedHeight.CAS(old, needHeight) {
|
if !lastRequestedHeight.CAS(old, needHeight) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
} else if old < currHeight+(blockCacheSize-payload.MaxHashesCount) {
|
} else if old < currHeight+(bqueue.CacheSize-payload.MaxHashesCount) {
|
||||||
needHeight = currHeight + 1
|
needHeight = currHeight + 1
|
||||||
if peerHeight > old+payload.MaxHashesCount {
|
if peerHeight > old+payload.MaxHashesCount {
|
||||||
needHeight = old + payload.MaxHashesCount
|
needHeight = old + payload.MaxHashesCount
|
||||||
|
@ -1283,7 +1289,7 @@ func getRequestBlocksPayload(p Peer, currHeight uint32, lastRequestedHeight *ato
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
index := mrand.Intn(blockCacheSize / payload.MaxHashesCount)
|
index := mrand.Intn(bqueue.CacheSize / payload.MaxHashesCount)
|
||||||
needHeight = currHeight + 1 + uint32(index*payload.MaxHashesCount)
|
needHeight = currHeight + 1 + uint32(index*payload.MaxHashesCount)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
@ -1381,7 +1387,7 @@ func (s *Server) handleMessage(peer Peer, msg *Message) error {
|
||||||
|
|
||||||
func (s *Server) tryInitStateSync() {
|
func (s *Server) tryInitStateSync() {
|
||||||
if !s.stateSync.IsActive() {
|
if !s.stateSync.IsActive() {
|
||||||
s.bSyncQueue.discard()
|
s.bSyncQueue.Discard()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1421,7 +1427,7 @@ func (s *Server) tryInitStateSync() {
|
||||||
|
|
||||||
// module can be inactive after init (i.e. full state is collected and ordinary block processing is needed)
|
// module can be inactive after init (i.e. full state is collected and ordinary block processing is needed)
|
||||||
if !s.stateSync.IsActive() {
|
if !s.stateSync.IsActive() {
|
||||||
s.bSyncQueue.discard()
|
s.bSyncQueue.Discard()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,14 @@ package network
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/mpt"
|
"github.com/nspcc-dev/neo-go/pkg/core/mpt"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/network/bqueue"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StateSync represents state sync module.
|
// StateSync represents state sync module.
|
||||||
type StateSync interface {
|
type StateSync interface {
|
||||||
AddMPTNodes([][]byte) error
|
AddMPTNodes([][]byte) error
|
||||||
Blockqueuer
|
bqueue.Blockqueuer
|
||||||
Init(currChainHeight uint32) error
|
Init(currChainHeight uint32) error
|
||||||
IsActive() bool
|
IsActive() bool
|
||||||
IsInitialized() bool
|
IsInitialized() bool
|
||||||
|
|
|
@ -65,6 +65,11 @@ type (
|
||||||
mp *mempool.Pool
|
mp *mempool.Pool
|
||||||
// requests channel
|
// requests channel
|
||||||
reqCh chan mempoolevent.Event
|
reqCh chan mempoolevent.Event
|
||||||
|
// blocksCh is a channel used to receive block notifications from the
|
||||||
|
// Blockchain. It is not buffered intentionally, as it's important to keep
|
||||||
|
// the notary request pool in sync with the current blockchain heigh, thus,
|
||||||
|
// it's not recommended to use a large size of notary requests pool as it may
|
||||||
|
// slow down the block processing.
|
||||||
blocksCh chan *block.Block
|
blocksCh chan *block.Block
|
||||||
stopCh chan struct{}
|
stopCh chan struct{}
|
||||||
done chan struct{}
|
done chan struct{}
|
||||||
|
|
|
@ -63,6 +63,9 @@ type (
|
||||||
timePerBlock time.Duration
|
timePerBlock time.Duration
|
||||||
maxRetries int
|
maxRetries int
|
||||||
relayExtensible RelayCallback
|
relayExtensible RelayCallback
|
||||||
|
// blockCh is a channel used to receive block notifications from the
|
||||||
|
// Blockchain. It has a tiny buffer in order to avoid Blockchain blocking
|
||||||
|
// on block addition under the high load.
|
||||||
blockCh chan *block.Block
|
blockCh chan *block.Block
|
||||||
stopCh chan struct{}
|
stopCh chan struct{}
|
||||||
done chan struct{}
|
done chan struct{}
|
||||||
|
@ -84,7 +87,7 @@ func New(cfg config.StateRoot, sm *stateroot.Module, log *zap.Logger, bc Ledger,
|
||||||
chain: bc,
|
chain: bc,
|
||||||
log: log,
|
log: log,
|
||||||
incompleteRoots: make(map[uint32]*incompleteRoot),
|
incompleteRoots: make(map[uint32]*incompleteRoot),
|
||||||
blockCh: make(chan *block.Block),
|
blockCh: make(chan *block.Block, 1),
|
||||||
stopCh: make(chan struct{}),
|
stopCh: make(chan struct{}),
|
||||||
done: make(chan struct{}),
|
done: make(chan struct{}),
|
||||||
timePerBlock: bcConf.TimePerBlock,
|
timePerBlock: bcConf.TimePerBlock,
|
||||||
|
|
Loading…
Reference in a new issue