web3-hpb 0.0.2

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.
@@ -0,0 +1,172 @@
1
+ module Web3
2
+ module Hpb
3
+
4
+ class Contract
5
+
6
+ class ContractInstance
7
+
8
+ def initialize(contract, address)
9
+ @contract = contract
10
+ @address = address
11
+ end
12
+
13
+ def method_missing(m, *args)
14
+ @contract.call_contract(@address, m.to_s, args)
15
+ end
16
+
17
+ def __contract__
18
+ @contract
19
+ end
20
+
21
+ def __address__
22
+ @address
23
+ end
24
+ end
25
+
26
+ class ContractMethod
27
+
28
+ include Abi::AbiCoder
29
+ include Abi::Utils
30
+ include Utility
31
+
32
+ attr_reader :abi, :signature, :name, :signature_hash, :input_types, :output_types, :constant
33
+
34
+ def initialize(abi)
35
+ @abi = abi
36
+ @name = abi['name']
37
+ @constant = !!abi['constant']
38
+ @input_types = abi['inputs'].map{|a| a['type']}
39
+ @output_types = abi['outputs'].map{|a| a['type']} if abi['outputs']
40
+ @signature = Abi::Utils.function_signature(@name, @input_types)
41
+ @signature_hash = Abi::Utils.signature_hash(@signature, (abi['type']=='event' ? 64 : 8))
42
+ end
43
+
44
+ def parse_event_args(log)
45
+
46
+ log_data = remove_0x_head(log.raw_data['data'])
47
+ indexed_types = abi['inputs'].select{|a| a['indexed']}.collect{|a| a['type']}
48
+ not_indexed_types = abi['inputs'].select{|a| !a['indexed']}.collect{|a| a['type']}
49
+
50
+ indexed_args = log.indexed_args
51
+
52
+ if indexed_args.size = indexed_types.size
53
+
54
+ indexed_values = [indexed_types, indexed_args].transpose.collect{|arg|
55
+ decode_type_data(arg.first, [arg.second].pack('H*'))
56
+ }
57
+
58
+ not_indexed_values = not_indexed_types.empty? ? [] :
59
+ decode_abi(not_indexed_types, [log_data].pack('H*'))
60
+
61
+ i = j = 0
62
+
63
+ abi['inputs'].collect{|collect|
64
+ input['indexed'] ? (i+=1; indexed_values[i-1]) :(j+=1; not_indexed_values[j-1])
65
+ }
66
+
67
+ elsif !indexed_args.empty? || !log_data.empty?
68
+ all_types = abi['inputs'].collect{|a| a['type']}
69
+ [all_types[0...indexed_args.size], indexed_args].transpose.collect{|arg|
70
+ decode_typed_data( arg.first, [arg.second].pack('H*') )
71
+ } + decode_abi(all_types[indexed_args.size..-1], [log_data].pack('H*') )
72
+ else
73
+ []
74
+ end
75
+
76
+ end
77
+
78
+ def parse_method_args(transaction)
79
+ d = transaction.call_input_data
80
+ (!d || d.empty?) ? [] : decode_abi(input_types, [d].pack('H*'))
81
+ end
82
+
83
+ def do_call(web3_rpc, contract_address, args)
84
+ data = '0x' + signature_hash + encode_hex(encode_abi(input_types, args))
85
+ puts data
86
+
87
+ response = web3_rpc.request("hpb_call", [{ to: contract_address, data: data}, 'latest'])
88
+ string_data = [remove_0x_head(response)].pack('H*')
89
+
90
+ return nil if string_data.empty?
91
+
92
+ result = decode_abi(output_types, string_data)
93
+ result.length == 1 ? result.first : result
94
+ end
95
+
96
+ end
97
+
98
+ attr_reader :web3_rpc, :abi, :functions, :events, :constructor, :fucntions_by_hash, :events_by_hash
99
+
100
+ def initialize(abi, web_rpc = nil)
101
+ @web3_rpc = web_rpc
102
+ # parse to json format
103
+ @abi = abi.is_a?(String) ? JSON.parse(abi) : abi
104
+ parse_abi(@abi)
105
+ end
106
+
107
+ def at(address)
108
+ ContractInstance.new(self, address)
109
+ end
110
+
111
+ def call_contract(contract_address, method_name, args)
112
+ function = functions[method_name]
113
+ raise "No method found in ABI: #{method_name}" unless function
114
+ raise "Function #{method_name} is not constant: #{method_name}, requires to sign transaction" unless function.constant
115
+ function.do_call(web3_rpc, contract_address, args)
116
+ end
117
+
118
+ def find_event_by_hash(method_hash)
119
+ @events_by_hash[method_hash]
120
+ end
121
+
122
+ def find_function_by_hash(method_hash)
123
+ @functions_by_hash[method_hash]
124
+ end
125
+
126
+ def parse_log_args(log)
127
+ event = find_event_by_hash(log.method_hash)
128
+ raise "No event found by hash #{log.method_hash}, probably ABI is not related to log event" unless event
129
+ event.parse_method_args(log)
130
+ end
131
+
132
+ def parse_call_args(transaction)
133
+ function = find_function_by_hash transaction.method_hash
134
+ raise "No function found by hash #{transaction.method_hash}, probably ABI is not related to call" unless function
135
+ function.parse_method_args transaction
136
+ end
137
+
138
+ def parse_constructor_args(transaction)
139
+ constructor ? constructor.parse_method_args(transaction) : []
140
+ end
141
+
142
+ private
143
+
144
+ def parse_abi(abi)
145
+ @functions = {}
146
+ @events = {}
147
+
148
+ @functions_by_hash = {}
149
+ @events_by_hash = {}
150
+
151
+ abi.each do |a|
152
+ case a['type']
153
+ when 'function'
154
+ method = ContractMethod.new(a)
155
+ @functions[method.name] = method
156
+ puts @functions
157
+ @functions_by_hash[method.signature_hash] = method
158
+ when 'event'
159
+ method = ContractMethod.new(a)
160
+ @events[method.name] = method
161
+ @events_by_hash[method.signature_hash] = method
162
+ when 'constructor'
163
+ method = ContractMethod.new(a)
164
+ @constructor = method
165
+ end
166
+ end
167
+
168
+ end
169
+
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,47 @@
1
+ module Web3
2
+ module Hpb
3
+
4
+ class HpbModule
5
+
6
+ include Web3::Hpb::Utility
7
+
8
+ PREFIX = 'hpb_'
9
+
10
+ def initialize web3_rpc
11
+ @web3_rpc = web3_rpc
12
+ end
13
+
14
+ def getBalance address, block = 'latest', convert_to_hpb = true
15
+ wei = @web3_rpc.request("#{PREFIX}#{__method__}", [address, block]).to_i 16
16
+ convert_to_hpb ? wei_to_hpb(wei) : wei
17
+ end
18
+
19
+ def getBlockByNumber block, full = true, convert_to_object = true
20
+ resp = @web3_rpc.request("#{PREFIX}#{__method__}", [hex(block), full])
21
+ convert_to_object ? Block.new(resp) : resp
22
+ end
23
+
24
+ def blockNumber
25
+ from_hex @web3_rpc.request("#{PREFIX}#{__method__}")
26
+ end
27
+
28
+ def getTransactionByHash tx_hash
29
+ Transaction.new @web3_rpc.request("#{PREFIX}#{__method__}", [tx_hash])
30
+ end
31
+
32
+ def getTransactionReceipt tx_hash
33
+ TransactionReceipt.new @web3_rpc.request("#{PREFIX}#{__method__}", [tx_hash])
34
+ end
35
+
36
+ def contract abi
37
+ Web3::Hpb::Contract.new abi, @web3_rpc
38
+ end
39
+
40
+ def method_missing m, *args
41
+ @web3_rpc.request "#{PREFIX}#{m}", args[0]
42
+ end
43
+
44
+
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,30 @@
1
+ module Web3
2
+ module Hpb
3
+
4
+ class Log
5
+ attr_reader :raw_data
6
+
7
+ def initialize(log)
8
+ @raw_data = log
9
+ log.each do |k, v|
10
+ self.instance_variable_set("@#{k}", v)
11
+ self.class.send(:define_method, k, proc{ self.instance_variable_get("@#{k}")})
12
+ end
13
+ end
14
+
15
+ def has_topics?
16
+ !!topics.first
17
+ end
18
+
19
+ def method_hash
20
+ topics.first && topics.first[2..65]
21
+ end
22
+
23
+ def indexed_args
24
+ topics[1...topics.size].collect{ |x| x[2..65] }
25
+ end
26
+
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,64 @@
1
+ module Web3
2
+ module Hpb
3
+
4
+ class Rpc
5
+
6
+ require 'json'
7
+ require 'net/http'
8
+
9
+ JSON_RPC_VERSION = '2.0'
10
+ DEFAULT_CONNECT_OPTIONS = {
11
+ use_ssl: false,
12
+ open_timeout: 10,
13
+ read_timeout: 70
14
+ }
15
+
16
+ DEFAULT_HOST = 'localhost'
17
+ DEFAULT_PORT = 8545
18
+
19
+ attr_reader :hpb, :trace
20
+
21
+ def initialize(host: DEFAULT_HOST, port:DEFAULT_PORT, connect_options: DEFAULT_CONNECT_OPTIONS)
22
+
23
+ @client_id = Random.rand(1000_000)
24
+
25
+ @uri = URI((connect_options[:use_ssl] ? 'https' : 'http') + "://#{host}:#{port}#{connect_options[:rpc_path]}")
26
+ @connect_options = connect_options
27
+ @hpb = HpbModule.new(self)
28
+ @trace = TraceModule.new(self)
29
+ end
30
+
31
+ def request(method, params = nil)
32
+
33
+ Net::HTTP.start(@uri.host, @uri.port, @connect_options) do |http|
34
+ request = Net::HTTP::Post.new(@uri, {"Content-Type": "application/json"})
35
+ request.body = {jsonrpc: JSON_RPC_VERSION, method: method, params: params, id: @client_id}.compact.to_json
36
+ response = http.request(request)
37
+
38
+ raise "Error code #{response.code} on request #{@uri.to_s} #{request.body}" unless response.kind_of? Net::HTTPOK
39
+
40
+ body = JSON.parse(response.body)
41
+
42
+ if body["result"]
43
+ body["result"]
44
+ elsif body["error"]
45
+ raise "Error #{@uri.to_s} #{body['error']} on request #{@uri.to_s} #{request.body}"
46
+ else
47
+ raise "No response on request #{@uri.to_s} #{request.body}"
48
+ end
49
+
50
+ end
51
+ end
52
+ end
53
+
54
+ end
55
+ end
56
+
57
+
58
+ unless Hash.method_defined?(:compact)
59
+ class Hash
60
+ def compact
61
+ self.reject{ |_k, v| v.nil? }
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,26 @@
1
+ module Web3
2
+ module Hpb
3
+
4
+ class TraceModule
5
+
6
+ include Web3::Hpb::Utility
7
+
8
+ PREFIX = 'trace_'
9
+
10
+ def initialize web3_rpc
11
+ @web3_rpc = web3_rpc
12
+ end
13
+
14
+ def method_missing m, *args
15
+ @web3_rpc.request "#{PREFIX}#{m}", args[0]
16
+ end
17
+
18
+ def internalCallsByHash tx_hash
19
+ @web3_rpc.request("#{PREFIX}transaction", [tx_hash]).select{|t| t['traceAddress']!=[]}.collect{|t|
20
+ CallTrace.new t
21
+ }
22
+ end
23
+
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,73 @@
1
+ module Web3
2
+ module Hpb
3
+
4
+ class Transaction
5
+
6
+ include Web3::Hpb::Utility
7
+
8
+ attr_reader :raw_data
9
+
10
+ def initialize transaction_data
11
+ @raw_data = transaction_data
12
+ transaction_data.each do |k, v|
13
+ self.instance_variable_set("@#{k}", v)
14
+ self.class.send(:define_method, k, proc {self.instance_variable_get("@#{k}")})
15
+ end
16
+ end
17
+
18
+ def method_hash
19
+ if input && input.length>=10
20
+ input[2...10]
21
+ else
22
+ nil
23
+ end
24
+ end
25
+
26
+ # suffix # 0xa1 0x65 'b' 'z' 'z' 'r' '0' 0x58 0x20 <32 bytes swarm hash> 0x00 0x29
27
+ # look http://solidity.readthedocs.io/en/latest/metadata.html for details
28
+ def call_input_data
29
+ if raw_data['creates'] && input
30
+ fetch_constructor_data input
31
+ elsif input && input.length>10
32
+ input[10..input.length]
33
+ else
34
+ []
35
+ end
36
+ end
37
+
38
+ def block_number
39
+ # if transaction is less than 12 seconds old, blockNumber will be nil
40
+ # :. nil check before calling `to_hex` to avoid argument error
41
+ blockNumber && from_hex(blockNumber)
42
+ end
43
+
44
+ def value_wei
45
+ from_hex(value)
46
+ end
47
+
48
+ def value_hpb
49
+ wei_to_hpb(from_hex value)
50
+ end
51
+
52
+ def gas_limit
53
+ from_hex gas
54
+ end
55
+
56
+ def gasPrice_hpb
57
+ wei_to_hpb(from_hex gasPrice)
58
+ end
59
+
60
+ private
61
+
62
+ CONSTRUCTOR_SEQ = /a165627a7a72305820\w{64}0029(\w*)$/
63
+ def fetch_constructor_data input
64
+ data = input[CONSTRUCTOR_SEQ,1]
65
+ while data && (d = data[CONSTRUCTOR_SEQ,1])
66
+ data = d
67
+ end
68
+ data
69
+ end
70
+
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,41 @@
1
+ module Web3
2
+ module Hpb
3
+
4
+ class TransactionReceipt
5
+
6
+ include Web3::Hpb::Utility
7
+
8
+ attr_reader :raw_data
9
+
10
+ def initialize transaction_data
11
+ @raw_data = transaction_data
12
+
13
+ transaction_data.each do |k, v|
14
+ self.instance_variable_set("@#{k}", v)
15
+ self.class.send(:define_method, k, proc {self.instance_variable_get("@#{k}")})
16
+ end
17
+
18
+ @logs = @logs.collect {|log| Web3::Hpb::Log.new log }
19
+
20
+ end
21
+
22
+ def block_number
23
+ from_hex blockNumber
24
+ end
25
+
26
+ def success?
27
+ status==1 || status=='0x1' || status.nil?
28
+ end
29
+
30
+ def gas_used
31
+ from_hex gasUsed
32
+ end
33
+
34
+
35
+ def cumulative_gas_used
36
+ from_hex cumulativeGasUsed
37
+ end
38
+
39
+ end
40
+ end
41
+ end