mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-11-29 03:41:45 +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("to", smartcontract.PublicKeyType),
|
||||||
manifest.NewParameter("amount", smartcontract.IntegerType),
|
manifest.NewParameter("amount", smartcontract.IntegerType),
|
||||||
)
|
)
|
||||||
|
n.AddEvent("CommitteeChanged",
|
||||||
|
manifest.NewParameter("old", smartcontract.ArrayType),
|
||||||
|
manifest.NewParameter("new", smartcontract.ArrayType),
|
||||||
|
)
|
||||||
|
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
@ -425,6 +429,16 @@ func (n *NEO) OnPersist(ic *interop.Context) error {
|
||||||
cache := ic.DAO.GetRWCache(n.ID).(*NeoCache)
|
cache := ic.DAO.GetRWCache(n.ID).(*NeoCache)
|
||||||
// Cached newEpoch* values always have proper value set (either by PostPersist
|
// Cached newEpoch* values always have proper value set (either by PostPersist
|
||||||
// during the last epoch block handling or by initialization code).
|
// 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.nextValidators = cache.newEpochNextValidators
|
||||||
cache.committee = cache.newEpochCommittee
|
cache.committee = cache.newEpochCommittee
|
||||||
cache.committeeHash = cache.newEpochCommitteeHash
|
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
|
// We need to put in storage anyway, as it affects dumps
|
||||||
ic.DAO.PutStorageItem(n.ID, prefixCommittee, cache.committee.Bytes(ic.DAO.GetItemCtx()))
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
"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/opcode"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -118,6 +119,70 @@ func TestNEO_CandidateEvents(t *testing.T) {
|
||||||
require.Equal(t, 0, len(aer.Events))
|
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) {
|
func TestNEO_Vote(t *testing.T) {
|
||||||
neoCommitteeInvoker := newNeoCommitteeClient(t, 100_0000_0000)
|
neoCommitteeInvoker := newNeoCommitteeClient(t, 100_0000_0000)
|
||||||
neoValidatorsInvoker := neoCommitteeInvoker.WithSigners(neoCommitteeInvoker.Validator)
|
neoValidatorsInvoker := neoCommitteeInvoker.WithSigners(neoCommitteeInvoker.Validator)
|
||||||
|
|
|
@ -52,6 +52,16 @@ func (k keysWithVotes) toStackItem() stackitem.Item {
|
||||||
return stackitem.NewArray(arr)
|
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 {
|
func (k *keysWithVotes) fromStackItem(item stackitem.Item) error {
|
||||||
arr, ok := item.Value().([]stackitem.Item)
|
arr, ok := item.Value().([]stackitem.Item)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
|
@ -75,6 +75,12 @@ type CandidateStateEvent struct {
|
||||||
Votes *big.Int
|
Votes *big.Int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CommitteeChangedEvent represents a CommitteeChanged NEO event.
|
||||||
|
type CommitteeChangedEvent struct {
|
||||||
|
Old []keys.PublicKey
|
||||||
|
New []keys.PublicKey
|
||||||
|
}
|
||||||
|
|
||||||
// VoteEvent represents a Vote NEO event.
|
// VoteEvent represents a Vote NEO event.
|
||||||
type VoteEvent struct {
|
type VoteEvent struct {
|
||||||
Account util.Uint160
|
Account util.Uint160
|
||||||
|
|
Loading…
Reference in a new issue