native: fix getvalidators to match C# implementation

ValidatorsCount is not initialized at block 0 with C# node (the first voter
initializes it) and until that initialization happens the standby validators
list is being returned as is without sorting.

Fixes state mismatch for the key ffffffff0e00000000000000000000000000000001 in
the first blocks.

It also affects tests as now the first validator is different and it receives
the network fees.
This commit is contained in:
Roman Khimov 2020-06-23 13:26:39 +03:00
parent 66df805f3b
commit 2f8e7e4d33
4 changed files with 36 additions and 24 deletions

View file

@ -213,7 +213,7 @@ func newTestService(t *testing.T) *service {
}
func getTestValidator(i int) (*privateKey, *publicKey) {
key := testchain.PrivateKey(i)
key := testchain.PrivateKeyByID(i)
return &privateKey{PrivateKey: key}, &publicKey{PublicKey: key.PublicKey()}
}
@ -241,9 +241,8 @@ func signTx(t *testing.T, feePerByte util.Fixed8, txs ...*transaction.Transactio
validators := make([]*keys.PublicKey, 4)
privNetKeys := make([]*keys.PrivateKey, 4)
for i := 0; i < 4; i++ {
privateKey, publicKey := getTestValidator(i)
validators[i] = publicKey.PublicKey
privNetKeys[i] = privateKey.PrivateKey
privNetKeys[i] = testchain.PrivateKey(i)
validators[i] = privNetKeys[i].PublicKey()
}
rawScript, err := smartcontract.CreateMultiSigRedeemScript(3, validators)
require.NoError(t, err)

View file

@ -210,7 +210,7 @@ func TestCreateBasicChain(t *testing.T) {
t.Logf("txMoveNeo: %s", txMoveNeo.Hash().StringLE())
t.Logf("txMoveGas: %s", txMoveGas.Hash().StringLE())
require.Equal(t, util.Fixed8FromInt64(1000), bc.GetUtilityTokenBalance(priv0ScriptHash))
require.True(t, util.Fixed8FromInt64(1000).CompareTo(bc.GetUtilityTokenBalance(priv0ScriptHash)) <= 0)
// info for getblockheader rpc tests
t.Logf("header hash: %s", b.Hash().StringLE())
buf := io.NewBufBinWriter()

View file

@ -112,8 +112,6 @@ func NewNEO() *NEO {
// Initialize initializes NEO contract.
func (n *NEO) Initialize(ic *interop.Context) error {
var si state.StorageItem
if err := n.nep5TokenNative.Initialize(ic); err != nil {
return err
}
@ -122,11 +120,6 @@ func (n *NEO) Initialize(ic *interop.Context) error {
return errors.New("already initialized")
}
vc := new(ValidatorsCount)
si.Value = vc.Bytes()
if err := ic.DAO.PutStorageItem(n.ContractID, validatorsCountKey, &si); err != nil {
return err
}
h, vs, err := getStandbyValidatorsHash(ic)
if err != nil {
return err
@ -284,14 +277,21 @@ func (n *NEO) VoteInternal(ic *interop.Context, h util.Uint160, pubs keys.Public
newPubs = append(newPubs, pub)
}
if lp, lv := len(newPubs), len(acc.Votes); lp != lv {
si := ic.DAO.GetStorageItem(n.ContractID, validatorsCountKey)
var si *state.StorageItem
var vc *ValidatorsCount
var err error
si = ic.DAO.GetStorageItem(n.ContractID, validatorsCountKey)
if si == nil {
return errors.New("validators count uninitialized")
}
vc, err := ValidatorsCountFromBytes(si.Value)
// The first voter.
si = new(state.StorageItem)
vc = new(ValidatorsCount)
} else {
vc, err = ValidatorsCountFromBytes(si.Value)
if err != nil {
return err
}
}
if lv > 0 {
vc[lv-1].Sub(&vc[lv-1], &acc.Balance)
}
@ -384,9 +384,13 @@ func (n *NEO) getRegisteredValidatorsCall(ic *interop.Context, _ []stackitem.Ite
// GetValidatorsInternal returns a list of current validators.
func (n *NEO) GetValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) (keys.PublicKeys, error) {
standByValidators, err := bc.GetStandByValidators()
if err != nil {
return nil, err
}
si := d.GetStorageItem(n.ContractID, validatorsCountKey)
if si == nil {
return nil, errors.New("validators count uninitialized")
return standByValidators, nil
}
validatorsCount, err := ValidatorsCountFromBytes(si.Value)
if err != nil {
@ -407,10 +411,6 @@ func (n *NEO) GetValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) (ke
})
count := validatorsCount.GetWeightedAverage()
standByValidators, err := bc.GetStandByValidators()
if err != nil {
return nil, err
}
if count < len(standByValidators) {
count = len(standByValidators)
}

View file

@ -147,7 +147,7 @@ var rpcTestCases = map[string][]rpcTestCase{
},
{
Asset: e.chain.UtilityTokenHash(),
Amount: "923.96937740",
Amount: "924.01732700",
LastUpdated: 6,
}},
Address: testchain.PrivateKeyByID(0).GetScriptHash().StringLE(),
@ -259,6 +259,7 @@ var rpcTestCases = map[string][]rpcTestCase{
// take burned gas into account
u := testchain.PrivateKeyByID(0).GetScriptHash()
for i := 0; i <= int(e.chain.BlockHeight()); i++ {
var netFee util.Fixed8
h := e.chain.GetHeaderHash(i)
b, err := e.chain.GetBlock(h)
require.NoError(t, err)
@ -274,7 +275,19 @@ var rpcTestCases = map[string][]rpcTestCase{
TxHash: b.Hash(),
})
}
netFee += b.Transactions[j].NetworkFee
}
if i > 0 {
expected.Received = append(expected.Received, result.NEP5Transfer{
Timestamp: b.Timestamp,
Asset: e.chain.UtilityTokenHash(),
Address: "", // minted from network fees.
Amount: amountToString(int64(netFee), 8),
Index: b.Index,
TxHash: b.Hash(),
})
}
}
require.Equal(t, expected.Address, res.Address)
require.ElementsMatch(t, expected.Sent, res.Sent)