Merge pull request #1898 from poanetwork/ab-check-if-contract-has-arguments

check if constructor has arguments
pull/1915/head
Ayrat Badykov 6 years ago committed by GitHub
commit 9246c1dcbf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      CHANGELOG.md
  2. 6
      apps/explorer/lib/explorer/smart_contract/verifier.ex
  3. 95
      apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs
  4. 25
      apps/explorer/test/explorer/smart_contract/verifier_test.exs

@ -25,6 +25,7 @@
- [#1885](https://github.com/poanetwork/blockscout/pull/1885) - highlight reserved words in decompiled code
- [#1896](https://github.com/poanetwork/blockscout/pull/1896) - re-query tokens in top nav automplete
- [#1881](https://github.com/poanetwork/blockscout/pull/1881) - fix: store solc versions locally for performance
- [#1898](https://github.com/poanetwork/blockscout/pull/1898) - check if the constructor has arguments before verifying constructor arguments
### Chore

@ -74,7 +74,7 @@ defmodule Explorer.SmartContract.Verifier do
generated_bytecode != blockchain_bytecode_without_whisper ->
{:error, :generated_bytecode}
!ConstructorArguments.verify(address_hash, arguments_data) ->
has_constructor_with_params?(abi) && !ConstructorArguments.verify(address_hash, arguments_data) ->
{:error, :constructor_arguments}
true ->
@ -111,4 +111,8 @@ defmodule Explorer.SmartContract.Verifier do
prev_version
end
end
defp has_constructor_with_params?(abi) do
Enum.any?(abi, fn el -> el["type"] == "constructor" && el["inputs"] != [] end)
end
end

@ -173,6 +173,101 @@ defmodule Explorer.SmartContract.Solidity.CodeCompilerTest do
assert {:error, :compilation} = response
end
test "returns constructor in abi" do
code = """
pragma solidity ^0.4.22;
contract OwnedToken {
// TokenCreator is a contract type that is defined below.
// It is fine to reference it as long as it is not used
// to create a new contract.
TokenCreator creator;
address owner;
bytes32 name;
// This is the constructor which registers the
// creator and the assigned name.
constructor(bytes32 _name) public {
// State variables are accessed via their name
// and not via e.g. this.owner. This also applies
// to functions and especially in the constructors,
// you can only call them like that ("internally"),
// because the contract itself does not exist yet.
owner = msg.sender;
// We do an explicit type conversion from `address`
// to `TokenCreator` and assume that the type of
// the calling contract is TokenCreator, there is
// no real way to check that.
creator = TokenCreator(msg.sender);
name = _name;
}
function changeName(bytes32 newName) public {
// Only the creator can alter the name --
// the comparison is possible since contracts
// are implicitly convertible to addresses.
if (msg.sender == address(creator))
name = newName;
}
function transfer(address newOwner) public {
// Only the current owner can transfer the token.
if (msg.sender != owner) return;
// We also want to ask the creator if the transfer
// is fine. Note that this calls a function of the
// contract defined below. If the call fails (e.g.
// due to out-of-gas), the execution here stops
// immediately.
if (creator.isTokenTransferOK(owner, newOwner))
owner = newOwner;
}
}
contract TokenCreator {
function createToken(bytes32 name)
public
returns (OwnedToken tokenAddress)
{
// Create a new Token contract and return its address.
// From the JavaScript side, the return type is simply
// `address`, as this is the closest type available in
// the ABI.
return new OwnedToken(name);
}
function changeName(OwnedToken tokenAddress, bytes32 name) public {
// Again, the external type of `tokenAddress` is
// simply `address`.
tokenAddress.changeName(name);
}
function isTokenTransferOK(address currentOwner, address newOwner)
public
view
returns (bool ok)
{
// Check some arbitrary condition.
address tokenAddress = msg.sender;
return (keccak256(newOwner) & 0xff) == (bytes20(tokenAddress) & 0xff);
}
}
"""
name = "OwnedToken"
compiler_version = "v0.4.22+commit.4cb486ee"
{:ok, %{"abi" => abi}} =
CodeCompiler.run(
name: name,
compiler_version: compiler_version,
code: code,
evm_version: "byzantium",
optimize: true
)
assert Enum.any?(abi, fn el -> el["type"] == "constructor" end)
end
end
describe "get_contract_info/1" do

@ -117,31 +117,6 @@ defmodule Explorer.SmartContract.VerifierTest do
assert abi != nil
end
test "returns error when constructor arguments do not match", %{
contract_code_info: contract_code_info
} do
contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode)
constructor_arguments = "0102030405"
params = %{
"contract_source_code" => contract_code_info.source_code,
"compiler_version" => contract_code_info.version,
"name" => contract_code_info.name,
"optimization" => contract_code_info.optimized,
"constructor_arguments" => constructor_arguments
}
:transaction
|> insert(
created_contract_address_hash: contract_address.hash,
input: Verifier.extract_bytecode(contract_code_info.bytecode) <> "010203"
)
|> with_block()
assert {:error, :constructor_arguments} = Verifier.evaluate_authenticity(contract_address.hash, params)
end
test "returns error when bytecode doesn't match", %{contract_code_info: contract_code_info} do
contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode)

Loading…
Cancel
Save