Skip to content

Commit

Permalink
add events to helloworld
Browse files Browse the repository at this point in the history
  • Loading branch information
ceyonur committed Feb 27, 2024
1 parent ce4fd62 commit c8b36d5
Show file tree
Hide file tree
Showing 10 changed files with 322 additions and 44 deletions.
1 change: 1 addition & 0 deletions contracts/contracts/interfaces/IHelloWorld.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pragma solidity >=0.8.0;
import "./IAllowList.sol";

interface IHelloWorld is IAllowList {
event GreetingChanged(address indexed sender, string oldGreeting, string newGreeting);
// sayHello returns the stored greeting string
function sayHello() external view returns (string calldata result);

Expand Down
29 changes: 29 additions & 0 deletions contracts/test/hello_world.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// (c) 2019-2022, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

import { expect } from "chai"
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
import { Contract } from "ethers"
import { ethers } from "hardhat"
import { test } from "./utils"

Expand Down Expand Up @@ -32,3 +35,29 @@ describe("ExampleHelloWorldTest", function () {

test("should set and get greeting with enabled account", "step_setAndGetGreeting")
});

describe("IHelloWorld events", function () {
let owner: SignerWithAddress
let contract: Contract
let defaultGreeting = "Hello, World!"
before(async function () {
owner = await ethers.getSigner(ADMIN_ADDRESS);
contract = await ethers.getContractAt("IHelloWorld", HELLO_WORLD_ADDRESS, owner)

// reset greeting
let tx = await contract.setGreeting(defaultGreeting)
await tx.wait()
});

it("should emit GreetingChanged event", async function () {
let newGreeting = "helloprecompile"
await expect(contract.setGreeting(newGreeting)
)
.to.emit(contract, "GreetingChanged").withArgs(owner.address,
// old greeting
defaultGreeting,
// new greeting
newGreeting
)
})
})
13 changes: 6 additions & 7 deletions precompile/contracts/helloworld/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
package helloworld

import (
"math/big"

"github.com/ava-labs/subnet-evm/precompile/allowlist"
"github.com/ava-labs/subnet-evm/precompile/precompileconfig"

Expand All @@ -25,20 +23,21 @@ type Config struct {
}

// NewConfig returns a config for a network upgrade at [blockTimestamp] that enables
// HelloWorld with the given [admins] as members of the allowlist .
func NewConfig(blockTimestamp *big.Int, admins []common.Address, enableds []common.Address) *Config {
// HelloWorld with the given [admins], [enableds] and [managers] members of the allowlist .
func NewConfig(blockTimestamp *uint64, admins []common.Address, enableds []common.Address, managers []common.Address) *Config {
return &Config{
AllowListConfig: allowlist.AllowListConfig{
AdminAddresses: admins,
EnabledAddresses: enableds,
ManagerAddresses: managers,
},
Upgrade: precompileconfig.Upgrade{BlockTimestamp: blockTimestamp},
}
}

// NewDisableConfig returns config for a network upgrade at [blockTimestamp]
// that disables HelloWorld.
func NewDisableConfig(blockTimestamp *big.Int) *Config {
func NewDisableConfig(blockTimestamp *uint64) *Config {
return &Config{
Upgrade: precompileconfig.Upgrade{
BlockTimestamp: blockTimestamp,
Expand All @@ -52,9 +51,9 @@ func NewDisableConfig(blockTimestamp *big.Int) *Config {
func (*Config) Key() string { return ConfigKey }

// Verify tries to verify Config and returns an error accordingly.
func (c *Config) Verify() error {
func (c *Config) Verify(chainConfig precompileconfig.ChainConfig) error {
// Verify AllowList first
if err := c.AllowListConfig.Verify(); err != nil {
if err := c.AllowListConfig.Verify(chainConfig, c.Upgrade); err != nil {
return err
}
// CUSTOM CODE STARTS HERE
Expand Down
33 changes: 19 additions & 14 deletions precompile/contracts/helloworld/config_test.go
Original file line number Diff line number Diff line change
@@ -1,40 +1,44 @@
// (c) 2019-2023, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

// Code generated
// This file is a generated precompile config test with the skeleton of test functions.
// The file is generated by a template. Please inspect every code and comment in this file before use.

package helloworld

import (
"math/big"
"testing"

"github.com/ava-labs/subnet-evm/precompile/allowlist"
"github.com/ava-labs/subnet-evm/precompile/precompileconfig"
"github.com/ava-labs/subnet-evm/precompile/testutils"
"github.com/ava-labs/subnet-evm/utils"

"github.com/ethereum/go-ethereum/common"
"go.uber.org/mock/gomock"
)

// TestVerify tests the verification of Config.
func TestVerify(t *testing.T) {
admins := []common.Address{allowlist.TestAdminAddr}
enableds := []common.Address{allowlist.TestEnabledAddr}
managers := []common.Address{allowlist.TestManagerAddr}
tests := map[string]testutils.ConfigVerifyTest{
"valid config": {
Config: NewConfig(big.NewInt(3), admins, enableds),
Config: NewConfig(utils.NewUint64(3), admins, enableds, managers),
ChainConfig: func() precompileconfig.ChainConfig {
config := precompileconfig.NewMockChainConfig(gomock.NewController(t))
config.EXPECT().IsDurango(gomock.Any()).Return(true).AnyTimes()
return config
}(),
ExpectedError: "",
},
// CUSTOM CODE STARTS HERE
// Add your own Verify tests here, e.g.:
// "your custom test name": {
// Config: NewConfig(big.NewInt(3), admins, enableds),
// Config: NewConfig(utils.NewUint64(3), admins, enableds, managers),
// ExpectedError: ErrYourCustomError.Error(),
// },
"invalid allow list config in hello world allowlist": {
Config: NewConfig(big.NewInt(3), admins, admins),
Config: NewConfig(utils.NewUint64(3), admins, admins, nil),
ExpectedError: "cannot set address",
},
}
Expand All @@ -50,25 +54,26 @@ func TestVerify(t *testing.T) {
func TestEqual(t *testing.T) {
admins := []common.Address{allowlist.TestAdminAddr}
enableds := []common.Address{allowlist.TestEnabledAddr}
managers := []common.Address{allowlist.TestManagerAddr}
tests := map[string]testutils.ConfigEqualTest{
"non-nil config and nil other": {
Config: NewConfig(big.NewInt(3), admins, enableds),
Config: NewConfig(utils.NewUint64(3), admins, enableds, managers),
Other: nil,
Expected: false,
},
"different type": {
Config: NewConfig(big.NewInt(3), admins, enableds),
Other: precompileconfig.NewNoopStatefulPrecompileConfig(),
Config: NewConfig(utils.NewUint64(3), admins, enableds, managers),
Other: precompileconfig.NewMockConfig(gomock.NewController(t)),
Expected: false,
},
"different timestamp": {
Config: NewConfig(big.NewInt(3), admins, enableds),
Other: NewConfig(big.NewInt(4), admins, enableds),
Config: NewConfig(utils.NewUint64(3), admins, enableds, managers),
Other: NewConfig(utils.NewUint64(4), admins, enableds, managers),
Expected: false,
},
"same config": {
Config: NewConfig(big.NewInt(3), admins, enableds),
Other: NewConfig(big.NewInt(3), admins, enableds),
Config: NewConfig(utils.NewUint64(3), admins, enableds, managers),
Other: NewConfig(utils.NewUint64(3), admins, enableds, managers),
Expected: true,
},
// CUSTOM CODE STARTS HERE
Expand Down
2 changes: 1 addition & 1 deletion precompile/contracts/helloworld/contract.abi
Original file line number Diff line number Diff line change
@@ -1 +1 @@
[{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"readAllowList","outputs":[{"internalType":"uint256","name":"role","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sayHello","outputs":[{"internalType":"string","name":"result","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"setAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"setEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"response","type":"string"}],"name":"setGreeting","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"setNone","outputs":[],"stateMutability":"nonpayable","type":"function"}]
[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"string","name":"oldGreeting","type":"string"},{"indexed":false,"internalType":"string","name":"newGreeting","type":"string"}],"name":"GreetingChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"role","type":"uint256"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"oldRole","type":"uint256"}],"name":"RoleSet","type":"event"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"readAllowList","outputs":[{"internalType":"uint256","name":"role","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sayHello","outputs":[{"internalType":"string","name":"result","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"setAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"setEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"response","type":"string"}],"name":"setGreeting","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"setManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"setNone","outputs":[],"stateMutability":"nonpayable","type":"function"}]
64 changes: 58 additions & 6 deletions precompile/contracts/helloworld/contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,17 @@ func PackSayHelloOutput(result string) ([]byte, error) {
return HelloWorldABI.PackOutput("sayHello", result)
}

// UnpackSayHelloOutput attempts to unpack given [output] into the string type output
// assumes that [output] does not include selector (omits first 4 func signature bytes)
func UnpackSayHelloOutput(output []byte) (string, error) {
res, err := HelloWorldABI.Unpack("sayHello", output)
if err != nil {
return "", err
}
unpacked := *abi.ConvertType(res[0], new(string)).(*string)
return unpacked, nil
}

// GetGreeting returns the value of the storage key "storageKey" in the contract storage,
// with leading zeroes trimmed.
// This function is mostly used for tests.
Expand Down Expand Up @@ -105,8 +116,16 @@ func sayHello(accessibleState contract.AccessibleState, caller common.Address, a

// UnpackSetGreetingInput attempts to unpack [input] into the string type argument
// assumes that [input] does not include selector (omits first 4 func signature bytes)
func UnpackSetGreetingInput(input []byte) (string, error) {
res, err := HelloWorldABI.UnpackInput("setGreeting", input)
// if [useStrictMode] is true, it will return an error if the length of [input] is not [common.HashLength]
func UnpackSetGreetingInput(input []byte, useStrictMode bool) (string, error) {
// Initially we had this check to ensure that the input was the correct length.
// However solidity does not always pack the input to the correct length, and allows
// for extra padding bytes to be added to the end of the input. Therefore, we have removed
// this check with the Durango. We still need to keep this check for backwards compatibility.
if useStrictMode && len(input) > common.HashLength {
return "", ErrInputExceedsLimit
}
res, err := HelloWorldABI.UnpackInput("setGreeting", input, useStrictMode)
if err != nil {
return "", err
}
Expand Down Expand Up @@ -137,10 +156,12 @@ func setGreeting(accessibleState contract.AccessibleState, caller common.Address
if readOnly {
return nil, remainingGas, vmerrs.ErrWriteProtection
}
// do not use strict mode after Durango
useStrictMode := !contract.IsDurangoActivated(accessibleState)
// attempts to unpack [input] into the arguments to the SetGreetingInput.
// Assumes that [input] does not include selector
// You can use unpacked [inputStruct] variable in your code
inputStruct, err := UnpackSetGreetingInput(input)
inputStruct, err := UnpackSetGreetingInput(input, useStrictMode)
if err != nil {
return nil, remainingGas, err
}
Expand All @@ -157,9 +178,40 @@ func setGreeting(accessibleState contract.AccessibleState, caller common.Address
// allow list code ends here.

// CUSTOM CODE STARTS HERE
// Check if the input string is longer than HashLength
if len(inputStruct) > common.HashLength {
return nil, 0, ErrInputExceedsLimit
// With Durango, you can emit an event in your state-changing precompile functions.
// Note: If you have been using the precompile before Durango, you should activate it only after Durango.
// Activating this code before Durango will result in a consensus failure.
// If this is a new precompile and never deployed before Durango, you can activate it immediately by removing
// the if condition.
// This example assumes that the HelloWorld precompile contract has been deployed before Durango.
if contract.IsDurangoActivated(accessibleState) {
// We will first read the old greeting. So we should charge the gas for reading the storage.
if remainingGas, err = contract.DeductGas(remainingGas, contract.ReadGasCostPerSlot); err != nil {
return nil, 0, err
}
oldGreeting := GetGreeting(stateDB)

eventData := GreetingChangedEventData{
OldGreeting: oldGreeting,
NewGreeting: inputStruct,
}
topics, data, err := PackGreetingChangedEvent(caller, eventData)
if err != nil {
return nil, remainingGas, err
}
// Charge the gas for emitting the event.
eventGasCost := GetGreetingChangedEventGasCost(eventData)
if remainingGas, err = contract.DeductGas(remainingGas, eventGasCost); err != nil {
return nil, 0, err
}

// Emit the event
stateDB.AddLog(
ContractAddress,
topics,
data,
accessibleState.GetBlockContext().Number().Uint64(),
)
}

// setGreeting is the execution function
Expand Down
Loading

0 comments on commit c8b36d5

Please sign in to comment.