ethereum.rb 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|