Skip to content

Commit

Permalink
native: add Designation event extension starting from HFEchidna
Browse files Browse the repository at this point in the history
Close #3549

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
  • Loading branch information
AliceInHunterland committed Dec 23, 2024
1 parent 6d20772 commit dc68e39
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 9 deletions.
2 changes: 1 addition & 1 deletion docs/node-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,7 @@ in development and can change in an incompatible way.
| `Basilisk` | Enables strict smart contract script check against a set of JMP instructions and against method boundaries enabled on contract deploy or update. Increases `stackitem.Integer` JSON parsing precision up to the maximum value supported by the NeoVM. Enables strict check for notifications emitted by a contract to precisely match the events specified in the contract manifest. | https://github.com/nspcc-dev/neo-go/pull/3056 <br> https://github.com/neo-project/neo/pull/2881 <br> https://github.com/nspcc-dev/neo-go/pull/3080 <br> https://github.com/neo-project/neo/pull/2883 <br> https://github.com/nspcc-dev/neo-go/pull/3085 <br> https://github.com/neo-project/neo/pull/2810 |
| `Cockatrice` | Introduces the ability to update native contracts. Includes a couple of new native smart contract APIs: `keccak256` of native CryptoLib contract and `getCommitteeAddress` of native NeoToken contract. | https://github.com/nspcc-dev/neo-go/pull/3402 <br> https://github.com/neo-project/neo/pull/2942 <br> https://github.com/nspcc-dev/neo-go/pull/3301 <br> https://github.com/neo-project/neo/pull/2925 <br> https://github.com/nspcc-dev/neo-go/pull/3362 <br> https://github.com/neo-project/neo/pull/3154 |
| `Domovoi` | Makes node use executing contract state for the contract call permissions check instead of the state stored in the native Management contract. In C# also makes System.Runtime.GetNotifications interop properly count stack references of notification parameters which prevents users from creating objects that exceed MaxStackSize constraint, but NeoGo has never had this bug, thus proper behaviour is preserved even before HFDomovoi. It results in the fact that some T5 testnet transactions have different ApplicationLogs compared to the C# node, but the node states match. | https://github.com/nspcc-dev/neo-go/pull/3476 <br> https://github.com/neo-project/neo/pull/3290 <br> https://github.com/nspcc-dev/neo-go/pull/3473 <br> https://github.com/neo-project/neo/pull/3290 <br> https://github.com/neo-project/neo/pull/3301 <br> https://github.com/nspcc-dev/neo-go/pull/3485 |
| `Echidna` | No changes for now | https://github.com/nspcc-dev/neo-go/pull/3554 |
| `Echidna` | Introduces `Designation` event extension with `Old` and `New` roles data to native RoleManagement contract. | https://github.com/nspcc-dev/neo-go/pull/3554 <br> https://github.com/nspcc-dev/neo-go/pull/3761 |


## DB compatibility
Expand Down
23 changes: 20 additions & 3 deletions pkg/core/native/designate.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,15 @@ func newDesignate(initialNodeRoles map[noderoles.Role]keys.PublicKeys) *Designat
eDesc := newEventDescriptor(DesignationEventName,
manifest.NewParameter("Role", smartcontract.IntegerType),
manifest.NewParameter("BlockIndex", smartcontract.IntegerType))
eMD := newEvent(eDesc)
eMD := newEvent(eDesc, config.HFDefault, config.HFEchidna)
s.AddEvent(eMD)

eDesc = newEventDescriptor(DesignationEventName,
manifest.NewParameter("Role", smartcontract.IntegerType),
manifest.NewParameter("BlockIndex", smartcontract.IntegerType),
manifest.NewParameter("Old", smartcontract.ArrayType),
manifest.NewParameter("New", smartcontract.ArrayType))
eMD = newEvent(eDesc, config.HFEchidna)
s.AddEvent(eMD)

