mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-12-25 23:43:49 +00:00
native: add committee change events
Port neo-project/neo#3158. Close #3326 Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
This commit is contained in:
parent
a81dc5c795
commit
270515de6a
4 changed files with 101 additions and 0 deletions
|
@ -265,6 +265,10 @@ func newNEO(cfg config.ProtocolConfiguration) *NEO {
|
|||
manifest.NewParameter("to", smartcontract.PublicKeyType),
|
||||
manifest.NewParameter("amount", smartcontract.IntegerType),
|
||||
)
|
||||
n.AddEvent("CommitteeChanged",
|
||||
manifest.NewParameter("old", smartcontract.ArrayType),
|
||||
manifest.NewParameter("new", smartcontract.ArrayType),
|
||||
)
|
||||
|
||||
return n
|
||||
}
|
||||
|
@ -425,6 +429,16 @@ func (n *NEO) OnPersist(ic *interop.Context) error {
|
|||
cache := ic.DAO.GetRWCache(n.ID).(*NeoCache)
|
||||
// Cached newEpoch* values always have proper value set (either by PostPersist
|
||||
// during the last epoch block handling or by initialization code).
|
||||
|
||||
var oldCommittee, newCommittee stackitem.Item
|
||||
for i := 0; i < len(cache.committee); i++ {
|
||||
if cache.newEpochCommittee[i].Key != cache.committee[i].Key ||
|
||||
(i == 0 && len(cache.newEpochCommittee) != len(cache.committee)) {
|
||||
oldCommittee, newCommittee = cache.committee.toNotificationItem(), cache.newEpochCommittee.toNotificationItem()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
cache.nextValidators = cache.newEpochNextValidators
|
||||
cache.committee = cache.newEpochCommittee
|
||||
cache.committeeHash = cache.newEpochCommitteeHash
|
||||
|
@ -432,6 +446,12 @@ func (n *NEO) OnPersist(ic *interop.Context) error {
|
|||
|
||||
// We need to put in storage anyway, as it affects dumps
|
||||
ic.DAO.PutStorageItem(n.ID, prefixCommittee, cache.committee.Bytes(ic.DAO.GetItemCtx()))
|
||||
|
||||
if oldCommittee != nil {
|
||||
ic.AddNotification(n.Hash, "CommitteeChanged", stackitem.NewArray([]stackitem.Item{
|
||||
oldCommittee, newCommittee,
|
||||
}))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
@ -118,6 +119,70 @@ func TestNEO_CandidateEvents(t *testing.T) {
|
|||
require.Equal(t, 0, len(aer.Events))
|
||||
}
|
||||
|
||||
func TestNEO_CommitteeEvents(t *testing.T) {
|
||||
neoCommitteeInvoker := newNeoCommitteeClient(t, 100_0000_0000)
|
||||
neoValidatorsInvoker := neoCommitteeInvoker.WithSigners(neoCommitteeInvoker.Validator)
|
||||
e := neoCommitteeInvoker.Executor
|
||||
|
||||
cfg := e.Chain.GetConfig()
|
||||
committeeSize := cfg.GetCommitteeSize(0)
|
||||
|
||||
voters := make([]neotest.Signer, committeeSize)
|
||||
candidates := make([]neotest.Signer, committeeSize)
|
||||
for i := 0; i < committeeSize; 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; i++ {
|
||||
transferTx := neoValidatorsInvoker.PrepareInvoke(t, "transfer", e.Validator.ScriptHash(), voters[i].(neotest.SingleSigner).Account().PrivateKey().GetScriptHash(), int64(committeeSize-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)
|
||||
}
|
||||
block := neoValidatorsInvoker.AddNewBlock(t, txes...)
|
||||
for _, tx := range txes {
|
||||
e.CheckHalt(t, tx.Hash(), stackitem.Make(true))
|
||||
}
|
||||
|
||||
// Advance the chain to trigger committee recalculation and potential change.
|
||||
for (block.Index)%uint32(committeeSize) != 0 {
|
||||
block = neoCommitteeInvoker.AddNewBlock(t)
|
||||
}
|
||||
|
||||
// Check for CommitteeChanged event in the last persisted block's AER.
|
||||
blockHash := e.Chain.CurrentBlockHash()
|
||||
aer, err := e.Chain.GetAppExecResults(blockHash, trigger.OnPersist)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(aer))
|
||||
|
||||
require.Equal(t, aer[0].Events[0].Name, "CommitteeChanged")
|
||||
require.Equal(t, 2, len(aer[0].Events[0].Item.Value().([]stackitem.Item)))
|
||||
|
||||
expectedOldCommitteePublicKeys, err := keys.NewPublicKeysFromStrings(cfg.StandbyCommittee)
|
||||
require.NoError(t, err)
|
||||
expectedOldCommitteeStackItems := make([]stackitem.Item, len(expectedOldCommitteePublicKeys))
|
||||
for i, pubKey := range expectedOldCommitteePublicKeys {
|
||||
expectedOldCommitteeStackItems[i] = stackitem.NewByteArray(pubKey.Bytes())
|
||||
}
|
||||
oldCommitteeStackItem := aer[0].Events[0].Item.Value().([]stackitem.Item)[0].(*stackitem.Array)
|
||||
for i, item := range oldCommitteeStackItem.Value().([]stackitem.Item) {
|
||||
assert.Equal(t, expectedOldCommitteeStackItems[i].(*stackitem.ByteArray).Value().([]byte), item.Value().([]byte))
|
||||
}
|
||||
expectedNewCommitteeStackItems := make([]stackitem.Item, 0, committeeSize)
|
||||
for _, candidate := range candidates {
|
||||
expectedNewCommitteeStackItems = append(expectedNewCommitteeStackItems, stackitem.NewByteArray(candidate.(neotest.SingleSigner).Account().PublicKey().Bytes()))
|
||||
}
|
||||
newCommitteeStackItem := aer[0].Events[0].Item.Value().([]stackitem.Item)[1].(*stackitem.Array)
|
||||
for i, item := range newCommitteeStackItem.Value().([]stackitem.Item) {
|
||||
assert.Equal(t, expectedNewCommitteeStackItems[i].(*stackitem.ByteArray).Value().([]byte), item.Value().([]byte))
|
||||
}
|
||||
}
|
||||
|
||||
func TestNEO_Vote(t *testing.T) {
|
||||
neoCommitteeInvoker := newNeoCommitteeClient(t, 100_0000_0000)
|
||||
neoValidatorsInvoker := neoCommitteeInvoker.WithSigners(neoCommitteeInvoker.Validator)
|
||||
|
|
|
@ -52,6 +52,16 @@ func (k keysWithVotes) toStackItem() stackitem.Item {
|
|||
return stackitem.NewArray(arr)
|
||||
}
|
||||
|
||||
// toNotificationItem converts keysWithVotes to a stackitem.Item suitable for use in a notification,
|
||||
// including public keys only.
|
||||
func (k keysWithVotes) toNotificationItem() stackitem.Item {
|
||||
arr := make([]stackitem.Item, len(k))
|
||||
for i := range k {
|
||||
arr[i] = stackitem.NewByteArray([]byte(k[i].Key))
|
||||
}
|
||||
return stackitem.NewArray(arr)
|
||||
}
|
||||
|
||||
func (k *keysWithVotes) fromStackItem(item stackitem.Item) error {
|
||||
arr, ok := item.Value().([]stackitem.Item)
|
||||
if !ok {
|
||||
|
|
|
@ -75,6 +75,12 @@ type CandidateStateEvent struct {
|
|||
Votes *big.Int
|
||||
}
|
||||
|
||||
// CommitteeChangedEvent represents a CommitteeChanged NEO event.
|
||||
type CommitteeChangedEvent struct {
|
||||
Old []keys.PublicKey
|
||||
New []keys.PublicKey
|
||||
}
|
||||
|
||||
// VoteEvent represents a Vote NEO event.
|
||||
type VoteEvent struct {
|
||||
Account util.Uint160
|
||||
|
|
Loading…
Reference in a new issue