-
Notifications
You must be signed in to change notification settings - Fork 94
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
eth/solidity: add solidity compiler bindings (#66)
* ci: install solidity compiler solc * eth/solidity: add solidity compiler bindings * eth/solidity: add tests and test contracts * docs: update readme * eth/solidity: fix error message * eth/solidity: improve extension handling * eth/solidity: test deploying a deposit contract * spec: run rufo * If read.length is greater than 8192, call recvmsg one more time to extract Not yet known if this is a Mac OS specific problem. * Use end_with? * client/ipc: loop socket receive buffers until they close Co-authored-by: q9f <q9f@users.noreply.github.com> Co-authored-by: Yuta Kurotaki <yuta.kurotaki@gmail.com>
- Loading branch information
1 parent
0773bbf
commit c1169ea
Showing
10 changed files
with
409 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
# Copyright (c) 2016-2022 The Ruby-Eth Contributors | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
require "open3" | ||
|
||
# Provides the {Eth} module. | ||
module Eth | ||
|
||
# Class to create {Solidity} compiler bingings for Ruby. | ||
class Solidity | ||
|
||
# Provides a Compiler Error in case the contract does not compile. | ||
class CompilerError < StandardError; end | ||
|
||
# Solidity compiler binary path. | ||
attr_reader :compiler | ||
|
||
# Instantiates a Solidity `solc` system compiler binding that can be | ||
# used to compile Solidity contracts. | ||
def initialize | ||
|
||
# Currently only supports `solc`. | ||
solc = get_compiler_path | ||
raise SystemCallError, "Unable to find the solc compiler path!" if solc.nil? | ||
@compiler = solc | ||
end | ||
|
||
# Use the bound Solidity executable to compile the given contract. | ||
# | ||
# @param contract [String] path of the contract to compile. | ||
# @return [Array] JSON containing the compiled contract and ABI for all contracts. | ||
def compile(contract) | ||
raise Errno::ENOENT, "Contract file not found: #{contract}" unless File.exist? contract | ||
command = "#{@compiler} --optimize --combined-json bin,abi #{contract}" | ||
output, error, status = Open3.capture3 command | ||
raise SystemCallError, "Unable to run solc compiler!" if status.exitstatus === 127 | ||
raise CompilerError, error unless status.success? | ||
json = JSON.parse output | ||
result = {} | ||
json["contracts"].each do |key, value| | ||
_file, name = key.split ":" | ||
result[name] = {} | ||
result[name]["abi"] = value["abi"] | ||
result[name]["bin"] = value["bin"] | ||
end | ||
return result | ||
end | ||
|
||
private | ||
|
||
# Tries to find a system executable path for the given compiler binary name. | ||
def get_compiler_path(name = "solc") | ||
extensions = [""] | ||
extensions = ENV["PATHEXT"].split(";") unless ENV["PATHEXT"].nil? | ||
ENV["PATH"].split(File::PATH_SEPARATOR).each do |path| | ||
extensions.each do |ext| | ||
executable = File.join path, "#{name}#{ext}" | ||
return executable if File.executable? executable and !File.directory? executable | ||
end | ||
end | ||
return nil | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
require "spec_helper" | ||
|
||
describe Solidity do | ||
it "finds a solc compiler" do | ||
|
||
# This fails if no `solc` is in the $PATH. | ||
expect(Solidity.new).to be | ||
end | ||
|
||
subject(:solc) { Solidity.new } | ||
|
||
it "compiles the dummy contract" do | ||
contract = "#{Dir.pwd}/spec/fixtures/contracts/dummy.sol" | ||
result = solc.compile contract | ||
expect(result.keys).to eq ["Dummy"] | ||
expect(result["Dummy"]["abi"]).to eq JSON.parse '[{"inputs":[],"name":"get","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"}],"name":"set","outputs":[],"stateMutability":"nonpayable","type":"function"}]' | ||
expect(result["Dummy"]["bin"]).to start_with "6080604052348015600f57600080fd5b5060" | ||
end | ||
|
||
it "compiles the greeter contract" do | ||
contract = "#{Dir.pwd}/spec/fixtures/contracts/greeter.sol" | ||
result = solc.compile contract | ||
expect(result.keys).to eq ["Greeter", "Mortal"] | ||
expect(result["Mortal"]["abi"]).to eq JSON.parse '[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"kill","outputs":[],"stateMutability":"nonpayable","type":"function"}]' | ||
expect(result["Greeter"]["abi"]).to eq JSON.parse '[{"inputs":[{"internalType":"string","name":"message","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"greet","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"kill","outputs":[],"stateMutability":"nonpayable","type":"function"}]' | ||
expect(result["Mortal"]["bin"]).to start_with "6080604052348015600f57600080fd5b5060" | ||
expect(result["Greeter"]["bin"]).to start_with "608060405234801561001057600080fd5b5060" | ||
end | ||
|
||
it "deploys an ethereum-consensus deposit contract" do | ||
geth = Client.create "/tmp/geth.ipc" | ||
contract = "#{Dir.pwd}/spec/fixtures/contracts/deposit.sol" | ||
result = solc.compile contract | ||
expect(result["DepositContract"]).to be | ||
payload = result["DepositContract"]["bin"] | ||
expect(payload).to start_with "60806040523480156200001157600080fd5b5060" | ||
params = { | ||
from: geth.default_account, | ||
priority_fee: 0, | ||
max_gas_fee: Unit::GWEI, | ||
gas_limit: Tx.estimate_intrinsic_gas(payload), | ||
data: payload, | ||
} | ||
deploy = geth.eth_send_transaction(params) | ||
hash = deploy["result"] | ||
expect(hash).to start_with "0x" | ||
geth.wait_for_tx hash | ||
receipt = geth.eth_get_transaction_receipt hash | ||
expect(receipt["result"]).to be | ||
address = receipt["result"]["contractAddress"] | ||
expect(address).to be | ||
expect { Address.new address }.not_to raise_error | ||
end | ||
|
||
it "handles file-system errors" do | ||
contract = "#{Dir.pwd}/spec/fixtures/contracts/null.sol" | ||
expect { solc.compile contract }.to raise_error Errno::ENOENT, /No such file or directory - Contract file not found:/ | ||
end | ||
|
||
it "handles compiler errors" do | ||
contract = "#{Dir.pwd}/spec/fixtures/contracts/error.sol" | ||
expect { solc.compile contract }.to raise_error Solidity::CompilerError, /Error: Identifier not found or not unique./ | ||
end | ||
end |
Oops, something went wrong.