diff --git a/miner/builder.go b/miner/builder.go index fb044e16c6..20c351ec0f 100644 --- a/miner/builder.go +++ b/miner/builder.go @@ -85,9 +85,13 @@ type SBundle struct { } func (b *Builder) AddTransaction(txn *types.Transaction) (*types.SimulateTransactionResult, error) { + // If the context is not set, the logs will not be recorded + b.env.state.SetTxContext(txn.Hash(), b.env.tcount) + logs, err := b.wrk.commitTransaction(b.env, txn) if err != nil { return &types.SimulateTransactionResult{ + Error: err.Error(), Success: false, }, nil } diff --git a/miner/builder_test.go b/miner/builder_test.go index 6ef847675d..dc615f579c 100644 --- a/miner/builder_test.go +++ b/miner/builder_test.go @@ -1,8 +1,16 @@ package miner import ( + "encoding/json" + "fmt" + "math/big" + "os" + "path/filepath" + "runtime" "testing" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus/clique" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" @@ -73,6 +81,29 @@ func TestBuilder_BuildBlock(t *testing.T) { require.Len(t, block.Transactions(), 1) } +func TestBuilder_ContractWithLogs(t *testing.T) { + // test that we can simulate a txn with a contract that emits events + t.Parallel() + + config, backend := newMockBuilderConfig(t) + + input, err := suaveExample1Artifact.Abi.Pack("get", big.NewInt(1)) + require.NoError(t, err) + + tx := backend.newCall(suaveExample1Addr, input) + + builder, err := NewBuilder(config, &BuilderArgs{}) + require.NoError(t, err) + + simResult, err := builder.AddTransaction(tx) + require.NoError(t, err) + require.True(t, simResult.Success) + require.Len(t, simResult.Logs, 1) + + require.Equal(t, simResult.Logs[0].Addr, suaveExample1Addr) + require.Equal(t, simResult.Logs[0].Topics[0], suaveExample1Artifact.Abi.Events["SomeEvent"].ID) +} + func newMockBuilderConfig(t *testing.T) (*BuilderConfig, *testWorkerBackend) { var ( db = rawdb.NewMemoryDatabase() @@ -93,3 +124,43 @@ func newMockBuilderConfig(t *testing.T) (*BuilderConfig, *testWorkerBackend) { } return bConfig, backend } + +func (b *testWorkerBackend) newCall(to common.Address, data []byte) *types.Transaction { + gasPrice := big.NewInt(10 * params.InitialBaseFee) + tx, _ := types.SignTx(types.NewTransaction(b.txPool.Nonce(testBankAddress), to, big.NewInt(0), 1000000, gasPrice, data), types.HomesteadSigner{}, testBankKey) + return tx +} + +var ( + suaveExample1Addr = common.Address{0x1} + suaveExample1Artifact = readContractArtifact("Example.sol/Example.json") +) + +type artifactObj struct { + Abi *abi.ABI `json:"abi"` + DeployedBytecode struct { + Object string + } `json:"deployedBytecode"` +} + +func readContractArtifact(name string) *artifactObj { + // Get the caller's file path. + _, filename, _, _ := runtime.Caller(1) + + // Resolve the directory of the caller's file. + callerDir := filepath.Dir(filename) + + // Construct the absolute path to the target file. + targetFilePath := filepath.Join(callerDir, "./contracts/out/", name) + + data, err := os.ReadFile(targetFilePath) + if err != nil { + panic(fmt.Sprintf("failed to read artifact %s: %v. Maybe you forgot to generate the artifacts? `cd suave && forge build`", name, err)) + } + + var obj artifactObj + if err := json.Unmarshal(data, &obj); err != nil { + panic(fmt.Sprintf("failed to unmarshal artifact %s: %v", name, err)) + } + return &obj +} diff --git a/miner/contracts/Example.sol b/miner/contracts/Example.sol new file mode 100644 index 0000000000..ee19421ae7 --- /dev/null +++ b/miner/contracts/Example.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +contract Example { + event SomeEvent(uint256 value); + + function get(uint256 value) public { + emit SomeEvent(value); + } +} diff --git a/miner/contracts/out/Example.sol/Example.json b/miner/contracts/out/Example.sol/Example.json new file mode 100644 index 0000000000..a6d29a789a --- /dev/null +++ b/miner/contracts/out/Example.sol/Example.json @@ -0,0 +1 @@ +{"abi":[{"type":"function","name":"get","inputs":[{"name":"value","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"event","name":"SomeEvent","inputs":[{"name":"value","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false}],"bytecode":{"object":"0x608060405234801561001057600080fd5b5060c28061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c80639507d39a14602d575b600080fd5b603c60383660046074565b603e565b005b6040518181527f379340f64b65a8890c7ea4f6d86d2359beaf41080f36a7ea64b78a2c06eee3f09060200160405180910390a150565b600060208284031215608557600080fd5b503591905056fea26469706673582212203cfea3df666a7e41bff65aa69c07e26051460f3288035e35ea606ab0614229b564736f6c63430008170033","sourceMap":"65:135:0:-:0;;;;;;;;;;;;;;;;;;;","linkReferences":{}},"deployedBytecode":{"object":"0x6080604052348015600f57600080fd5b506004361060285760003560e01c80639507d39a14602d575b600080fd5b603c60383660046074565b603e565b005b6040518181527f379340f64b65a8890c7ea4f6d86d2359beaf41080f36a7ea64b78a2c06eee3f09060200160405180910390a150565b600060208284031215608557600080fd5b503591905056fea26469706673582212203cfea3df666a7e41bff65aa69c07e26051460f3288035e35ea606ab0614229b564736f6c63430008170033","sourceMap":"65:135:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;125:73;;;;;;:::i;:::-;;:::i;:::-;;;175:16;;345:25:1;;;175:16:0;;333:2:1;318:18;175:16:0;;;;;;;125:73;:::o;14:180:1:-;73:6;126:2;114:9;105:7;101:23;97:32;94:52;;;142:1;139;132:12;94:52;-1:-1:-1;165:23:1;;14:180;-1:-1:-1;14:180:1:o","linkReferences":{}},"methodIdentifiers":{"get(uint256)":"9507d39a"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.23+commit.f704f362\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"SomeEvent\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"get\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"miner/contracts/Example.sol\":\"Example\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"miner/contracts/Example.sol\":{\"keccak256\":\"0xfadb2989315258b80dfee467dcef581e6782c5b69f59f5d7caa2886a355b62ff\",\"license\":\"UNLICENSED\",\"urls\":[\"bzz-raw://741d3e955be2a353512f12022dd091cc4ec6f05b7babf5f80e4761291defe761\",\"dweb:/ipfs/QmShqnnsLiNKQYKXdmnJmkUAxYHbs6DLNTt1JoR9VjRbH4\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.23+commit.f704f362"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"uint256","name":"value","type":"uint256","indexed":false}],"type":"event","name":"SomeEvent","anonymous":false},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"stateMutability":"nonpayable","type":"function","name":"get"}],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"remappings":[],"optimizer":{"enabled":true,"runs":200},"metadata":{"bytecodeHash":"ipfs"},"compilationTarget":{"miner/contracts/Example.sol":"Example"},"evmVersion":"paris","libraries":{}},"sources":{"miner/contracts/Example.sol":{"keccak256":"0xfadb2989315258b80dfee467dcef581e6782c5b69f59f5d7caa2886a355b62ff","urls":["bzz-raw://741d3e955be2a353512f12022dd091cc4ec6f05b7babf5f80e4761291defe761","dweb:/ipfs/QmShqnnsLiNKQYKXdmnJmkUAxYHbs6DLNTt1JoR9VjRbH4"],"license":"UNLICENSED"}},"version":1},"ast":{"absolutePath":"miner/contracts/Example.sol","id":17,"exportedSymbols":{"Example":[16]},"nodeType":"SourceUnit","src":"39:162:0","nodes":[{"id":1,"nodeType":"PragmaDirective","src":"39:24:0","nodes":[],"literals":["solidity","^","0.8",".13"]},{"id":16,"nodeType":"ContractDefinition","src":"65:135:0","nodes":[{"id":5,"nodeType":"EventDefinition","src":"88:31:0","nodes":[],"anonymous":false,"eventSelector":"379340f64b65a8890c7ea4f6d86d2359beaf41080f36a7ea64b78a2c06eee3f0","name":"SomeEvent","nameLocation":"94:9:0","parameters":{"id":4,"nodeType":"ParameterList","parameters":[{"constant":false,"id":3,"indexed":false,"mutability":"mutable","name":"value","nameLocation":"112:5:0","nodeType":"VariableDeclaration","scope":5,"src":"104:13:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":2,"name":"uint256","nodeType":"ElementaryTypeName","src":"104:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"103:15:0"}},{"id":15,"nodeType":"FunctionDefinition","src":"125:73:0","nodes":[],"body":{"id":14,"nodeType":"Block","src":"160:38:0","nodes":[],"statements":[{"eventCall":{"arguments":[{"id":11,"name":"value","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":7,"src":"185:5:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":10,"name":"SomeEvent","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":5,"src":"175:9:0","typeDescriptions":{"typeIdentifier":"t_function_event_nonpayable$_t_uint256_$returns$__$","typeString":"function (uint256)"}},"id":12,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"nameLocations":[],"names":[],"nodeType":"FunctionCall","src":"175:16:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":13,"nodeType":"EmitStatement","src":"170:21:0"}]},"functionSelector":"9507d39a","implemented":true,"kind":"function","modifiers":[],"name":"get","nameLocation":"134:3:0","parameters":{"id":8,"nodeType":"ParameterList","parameters":[{"constant":false,"id":7,"mutability":"mutable","name":"value","nameLocation":"146:5:0","nodeType":"VariableDeclaration","scope":15,"src":"138:13:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":6,"name":"uint256","nodeType":"ElementaryTypeName","src":"138:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"137:15:0"},"returnParameters":{"id":9,"nodeType":"ParameterList","parameters":[],"src":"160:0:0"},"scope":16,"stateMutability":"nonpayable","virtual":false,"visibility":"public"}],"abstract":false,"baseContracts":[],"canonicalName":"Example","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[16],"name":"Example","nameLocation":"74:7:0","scope":17,"usedErrors":[],"usedEvents":[5]}],"license":"UNLICENSED"},"id":0} \ No newline at end of file diff --git a/miner/worker_test.go b/miner/worker_test.go index 59fbbbcdca..7eb517c927 100644 --- a/miner/worker_test.go +++ b/miner/worker_test.go @@ -118,6 +118,10 @@ func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine Config: chainConfig, Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}, } + // == SUAVE SPECIFIC == + // Add custom contracts for the genesis + gspec.Alloc[suaveExample1Addr] = core.GenesisAccount{Balance: big.NewInt(0), Code: common.FromHex(suaveExample1Artifact.DeployedBytecode.Object)} + // == END OF SUAVE SPECIFIC == switch e := engine.(type) { case *clique.Clique: gspec.ExtraData = make([]byte, 32+common.AddressLength+crypto.SignatureLength)