Skip to content

Commit

Permalink
native: add committee change events
Browse files Browse the repository at this point in the history
Port neo-project/neo#3158.

Close #3326

Signed-off-by: Ekaterina Pavlova <[email protected]>
  • Loading branch information
AliceInHunterland committed Mar 18, 2024
1 parent bfc3aa6 commit 20fa789
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 0 deletions.
39 changes: 39 additions & 0 deletions pkg/core/native/native_neo.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -425,17 +429,52 @@ 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).

committeeChanged, prevCommitteeStackItem, newCommitteeStackItem := n.checkCommitteeChange(cache)

cache.nextValidators = cache.newEpochNextValidators
cache.committee = cache.newEpochCommittee
cache.committeeHash = cache.newEpochCommitteeHash
cache.votesChanged = false

if committeeChanged {
// Notify subscribers if the committee has changed.
ic.AddNotification(n.Hash, "CommitteeChanged", stackitem.NewArray([]stackitem.Item{
prevCommitteeStackItem, newCommitteeStackItem,
}))
}
// We need to put in storage anyway, as it affects dumps
ic.DAO.PutStorageItem(n.ID, prefixCommittee, cache.committee.Bytes(ic.DAO.GetItemCtx()))
}
return nil
}

// checkCommitteeChange checks if the committee has changed and returns the previous and new committee as stackitem.Item.
func (n *NEO) checkCommitteeChange(cache *NeoCache) (bool, stackitem.Item, stackitem.Item) {
if len(cache.committee) != len(cache.newEpochCommittee) {
return true, cache.committee.toNotificationItem(), cache.newEpochCommittee.toNotificationItem()
}

// Compare every key in the new committee to every key in the previous committee.
for i := 0; i < len(cache.newEpochCommittee); i++ {
keyFound := false

for j := 0; j < len(cache.committee) && !keyFound; j++ {
if cache.newEpochCommittee[i].Key == cache.committee[j].Key {
keyFound = true
}
}

// If a key in the new committee wasn't found in the previous committee, a change has occurred.
if !keyFound {
return true, cache.committee.toNotificationItem(), cache.newEpochCommittee.toNotificationItem()
}
}

// No changes detected in the committee.
return false, nil, nil
}

// PostPersist implements the Contract interface.
func (n *NEO) PostPersist(ic *interop.Context) error {
gas := n.GetGASPerBlock(ic.DAO, ic.Block.Index)
Expand Down
48 changes: 48 additions & 0 deletions pkg/core/native/native_test/neo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,54 @@ func TestNEO_CandidateEvents(t *testing.T) {
aer = e.GetTxExecResult(t, tx)
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.NotEmpty(t, aer)
found := false
for _, event := range aer[0].Events {
if event.Name == "CommitteeChanged" {
found = true
break
}
}
require.True(t, found, "CommitteeChanged event not found")
}

func TestNEO_Vote(t *testing.T) {
neoCommitteeInvoker := newNeoCommitteeClient(t, 100_0000_0000)
Expand Down
12 changes: 12 additions & 0 deletions pkg/core/native/neo_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,18 @@ 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.NewStruct([]stackitem.Item{
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 {
Expand Down
6 changes: 6 additions & 0 deletions pkg/rpcclient/neo/neo.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 20fa789

Please sign in to comment.