return s
Expand Down Expand Up @@ -412,10 +420,19 @@ func (s *Designate) DesignateAsRole(ic *interop.Context, r noderoles.Role, pubs
return fmt.Errorf("failed to update Designation role data cache: %w", err)
}

ic.AddNotification(s.Hash, DesignationEventName, stackitem.NewArray([]stackitem.Item{
ntf := stackitem.NewArray([]stackitem.Item{
stackitem.NewBigInteger(big.NewInt(int64(r))),
stackitem.NewBigInteger(big.NewInt(int64(ic.Block.Index))),
}))
})
if ic.IsHardforkEnabled(config.HFEchidna) {
old, _, err := s.GetDesignatedByRole(ic.DAO, r, ic.Block.Index)
if err != nil {
return fmt.Errorf("failed to get old nodes for role %d: %w", r, err)
}
ntf.Append(pubsToArray(old))
ntf.Append(pubsToArray(pubs))
}
ic.AddNotification(s.Hash, DesignationEventName, ntf)
return nil
}

Expand Down
23 changes: 18 additions & 5 deletions pkg/core/native/native_test/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,20 +117,33 @@ func testGetSetCache(t *testing.T, c *neotest.ContractInvoker, name string, defa
}
}

func setNodesByRole(t *testing.T, designateInvoker *neotest.ContractInvoker, ok bool, r noderoles.Role, nodes keys.PublicKeys) {
func setNodesByRole(t *testing.T, designateInvoker *neotest.ContractInvoker, ok bool, r noderoles.Role, nodes keys.PublicKeys, oldKeys ...keys.PublicKeys) {
pubs := make([]any, len(nodes))
for i := range nodes {
pubs[i] = nodes[i].Bytes()
}
if ok {
h := designateInvoker.Invoke(t, stackitem.Null{}, "designateAsRole", int64(r), pubs)
ntf := stackitem.NewArray([]stackitem.Item{
stackitem.Make(int64(r)),
stackitem.Make(designateInvoker.Chain.BlockHeight()),
})
if len(oldKeys) > 0 {
old := stackitem.NewArray([]stackitem.Item{})
for _, key := range oldKeys[0] {
old.Append(stackitem.NewByteArray(key.Bytes()))
}
newKeys := stackitem.NewArray([]stackitem.Item{})
for _, key := range nodes {
newKeys.Append(stackitem.NewByteArray(key.Bytes()))
}
ntf.Append(old)
ntf.Append(newKeys)
}
designateInvoker.CheckTxNotificationEvent(t, h, 0, state.NotificationEvent{
ScriptHash: designateInvoker.Hash,
Name: native.DesignationEventName,
Item: stackitem.NewArray([]stackitem.Item{
stackitem.Make(int64(r)),
stackitem.Make(designateInvoker.Chain.BlockHeight()),
}),
Item: ntf,
})
} else {
designateInvoker.InvokeFail(t, "", "designateAsRole", int64(r), pubs)
Expand Down
23 changes: 23 additions & 0 deletions pkg/core/native/native_test/designate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,29 @@ func TestDesignate_DesignateAsRole(t *testing.T) {
})
}

func TestDesignate_DesignateAsRole_Echidna(t *testing.T) {
c := newCustomNativeClient(t, nativenames.Designation, func(cfg *config.Blockchain) {
cfg.Hardforks = map[string]uint32{
config.HFEchidna.String(): 3,
}
})
designateInvoker := c.WithSigners(c.Committee)

priv, err := keys.NewPrivateKey()
require.NoError(t, err)
oldPubs := keys.PublicKeys{priv.PublicKey()}

checkNodeRoles(t, designateInvoker, true, noderoles.Oracle, 0, keys.PublicKeys{}) // BlockHeight is 1.
setNodesByRole(t, designateInvoker, true, noderoles.Oracle, oldPubs) // BlockHeight is 2.
checkNodeRoles(t, designateInvoker, true, noderoles.Oracle, designateInvoker.Chain.BlockHeight()+1, oldPubs) // BlockHeight is 3.

priv, err = keys.NewPrivateKey()
require.NoError(t, err)
pubs := keys.PublicKeys{priv.PublicKey()}
setNodesByRole(t, designateInvoker, true, noderoles.Oracle, pubs, oldPubs) // BlockHeight is 4.
checkNodeRoles(t, designateInvoker, true, noderoles.Oracle, designateInvoker.Chain.BlockHeight()+1, pubs) // BlockHeight is 5.
}

type dummyOracle struct {
updateNodes func(k keys.PublicKeys)
}
Expand Down
20 changes: 20 additions & 0 deletions pkg/core/native/native_test/management_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,19 @@ var (
nativenames.CryptoLib: `{"id":-3,"hash":"0x726cb6e0cd8628a1350a611384688911ab75f51b","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":1094259016},"manifest":{"name":"CryptoLib","abi":{"methods":[{"name":"bls12381Add","offset":0,"parameters":[{"name":"x","type":"InteropInterface"},{"name":"y","type":"InteropInterface"}],"returntype":"InteropInterface","safe":true},{"name":"bls12381Deserialize","offset":7,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"InteropInterface","safe":true},{"name":"bls12381Equal","offset":14,"parameters":[{"name":"x","type":"InteropInterface"},{"name":"y","type":"InteropInterface"}],"returntype":"Boolean","safe":true},{"name":"bls12381Mul","offset":21,"parameters":[{"name":"x","type":"InteropInterface"},{"name":"mul","type":"ByteArray"},{"name":"neg","type":"Boolean"}],"returntype":"InteropInterface","safe":true},{"name":"bls12381Pairing","offset":28,"parameters":[{"name":"g1","type":"InteropInterface"},{"name":"g2","type":"InteropInterface"}],"returntype":"InteropInterface","safe":true},{"name":"bls12381Serialize","offset":35,"parameters":[{"name":"g","type":"InteropInterface"}],"returntype":"ByteArray","safe":true},{"name":"keccak256","offset":42,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","safe":true},{"name":"murmur32","offset":49,"parameters":[{"name":"data","type":"ByteArray"},{"name":"seed","type":"Integer"}],"returntype":"ByteArray","safe":true},{"name":"ripemd160","offset":56,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","safe":true},{"name":"sha256","offset":63,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","safe":true},{"name":"verifyWithECDsa","offset":70,"parameters":[{"name":"message","type":"ByteArray"},{"name":"pubkey","type":"ByteArray"},{"name":"signature","type":"ByteArray"},{"name":"curveHash","type":"Integer"}],"returntype":"Boolean","safe":true}],"events":[]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`,
nativenames.Neo: `{"id":-5,"hash":"0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":1325686241},"manifest":{"name":"NeoToken","abi":{"methods":[{"name":"balanceOf","offset":0,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Integer","safe":true},{"name":"decimals","offset":7,"parameters":[],"returntype":"Integer","safe":true},{"name":"getAccountState","offset":14,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Array","safe":true},{"name":"getAllCandidates","offset":21,"parameters":[],"returntype":"InteropInterface","safe":true},{"name":"getCandidateVote","offset":28,"parameters":[{"name":"pubKey","type":"PublicKey"}],"returntype":"Integer","safe":true},{"name":"getCandidates","offset":35,"parameters":[],"returntype":"Array","safe":true},{"name":"getCommittee","offset":42,"parameters":[],"returntype":"Array","safe":true},{"name":"getCommitteeAddress","offset":49,"parameters":[],"returntype":"Hash160","safe":true},{"name":"getGasPerBlock","offset":56,"parameters":[],"returntype":"Integer","safe":true},{"name":"getNextBlockValidators","offset":63,"parameters":[],"returntype":"Array","safe":true},{"name":"getRegisterPrice","offset":70,"parameters":[],"returntype":"Integer","safe":true},{"name":"registerCandidate","offset":77,"parameters":[{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean","safe":false},{"name":"setGasPerBlock","offset":84,"parameters":[{"name":"gasPerBlock","type":"Integer"}],"returntype":"Void","safe":false},{"name":"setRegisterPrice","offset":91,"parameters":[{"name":"registerPrice","type":"Integer"}],"returntype":"Void","safe":false},{"name":"symbol","offset":98,"parameters":[],"returntype":"String","safe":true},{"name":"totalSupply","offset":105,"parameters":[],"returntype":"Integer","safe":true},{"name":"transfer","offset":112,"parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Boolean","safe":false},{"name":"unclaimedGas","offset":119,"parameters":[{"name":"account","type":"Hash160"},{"name":"end","type":"Integer"}],"returntype":"Integer","safe":true},{"name":"unregisterCandidate","offset":126,"parameters":[{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean","safe":false},{"name":"vote","offset":133,"parameters":[{"name":"account","type":"Hash160"},{"name":"voteTo","type":"PublicKey"}],"returntype":"Boolean","safe":false}],"events":[{"name":"Transfer","parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"}]},{"name":"CandidateStateChanged","parameters":[{"name":"pubkey","type":"PublicKey"},{"name":"registered","type":"Boolean"},{"name":"votes","type":"Integer"}]},{"name":"Vote","parameters":[{"name":"account","type":"Hash160"},{"name":"from","type":"PublicKey"},{"name":"to","type":"PublicKey"},{"name":"amount","type":"Integer"}]},{"name":"CommitteeChanged","parameters":[{"name":"old","type":"Array"},{"name":"new","type":"Array"}]}]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":["NEP-17"],"trusts":[],"extra":null},"updatecounter":0}`,
}
echidnaCSS = map[string]string{
nativenames.Designation: `{"id":-8,"hash":"0x49cf4e5378ffcd4dec034fd98a174c5491e395e2","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0A=","checksum":983638438},"manifest":{"name":"RoleManagement","abi":{"methods":[{"name":"designateAsRole","offset":0,"parameters":[{"name":"role","type":"Integer"},{"name":"nodes","type":"Array"}],"returntype":"Void","safe":false},{"name":"getDesignatedByRole","offset":7,"parameters":[{"name":"role","type":"Integer"},{"name":"index","type":"Integer"}],"returntype":"Array","safe":true}],"events":[{"name":"Designation","parameters":[{"name":"Role","type":"Integer"},{"name":"BlockIndex","type":"Integer"},{"name":"Old","type":"Array"},{"name":"New","type":"Array"}]}]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`,
}
)

