From 57b294a592ec83c4338aca4a16a513e18d0ec71a Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Fri, 9 Aug 2024 15:25:55 -0700 Subject: [PATCH] Add contracts --- .github/workflows/test.yml | 26 +- .gitignore | 1 + .gitmodules | 6 + .vscode/settings.json | 11 +- contracts/.gitignore | 14 + contracts/README.md | 66 ++ contracts/foundry.toml | 6 + contracts/lib/forge-std | 1 + contracts/lib/openzeppelin-contracts | 1 + contracts/remappings.txt | 1 + contracts/script/Deployer.s.sol | 16 + contracts/src/GroupMessages.sol | 14 + contracts/src/Nodes.sol | 91 +++ contracts/test/GroupMessage.t.sol | 25 + dev/abigen | 17 + dev/contracts/.env | 3 + dev/contracts/deploy-local | 14 + dev/docker/anvil.Dockerfile | 5 + dev/docker/compose | 3 +- dev/docker/docker-compose.yml | 8 + dev/docker/down | 3 +- dev/generate | 1 + dev/up | 2 + pkg/abis/groupMessages.go | 338 +++++++++ pkg/abis/nodes.go | 657 ++++++++++++++++++ pkg/indexer/blockchain/rpcLogStreamer_test.go | 1 - pkg/utils/chainEvent.go | 28 + pkg/utils/chainEvent_test.go | 30 + 28 files changed, 1380 insertions(+), 9 deletions(-) create mode 100644 .gitmodules create mode 100644 contracts/.gitignore create mode 100644 contracts/README.md create mode 100644 contracts/foundry.toml create mode 160000 contracts/lib/forge-std create mode 160000 contracts/lib/openzeppelin-contracts create mode 100644 contracts/remappings.txt create mode 100644 contracts/script/Deployer.s.sol create mode 100644 contracts/src/GroupMessages.sol create mode 100644 contracts/src/Nodes.sol create mode 100644 contracts/test/GroupMessage.t.sol create mode 100755 dev/abigen create mode 100644 dev/contracts/.env create mode 100755 dev/contracts/deploy-local create mode 100644 dev/docker/anvil.Dockerfile create mode 100644 pkg/abis/groupMessages.go create mode 100644 pkg/abis/nodes.go create mode 100644 pkg/utils/chainEvent.go create mode 100644 pkg/utils/chainEvent_test.go diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index acbcebcd..ab29f22f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,7 +6,7 @@ on: pull_request: jobs: test: - name: Test + name: Test (Node) runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -31,3 +31,27 @@ jobs: service: xmtp-node-go files: report.xml env: ci + contracts: + name: Test (Contracts) + strategy: + fail-fast: true + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + + - name: Run Forge build + working-directory: contracts + run: | + forge --version + forge build --sizes + + - name: Run Forge tests + working-directory: contracts + run: | + forge test -vvv + id: test diff --git a/.gitignore b/.gitignore index 9c18e633..3b85664b 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ bin/ # Dependency directories (remove the comment below to include it) # vendor/ +build/* \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..ec8a8449 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "lib/forge-std"] + path = contracts/lib/forge-std + url = https://github.com/foundry-rs/forge-std +[submodule "lib/openzeppelin-contracts"] + path = contracts/lib/openzeppelin-contracts + url = https://github.com/openzeppelin/openzeppelin-contracts diff --git a/.vscode/settings.json b/.vscode/settings.json index 81a48377..0cd76396 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,10 +9,11 @@ // Instructions from https://github.com/segmentio/golines "emeraldwalk.runonsave": { "commands": [ - { - "match": "\\.go$", - "cmd": "golines ${file} -w" - } + { + "match": "\\.go$", + "cmd": "golines ${file} -w" + } ] - } + }, + "solidity.defaultCompiler": "localNodeModule" } diff --git a/contracts/.gitignore b/contracts/.gitignore new file mode 100644 index 00000000..85198aaa --- /dev/null +++ b/contracts/.gitignore @@ -0,0 +1,14 @@ +# Compiler files +cache/ +out/ + +# Ignores development broadcast logs +!/broadcast +/broadcast/*/31337/ +/broadcast/**/dry-run/ + +# Docs +docs/ + +# Dotenv file +.env diff --git a/contracts/README.md b/contracts/README.md new file mode 100644 index 00000000..9265b455 --- /dev/null +++ b/contracts/README.md @@ -0,0 +1,66 @@ +## Foundry + +**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.** + +Foundry consists of: + +- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools). +- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. +- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network. +- **Chisel**: Fast, utilitarian, and verbose solidity REPL. + +## Documentation + +https://book.getfoundry.sh/ + +## Usage + +### Build + +```shell +$ forge build +``` + +### Test + +```shell +$ forge test +``` + +### Format + +```shell +$ forge fmt +``` + +### Gas Snapshots + +```shell +$ forge snapshot +``` + +### Anvil + +```shell +$ anvil +``` + +### Deploy + +```shell +$ forge script script/Counter.s.sol:CounterScript --rpc-url --private-key +``` + +### Cast + +```shell +$ cast +``` + +### Help + +```shell +$ forge --help +$ anvil --help +$ cast --help +``` diff --git a/contracts/foundry.toml b/contracts/foundry.toml new file mode 100644 index 00000000..25b918f9 --- /dev/null +++ b/contracts/foundry.toml @@ -0,0 +1,6 @@ +[profile.default] +src = "src" +out = "out" +libs = ["lib"] + +# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options diff --git a/contracts/lib/forge-std b/contracts/lib/forge-std new file mode 160000 index 00000000..bf660614 --- /dev/null +++ b/contracts/lib/forge-std @@ -0,0 +1 @@ +Subproject commit bf6606142994b1e47e2882ce0cd477c020d77623 diff --git a/contracts/lib/openzeppelin-contracts b/contracts/lib/openzeppelin-contracts new file mode 160000 index 00000000..c304b671 --- /dev/null +++ b/contracts/lib/openzeppelin-contracts @@ -0,0 +1 @@ +Subproject commit c304b6710b4b5fcf2a319ad28c36c49df6caef14 diff --git a/contracts/remappings.txt b/contracts/remappings.txt new file mode 100644 index 00000000..eede3c19 --- /dev/null +++ b/contracts/remappings.txt @@ -0,0 +1 @@ +@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/ \ No newline at end of file diff --git a/contracts/script/Deployer.s.sol b/contracts/script/Deployer.s.sol new file mode 100644 index 00000000..5be2200f --- /dev/null +++ b/contracts/script/Deployer.s.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {Script, console} from "forge-std/Script.sol"; +import "../src/Nodes.sol"; + +contract Deployer is Script { + function setUp() public {} + + function run() public { + vm.startBroadcast(); + new Nodes(); + + vm.broadcast(); + } +} diff --git a/contracts/src/GroupMessages.sol b/contracts/src/GroupMessages.sol new file mode 100644 index 00000000..95b81043 --- /dev/null +++ b/contracts/src/GroupMessages.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +contract GroupMessages { + event MessageSent(bytes32 groupId, bytes message, uint64 sequenceId); + + uint64 sequenceId; + + function addMessage(bytes32 groupId, bytes memory message) public { + sequenceId++; + + emit MessageSent(groupId, message, sequenceId); + } +} diff --git a/contracts/src/Nodes.sol b/contracts/src/Nodes.sol new file mode 100644 index 00000000..485ff3e2 --- /dev/null +++ b/contracts/src/Nodes.sol @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import "@openzeppelin/contracts/access/Ownable.sol"; + +contract Nodes is Ownable { + constructor() Ownable(msg.sender) {} + + struct Node { + string httpAddress; + uint256 originatorId; + bool isHealthy; + // Maybe we want a TLS cert separate from the public key for MTLS authenticated connections? + } + + event NodeUpdate( + bytes publicKey, + string httpAddress, + uint256 originatorId, + bool isHealthy + ); + + // List of public keys + bytes[] publicKeys; + + // Mapping of publicKey to node + mapping(bytes => Node) public nodes; + + /** + Add a node to the network + */ + function addNode( + bytes calldata publicKey, + string calldata httpAddress + ) public onlyOwner { + require( + bytes(nodes[publicKey].httpAddress).length == 0, + "Node already exists" + ); + + require(bytes(httpAddress).length != 0, "HTTP address is required"); + + nodes[publicKey] = Node({ + httpAddress: httpAddress, + originatorId: publicKeys.length + 1, + isHealthy: true + }); + + publicKeys.push(publicKey); + + emit NodeUpdate(publicKey, httpAddress, publicKeys.length, true); + } + + /** + The contract owner can use this function to mark a node as unhealthy + triggering all other nodes to stop replicating to/from this node + */ + function markNodeUnhealthy(bytes calldata publicKey) public onlyOwner { + require( + bytes(nodes[publicKey].httpAddress).length != 0, + "Node does not exist" + ); + nodes[publicKey].isHealthy = false; + + emit NodeUpdate( + publicKey, + nodes[publicKey].httpAddress, + nodes[publicKey].originatorId, + false + ); + } + + /** + The contract owner can use this function to mark a node as healthy + triggering all other nodes to + */ + function markNodeHealthy(bytes calldata publicKey) public onlyOwner { + require( + bytes(nodes[publicKey].httpAddress).length != 0, + "Node does not exist" + ); + nodes[publicKey].isHealthy = true; + + emit NodeUpdate( + publicKey, + nodes[publicKey].httpAddress, + nodes[publicKey].originatorId, + true + ); + } +} diff --git a/contracts/test/GroupMessage.t.sol b/contracts/test/GroupMessage.t.sol new file mode 100644 index 00000000..d61c364d --- /dev/null +++ b/contracts/test/GroupMessage.t.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {Test, console} from "forge-std/Test.sol"; +import {GroupMessages} from "../src/GroupMessages.sol"; + +contract CounterTest is Test { + GroupMessages public groupMessages; + + function setUp() public { + groupMessages = new GroupMessages(); + } + + function test_AddMessage2kb() public { + bytes32 groupId = bytes32( + 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef + ); + bytes memory message = new bytes(1024); + for (uint256 i = 0; i < message.length; i++) { + message[i] = bytes1(uint8(i % 256)); // Set each byte to its index modulo 256 + } + + groupMessages.addMessage(groupId, message); + } +} diff --git a/dev/abigen b/dev/abigen new file mode 100755 index 00000000..b94878f1 --- /dev/null +++ b/dev/abigen @@ -0,0 +1,17 @@ +#!/bin/bash + +set -e + +rm -f ./build/*.abi.json +rm -f ./pkg/abis/*.go + +cd contracts + +# Generate the abi files out of the solidity code +forge inspect ./src/Nodes.sol:Nodes abi > ../build/Nodes.abi.json +forge inspect ./src/GroupMessages.sol:GroupMessages abi > ../build/GroupMessages.abi.json + +cd .. +# Generate Go code out of the ABI files +abigen --abi ./build/Nodes.abi.json --pkg abis --type Nodes --out ./pkg/abis/nodes.go +abigen --abi ./build/GroupMessages.abi.json --pkg abis --type GroupMessages --out ./pkg/abis/groupMessages.go \ No newline at end of file diff --git a/dev/contracts/.env b/dev/contracts/.env new file mode 100644 index 00000000..96f170a8 --- /dev/null +++ b/dev/contracts/.env @@ -0,0 +1,3 @@ +# This is the first default private key for anvil. Nothing sensitive here. +export PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 +export DOCKER_RPC_URL=http://localhost:7545 \ No newline at end of file diff --git a/dev/contracts/deploy-local b/dev/contracts/deploy-local new file mode 100755 index 00000000..61cc9316 --- /dev/null +++ b/dev/contracts/deploy-local @@ -0,0 +1,14 @@ +#!/bin/bash +# Deploy the smart contracts to the local anvil node + +source dev/contracts/.env + +cd ./contracts + +# Deploy a contract and save the output (which includes the contract address) to a JSON file to be used in tests +function deploy_contract() { + forge create --legacy --json --rpc-url $DOCKER_RPC_URL --private-key $PRIVATE_KEY "$1:$2" > ../build/$2.json +} + +deploy_contract src/GroupMessages.sol GroupMessages +deploy_contract src/Nodes.sol Nodes \ No newline at end of file diff --git a/dev/docker/anvil.Dockerfile b/dev/docker/anvil.Dockerfile new file mode 100644 index 00000000..1acdd2d7 --- /dev/null +++ b/dev/docker/anvil.Dockerfile @@ -0,0 +1,5 @@ +FROM ghcr.io/foundry-rs/foundry + +WORKDIR /anvil + +ENTRYPOINT anvil --host 0.0.0.0 \ No newline at end of file diff --git a/dev/docker/compose b/dev/docker/compose index ccc617d3..02ce8252 100755 --- a/dev/docker/compose +++ b/dev/docker/compose @@ -1,5 +1,6 @@ #!/bin/bash + set -e . dev/docker/env -docker_compose "$@" +docker_compose "$@" \ No newline at end of file diff --git a/dev/docker/docker-compose.yml b/dev/docker/docker-compose.yml index efd3c547..653cb67a 100644 --- a/dev/docker/docker-compose.yml +++ b/dev/docker/docker-compose.yml @@ -6,6 +6,14 @@ services: ports: - 8765:5432 + chain: + platform: linux/amd64 + build: + context: . + dockerfile: ./anvil.Dockerfile + ports: + - 7545:8545 + prometheus: image: prom/prometheus ports: diff --git a/dev/docker/down b/dev/docker/down index 94991e9f..7e6a3b83 100755 --- a/dev/docker/down +++ b/dev/docker/down @@ -1,5 +1,6 @@ -#!/usr/bin/env sh +#!/usr/bin/env bash set -e + . dev/docker/env docker_compose down diff --git a/dev/generate b/dev/generate index ca22db14..6d05c30e 100755 --- a/dev/generate +++ b/dev/generate @@ -3,6 +3,7 @@ set -e go generate ./... mockery +./dev/abigen rm -rf pkg/proto/**/*.pb.go pkg/proto/**/*.pb.gw.go pkg/proto/**/*.swagger.json if ! buf generate https://github.com/xmtp/proto.git#subdir=proto; then diff --git a/dev/up b/dev/up index 612761cb..47efabd9 100755 --- a/dev/up +++ b/dev/up @@ -3,6 +3,7 @@ set -e go mod tidy +if ! which forge &>/dev/null; then curl -L https://foundry.paradigm.xyz | bash ; fi if ! which migrate &>/dev/null; then brew install golang-migrate; fi if ! which golangci-lint &>/dev/null; then brew install golangci-lint; fi if ! which shellcheck &>/dev/null; then brew install shellcheck; fi @@ -13,3 +14,4 @@ if ! which golines &>/dev/null; then go install github.com/segmentio/golines@lat dev/generate dev/docker/up +dev/contracts/deploy-local \ No newline at end of file diff --git a/pkg/abis/groupMessages.go b/pkg/abis/groupMessages.go new file mode 100644 index 00000000..eff273f0 --- /dev/null +++ b/pkg/abis/groupMessages.go @@ -0,0 +1,338 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package abis + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// GroupMessagesMetaData contains all meta data concerning the GroupMessages contract. +var GroupMessagesMetaData = &bind.MetaData{ + ABI: "[{\"type\":\"function\",\"name\":\"addMessage\",\"inputs\":[{\"name\":\"groupId\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"message\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"event\",\"name\":\"MessageSent\",\"inputs\":[{\"name\":\"groupId\",\"type\":\"bytes32\",\"indexed\":false,\"internalType\":\"bytes32\"},{\"name\":\"message\",\"type\":\"bytes\",\"indexed\":false,\"internalType\":\"bytes\"},{\"name\":\"sequenceId\",\"type\":\"uint64\",\"indexed\":false,\"internalType\":\"uint64\"}],\"anonymous\":false}]", +} + +// GroupMessagesABI is the input ABI used to generate the binding from. +// Deprecated: Use GroupMessagesMetaData.ABI instead. +var GroupMessagesABI = GroupMessagesMetaData.ABI + +// GroupMessages is an auto generated Go binding around an Ethereum contract. +type GroupMessages struct { + GroupMessagesCaller // Read-only binding to the contract + GroupMessagesTransactor // Write-only binding to the contract + GroupMessagesFilterer // Log filterer for contract events +} + +// GroupMessagesCaller is an auto generated read-only Go binding around an Ethereum contract. +type GroupMessagesCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// GroupMessagesTransactor is an auto generated write-only Go binding around an Ethereum contract. +type GroupMessagesTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// GroupMessagesFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type GroupMessagesFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// GroupMessagesSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type GroupMessagesSession struct { + Contract *GroupMessages // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// GroupMessagesCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type GroupMessagesCallerSession struct { + Contract *GroupMessagesCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// GroupMessagesTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type GroupMessagesTransactorSession struct { + Contract *GroupMessagesTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// GroupMessagesRaw is an auto generated low-level Go binding around an Ethereum contract. +type GroupMessagesRaw struct { + Contract *GroupMessages // Generic contract binding to access the raw methods on +} + +// GroupMessagesCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type GroupMessagesCallerRaw struct { + Contract *GroupMessagesCaller // Generic read-only contract binding to access the raw methods on +} + +// GroupMessagesTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type GroupMessagesTransactorRaw struct { + Contract *GroupMessagesTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewGroupMessages creates a new instance of GroupMessages, bound to a specific deployed contract. +func NewGroupMessages(address common.Address, backend bind.ContractBackend) (*GroupMessages, error) { + contract, err := bindGroupMessages(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &GroupMessages{GroupMessagesCaller: GroupMessagesCaller{contract: contract}, GroupMessagesTransactor: GroupMessagesTransactor{contract: contract}, GroupMessagesFilterer: GroupMessagesFilterer{contract: contract}}, nil +} + +// NewGroupMessagesCaller creates a new read-only instance of GroupMessages, bound to a specific deployed contract. +func NewGroupMessagesCaller(address common.Address, caller bind.ContractCaller) (*GroupMessagesCaller, error) { + contract, err := bindGroupMessages(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &GroupMessagesCaller{contract: contract}, nil +} + +// NewGroupMessagesTransactor creates a new write-only instance of GroupMessages, bound to a specific deployed contract. +func NewGroupMessagesTransactor(address common.Address, transactor bind.ContractTransactor) (*GroupMessagesTransactor, error) { + contract, err := bindGroupMessages(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &GroupMessagesTransactor{contract: contract}, nil +} + +// NewGroupMessagesFilterer creates a new log filterer instance of GroupMessages, bound to a specific deployed contract. +func NewGroupMessagesFilterer(address common.Address, filterer bind.ContractFilterer) (*GroupMessagesFilterer, error) { + contract, err := bindGroupMessages(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &GroupMessagesFilterer{contract: contract}, nil +} + +// bindGroupMessages binds a generic wrapper to an already deployed contract. +func bindGroupMessages(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := GroupMessagesMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_GroupMessages *GroupMessagesRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _GroupMessages.Contract.GroupMessagesCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_GroupMessages *GroupMessagesRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _GroupMessages.Contract.GroupMessagesTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_GroupMessages *GroupMessagesRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _GroupMessages.Contract.GroupMessagesTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_GroupMessages *GroupMessagesCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _GroupMessages.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_GroupMessages *GroupMessagesTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _GroupMessages.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_GroupMessages *GroupMessagesTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _GroupMessages.Contract.contract.Transact(opts, method, params...) +} + +// AddMessage is a paid mutator transaction binding the contract method 0x4dff26b5. +// +// Solidity: function addMessage(bytes32 groupId, bytes message) returns() +func (_GroupMessages *GroupMessagesTransactor) AddMessage(opts *bind.TransactOpts, groupId [32]byte, message []byte) (*types.Transaction, error) { + return _GroupMessages.contract.Transact(opts, "addMessage", groupId, message) +} + +// AddMessage is a paid mutator transaction binding the contract method 0x4dff26b5. +// +// Solidity: function addMessage(bytes32 groupId, bytes message) returns() +func (_GroupMessages *GroupMessagesSession) AddMessage(groupId [32]byte, message []byte) (*types.Transaction, error) { + return _GroupMessages.Contract.AddMessage(&_GroupMessages.TransactOpts, groupId, message) +} + +// AddMessage is a paid mutator transaction binding the contract method 0x4dff26b5. +// +// Solidity: function addMessage(bytes32 groupId, bytes message) returns() +func (_GroupMessages *GroupMessagesTransactorSession) AddMessage(groupId [32]byte, message []byte) (*types.Transaction, error) { + return _GroupMessages.Contract.AddMessage(&_GroupMessages.TransactOpts, groupId, message) +} + +// GroupMessagesMessageSentIterator is returned from FilterMessageSent and is used to iterate over the raw logs and unpacked data for MessageSent events raised by the GroupMessages contract. +type GroupMessagesMessageSentIterator struct { + Event *GroupMessagesMessageSent // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *GroupMessagesMessageSentIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(GroupMessagesMessageSent) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(GroupMessagesMessageSent) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *GroupMessagesMessageSentIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *GroupMessagesMessageSentIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// GroupMessagesMessageSent represents a MessageSent event raised by the GroupMessages contract. +type GroupMessagesMessageSent struct { + GroupId [32]byte + Message []byte + SequenceId uint64 + Raw types.Log // Blockchain specific contextual infos +} + +// FilterMessageSent is a free log retrieval operation binding the contract event 0x91f47151424884a46811ed593aa8a02ee5012e9332a4dcf1e9236a8ed4443c3e. +// +// Solidity: event MessageSent(bytes32 groupId, bytes message, uint64 sequenceId) +func (_GroupMessages *GroupMessagesFilterer) FilterMessageSent(opts *bind.FilterOpts) (*GroupMessagesMessageSentIterator, error) { + + logs, sub, err := _GroupMessages.contract.FilterLogs(opts, "MessageSent") + if err != nil { + return nil, err + } + return &GroupMessagesMessageSentIterator{contract: _GroupMessages.contract, event: "MessageSent", logs: logs, sub: sub}, nil +} + +// WatchMessageSent is a free log subscription operation binding the contract event 0x91f47151424884a46811ed593aa8a02ee5012e9332a4dcf1e9236a8ed4443c3e. +// +// Solidity: event MessageSent(bytes32 groupId, bytes message, uint64 sequenceId) +func (_GroupMessages *GroupMessagesFilterer) WatchMessageSent(opts *bind.WatchOpts, sink chan<- *GroupMessagesMessageSent) (event.Subscription, error) { + + logs, sub, err := _GroupMessages.contract.WatchLogs(opts, "MessageSent") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(GroupMessagesMessageSent) + if err := _GroupMessages.contract.UnpackLog(event, "MessageSent", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseMessageSent is a log parse operation binding the contract event 0x91f47151424884a46811ed593aa8a02ee5012e9332a4dcf1e9236a8ed4443c3e. +// +// Solidity: event MessageSent(bytes32 groupId, bytes message, uint64 sequenceId) +func (_GroupMessages *GroupMessagesFilterer) ParseMessageSent(log types.Log) (*GroupMessagesMessageSent, error) { + event := new(GroupMessagesMessageSent) + if err := _GroupMessages.contract.UnpackLog(event, "MessageSent", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/pkg/abis/nodes.go b/pkg/abis/nodes.go new file mode 100644 index 00000000..1f91c859 --- /dev/null +++ b/pkg/abis/nodes.go @@ -0,0 +1,657 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package abis + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// NodesMetaData contains all meta data concerning the Nodes contract. +var NodesMetaData = &bind.MetaData{ + ABI: "[{\"type\":\"constructor\",\"inputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"addNode\",\"inputs\":[{\"name\":\"publicKey\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"httpAddress\",\"type\":\"string\",\"internalType\":\"string\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"markNodeHealthy\",\"inputs\":[{\"name\":\"publicKey\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"markNodeUnhealthy\",\"inputs\":[{\"name\":\"publicKey\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"nodes\",\"inputs\":[{\"name\":\"\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[{\"name\":\"httpAddress\",\"type\":\"string\",\"internalType\":\"string\"},{\"name\":\"originatorId\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"isHealthy\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"owner\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"renounceOwnership\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"transferOwnership\",\"inputs\":[{\"name\":\"newOwner\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"event\",\"name\":\"NodeUpdate\",\"inputs\":[{\"name\":\"publicKey\",\"type\":\"bytes\",\"indexed\":false,\"internalType\":\"bytes\"},{\"name\":\"httpAddress\",\"type\":\"string\",\"indexed\":false,\"internalType\":\"string\"},{\"name\":\"originatorId\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"},{\"name\":\"isHealthy\",\"type\":\"bool\",\"indexed\":false,\"internalType\":\"bool\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"OwnershipTransferred\",\"inputs\":[{\"name\":\"previousOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"error\",\"name\":\"OwnableInvalidOwner\",\"inputs\":[{\"name\":\"owner\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"OwnableUnauthorizedAccount\",\"inputs\":[{\"name\":\"account\",\"type\":\"address\",\"internalType\":\"address\"}]}]", +} + +// NodesABI is the input ABI used to generate the binding from. +// Deprecated: Use NodesMetaData.ABI instead. +var NodesABI = NodesMetaData.ABI + +// Nodes is an auto generated Go binding around an Ethereum contract. +type Nodes struct { + NodesCaller // Read-only binding to the contract + NodesTransactor // Write-only binding to the contract + NodesFilterer // Log filterer for contract events +} + +// NodesCaller is an auto generated read-only Go binding around an Ethereum contract. +type NodesCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// NodesTransactor is an auto generated write-only Go binding around an Ethereum contract. +type NodesTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// NodesFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type NodesFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// NodesSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type NodesSession struct { + Contract *Nodes // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// NodesCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type NodesCallerSession struct { + Contract *NodesCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// NodesTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type NodesTransactorSession struct { + Contract *NodesTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// NodesRaw is an auto generated low-level Go binding around an Ethereum contract. +type NodesRaw struct { + Contract *Nodes // Generic contract binding to access the raw methods on +} + +// NodesCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type NodesCallerRaw struct { + Contract *NodesCaller // Generic read-only contract binding to access the raw methods on +} + +// NodesTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type NodesTransactorRaw struct { + Contract *NodesTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewNodes creates a new instance of Nodes, bound to a specific deployed contract. +func NewNodes(address common.Address, backend bind.ContractBackend) (*Nodes, error) { + contract, err := bindNodes(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &Nodes{NodesCaller: NodesCaller{contract: contract}, NodesTransactor: NodesTransactor{contract: contract}, NodesFilterer: NodesFilterer{contract: contract}}, nil +} + +// NewNodesCaller creates a new read-only instance of Nodes, bound to a specific deployed contract. +func NewNodesCaller(address common.Address, caller bind.ContractCaller) (*NodesCaller, error) { + contract, err := bindNodes(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &NodesCaller{contract: contract}, nil +} + +// NewNodesTransactor creates a new write-only instance of Nodes, bound to a specific deployed contract. +func NewNodesTransactor(address common.Address, transactor bind.ContractTransactor) (*NodesTransactor, error) { + contract, err := bindNodes(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &NodesTransactor{contract: contract}, nil +} + +// NewNodesFilterer creates a new log filterer instance of Nodes, bound to a specific deployed contract. +func NewNodesFilterer(address common.Address, filterer bind.ContractFilterer) (*NodesFilterer, error) { + contract, err := bindNodes(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &NodesFilterer{contract: contract}, nil +} + +// bindNodes binds a generic wrapper to an already deployed contract. +func bindNodes(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := NodesMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Nodes *NodesRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _Nodes.Contract.NodesCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Nodes *NodesRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Nodes.Contract.NodesTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Nodes *NodesRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Nodes.Contract.NodesTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Nodes *NodesCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _Nodes.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Nodes *NodesTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Nodes.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Nodes *NodesTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Nodes.Contract.contract.Transact(opts, method, params...) +} + +// Nodes is a free data retrieval call binding the contract method 0x404608bd. +// +// Solidity: function nodes(bytes ) view returns(string httpAddress, uint256 originatorId, bool isHealthy) +func (_Nodes *NodesCaller) Nodes(opts *bind.CallOpts, arg0 []byte) (struct { + HttpAddress string + OriginatorId *big.Int + IsHealthy bool +}, error) { + var out []interface{} + err := _Nodes.contract.Call(opts, &out, "nodes", arg0) + + outstruct := new(struct { + HttpAddress string + OriginatorId *big.Int + IsHealthy bool + }) + if err != nil { + return *outstruct, err + } + + outstruct.HttpAddress = *abi.ConvertType(out[0], new(string)).(*string) + outstruct.OriginatorId = *abi.ConvertType(out[1], new(*big.Int)).(**big.Int) + outstruct.IsHealthy = *abi.ConvertType(out[2], new(bool)).(*bool) + + return *outstruct, err + +} + +// Nodes is a free data retrieval call binding the contract method 0x404608bd. +// +// Solidity: function nodes(bytes ) view returns(string httpAddress, uint256 originatorId, bool isHealthy) +func (_Nodes *NodesSession) Nodes(arg0 []byte) (struct { + HttpAddress string + OriginatorId *big.Int + IsHealthy bool +}, error) { + return _Nodes.Contract.Nodes(&_Nodes.CallOpts, arg0) +} + +// Nodes is a free data retrieval call binding the contract method 0x404608bd. +// +// Solidity: function nodes(bytes ) view returns(string httpAddress, uint256 originatorId, bool isHealthy) +func (_Nodes *NodesCallerSession) Nodes(arg0 []byte) (struct { + HttpAddress string + OriginatorId *big.Int + IsHealthy bool +}, error) { + return _Nodes.Contract.Nodes(&_Nodes.CallOpts, arg0) +} + +// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// +// Solidity: function owner() view returns(address) +func (_Nodes *NodesCaller) Owner(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _Nodes.contract.Call(opts, &out, "owner") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// +// Solidity: function owner() view returns(address) +func (_Nodes *NodesSession) Owner() (common.Address, error) { + return _Nodes.Contract.Owner(&_Nodes.CallOpts) +} + +// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// +// Solidity: function owner() view returns(address) +func (_Nodes *NodesCallerSession) Owner() (common.Address, error) { + return _Nodes.Contract.Owner(&_Nodes.CallOpts) +} + +// AddNode is a paid mutator transaction binding the contract method 0x5a48bd1d. +// +// Solidity: function addNode(bytes publicKey, string httpAddress) returns() +func (_Nodes *NodesTransactor) AddNode(opts *bind.TransactOpts, publicKey []byte, httpAddress string) (*types.Transaction, error) { + return _Nodes.contract.Transact(opts, "addNode", publicKey, httpAddress) +} + +// AddNode is a paid mutator transaction binding the contract method 0x5a48bd1d. +// +// Solidity: function addNode(bytes publicKey, string httpAddress) returns() +func (_Nodes *NodesSession) AddNode(publicKey []byte, httpAddress string) (*types.Transaction, error) { + return _Nodes.Contract.AddNode(&_Nodes.TransactOpts, publicKey, httpAddress) +} + +// AddNode is a paid mutator transaction binding the contract method 0x5a48bd1d. +// +// Solidity: function addNode(bytes publicKey, string httpAddress) returns() +func (_Nodes *NodesTransactorSession) AddNode(publicKey []byte, httpAddress string) (*types.Transaction, error) { + return _Nodes.Contract.AddNode(&_Nodes.TransactOpts, publicKey, httpAddress) +} + +// MarkNodeHealthy is a paid mutator transaction binding the contract method 0x97d6305a. +// +// Solidity: function markNodeHealthy(bytes publicKey) returns() +func (_Nodes *NodesTransactor) MarkNodeHealthy(opts *bind.TransactOpts, publicKey []byte) (*types.Transaction, error) { + return _Nodes.contract.Transact(opts, "markNodeHealthy", publicKey) +} + +// MarkNodeHealthy is a paid mutator transaction binding the contract method 0x97d6305a. +// +// Solidity: function markNodeHealthy(bytes publicKey) returns() +func (_Nodes *NodesSession) MarkNodeHealthy(publicKey []byte) (*types.Transaction, error) { + return _Nodes.Contract.MarkNodeHealthy(&_Nodes.TransactOpts, publicKey) +} + +// MarkNodeHealthy is a paid mutator transaction binding the contract method 0x97d6305a. +// +// Solidity: function markNodeHealthy(bytes publicKey) returns() +func (_Nodes *NodesTransactorSession) MarkNodeHealthy(publicKey []byte) (*types.Transaction, error) { + return _Nodes.Contract.MarkNodeHealthy(&_Nodes.TransactOpts, publicKey) +} + +// MarkNodeUnhealthy is a paid mutator transaction binding the contract method 0xf0da65bc. +// +// Solidity: function markNodeUnhealthy(bytes publicKey) returns() +func (_Nodes *NodesTransactor) MarkNodeUnhealthy(opts *bind.TransactOpts, publicKey []byte) (*types.Transaction, error) { + return _Nodes.contract.Transact(opts, "markNodeUnhealthy", publicKey) +} + +// MarkNodeUnhealthy is a paid mutator transaction binding the contract method 0xf0da65bc. +// +// Solidity: function markNodeUnhealthy(bytes publicKey) returns() +func (_Nodes *NodesSession) MarkNodeUnhealthy(publicKey []byte) (*types.Transaction, error) { + return _Nodes.Contract.MarkNodeUnhealthy(&_Nodes.TransactOpts, publicKey) +} + +// MarkNodeUnhealthy is a paid mutator transaction binding the contract method 0xf0da65bc. +// +// Solidity: function markNodeUnhealthy(bytes publicKey) returns() +func (_Nodes *NodesTransactorSession) MarkNodeUnhealthy(publicKey []byte) (*types.Transaction, error) { + return _Nodes.Contract.MarkNodeUnhealthy(&_Nodes.TransactOpts, publicKey) +} + +// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6. +// +// Solidity: function renounceOwnership() returns() +func (_Nodes *NodesTransactor) RenounceOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Nodes.contract.Transact(opts, "renounceOwnership") +} + +// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6. +// +// Solidity: function renounceOwnership() returns() +func (_Nodes *NodesSession) RenounceOwnership() (*types.Transaction, error) { + return _Nodes.Contract.RenounceOwnership(&_Nodes.TransactOpts) +} + +// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6. +// +// Solidity: function renounceOwnership() returns() +func (_Nodes *NodesTransactorSession) RenounceOwnership() (*types.Transaction, error) { + return _Nodes.Contract.RenounceOwnership(&_Nodes.TransactOpts) +} + +// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. +// +// Solidity: function transferOwnership(address newOwner) returns() +func (_Nodes *NodesTransactor) TransferOwnership(opts *bind.TransactOpts, newOwner common.Address) (*types.Transaction, error) { + return _Nodes.contract.Transact(opts, "transferOwnership", newOwner) +} + +// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. +// +// Solidity: function transferOwnership(address newOwner) returns() +func (_Nodes *NodesSession) TransferOwnership(newOwner common.Address) (*types.Transaction, error) { + return _Nodes.Contract.TransferOwnership(&_Nodes.TransactOpts, newOwner) +} + +// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. +// +// Solidity: function transferOwnership(address newOwner) returns() +func (_Nodes *NodesTransactorSession) TransferOwnership(newOwner common.Address) (*types.Transaction, error) { + return _Nodes.Contract.TransferOwnership(&_Nodes.TransactOpts, newOwner) +} + +// NodesNodeUpdateIterator is returned from FilterNodeUpdate and is used to iterate over the raw logs and unpacked data for NodeUpdate events raised by the Nodes contract. +type NodesNodeUpdateIterator struct { + Event *NodesNodeUpdate // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *NodesNodeUpdateIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(NodesNodeUpdate) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(NodesNodeUpdate) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *NodesNodeUpdateIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *NodesNodeUpdateIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// NodesNodeUpdate represents a NodeUpdate event raised by the Nodes contract. +type NodesNodeUpdate struct { + PublicKey []byte + HttpAddress string + OriginatorId *big.Int + IsHealthy bool + Raw types.Log // Blockchain specific contextual infos +} + +// FilterNodeUpdate is a free log retrieval operation binding the contract event 0x94b61d0a2ca1645b5632d2a8d7037021433f9d678f3d9bfc4b69089acb64eead. +// +// Solidity: event NodeUpdate(bytes publicKey, string httpAddress, uint256 originatorId, bool isHealthy) +func (_Nodes *NodesFilterer) FilterNodeUpdate(opts *bind.FilterOpts) (*NodesNodeUpdateIterator, error) { + + logs, sub, err := _Nodes.contract.FilterLogs(opts, "NodeUpdate") + if err != nil { + return nil, err + } + return &NodesNodeUpdateIterator{contract: _Nodes.contract, event: "NodeUpdate", logs: logs, sub: sub}, nil +} + +// WatchNodeUpdate is a free log subscription operation binding the contract event 0x94b61d0a2ca1645b5632d2a8d7037021433f9d678f3d9bfc4b69089acb64eead. +// +// Solidity: event NodeUpdate(bytes publicKey, string httpAddress, uint256 originatorId, bool isHealthy) +func (_Nodes *NodesFilterer) WatchNodeUpdate(opts *bind.WatchOpts, sink chan<- *NodesNodeUpdate) (event.Subscription, error) { + + logs, sub, err := _Nodes.contract.WatchLogs(opts, "NodeUpdate") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(NodesNodeUpdate) + if err := _Nodes.contract.UnpackLog(event, "NodeUpdate", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseNodeUpdate is a log parse operation binding the contract event 0x94b61d0a2ca1645b5632d2a8d7037021433f9d678f3d9bfc4b69089acb64eead. +// +// Solidity: event NodeUpdate(bytes publicKey, string httpAddress, uint256 originatorId, bool isHealthy) +func (_Nodes *NodesFilterer) ParseNodeUpdate(log types.Log) (*NodesNodeUpdate, error) { + event := new(NodesNodeUpdate) + if err := _Nodes.contract.UnpackLog(event, "NodeUpdate", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// NodesOwnershipTransferredIterator is returned from FilterOwnershipTransferred and is used to iterate over the raw logs and unpacked data for OwnershipTransferred events raised by the Nodes contract. +type NodesOwnershipTransferredIterator struct { + Event *NodesOwnershipTransferred // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *NodesOwnershipTransferredIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(NodesOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(NodesOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *NodesOwnershipTransferredIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *NodesOwnershipTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// NodesOwnershipTransferred represents a OwnershipTransferred event raised by the Nodes contract. +type NodesOwnershipTransferred struct { + PreviousOwner common.Address + NewOwner common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterOwnershipTransferred is a free log retrieval operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0. +// +// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner) +func (_Nodes *NodesFilterer) FilterOwnershipTransferred(opts *bind.FilterOpts, previousOwner []common.Address, newOwner []common.Address) (*NodesOwnershipTransferredIterator, error) { + + var previousOwnerRule []interface{} + for _, previousOwnerItem := range previousOwner { + previousOwnerRule = append(previousOwnerRule, previousOwnerItem) + } + var newOwnerRule []interface{} + for _, newOwnerItem := range newOwner { + newOwnerRule = append(newOwnerRule, newOwnerItem) + } + + logs, sub, err := _Nodes.contract.FilterLogs(opts, "OwnershipTransferred", previousOwnerRule, newOwnerRule) + if err != nil { + return nil, err + } + return &NodesOwnershipTransferredIterator{contract: _Nodes.contract, event: "OwnershipTransferred", logs: logs, sub: sub}, nil +} + +// WatchOwnershipTransferred is a free log subscription operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0. +// +// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner) +func (_Nodes *NodesFilterer) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *NodesOwnershipTransferred, previousOwner []common.Address, newOwner []common.Address) (event.Subscription, error) { + + var previousOwnerRule []interface{} + for _, previousOwnerItem := range previousOwner { + previousOwnerRule = append(previousOwnerRule, previousOwnerItem) + } + var newOwnerRule []interface{} + for _, newOwnerItem := range newOwner { + newOwnerRule = append(newOwnerRule, newOwnerItem) + } + + logs, sub, err := _Nodes.contract.WatchLogs(opts, "OwnershipTransferred", previousOwnerRule, newOwnerRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(NodesOwnershipTransferred) + if err := _Nodes.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseOwnershipTransferred is a log parse operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0. +// +// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner) +func (_Nodes *NodesFilterer) ParseOwnershipTransferred(log types.Log) (*NodesOwnershipTransferred, error) { + event := new(NodesOwnershipTransferred) + if err := _Nodes.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/pkg/indexer/blockchain/rpcLogStreamer_test.go b/pkg/indexer/blockchain/rpcLogStreamer_test.go index 1b84130a..55fa0ccf 100644 --- a/pkg/indexer/blockchain/rpcLogStreamer_test.go +++ b/pkg/indexer/blockchain/rpcLogStreamer_test.go @@ -40,7 +40,6 @@ func TestBuilder(t *testing.T) { streamer, err := builder.Build() require.NoError(t, err) require.NotNil(t, streamer) - } func TestRpcLogStreamer(t *testing.T) { diff --git a/pkg/utils/chainEvent.go b/pkg/utils/chainEvent.go new file mode 100644 index 00000000..55524a0f --- /dev/null +++ b/pkg/utils/chainEvent.go @@ -0,0 +1,28 @@ +package utils + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" +) + +// Extract the an event signature from an ABI by event name +func GetEventSig(abi *abi.ABI, eventName string) (string, error) { + event, ok := abi.Events[eventName] + if !ok { + return "", fmt.Errorf("event %s not found", eventName) + } + return event.Sig, nil +} + +// Extract the an event topic (the hash of the signature) from an ABI by event name +func GetEventTopic(abi *abi.ABI, eventName string) (common.Hash, error) { + sig, err := GetEventSig(abi, eventName) + if err != nil { + return common.Hash{}, err + } + hashed := crypto.Keccak256Hash([]byte(sig)) + return hashed, nil +} diff --git a/pkg/utils/chainEvent_test.go b/pkg/utils/chainEvent_test.go new file mode 100644 index 00000000..bb3d2eff --- /dev/null +++ b/pkg/utils/chainEvent_test.go @@ -0,0 +1,30 @@ +package utils + +import ( + "testing" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/stretchr/testify/require" + "github.com/xmtp/xmtpd/pkg/abis" +) + +const ( + // Update this if event changes + EXPECTED_MESSAGE_SENT_SIGNATURE = "MessageSent(bytes32,bytes,uint64)" +) + +func TestGetEventSignature(t *testing.T) { + abi, _ := abis.GroupMessagesMetaData.GetAbi() + + signature, err := GetEventSig(abi, "MessageSent") + require.NoError(t, err) + require.Equal(t, signature, EXPECTED_MESSAGE_SENT_SIGNATURE) +} + +func TestGetEventTopic(t *testing.T) { + abi, _ := abis.GroupMessagesMetaData.GetAbi() + + topic, err := GetEventTopic(abi, "MessageSent") + require.NoError(t, err) + require.Equal(t, topic, crypto.Keccak256Hash([]byte(EXPECTED_MESSAGE_SENT_SIGNATURE))) +}