upgrade to ethermint v0.21.0 #99

Closed
0xmuralik wants to merge 384 commits from murali/update-fork into main
16 changed files with 1847 additions and 587 deletions
Showing only changes of commit ecd76396eb - Show all commits

View File

@ -20,8 +20,8 @@ schema = 3
version = "v1.0.0-beta.7"
hash = "sha256-XblGvIx6Wvvq6wggXjp+KbeJGXoe7AZH7hXEdauCezU="
[mod."cosmossdk.io/math"]
version = "v1.0.0-beta.3"
hash = "sha256-lTQ27ZlL+kWlc+S//sJmyiOwaf9qS+YLv61I4OXi9XE="
version = "v1.0.0-beta.4"
hash = "sha256-UYdq/46EubyjxkldGike8FlwJLWGCB576VB7th285ao="
[mod."filippo.io/edwards25519"]
version = "v1.0.0-rc.1"
hash = "sha256-3DboBqby2ejRU33FG96Z8JF5AJ8HP2rC/v++VyoQ2LQ="

View File

@ -6,7 +6,7 @@ cd "$(dirname "$0")"
export TMPDIR=/tmp
echo "build test contracts"
cd ../tests/integration_tests/contracts
cd ../tests/integration_tests/hardhat
HUSKY_SKIP_INSTALL=1 npm install
npm run typechain
cd ..

View File

@ -1,9 +0,0 @@
pragma solidity 0.8.10;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract TestERC20A is ERC20 {
constructor() public ERC20("TestERC20", "Test") {
_mint(msg.sender, 100000000000000000000000000);
}
}

View File

@ -0,0 +1,25 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
contract Mars is Initializable, ERC20Upgradeable, UUPSUpgradeable, OwnableUpgradeable {
function initialize() public initializer {
__ERC20_init("Mars", "MRS");
__Ownable_init();
_mint(msg.sender, 1000000 * 10 ** decimals());
}
function _authorizeUpgrade(address newImplementation) internal
override
onlyOwner {}
}
contract MarsV2 is Mars {
function version() pure public returns (string memory) {
return "v2";
}
}

View File

@ -0,0 +1,10 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract TestERC20A is ERC20 {
constructor() public ERC20("TestERC20", "Test") {
_mint(msg.sender, 100000000000000000000000000);
}
}

View File

