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.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.travis.yml +7 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +26 -0
- data/README.md +87 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/web3/hpb.rb +12 -0
- data/lib/web3/hpb/abi/abi_coder.rb +389 -0
- data/lib/web3/hpb/abi/constant.rb +30 -0
- data/lib/web3/hpb/abi/exceptions.rb +28 -0
- data/lib/web3/hpb/abi/type.rb +90 -0
- data/lib/web3/hpb/abi/utils.rb +224 -0
- data/lib/web3/hpb/block.rb +32 -0
- data/lib/web3/hpb/call_trace.rb +87 -0
- data/lib/web3/hpb/contract.rb +172 -0
- data/lib/web3/hpb/hpb_module.rb +47 -0
- data/lib/web3/hpb/log.rb +30 -0
- data/lib/web3/hpb/rpc.rb +64 -0
- data/lib/web3/hpb/trace_module.rb +26 -0
- data/lib/web3/hpb/transaction.rb +73 -0
- data/lib/web3/hpb/transaction_receipt.rb +41 -0
- data/lib/web3/hpb/utility.rb +24 -0
- data/lib/web3/hpb/version.rb +5 -0
- data/web3-hpb-0.1.0.gem +0 -0
- data/web3-hpb.gemspec +30 -0
- metadata +141 -0
@@ -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
|
data/lib/web3/hpb/log.rb
ADDED
@@ -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
|
data/lib/web3/hpb/rpc.rb
ADDED
@@ -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
|