ethereum.rb 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rspec +2 -0
  4. data/.ruby-gemset +1 -0
  5. data/.travis.yml +26 -0
  6. data/CODE_OF_CONDUCT.md +13 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE +22 -0
  9. data/LICENSE.txt +21 -0
  10. data/README.md +183 -0
  11. data/Rakefile +11 -0
  12. data/bin/console +14 -0
  13. data/bin/install_parity +29 -0
  14. data/bin/setup +7 -0
  15. data/contracts/AccountingLib.sol +112 -0
  16. data/contracts/AuditorInterface.sol +4 -0
  17. data/contracts/AuditorRegistry.sol +14 -0
  18. data/contracts/CustodianInterface.sol +27 -0
  19. data/contracts/CustodianRegistry.sol +40 -0
  20. data/contracts/DigixConfiguration.sol +68 -0
  21. data/contracts/Directory.sol +67 -0
  22. data/contracts/DoublyLinked.sol +54 -0
  23. data/contracts/GenericInterface.sol +56 -0
  24. data/contracts/GenericRegistry.sol +15 -0
  25. data/contracts/Gold.sol +105 -0
  26. data/contracts/GoldRegistry.sol +82 -0
  27. data/contracts/GoldTokenLedger.sol +3 -0
  28. data/contracts/Interface.sol +27 -0
  29. data/contracts/Minter.sol +3 -0
  30. data/contracts/Recaster.sol +3 -0
  31. data/contracts/Testing.sol +59 -0
  32. data/contracts/VendorInterface.sol +82 -0
  33. data/contracts/VendorRegistry.sol +39 -0
  34. data/contracts/classic/Digixbot.sol +106 -0
  35. data/contracts/classic/DigixbotConfiguration.sol +62 -0
  36. data/contracts/classic/DigixbotEthereum.sol +86 -0
  37. data/contracts/classic/DigixbotUsers.sol +103 -0
  38. data/contracts/classic/Gold.sol +497 -0
  39. data/contracts/classic/GoldRegistry.sol +503 -0
  40. data/contracts/classic/GoldTokenLedger.sol +560 -0
  41. data/contracts/classic/GoldTokenMinter.sol +607 -0
  42. data/contracts/classic/ParticipantRegistry.sol +94 -0
  43. data/contracts/classic/QueueSample.sol +54 -0
  44. data/ethereum.gemspec +35 -0
  45. data/lib/ethereum.rb +24 -0
  46. data/lib/ethereum/client.rb +97 -0
  47. data/lib/ethereum/contract.rb +266 -0
  48. data/lib/ethereum/contract_event.rb +25 -0
  49. data/lib/ethereum/contract_initializer.rb +54 -0
  50. data/lib/ethereum/deployment.rb +49 -0
  51. data/lib/ethereum/formatter.rb +172 -0
  52. data/lib/ethereum/function.rb +20 -0
  53. data/lib/ethereum/function_input.rb +13 -0
  54. data/lib/ethereum/function_output.rb +14 -0
  55. data/lib/ethereum/http_client.rb +38 -0
  56. data/lib/ethereum/initializer.rb +27 -0
  57. data/lib/ethereum/ipc_client.rb +46 -0
  58. data/lib/ethereum/project_initializer.rb +28 -0
  59. data/lib/ethereum/railtie.rb +10 -0
  60. data/lib/ethereum/singleton.rb +39 -0
  61. data/lib/ethereum/solidity.rb +47 -0
  62. data/lib/ethereum/transaction.rb +36 -0
  63. data/lib/ethereum/version.rb +3 -0
  64. data/lib/tasks/ethereum_contract.rake +27 -0
  65. data/lib/tasks/ethereum_node.rake +51 -0
  66. data/lib/tasks/ethereum_test.rake +32 -0
  67. data/lib/tasks/ethereum_transaction.rake +24 -0
  68. 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,13 @@
1
+ module Ethereum
2
+ class FunctionInput
3
+
4
+ attr_accessor :type, :name
5
+
6
+ def initialize(data)
7
+ @type = data["type"]
8
+ @name = data["name"]
9
+ end
10
+
11
+ end
12
+
13
+ end
@@ -0,0 +1,14 @@
1
+ module Ethereum
2
+
3
+ class FunctionOutput
4
+
5
+ attr_accessor :type, :name
6
+
7
+ def initialize(data)
8
+ @type = data["type"]
9
+ @name = data["name"]
10
+ end
11
+
12
+ end
13
+
14
+ 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