beowulf-ruby-testnet 0.0.1

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,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
+ ]