beowulf-ruby-testnet 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,21 @@
1
+ module Beowulf
2
+ class BaseError < StandardError
3
+ def initialize(error, cause = nil)
4
+ @error = error
5
+ @cause = cause
6
+ end
7
+
8
+ def to_s
9
+ if !!@cause
10
+ JSON[error: @error, cause: @cause] rescue {error: @error, cause: @cause}.to_s
11
+ else
12
+ JSON[@error] rescue @error
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ module Beowulf; class ApiError < BaseError; end; end
19
+ module Beowulf; class TypeError < BaseError; end; end
20
+ module Beowulf; class OperationError < BaseError; end; end
21
+ module Beowulf; class TransactionError < BaseError; end; end
@@ -0,0 +1,14 @@
1
+ module Beowulf
2
+ class BlockApi < Api
3
+ def method_names
4
+ @method_names ||= [
5
+ :get_block_header,
6
+ :get_block
7
+ ].freeze
8
+ end
9
+
10
+ def api_name
11
+ :block_api
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,74 @@
1
+ [
2
+ {
3
+ "roles": ["owner"],
4
+ "operation": "transfer",
5
+ "params": [
6
+ "from",
7
+ "to",
8
+ "amount",
9
+ "fee",
10
+ "memo"
11
+ ]
12
+ },
13
+ {
14
+ "roles": ["owner"],
15
+ "operation": "transfer_to_vesting",
16
+ "params": [
17
+ "from",
18
+ "to",
19
+ "amount",
20
+ "fee"
21
+ ]
22
+ },
23
+ {
24
+ "roles": ["owner"],
25
+ "operation": "withdraw_vesting",
26
+ "params": [
27
+ "account",
28
+ "vesting_shares",
29
+ "fee"
30
+ ]
31
+ },
32
+ {
33
+ "roles": ["owner"],
34
+ "operation": "account_create",
35
+ "params": [
36
+ "fee",
37
+ "creator",
38
+ "new_account_name",
39
+ "owner",
40
+ "json_metadata"
41
+ ]
42
+ },
43
+ {
44
+ "roles": ["owner"],
45
+ "operation": "account_update",
46
+ "params": [
47
+ "account",
48
+ "owner",
49
+ "json_metadata",
50
+ "fee"
51
+ ]
52
+ },
53
+ {
54
+ "roles": ["owner"],
55
+ "operation": "supernode_update",
56
+ "params": [
57
+ "owner",
58
+ "url",
59
+ "block_signing_key",
60
+ "props",
61
+ "fee"
62
+ ]
63
+ },
64
+ {
65
+ "roles": ["owner"],
66
+ "operation": "account_supernode_vote",
67
+ "params": [
68
+ "account",
69
+ "supernode",
70
+ "approve",
71
+ "fee"
72
+ ]
73
+ }
74
+ ]
@@ -0,0 +1,15 @@
1
+ module Beowulf
2
+ module ChainConfig
3
+ EXPIRE_IN_SECS = 600
4
+ EXPIRE_IN_SECS_PROPOSAL = 24 * 60 * 60
5
+
6
+ NETWORKS_BEOWULF_CHAIN_ID = '430b37f23cf146d42f15376f341d7f8f5a1ad6f4e63affdeb5dc61d55d8c95a7'
7
+ NETWORKS_BEOWULF_ADDRESS_PREFIX = 'BEO'
8
+ NETWORKS_BEOWULF_BWF_ASSET = 'BWF'
9
+ NETWORKS_BEOWULF_W_ASSET = 'W'
10
+ NETWORKS_BEOWULF_VEST_ASSET = 'M'
11
+ NETWORKS_BEOWULF_DEFAULT_NODE = 'https://testnet-bw.beowulfchain.com/rpc'
12
+
13
+ NETWORK_CHAIN_IDS = [NETWORKS_BEOWULF_CHAIN_ID]
14
+ end
15
+ end
@@ -0,0 +1,48 @@
1
+ module Beowulf
2
+ class CondenserApi < Api
3
+ METHOD_NAMES = [
4
+ # :broadcast_block,
5
+ :broadcast_transaction,
6
+ :broadcast_transaction_synchronous,
7
+ :get_account_count,
8
+ :get_accounts,
9
+ :get_balance,
10
+ :get_active_supernodes,
11
+ :get_block,
12
+ :get_block_header,
13
+ :get_config,
14
+ :get_dynamic_global_properties,
15
+ :get_hardfork_version,
16
+ :get_key_references,
17
+ :get_next_scheduled_hardfork,
18
+ :get_owner_history,
19
+ :get_potential_signatures,
20
+ :get_required_signatures,
21
+ :get_transaction,
22
+ :get_transaction_hex,
23
+ :get_transaction_with_status,
24
+ :get_version,
25
+ :get_supernode_by_account,
26
+ :get_supernode_count,
27
+ :get_supernode_schedule,
28
+ :get_supernodes,
29
+ :get_supernodes_by_vote,
30
+ :lookup_account_names,
31
+ :lookup_accounts,
32
+ :lookup_supernode_accounts,
33
+ :list_smt_tokens,
34
+ :verify_authority,
35
+ :find_smt_tokens_by_name,
36
+ :get_supernode_voted_by_acc,
37
+ :get_pending_transaction_count
38
+ ].freeze
39
+
40
+ def method_names
41
+ METHOD_NAMES
42
+ end
43
+
44
+ def api_name
45
+ :condenser_api
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,5 @@
1
+ module Beowulf
2
+ # @see Api
3
+ class DatabaseApi < Api
4
+ end
5
+ end
@@ -0,0 +1,228 @@
1
+ module Beowulf
2
+ class ErrorParser
3
+ include Utils
4
+
5
+ attr_reader :response, :error, :error_code, :error_message,
6
+ :api_name, :api_method, :api_params,
7
+ :expiry, :can_retry, :can_reprepare, :node_degraded, :trx_id, :debug
8
+
9
+ alias expiry? expiry
10
+ alias can_retry? can_retry
11
+ alias can_reprepare? can_reprepare
12
+ alias node_degraded? node_degraded
13
+
14
+ REPREPARE_WHITELIST = [
15
+ 'is_canonical( c ): signature is not canonical',
16
+ 'now < trx.expiration: '
17
+ ]
18
+
19
+ DUPECHECK = '(skip & skip_transaction_dupe_check) || trx_idx.indices().get<by_trx_id>().find(trx_id) == trx_idx.indices().get<by_trx_id>().end(): Duplicate transaction check failed'
20
+
21
+ REPREPARE_BLACKLIST = [DUPECHECK]
22
+
23
+ def initialize(response)
24
+ @response = response
25
+
26
+ @error = nil
27
+ @error_code = nil
28
+ @error_message = nil
29
+ @api_name = nil
30
+ @api_method = nil
31
+ @api_params = nil
32
+
33
+ @expiry = nil
34
+ @can_retry = nil
35
+ @can_reprepare = nil
36
+ @trx_id = nil
37
+ @debug = nil
38
+
39
+ parse_error_response
40
+ end
41
+
42
+ def parse_error_response
43
+ if response.nil?
44
+ @expiry = false
45
+ @can_retry = false
46
+ @can_reprepare = false
47
+
48
+ return
49
+ end
50
+
51
+ @response = JSON[response] if response.class == String
52
+
53
+ @error = if !!@response['error']
54
+ response['error']
55
+ else
56
+ response
57
+ end
58
+
59
+ begin
60
+ if !!@error['data']
61
+ # These are, by far, the more interesting errors, so we try to pull
62
+ # them out first, if possible.
63
+
64
+ @error_code = @error['data']['code']
65
+ stacks = @error['data']['stack']
66
+ stack_formats = nil
67
+
68
+ @error_message = if !!stacks
69
+ stack_formats = stacks.map { |s| s['format'] }
70
+ stack_datum = stacks.map { |s| s['data'] }
71
+ data_call_method = stack_datum.find { |data| data['call.method'] == 'call' }
72
+ data_name = stack_datum.find { |data| !!data['name'] }
73
+
74
+ # See if we can recover a transaction id out of this hot mess.
75
+ data_trx_ix = stack_datum.find { |data| !!data['trx_ix'] }
76
+ @trx_id = data_trx_ix['trx_ix'] if !!data_trx_ix
77
+
78
+ stack_formats.reject(&:empty?).join('; ')
79
+ else
80
+ @error_code ||= @error['code']
81
+ @error['message']
82
+ end
83
+
84
+ @api_name, @api_method, @api_params = if !!data_call_method
85
+ @api_name = data_call_method['call.params']
86
+ end
87
+ else
88
+ @error_code = @error['code']
89
+ @error_message = @error['message']
90
+ @expiry = false
91
+ @can_retry = false
92
+ @can_reprepare = false
93
+ end
94
+
95
+ case @error_code
96
+ when -32603
97
+ if error_match?('Internal Error')
98
+ @expiry = false
99
+ @can_retry = true
100
+ @can_reprepare = true
101
+ end
102
+ when -32003
103
+ if error_match?('Unable to acquire database lock')
104
+ @expiry = false
105
+ @can_retry = true
106
+ @can_reprepare = true
107
+ end
108
+ when -32000
109
+ @expiry = false
110
+ @can_retry = coerce_backtrace
111
+ @can_reprepare = if @api_name == 'network_broadcast_api'
112
+ error_match(REPREPARE_WHITELIST)
113
+ else
114
+ false
115
+ end
116
+ when 10
117
+ @expiry = false
118
+ @can_retry = coerce_backtrace
119
+ @can_reprepare = !!stack_formats && (stack_formats & REPREPARE_WHITELIST).any?
120
+ when 13
121
+ @error_message = @error['data']['message']
122
+ @expiry = false
123
+ @can_retry = false
124
+ @can_reprepare = false
125
+ when 3030000
126
+ @error_message = @error['data']['message']
127
+ @expiry = false
128
+ @can_retry = false
129
+ @can_reprepare = false
130
+ when 4030100
131
+ # Code 4030100 is "transaction_expiration_exception: transaction
132
+ # expiration exception". If we assume the expiration was valid, the
133
+ # node might be bad and needs to be dropped.
134
+
135
+ @expiry = true
136
+ @can_retry = true
137
+ @can_reprepare = false
138
+ when 4030200
139
+ # Code 4030200 is "transaction tapos exception". They are recoverable
140
+ # if the transaction hasn't expired yet. A tapos exception can be
141
+ # retried in situations where the node is behind and the tapos is
142
+ # based on a block the node doesn't know about yet.
143
+
144
+ @expiry = false
145
+ @can_retry = true
146
+
147
+ # Allow fall back to reprepare if retry fails.
148
+ @can_reprepare = true
149
+ else
150
+ @expiry = false
151
+ @can_retry = false
152
+ @can_reprepare = false
153
+ end
154
+ rescue => e
155
+ if defined? ap
156
+ if ENV['DEBUG'] == 'true'
157
+ ap error_parser_exception: e, original_response: response, backtrace: e.backtrace
158
+ else
159
+ ap error_parser_exception: e, original_response: response
160
+ end
161
+ end
162
+
163
+ @expiry = false
164
+ @can_retry = false
165
+ @can_reprepare = false
166
+ end
167
+ end
168
+
169
+ def coerce_backtrace
170
+ can_retry = false
171
+
172
+ case @error['code']
173
+ when -32003
174
+ any_of = [
175
+ 'Internal Error"',
176
+ '_api_plugin not enabled.'
177
+ ]
178
+
179
+ can_retry = error_match?('Unable to acquire database lock')
180
+
181
+ if !can_retry && error_match?(any_of)
182
+ can_retry = true
183
+ @node_degraded = true
184
+ else
185
+ @node_degraded = false
186
+ end
187
+ when -32002
188
+ can_retry = @node_degraded = error_match?('Could not find API')
189
+ when 1
190
+ can_retry = @node_degraded = error_match?('no method with name \'condenser_api')
191
+ end
192
+
193
+ can_retry
194
+ end
195
+
196
+ def error_match?(matches)
197
+ matches = [matches].flatten
198
+
199
+ any = matches.map do |match|
200
+ case match
201
+ when String
202
+ @error['message'] && @error['message'].include?(match)
203
+ when ::Array
204
+ if @error['message']
205
+ match.map { |m| m.include?(match) }.include? true
206
+ else
207
+ false
208
+ end
209
+ else; false
210
+ end
211
+ end
212
+
213
+ any.include?(true)
214
+ end
215
+
216
+ def to_s
217
+ if !!error_message && !error_message.empty?
218
+ "#{error_code}: #{error_message}"
219
+ else
220
+ error_code.to_s
221
+ end
222
+ end
223
+
224
+ def inspect
225
+ "#<#{self.class.name} [#{to_s}]>"
226
+ end
227
+ end
228
+ end
@@ -0,0 +1,20 @@
1
+ require 'logger'
2
+
3
+ module Beowulf
4
+ # The logger that Beowulf uses for reporting errors.
5
+ #
6
+ # @return [Logger]
7
+ def self.logger
8
+ @@logger ||= Logger.new(STDOUT).tap do |logger|
9
+ logger.progname = 'beowulf'
10
+ end
11
+ end
12
+
13
+ # Sets the logger that Beowulf uses for reporting errors.
14
+ #
15
+ # @param logger [Logger] The logger to set as Beowulf's logger.
16
+ # @return [void]
17
+ def self.logger=(logger)
18
+ @@logger = logger
19
+ end
20
+ end
@@ -0,0 +1,129 @@
1
+ [
2
+ {
3
+ "api": "database_api",
4
+ "method": "get_block_header",
5
+ "params": ["blockNum"]
6
+ },
7
+ {
8
+ "api": "database_api",
9
+ "method": "get_block",
10
+ "params": ["blockNum"]
11
+ },
12
+ {
13
+ "api": "database_api",
14
+ "method": "get_config"
15
+ },
16
+ {
17
+ "api": "database_api",
18
+ "method": "get_dynamic_global_properties"
19
+ },
20
+ {
21
+ "api": "database_api",
22
+ "method": "get_supernode_schedule"
23
+ },
24
+ {
25
+ "api": "database_api",
26
+ "method": "get_hardfork_version"
27
+ },
28
+ {
29
+ "api": "database_api",
30
+ "method": "get_next_scheduled_hardfork"
31
+ },
32
+ {
33
+ "api": "account_by_key_api",
34
+ "method": "get_key_references",
35
+ "params": ["key"]
36
+ },
37
+ {
38
+ "api": "database_api",
39
+ "method": "get_accounts",
40
+ "params": ["names"]
41
+ },
42
+ {
43
+ "api": "database_api",
44
+ "method": "lookup_account_names",
45
+ "params": ["accountNames"]
46
+ },
47
+ {
48
+ "api": "database_api",
49
+ "method": "lookup_accounts",
50
+ "params": ["lowerBoundName", "limit"]
51
+ },
52
+ {
53
+ "api": "database_api",
54
+ "method": "get_account_count"
55
+ },
56
+ {
57
+ "api": "database_api",
58
+ "method": "get_owner_history",
59
+ "params": ["account"]
60
+ },
61
+ {
62
+ "api": "database_api",
63
+ "method": "get_transaction_hex",
64
+ "params": ["trx"]
65
+ },
66
+ {
67
+ "api": "database_api",
68
+ "method": "get_transaction",
69
+ "params": ["trxId"]
70
+ },
71
+ {
72
+ "api": "database_api",
73
+ "method": "get_required_signatures",
74
+ "params": ["trx", "availableKeys"]
75
+ },
76
+ {
77
+ "api": "database_api",
78
+ "method": "get_potential_signatures",
79
+ "params": ["trx"]
80
+ },
81
+ {
82
+ "api": "database_api",
83
+ "method": "verify_authority",
84
+ "params": ["trx"]
85
+ },
86
+ {
87
+ "api": "database_api",
88
+ "method": "get_supernodes",
89
+ "params": ["supernodeIds"]
90
+ },
91
+ {
92
+ "api": "database_api",
93
+ "method": "get_supernode_by_account",
94
+ "params": ["accountName"]
95
+ },
96
+ {
97
+ "api": "database_api",
98
+ "method": "get_supernodes_by_vote",
99
+ "params": ["from", "limit"]
100
+ },
101
+ {
102
+ "api": "database_api",
103
+ "method": "lookup_supernode_accounts",
104
+ "params": ["lowerBoundName", "limit"]
105
+ },
106
+ {
107
+ "api": "database_api",
108
+ "method": "get_supernode_count"
109
+ },
110
+ {
111
+ "api": "database_api",
112
+ "method": "get_active_supernodes"
113
+ },
114
+ {
115
+ "api": "network_broadcast_api",
116
+ "method": "broadcast_transaction",
117
+ "params": ["trx"]
118
+ },
119
+ {
120
+ "api": "network_broadcast_api",
121
+ "method": "broadcast_transaction_synchronous",
122
+ "params": ["trx"]
123
+ },
124
+ {
125
+ "api": "network_broadcast_api",
126
+ "method": "broadcast_block",
127
+ "params": ["b"]
128
+ }
129
+ ]