func init() {
for k, v := range defaultCSS {
if _, ok := cockatriceCSS[k]; !ok {
cockatriceCSS[k] = v
}
if _, ok := echidnaCSS[k]; !ok {
echidnaCSS[k] = cockatriceCSS[k]
}
}
}

Expand Down Expand Up @@ -114,6 +120,7 @@ func TestManagement_GenesisNativeState(t *testing.T) {
config.HFAspidochelone.String(): 100500,
config.HFBasilisk.String(): 100500,
config.HFCockatrice.String(): 100500,
config.HFEchidna.String(): 100500,
}
cfg.P2PSigExtensions = true
})
Expand Down Expand Up @@ -149,6 +156,19 @@ func TestManagement_GenesisNativeState(t *testing.T) {
})
check(t, mgmt, cockatriceCSS)
})
t.Run("Echidna enabled", func(t *testing.T) {
mgmt := newCustomManagementClient(t, func(cfg *config.Blockchain) {
cfg.Hardforks = map[string]uint32{
config.HFAspidochelone.String(): 0,
config.HFBasilisk.String(): 0,
config.HFCockatrice.String(): 0,
config.HFEchidna.String(): 0,
}
cfg.P2PSigExtensions = true
})

check(t, mgmt, echidnaCSS)
})
}

func TestManagement_NativeDeployUpdateNotifications(t *testing.T) {
Expand Down

0 comments on commit dc68e39

Please sign in to comment.