mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-12-23 03:41:34 +00:00
native: fix Policy's IsBlocked behaviour
Account is blocked when it's in the Policy's storage, not when it's
missing from the Policy storage. Introduced in
bbbc6805a8
.
This bug leads to the fact that during native Neo cache initialization
at the last block in the dBFT epoch, all candidates accounts are
"blocked", and thus, stand-by committee and validators are used in the
subsequent new epoch. Close #3424.
This bug may lead to the consequences described in #3273, but it needs
to be confirmed.
Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
This commit is contained in:
parent
2d4993a837
commit
c43cfae24c
2 changed files with 88 additions and 1 deletions
|
@ -6,6 +6,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
@ -316,6 +317,92 @@ func TestBlockchain_InitializeNeoCache_Bug3181(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestBlockchain_InitializeNeoCache_Bug3424 ensures that Neo cache (new epoch
|
||||||
|
// committee and stand by validators) is properly initialized after node restart
|
||||||
|
// at the dBFT epoch boundary.
|
||||||
|
func TestBlockchain_InitializeNeoCache_Bug3424(t *testing.T) {
|
||||||
|
ps, path := newLevelDBForTestingWithPath(t, "")
|
||||||
|
bc, validators, committee, err := chain.NewMultiWithCustomConfigAndStoreNoCheck(t, nil, ps)
|
||||||
|
require.NoError(t, err)
|
||||||
|
go bc.Run()
|
||||||
|
e := neotest.NewExecutor(t, bc, validators, committee)
|
||||||
|
cfg := e.Chain.GetConfig()
|
||||||
|
committeeSize := cfg.GetCommitteeSize(0)
|
||||||
|
validatorsCount := cfg.GetNumOfCNs(0)
|
||||||
|
|
||||||
|
// Stand by committee drives the chain.
|
||||||
|
standBySorted, err := keys.NewPublicKeysFromStrings(e.Chain.GetConfig().StandbyCommittee)
|
||||||
|
require.NoError(t, err)
|
||||||
|
standBySorted = standBySorted[:validatorsCount]
|
||||||
|
sort.Sort(standBySorted)
|
||||||
|
pubs := e.Chain.ComputeNextBlockValidators()
|
||||||
|
require.Equal(t, standBySorted, keys.PublicKeys(pubs))
|
||||||
|
|
||||||
|
// Move from stand by committee to the elected nodes.
|
||||||
|
e.ValidatorInvoker(e.NativeHash(t, nativenames.Gas)).Invoke(t, true, "transfer", e.Validator.ScriptHash(), e.CommitteeHash, 100_0000_0000, nil)
|
||||||
|
neoCommitteeInvoker := e.CommitteeInvoker(e.NativeHash(t, nativenames.Neo))
|
||||||
|
neoValidatorsInvoker := neoCommitteeInvoker.WithSigners(neoCommitteeInvoker.Validator)
|
||||||
|
policyInvoker := neoCommitteeInvoker.CommitteeInvoker(neoCommitteeInvoker.NativeHash(t, nativenames.Policy))
|
||||||
|
|
||||||
|
advanceChain := func(t *testing.T) {
|
||||||
|
for int(e.Chain.BlockHeight())%committeeSize != 0 {
|
||||||
|
neoCommitteeInvoker.AddNewBlock(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
advanceChainToEpochBoundary := func(t *testing.T) {
|
||||||
|
for int(e.Chain.BlockHeight()+1)%committeeSize != 0 {
|
||||||
|
neoCommitteeInvoker.AddNewBlock(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// voters vote for candidates.
|
||||||
|
voters := make([]neotest.Signer, committeeSize+1)
|
||||||
|
candidates := make([]neotest.Signer, committeeSize+1)
|
||||||
|
for i := 0; i < committeeSize+1; i++ {
|
||||||
|
voters[i] = e.NewAccount(t, 10_0000_0000)
|
||||||
|
candidates[i] = e.NewAccount(t, 2000_0000_0000) // enough for one registration
|
||||||
|
}
|
||||||
|
txes := make([]*transaction.Transaction, 0, committeeSize*3)
|
||||||
|
for i := 0; i < committeeSize+1; i++ {
|
||||||
|
transferTx := neoValidatorsInvoker.PrepareInvoke(t, "transfer", e.Validator.ScriptHash(), voters[i].(neotest.SingleSigner).Account().PrivateKey().GetScriptHash(), int64(committeeSize+1-i)*1000000, nil)
|
||||||
|
txes = append(txes, transferTx)
|
||||||
|
registerTx := neoValidatorsInvoker.WithSigners(candidates[i]).PrepareInvoke(t, "registerCandidate", candidates[i].(neotest.SingleSigner).Account().PublicKey().Bytes())
|
||||||
|
txes = append(txes, registerTx)
|
||||||
|
voteTx := neoValidatorsInvoker.WithSigners(voters[i]).PrepareInvoke(t, "vote", voters[i].(neotest.SingleSigner).Account().PrivateKey().GetScriptHash(), candidates[i].(neotest.SingleSigner).Account().PublicKey().Bytes())
|
||||||
|
txes = append(txes, voteTx)
|
||||||
|
}
|
||||||
|
txes = append(txes, policyInvoker.PrepareInvoke(t, "blockAccount", candidates[len(candidates)-1].(neotest.SingleSigner).Account().ScriptHash()))
|
||||||
|
neoValidatorsInvoker.AddNewBlock(t, txes...)
|
||||||
|
for _, tx := range txes {
|
||||||
|
e.CheckHalt(t, tx.Hash(), stackitem.Make(true)) // luckily, both `transfer`, `registerCandidate` and `vote` return boolean values
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure validators are properly updated.
|
||||||
|
advanceChain(t)
|
||||||
|
pubs = e.Chain.ComputeNextBlockValidators()
|
||||||
|
sortedCandidates := make(keys.PublicKeys, validatorsCount)
|
||||||
|
for i := range candidates[:validatorsCount] {
|
||||||
|
sortedCandidates[i] = candidates[i].(neotest.SingleSigner).Account().PublicKey()
|
||||||
|
}
|
||||||
|
sort.Sort(sortedCandidates)
|
||||||
|
require.EqualValues(t, sortedCandidates, keys.PublicKeys(pubs))
|
||||||
|
|
||||||
|
// Move to the last block in the epoch and restart the node.
|
||||||
|
advanceChainToEpochBoundary(t)
|
||||||
|
bc.Close() // Ensure persist is done and persistent store is properly closed.
|
||||||
|
ps, _ = newLevelDBForTestingWithPath(t, path)
|
||||||
|
t.Cleanup(func() { require.NoError(t, ps.Close()) })
|
||||||
|
|
||||||
|
// Start chain from the existing database that should trigger an update of native
|
||||||
|
// Neo newEpoch* cached values during initializaition. This update requires candidates
|
||||||
|
// list recalculation and caldidates policies checks.
|
||||||
|
bc, _, _, err = chain.NewMultiWithCustomConfigAndStoreNoCheck(t, nil, ps)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
pubs = bc.ComputeNextBlockValidators()
|
||||||
|
require.EqualValues(t, sortedCandidates, keys.PublicKeys(pubs))
|
||||||
|
}
|
||||||
|
|
||||||
// This test enables Notary native contract at non-zero height and checks that no
|
// This test enables Notary native contract at non-zero height and checks that no
|
||||||
// Notary cache initialization is performed before that height on node restart.
|
// Notary cache initialization is performed before that height on node restart.
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -319,7 +319,7 @@ func (p *Policy) IsBlocked(dao *dao.Simple, hash util.Uint160) bool {
|
||||||
cache := dao.GetROCache(p.ID)
|
cache := dao.GetROCache(p.ID)
|
||||||
if cache == nil {
|
if cache == nil {
|
||||||
key := append([]byte{blockedAccountPrefix}, hash.BytesBE()...)
|
key := append([]byte{blockedAccountPrefix}, hash.BytesBE()...)
|
||||||
return dao.GetStorageItem(p.ID, key) == nil
|
return dao.GetStorageItem(p.ID, key) != nil
|
||||||
}
|
}
|
||||||
_, isBlocked := p.isBlockedInternal(cache.(*PolicyCache), hash)
|
_, isBlocked := p.isBlockedInternal(cache.(*PolicyCache), hash)
|
||||||
return isBlocked
|
return isBlocked
|
||||||
|
|
Loading…
Reference in a new issue