ethereum.rb 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.travis.yml +26 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/LICENSE.txt +21 -0
- data/README.md +183 -0
- data/Rakefile +11 -0
- data/bin/console +14 -0
- data/bin/install_parity +29 -0
- data/bin/setup +7 -0
- data/contracts/AccountingLib.sol +112 -0
- data/contracts/AuditorInterface.sol +4 -0
- data/contracts/AuditorRegistry.sol +14 -0
- data/contracts/CustodianInterface.sol +27 -0
- data/contracts/CustodianRegistry.sol +40 -0
- data/contracts/DigixConfiguration.sol +68 -0
- data/contracts/Directory.sol +67 -0
- data/contracts/DoublyLinked.sol +54 -0
- data/contracts/GenericInterface.sol +56 -0
- data/contracts/GenericRegistry.sol +15 -0
- data/contracts/Gold.sol +105 -0
- data/contracts/GoldRegistry.sol +82 -0
- data/contracts/GoldTokenLedger.sol +3 -0
- data/contracts/Interface.sol +27 -0
- data/contracts/Minter.sol +3 -0
- data/contracts/Recaster.sol +3 -0
- data/contracts/Testing.sol +59 -0
- data/contracts/VendorInterface.sol +82 -0
- data/contracts/VendorRegistry.sol +39 -0
- data/contracts/classic/Digixbot.sol +106 -0
- data/contracts/classic/DigixbotConfiguration.sol +62 -0
- data/contracts/classic/DigixbotEthereum.sol +86 -0
- data/contracts/classic/DigixbotUsers.sol +103 -0
- data/contracts/classic/Gold.sol +497 -0
- data/contracts/classic/GoldRegistry.sol +503 -0
- data/contracts/classic/GoldTokenLedger.sol +560 -0
- data/contracts/classic/GoldTokenMinter.sol +607 -0
- data/contracts/classic/ParticipantRegistry.sol +94 -0
- data/contracts/classic/QueueSample.sol +54 -0
- data/ethereum.gemspec +35 -0
- data/lib/ethereum.rb +24 -0
- data/lib/ethereum/client.rb +97 -0
- data/lib/ethereum/contract.rb +266 -0
- data/lib/ethereum/contract_event.rb +25 -0
- data/lib/ethereum/contract_initializer.rb +54 -0
- data/lib/ethereum/deployment.rb +49 -0
- data/lib/ethereum/formatter.rb +172 -0
- data/lib/ethereum/function.rb +20 -0
- data/lib/ethereum/function_input.rb +13 -0
- data/lib/ethereum/function_output.rb +14 -0
- data/lib/ethereum/http_client.rb +38 -0
- data/lib/ethereum/initializer.rb +27 -0
- data/lib/ethereum/ipc_client.rb +46 -0
- data/lib/ethereum/project_initializer.rb +28 -0
- data/lib/ethereum/railtie.rb +10 -0
- data/lib/ethereum/singleton.rb +39 -0
- data/lib/ethereum/solidity.rb +47 -0
- data/lib/ethereum/transaction.rb +36 -0
- data/lib/ethereum/version.rb +3 -0
- data/lib/tasks/ethereum_contract.rake +27 -0
- data/lib/tasks/ethereum_node.rake +51 -0
- data/lib/tasks/ethereum_test.rake +32 -0
- data/lib/tasks/ethereum_transaction.rake +24 -0
- metadata +198 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
module Ethereum
|
2
|
+
class ContractEvent
|
3
|
+
|
4
|
+
attr_accessor :name, :signature, :input_types, :inputs, :event_string, :address, :client
|
5
|
+
|
6
|
+
def initialize(data)
|
7
|
+
@name = data["name"]
|
8
|
+
@input_types = data["inputs"].collect {|x| x["type"]}
|
9
|
+
@inputs = data["inputs"].collect {|x| x["name"]}
|
10
|
+
@event_string = "#{@name}(#{@input_types.join(",")})"
|
11
|
+
@signature = SHA3::Digest::SHA256.hexdigest(@event_string)
|
12
|
+
# @signature = Digest::SHA3.hexdigest @event_string, 256
|
13
|
+
end
|
14
|
+
|
15
|
+
def set_address(address)
|
16
|
+
@address = address
|
17
|
+
end
|
18
|
+
|
19
|
+
def set_client(client)
|
20
|
+
@client = client
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Ethereum
|
2
|
+
|
3
|
+
class ContractInitializer
|
4
|
+
|
5
|
+
attr_accessor :abi, :binary, :name, :libraries, :needs_linking, :project_initializer, :contract
|
6
|
+
|
7
|
+
def initialize(contract_name, contract, project_initializer)
|
8
|
+
@abi = JSON.parse(contract["abi"]) unless contract.nil?
|
9
|
+
@binary = contract["bin"] unless contract.nil?
|
10
|
+
@name = contract_name
|
11
|
+
@project_initializer = project_initializer
|
12
|
+
matchdata = @binary.scan(/_+[a-zA-Z]+_+/).uniq
|
13
|
+
@needs_linking = matchdata.present?
|
14
|
+
if @needs_linking
|
15
|
+
@libraries = matchdata.collect do |libname|
|
16
|
+
{name: libname.gsub(/_+/,''), sigil: libname}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def link_libraries
|
22
|
+
if @needs_linking
|
23
|
+
@libraries.each do |library|
|
24
|
+
name = library[:name]
|
25
|
+
if @project_initializer.libraries[name].nil?
|
26
|
+
ENV['ETHEREUM_DEPLOYER_WAIT_TIME'] ||= "120"
|
27
|
+
wait_time = ENV['ETHEREUM_DEPLOYER_WAIT_TIME'].to_i
|
28
|
+
library_instance = library[:name].constantize.new
|
29
|
+
puts "Deploying library #{name}"
|
30
|
+
library_instance.deploy_and_wait(wait_time)
|
31
|
+
puts "Library deployed at #{library_instance.address}"
|
32
|
+
@project_initializer.libraries[name] = library_instance.address
|
33
|
+
@binary.gsub!(library[:sigil], library_instance.address.gsub(/^0x/,''))
|
34
|
+
else
|
35
|
+
address = @project_initializer.libraries[name]
|
36
|
+
@binary.gsub!(library[:sigil], address.gsub(/^0x/,''))
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def build(connection)
|
43
|
+
@contract = Ethereum::Contract.new(@name, @binary, @abi)
|
44
|
+
@contract.build(connection)
|
45
|
+
end
|
46
|
+
|
47
|
+
def generate_javascripts(path)
|
48
|
+
data = {name: @name, abi: @abi, binary: @binary}
|
49
|
+
File.open(File.join(path, "#{@name}.json"), 'w') {|f| f.puts data.to_json}
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Ethereum
|
2
|
+
|
3
|
+
class Deployment
|
4
|
+
|
5
|
+
DEFAULT_TIMEOUT = 300.seconds
|
6
|
+
DEFAULT_STEP = 5.seconds
|
7
|
+
|
8
|
+
attr_accessor :id, :contract_address, :connection, :deployed, :mined
|
9
|
+
attr_reader :valid_deployment
|
10
|
+
|
11
|
+
def initialize(txid, connection)
|
12
|
+
@id = txid
|
13
|
+
@connection = connection
|
14
|
+
@deployed = false
|
15
|
+
@contract_address = nil
|
16
|
+
@valid_deployment = false
|
17
|
+
end
|
18
|
+
|
19
|
+
def mined?
|
20
|
+
return true if @mined
|
21
|
+
@mined = @connection.get_transaction_by_hash(@id)["result"]["blockNumber"].present? rescue nil
|
22
|
+
@mined ||= false
|
23
|
+
end
|
24
|
+
|
25
|
+
def check_deployed
|
26
|
+
return false unless @id
|
27
|
+
contract_receipt = @connection.eth_get_transaction_receipt(@id)
|
28
|
+
result = contract_receipt["result"]
|
29
|
+
has_contract_address = result && result["contractAddress"]
|
30
|
+
@contract_address ||= result["contractAddress"] if has_contract_address
|
31
|
+
has_contract_address && result["blockNumber"]
|
32
|
+
end
|
33
|
+
|
34
|
+
def deployed?
|
35
|
+
@valid_deployment ||= check_deployed
|
36
|
+
end
|
37
|
+
|
38
|
+
def wait_for_deployment(timeout = DEFAULT_TIMEOUT, step: DEFAULT_STEP, &block)
|
39
|
+
start_time = Time.now
|
40
|
+
while true
|
41
|
+
raise "Transaction #{@id} timed out." if ((Time.now - start_time) > timeout)
|
42
|
+
sleep step
|
43
|
+
yield if block_given?
|
44
|
+
return true if deployed?
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
module Ethereum
|
2
|
+
class Formatter
|
3
|
+
|
4
|
+
UNITS = {
|
5
|
+
wei: 1,
|
6
|
+
kwei: 1000,
|
7
|
+
ada: 1000,
|
8
|
+
femtoether: 1000,
|
9
|
+
mwei: 1000000,
|
10
|
+
babbage: 1000000,
|
11
|
+
picoether: 1000000,
|
12
|
+
gwei: 1000000000,
|
13
|
+
shannon: 1000000000,
|
14
|
+
nanoether: 1000000000,
|
15
|
+
nano: 1000000000,
|
16
|
+
szabo: 1000000000000,
|
17
|
+
microether: 1000000000000,
|
18
|
+
micro: 1000000000000,
|
19
|
+
finney: 1000000000000000,
|
20
|
+
milliether: 1000000000000000,
|
21
|
+
milli: 1000000000000000,
|
22
|
+
ether: 1000000000000000000,
|
23
|
+
eth: 1000000000000000000,
|
24
|
+
kether: 1000000000000000000000,
|
25
|
+
grand: 1000000000000000000000,
|
26
|
+
einstein: 1000000000000000000000,
|
27
|
+
mether: 1000000000000000000000000,
|
28
|
+
gether: 1000000000000000000000000000,
|
29
|
+
tether: 1000000000000000000000000000000
|
30
|
+
}
|
31
|
+
|
32
|
+
def valid_address?(address_string)
|
33
|
+
address = address_string.gsub(/^0x/,'')
|
34
|
+
return false if address == "0000000000000000000000000000000000000000"
|
35
|
+
return false if address.length != 40
|
36
|
+
return !(address.match(/[0-9a-fA-F]+/).nil?)
|
37
|
+
end
|
38
|
+
|
39
|
+
def from_bool(boolval)
|
40
|
+
return nil if boolval.nil?
|
41
|
+
boolval ? "1" : "0"
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_bool(hexstring)
|
45
|
+
return nil if hexstring.nil?
|
46
|
+
(hexstring == "0000000000000000000000000000000000000000000000000000000000000001")
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_ascii(hexstring)
|
50
|
+
return nil if hexstring.nil?
|
51
|
+
hexstring.gsub(/^0x/,'').scan(/.{2}/).collect {|x| x.hex}.pack("c*")
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_utf8(hexstring)
|
55
|
+
return nil if hexstring.nil?
|
56
|
+
hexstring.gsub(/^0x/,'').scan(/.{2}/).collect {|x| x.hex}.pack("U*").delete("\u0000")
|
57
|
+
end
|
58
|
+
|
59
|
+
def from_ascii(ascii_string)
|
60
|
+
return nil if ascii_string.nil?
|
61
|
+
ascii_string.unpack('H*')[0]
|
62
|
+
end
|
63
|
+
|
64
|
+
def from_utf8(utf8_string)
|
65
|
+
return nil if utf8_string.nil?
|
66
|
+
utf8_string.force_encoding('UTF-8').split("").collect {|x| x.ord.to_s(16)}.join("")
|
67
|
+
end
|
68
|
+
|
69
|
+
def to_address(hexstring)
|
70
|
+
return "0x0000000000000000000000000000000000000000" if hexstring.nil?
|
71
|
+
"0x" + hexstring[-40..-1]
|
72
|
+
end
|
73
|
+
|
74
|
+
def to_wei(amount, unit = "ether")
|
75
|
+
return nil if amount.nil?
|
76
|
+
BigDecimal.new(UNITS[unit.to_sym] * amount, 16).to_s.to_i rescue nil
|
77
|
+
end
|
78
|
+
|
79
|
+
def from_wei(amount, unit = "ether")
|
80
|
+
return nil if amount.nil?
|
81
|
+
(BigDecimal.new(amount, 16) / BigDecimal.new(UNITS[unit.to_sym], 16)).to_s rescue nil
|
82
|
+
end
|
83
|
+
|
84
|
+
def from_address(address)
|
85
|
+
return "0x0000000000000000000000000000000000000000" if address.nil?
|
86
|
+
address.gsub(/^0x/,'').rjust(64, "0")
|
87
|
+
end
|
88
|
+
|
89
|
+
def to_param(string)
|
90
|
+
string.ljust(64, '0')
|
91
|
+
end
|
92
|
+
|
93
|
+
def from_input(string)
|
94
|
+
string[10..-1].scan(/.{64}/)
|
95
|
+
end
|
96
|
+
|
97
|
+
def to_twos_complement(number)
|
98
|
+
(number & ((1 << 256) - 1)).to_s(16)
|
99
|
+
end
|
100
|
+
|
101
|
+
def to_int(hexstring)
|
102
|
+
return nil if hexstring.nil?
|
103
|
+
(hexstring.gsub(/^0x/,'')[0..1] == "ff") ? (hexstring.hex - (2 ** 256)) : hexstring.hex
|
104
|
+
end
|
105
|
+
|
106
|
+
def address_to_payload(address)
|
107
|
+
from_address(address)
|
108
|
+
end
|
109
|
+
|
110
|
+
def uint_to_payload(uint)
|
111
|
+
self.to_twos_complement(uint).rjust(64, '0')
|
112
|
+
end
|
113
|
+
|
114
|
+
def int_to_payload(int)
|
115
|
+
self.to_twos_complement(uint).rjust(64, '0')
|
116
|
+
end
|
117
|
+
|
118
|
+
def bytes_to_payload(bytes)
|
119
|
+
self.from_utf8(bytes).ljust(64, '0')
|
120
|
+
end
|
121
|
+
|
122
|
+
def string_to_payload(bytes)
|
123
|
+
self.bytes_to_payload(bytes)
|
124
|
+
end
|
125
|
+
|
126
|
+
def get_base_type(typename)
|
127
|
+
typename.gsub(/\d+/,'')
|
128
|
+
end
|
129
|
+
|
130
|
+
def to_payload(args)
|
131
|
+
converter = "#{self.get_base_type(args[0])}_to_payload".to_sym
|
132
|
+
self.send(converter, args[1])
|
133
|
+
end
|
134
|
+
|
135
|
+
def from_payload(args)
|
136
|
+
converter = "output_to_#{self.get_base_type(args[0])}".to_sym
|
137
|
+
self.send(converter, args[1])
|
138
|
+
end
|
139
|
+
|
140
|
+
def output_to_address(bytes)
|
141
|
+
self.to_address(bytes)
|
142
|
+
end
|
143
|
+
|
144
|
+
def output_to_bytes(bytes)
|
145
|
+
self.to_utf8(bytes)
|
146
|
+
end
|
147
|
+
|
148
|
+
def output_to_string(bytes)
|
149
|
+
self.to_utf8(bytes)
|
150
|
+
end
|
151
|
+
|
152
|
+
def output_to_uint(bytes)
|
153
|
+
self.to_int(bytes)
|
154
|
+
end
|
155
|
+
|
156
|
+
def output_to_int(bytes)
|
157
|
+
self.to_int(bytes)
|
158
|
+
end
|
159
|
+
|
160
|
+
def output_to_bool(bytes)
|
161
|
+
self.to_bool(bytes.gsub(/^0x/,''))
|
162
|
+
end
|
163
|
+
|
164
|
+
def to_output(args)
|
165
|
+
converter = "output_to_#{self.get_base_type(args[0])}".to_sym
|
166
|
+
self.send(converter, args[1])
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Ethereum
|
2
|
+
class Function
|
3
|
+
|
4
|
+
attr_accessor :name, :inputs, :outputs, :signature, :constant, :function_string
|
5
|
+
|
6
|
+
def initialize(data)
|
7
|
+
@name = data["name"]
|
8
|
+
@constant = data["constant"]
|
9
|
+
@inputs = data["inputs"].collect do |input|
|
10
|
+
Ethereum::FunctionInput.new(input)
|
11
|
+
end
|
12
|
+
@outputs = data["outputs"].collect do |output|
|
13
|
+
Ethereum::FunctionOutput.new(output)
|
14
|
+
end
|
15
|
+
@function_string = "#{@name}(#{@inputs.collect {|x| x.type }.join(",")})"
|
16
|
+
@signature = Digest::SHA3.hexdigest(@function_string, 256)[0..7]
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
module Ethereum
|
3
|
+
class HttpClient < Client
|
4
|
+
attr_accessor :host, :port, :uri, :ssl
|
5
|
+
|
6
|
+
def initialize(host, log = false)
|
7
|
+
super(log)
|
8
|
+
uri = URI.parse(host)
|
9
|
+
raise ArgumentError unless ['http', 'https'].include? uri.scheme
|
10
|
+
@host = uri.host
|
11
|
+
@port = uri.port
|
12
|
+
|
13
|
+
@ssl = uri.scheme == 'https'
|
14
|
+
if ssl
|
15
|
+
@uri = URI("https://#{@host}:#{@port}")
|
16
|
+
else
|
17
|
+
@uri = URI("http://#{@host}:#{@port}")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def send_single(payload)
|
22
|
+
http = ::Net::HTTP.new(@host, @port)
|
23
|
+
if @ssl
|
24
|
+
http.use_ssl = true
|
25
|
+
end
|
26
|
+
header = {'Content-Type' => 'application/json'}
|
27
|
+
request = ::Net::HTTP::Post.new(uri, header)
|
28
|
+
request.body = payload
|
29
|
+
response = http.request(request)
|
30
|
+
return response.body
|
31
|
+
end
|
32
|
+
|
33
|
+
def send_batch(batch)
|
34
|
+
raise NotImplementedError
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Ethereum
|
2
|
+
|
3
|
+
class Initializer
|
4
|
+
attr_accessor :contracts, :file, :client
|
5
|
+
|
6
|
+
def initialize(file, client = Ethereum::Singleton.instance)
|
7
|
+
@client = client
|
8
|
+
sol_output = Solidity.new.compile(file)
|
9
|
+
contracts = sol_output.keys
|
10
|
+
|
11
|
+
@contracts = []
|
12
|
+
contracts.each do |contract|
|
13
|
+
abi = JSON.parse(sol_output[contract]["abi"] )
|
14
|
+
name = contract
|
15
|
+
code = sol_output[contract]["bin"]
|
16
|
+
@contracts << Contract.new(name, code, abi)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def build_all
|
21
|
+
@contracts.each do |contract|
|
22
|
+
contract.build(@client)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'socket'
|
2
|
+
module Ethereum
|
3
|
+
class IpcClient < Client
|
4
|
+
attr_accessor :ipcpath
|
5
|
+
|
6
|
+
IPC_PATHS = [
|
7
|
+
"#{ENV['HOME']}/.parity/jsonrpc.ipc",
|
8
|
+
"#{ENV['HOME']}/Library/Ethereum/geth.ipc",
|
9
|
+
"#{ENV['HOME']}/Library/Ethereum/testnet/geth.ipc"
|
10
|
+
]
|
11
|
+
|
12
|
+
def initialize(ipcpath = nil, log = true)
|
13
|
+
super(log)
|
14
|
+
ipcpath ||= IpcClient.default_path
|
15
|
+
@ipcpath = ipcpath
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.default_path(paths = IPC_PATHS)
|
19
|
+
paths.select { |path| File.exist?(path) }.first || ""
|
20
|
+
end
|
21
|
+
|
22
|
+
def send_single(payload)
|
23
|
+
socket = UNIXSocket.new(@ipcpath)
|
24
|
+
socket.puts(payload)
|
25
|
+
read = socket.recvmsg(nil)[0]
|
26
|
+
socket.close
|
27
|
+
return read
|
28
|
+
end
|
29
|
+
|
30
|
+
# TODO: Not sure if multithread safe
|
31
|
+
# Note: Guarantees the results are in the same order as defined in batch call.
|
32
|
+
# client.batch do
|
33
|
+
# client.eth_block_number
|
34
|
+
# client.eth_mining
|
35
|
+
# end
|
36
|
+
# => [{"jsonrpc"=>"2.0", "id"=>1, "result"=>"0x26"}, {"jsonrpc"=>"2.0", "id"=>2, "result"=>false}]
|
37
|
+
def send_batch(batch)
|
38
|
+
result = send_single(batch.to_json)
|
39
|
+
result = JSON.parse(result)
|
40
|
+
|
41
|
+
# Make sure the order is the same as it was when batching calls
|
42
|
+
# See 6 Batch here http://www.jsonrpc.org/specification
|
43
|
+
return result.sort_by! { |c| c['id'] }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|