@ -1,5 +1,7 @@
import { HardhatUserConfig } from "hardhat/config";
import type { HardhatUserConfig } from "hardhat/config";
import "hardhat-typechain";
import "@openzeppelin/hardhat-upgrades";
import "@nomiclabs/hardhat-ethers";
const config: HardhatUserConfig = {
solidity: {

View File

@ -10,9 +10,11 @@
"author": "",
"license": "ISC",
"dependencies": {
"@nomiclabs/hardhat-ethers": "^2.1.0",
"@nomiclabs/hardhat-waffle": "^2.0.3",
"@openzeppelin/contracts": "^4.7.0",
"@openzeppelin/contracts": "^4.8.0",
"@nomiclabs/hardhat-ethers": "^2.2.1",
"@openzeppelin/hardhat-upgrades": "^1.21.0",
"@openzeppelin/contracts-upgradeable": "^4.3.1",
"@typechain/ethers-v5": "^5.0.0",
"hardhat": "^2.10.1",
"hardhat-typechain": "^0.3.5",

View File

@ -1,4 +1,6 @@
import pytest
from eth_abi import abi
from hexbytes import HexBytes
from web3 import Web3
from .utils import (
@ -9,6 +11,15 @@ from .utils import (
send_transaction,
)
# Smart contract names
GREETER_CONTRACT = "Greeter"
ERC20_CONTRACT = "TestERC20A"
# ChangeGreeting topic from Greeter contract calculated from event signature
CHANGE_GREETING_TOPIC = Web3.keccak(text="ChangeGreeting(address,string)")
# ERC-20 Transfer event topic
TRANSFER_TOPIC = Web3.keccak(text="Transfer(address,address,uint256)")
def test_pending_transaction_filter(cluster):
github-code-scanning[bot] commented 2023-03-13 07:10:33 +00:00 (Migrated from github.com)
Review

Variable defined multiple times

This assignment to 'test_get_logs_by_topic' is unnecessary as it is redefined before this value is used.

Show more details

## Variable defined multiple times This assignment to 'test_get_logs_by_topic' is unnecessary as it is [redefined](1) before this value is used. [Show more details](https://github.com/cerc-io/laconicd/security/code-scanning/658)
w3: Web3 = cluster.w3
@ -65,7 +76,9 @@ def test_event_log_filter_by_contract(cluster):
assert flt.get_all_entries() == [] # GetFilterLogs
# with tx
tx = contract.functions.setGreeting("world").build_transaction()
tx = contract.functions.setGreeting("world").build_transaction(
{"from": ADDRS["validator"]}
)
tx_receipt = send_transaction(w3, tx)
assert tx_receipt.status == 1
@ -102,7 +115,9 @@ def test_event_log_filter_by_address(cluster):
assert flt.get_all_entries() == [] # GetFilterLogs
# with tx
tx = contract.functions.setGreeting("world").build_transaction()
tx = contract.functions.setGreeting("world").build_transaction(
{"from": ADDRS["validator"]}
)
receipt = send_transaction(w3, tx)
assert receipt.status == 1
@ -110,19 +125,565 @@ def test_event_log_filter_by_address(cluster):
assert len(flt2.get_new_entries()) == 0
def test_get_logs(cluster):
def test_event_log_filter_by_topic(cluster):
w3: Web3 = cluster.w3
new_greeting = "world"
test_cases = [
{
"name": "one contract emiting one topic",
"filters": [
{"topics": [CHANGE_GREETING_TOPIC.hex()]},
{
"fromBlock": 1,
"toBlock": "latest",
"topics": [CHANGE_GREETING_TOPIC.hex()],
},
],
"exp_len": 1,
"exp_topics": [[CHANGE_GREETING_TOPIC]],
"contracts": [GREETER_CONTRACT],
},
{
"name": "multiple contracts emitting same topic",
"filters": [
{
"topics": [CHANGE_GREETING_TOPIC.hex()],
},
{
"fromBlock": 1,
"toBlock": "latest",
"topics": [CHANGE_GREETING_TOPIC.hex()],
},
],
"exp_len": 5,
"exp_topics": [[CHANGE_GREETING_TOPIC]],
"contracts": [GREETER_CONTRACT] * 5,
},
{
"name": "multiple contracts emitting different topics",
"filters": [
{
"topics": [[CHANGE_GREETING_TOPIC.hex(), TRANSFER_TOPIC.hex()]],
},
{
"fromBlock": 1,
"toBlock": "latest",
"topics": [[CHANGE_GREETING_TOPIC.hex(), TRANSFER_TOPIC.hex()]],
},
],
"exp_len": 3, # 2 transfer events, mint&transfer on deploy (2)tx in test
"exp_topics": [
[CHANGE_GREETING_TOPIC],
[
TRANSFER_TOPIC,
HexBytes(pad_left("0x0")),
HexBytes(pad_left(ADDRS["validator"].lower())),
],
[
TRANSFER_TOPIC,
HexBytes(pad_left(ADDRS["validator"].lower())),
HexBytes(pad_left(ADDRS["community"].lower())),
],
],
"contracts": [GREETER_CONTRACT, ERC20_CONTRACT],
},
]
for tc in test_cases:
print("\nCase: {}".format(tc["name"]))
# register filters
filters = []
for fltr in tc["filters"]:
filters.append(w3.eth.filter(fltr))
# without tx: filters should not return any entries
for flt in filters:
assert flt.get_new_entries() == [] # GetFilterChanges
# deploy all contracts
# perform tx that emits event in all contracts
for c in tc["contracts"]:
tx = None
if c == GREETER_CONTRACT:
contract, _ = deploy_contract(w3, CONTRACTS[c])
# validate deploy was successfull
assert contract.caller.greet() == "Hello"
# create tx that emits event
tx = contract.functions.setGreeting(new_greeting).build_transaction(
{"from": ADDRS["validator"]}
)
elif c == ERC20_CONTRACT:
contract, _ = deploy_contract(w3, CONTRACTS[c])
# validate deploy was successfull
assert contract.caller.name() == "TestERC20"
# create tx that emits event
tx = contract.functions.transfer(
ADDRS["community"], 10
).build_transaction({"from": ADDRS["validator"]})
receipt = send_transaction(w3, tx)
assert receipt.status == 1
# check filters new entries
for flt in filters:
new_entries = flt.get_new_entries() # GetFilterChanges
assert len(new_entries) == tc["exp_len"]
for log in new_entries:
# check if the new_entries have valid information
assert log["topics"] in tc["exp_topics"]
assert_log_block(w3, log)
# on next call of GetFilterChanges, no entries should be found
# because there were no new logs that meet the filters params
for flt in filters:
assert flt.get_new_entries() == [] # GetFilterChanges
w3.eth.uninstall_filter(flt.filter_id)
def test_multiple_filters(cluster):
w3: Web3 = cluster.w3
contract, _ = deploy_contract(w3, CONTRACTS["Greeter"])
# test the contract was deployed successfully
assert contract.caller.greet() == "Hello"
# without tx
new_greeting = "hello, world"
# calculate topic from event signature
topic = CHANGE_GREETING_TOPIC
# another topic not related to the contract deployed
another_topic = TRANSFER_TOPIC
filters = [
{
"params": {"address": contract.address},
"exp_len": 1,
},
{
"params": {"topics": [topic.hex()]},
"exp_len": 1,
},
{
"params": {
"topics": [
topic.hex(),
another_topic.hex(),
], # 'with all topics' condition
},
"exp_len": 0,
},
{
"params": {
"topics": [
[topic.hex(), another_topic.hex()]
], # 'with any topic' condition
},
"exp_len": 1,
},
{
"params": {
"address": contract.address,
"topics": [[topic.hex(), another_topic.hex()]],
},
"exp_len": 1,
},
{
"params": {
"fromBlock": 1,
"toBlock": 2,
"address": contract.address,
"topics": [[topic.hex(), another_topic.hex()]],
},
"exp_len": 0,
},
{
"params": {
"fromBlock": 1,
"toBlock": "latest",
"address": contract.address,
"topics": [[topic.hex(), another_topic.hex()]],
},
"exp_len": 1,
},
{
"params": {
"fromBlock": 1,
"toBlock": "latest",
"topics": [[topic.hex(), another_topic.hex()]],
},
"exp_len": 1,
},
]
test_cases = [
{"name": "register multiple filters and check for updates", "filters": filters},
{
"name": "register more filters than allowed (default: 200)",
"register_err": "error creating filter: max limit reached",
"filters": make_filter_array(205),
},
{
"name": "register some filters, remove 2 filters and check for updates",
"filters": filters,
"rm_filters_post_tx": 2,
},
]
for tc in test_cases:
print("\nCase: {}".format(tc["name"]))
# register the filters
fltrs = []
try:
for flt in tc["filters"]:
fltrs.append(w3.eth.filter(flt["params"]))
except Exception as err:
if "register_err" in tc:
# if exception was expected when registering filters
# the test is finished
assert tc["register_err"] in str(err)
# remove the registered filters
remove_filters(w3, fltrs, 300)
continue
else:
print(f"Unexpected {err=}, {type(err)=}")
raise
# without tx: filters should not return any entries
for flt in fltrs:
assert flt.get_new_entries() == [] # GetFilterChanges
# with tx
tx = contract.functions.setGreeting(new_greeting).build_transaction(
{"from": ADDRS["validator"]}
)
receipt = send_transaction(w3, tx)
assert receipt.status == 1
if "rm_filters_post_tx" in tc:
# remove the filters
remove_filters(w3, fltrs, tc["rm_filters_post_tx"])
for i, flt in enumerate(fltrs):
# if filters were removed, should get a 'filter not found' error
try:
new_entries = flt.get_new_entries() # GetFilterChanges
except Exception as err:
if "rm_filters_post_tx" in tc and i < tc["rm_filters_post_tx"]:
assert_no_filter_err(flt, err)
# filter was removed and error checked. Continue to next filter
continue
else:
print(f"Unexpected {err=}, {type(err)=}")
raise
assert len(new_entries) == tc["filters"][i]["exp_len"]
if tc["filters"][i]["exp_len"] == 1:
# check if the new_entries have valid information
log = new_entries[0]
assert log["address"] == contract.address
assert log["topics"] == [topic]
assert_log_block(w3, log)
assert_change_greet_log_data(log, new_greeting)
# on next call of GetFilterChanges, no entries should be found
# because there were no new logs that meet the filters params
for i, flt in enumerate(fltrs):
# if filters were removed, should get a 'filter not found' error
try:
assert flt.get_new_entries() == [] # GetFilterChanges
except Exception as err:
if "rm_filters_post_tx" in tc and i < tc["rm_filters_post_tx"]:
assert_no_filter_err(flt, err)
continue
else:
print(f"Unexpected {err=}, {type(err)=}")
raise
# remove the filters added on this test
# because the node is not reseted for each test
# otherwise may get a max-limit error for registering
# new filters
w3.eth.uninstall_filter(flt.filter_id)
def test_register_filters_before_contract_deploy(cluster):
w3: Web3 = cluster.w3
new_greeting = "hello, world"
# calculate topic from event signature
topic = CHANGE_GREETING_TOPIC
# another topic not related to the contract deployed
another_topic = TRANSFER_TOPIC
filters = [
{
"params": {"topics": [topic.hex()]},
"exp_len": 1,
},
{
"params": {
"topics": [
topic.hex(),
another_topic.hex(),
], # 'with all topics' condition
},
"exp_len": 0,
},
{
"params": {
"topics": [
[topic.hex(), another_topic.hex()]
], # 'with any topic' condition
},
"exp_len": 1,
},
{
"params": {
"fromBlock": 1,
"toBlock": "latest",
"topics": [[topic.hex(), another_topic.hex()]],
},
"exp_len": 1,
},
]
# register the filters
fltrs = []
for flt in filters:
fltrs.append(w3.eth.filter(flt["params"]))
# deploy contract
contract, _ = deploy_contract(w3, CONTRACTS["Greeter"])
# test the contract was deployed successfully
assert contract.caller.greet() == "Hello"
# without tx: filters should not return any entries
for flt in fltrs:
assert flt.get_new_entries() == [] # GetFilterChanges
# perform tx to call contract that emits event
tx = contract.functions.setGreeting(new_greeting).build_transaction(
{"from": ADDRS["validator"]}
)
receipt = send_transaction(w3, tx)
assert receipt.status == 1
for i, flt in enumerate(fltrs):
new_entries = flt.get_new_entries() # GetFilterChanges
assert len(new_entries) == filters[i]["exp_len"]
if filters[i]["exp_len"] == 1:
# check if the new_entries have valid information
log = new_entries[0]
assert log["address"] == contract.address
assert log["topics"] == [topic]
assert_log_block(w3, log)
assert_change_greet_log_data(log, new_greeting)
# on next call of GetFilterChanges, no entries should be found
# because there were no new logs that meet the filters params
for flt in fltrs:
assert flt.get_new_entries() == [] # GetFilterChanges
w3.eth.uninstall_filter(flt.filter_id)
def test_get_logs(cluster):
w3: Web3 = cluster.w3
# deploy greeter contract
contract, _ = deploy_contract(w3, CONTRACTS["Greeter"])
# test the contract was deployed successfully
assert contract.caller.greet() == "Hello"
# calculate topic from event signature
topic = CHANGE_GREETING_TOPIC
# another topic not related to the contract deployed
another_topic = TRANSFER_TOPIC
# without tx - logs should be empty
assert w3.eth.get_logs({"address": contract.address}) == []
assert w3.eth.get_logs({"address": ADDRS["validator"]}) == []
# with tx
tx = contract.functions.setGreeting("world").build_transaction()
# update greeting
new_greeting = "hello, world"
tx = contract.functions.setGreeting(new_greeting).build_transaction(
{"from": ADDRS["validator"]}
)
receipt = send_transaction(w3, tx)
assert receipt.status == 1
assert len(w3.eth.get_logs({"address": contract.address})) == 1
assert len(w3.eth.get_logs({"address": ADDRS["validator"]})) == 0
tx_block_num = w3.eth.block_number
test_cases = [
{
"name": "get logs by block range - tx block number is within the range",
"logs": w3.eth.get_logs({"fromBlock": 1, "toBlock": tx_block_num}),
"exp_log": True,
"exp_len": None, # there are other events within the block range specified
},
{
"name": "get logs by block range - tx block number outside the range",
"logs": w3.eth.get_logs({"fromBlock": 1, "toBlock": 2}),
"exp_log": False,
"exp_len": 0,
},
{
"name": "get logs by contract address",
"logs": w3.eth.get_logs({"address": contract.address}),
"exp_log": True,
"exp_len": 1,
},
{
"name": "get logs by topic",
"logs": w3.eth.get_logs({"topics": [topic.hex()]}),
"exp_log": True,
"exp_len": 1,
},
{
"name": "get logs by incorrect topic - should not have logs",
"logs": w3.eth.get_logs({"topics": [another_topic.hex()]}),
"exp_log": False,
"exp_len": 0,
},
{
"name": "get logs by multiple topics ('with all' condition)",
"logs": w3.eth.get_logs(
{
"topics": [
topic.hex(),
another_topic.hex(),
]
}
),
"exp_log": False,
"exp_len": 0,
},
{
"name": "get logs by multiple topics ('match any' condition)",
"logs": w3.eth.get_logs({"topics": [[topic.hex(), another_topic.hex()]]}),
"exp_log": True,
"exp_len": 1,
},
{
"name": "get logs by topic and block range",
"logs": w3.eth.get_logs(
{
"fromBlock": tx_block_num,
"toBlock": "latest",
"topics": [topic.hex()],
}
),
"exp_log": True,
"exp_len": 1,
},
]
for tc in test_cases:
print("\nCase: {}".format(tc["name"]))
# logs for validator address should remain empty
assert len(w3.eth.get_logs({"address": ADDRS["validator"]})) == 0
logs = tc["logs"]
if tc["exp_len"] is not None:
assert len(logs) == tc["exp_len"]
if tc["exp_log"]:
found_log = False
for log in logs:
if log["address"] == contract.address:
# for the current test cases,
# this event was emitted only once
# so one log from this contract should exist
# we check the flag to know it is not repeated
assert found_log is False
found_log = True
assert log["topics"] == [topic]
assert_log_block(w3, log)
assert_change_greet_log_data(log, new_greeting)
assert found_log is True
#################################################
# Helper functions to assert logs information
#################################################
def assert_log_block(w3, log):
block_hash = log["blockHash"]
# check if the returned block hash is correct
# getBlockByHash
block = w3.eth.get_block(block_hash)
# block should exist
assert block.hash == block_hash
# check tx hash is correct
tx_data = w3.eth.get_transaction(log["transactionHash"])
assert tx_data["blockHash"] == block.hash
def assert_change_greet_log_data(log, new_greeting):
# check event log data ('from' and 'value' fields)
types = ["address", "string"]
names = ["from", "value"]
values = abi.decode_abi(types, log["data"])
log_data = dict(zip(names, values))
# the address stored in the data field may defer on lower/upper case characters
# then, set all as lowercase for assertion
assert log_data["from"] == ADDRS["validator"].lower()
assert log_data["value"] == new_greeting
def assert_no_filter_err(flt, err):
msg_without_id = "filter not found" in str(err)
msg_with_id = f"filter {flt.filter_id} not found" in str(err)
assert msg_without_id or msg_with_id is True
#################################################
# Helper functions to add/remove filters
#################################################
def make_filter_array(array_len):
filters = []
for _ in range(array_len):
filters.append(
{
"params": {"fromBlock": 1, "toBlock": "latest"},
"exp_len": 1,
},
)
return filters
# removes the number of filters defined in 'count' argument, starting from index 0
def remove_filters(w3, filters, count):
# if number of filters to remove exceeds the amount of filters passed
# update the 'count' to the length of the filters array
if count > len(filters):
count = len(filters)
for i in range(count):
assert w3.eth.uninstall_filter(filters[i].filter_id)
# adds a padding of '0's to a hex address based on the total byte length desired
def pad_left(address, byte_len=32):
a = address.split("0x")
b = a[1].zfill(byte_len * 2)
return "0x" + b

View File

@ -30,13 +30,14 @@ TEST_CONTRACTS = {
"Greeter": "Greeter.sol",
"BurnGas": "BurnGas.sol",
"TestChainID": "ChainID.sol",
"Mars": "Mars.sol",
}
def contract_path(name, filename):
return (
Path(__file__).parent
/ "contracts/artifacts/contracts/"
/ "hardhat/artifacts/contracts/"
/ filename
/ (name + ".json")
)