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.
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