stellar_core_commander 0.0.11 → 0.0.12
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CONTRIBUTING.md +60 -0
- data/bin/scc +70 -11
- data/examples/cross_host_simple_payment.rb +5 -5
- data/examples/history_generate_and_catchup.rb +26 -0
- data/examples/history_testnet_catchup.rb +15 -0
- data/examples/inflation.rb +8 -0
- data/examples/load_generation.rb +15 -0
- data/examples/load_generation_auto.rb +15 -0
- data/examples/multi_host_simple_payment.rb +12 -5
- data/examples/set_options.rb +23 -0
- data/examples/simple_payment.rb +3 -1
- data/examples/version_mix_consensus.rb +46 -0
- data/lib/stellar_core_commander.rb +1 -0
- data/lib/stellar_core_commander/commander.rb +50 -16
- data/lib/stellar_core_commander/convert.rb +1 -5
- data/lib/stellar_core_commander/docker_process.rb +220 -44
- data/lib/stellar_core_commander/local_process.rb +82 -41
- data/lib/stellar_core_commander/operation_builder.rb +128 -59
- data/lib/stellar_core_commander/process.rb +478 -49
- data/lib/stellar_core_commander/transactor.rb +222 -85
- data/lib/stellar_core_commander/version.rb +1 -1
- data/stellar_core_commander.gemspec +1 -1
- metadata +14 -6
@@ -3,7 +3,7 @@ module StellarCoreCommander
|
|
3
3
|
class OperationBuilder
|
4
4
|
include Contracts
|
5
5
|
|
6
|
-
|
6
|
+
Asset = Or[
|
7
7
|
[String, Symbol],
|
8
8
|
:native,
|
9
9
|
]
|
@@ -13,25 +13,38 @@ module StellarCoreCommander
|
|
13
13
|
]
|
14
14
|
|
15
15
|
OfferCurrencies = Or[
|
16
|
-
{sell:
|
17
|
-
{buy:
|
16
|
+
{sell:Asset, for: Asset},
|
17
|
+
{buy:Asset, with: Asset},
|
18
18
|
]
|
19
19
|
|
20
|
-
|
20
|
+
Byte = And[Num, lambda{|n| (0..255).include? n}]
|
21
|
+
ThresholdByte = Byte
|
22
|
+
MasterWeightByte = Byte
|
21
23
|
|
22
24
|
Thresholds = {
|
23
|
-
low:
|
24
|
-
medium:
|
25
|
-
high:
|
26
|
-
master_weight: ThresholdByte
|
25
|
+
low: Maybe[ThresholdByte],
|
26
|
+
medium: Maybe[ThresholdByte],
|
27
|
+
high: Maybe[ThresholdByte],
|
27
28
|
}
|
28
29
|
|
30
|
+
SetOptionsArgs = {
|
31
|
+
inflation_dest: Maybe[Symbol],
|
32
|
+
clear_flags: Maybe[ArrayOf[Symbol]],
|
33
|
+
set_flags: Maybe[ArrayOf[Symbol]],
|
34
|
+
thresholds: Maybe[Thresholds],
|
35
|
+
master_weight: Maybe[MasterWeightByte],
|
36
|
+
home_domain: Maybe[String],
|
37
|
+
signer: Maybe[Stellar::Signer],
|
38
|
+
}
|
39
|
+
|
40
|
+
MAX_LIMIT= BigDecimal.new((2**63)-1) / Stellar::ONE
|
41
|
+
|
29
42
|
Contract Transactor => Any
|
30
43
|
def initialize(transactor)
|
31
44
|
@transactor = transactor
|
32
45
|
end
|
33
46
|
|
34
|
-
Contract Symbol, Symbol, Amount, Or[{}, {path: ArrayOf[
|
47
|
+
Contract Symbol, Symbol, Amount, Or[{}, {path: ArrayOf[Asset], with:Amount}] => Any
|
35
48
|
def payment(from, to, amount, options={})
|
36
49
|
from = get_account from
|
37
50
|
to = get_account to
|
@@ -45,7 +58,7 @@ module StellarCoreCommander
|
|
45
58
|
|
46
59
|
tx = if options[:with]
|
47
60
|
attrs[:with] = normalize_amount(options[:with])
|
48
|
-
attrs[:path] = options[:path].map{|p|
|
61
|
+
attrs[:path] = options[:path].map{|p| make_asset p}
|
49
62
|
Stellar::Transaction.path_payment(attrs)
|
50
63
|
else
|
51
64
|
Stellar::Transaction.payment(attrs)
|
@@ -55,7 +68,7 @@ module StellarCoreCommander
|
|
55
68
|
end
|
56
69
|
|
57
70
|
Contract Symbol, Symbol, Num => Any
|
58
|
-
def create_account(account, funder=:master, starting_balance=
|
71
|
+
def create_account(account, funder=:master, starting_balance=1000)
|
59
72
|
account = get_account account
|
60
73
|
funder = get_account funder
|
61
74
|
|
@@ -69,7 +82,7 @@ module StellarCoreCommander
|
|
69
82
|
|
70
83
|
Contract Symbol, Symbol, String => Any
|
71
84
|
def trust(account, issuer, code)
|
72
|
-
change_trust account, issuer, code,
|
85
|
+
change_trust account, issuer, code, MAX_LIMIT
|
73
86
|
end
|
74
87
|
|
75
88
|
Contract Symbol, Symbol, String, Num => Any
|
@@ -79,14 +92,14 @@ module StellarCoreCommander
|
|
79
92
|
Stellar::Transaction.change_trust({
|
80
93
|
account: account,
|
81
94
|
sequence: next_sequence(account),
|
82
|
-
line:
|
95
|
+
line: make_asset([code, issuer]),
|
83
96
|
limit: limit
|
84
97
|
}).to_envelope(account)
|
85
98
|
end
|
86
99
|
|
87
100
|
Contract Symbol, Symbol, String, Bool => Any
|
88
101
|
def allow_trust(account, trustor, code, authorize=true)
|
89
|
-
|
102
|
+
asset = make_asset([code, account])
|
90
103
|
account = get_account account
|
91
104
|
trustor = get_account trustor
|
92
105
|
|
@@ -94,7 +107,7 @@ module StellarCoreCommander
|
|
94
107
|
Stellar::Transaction.allow_trust({
|
95
108
|
account: account,
|
96
109
|
sequence: next_sequence(account),
|
97
|
-
|
110
|
+
asset: asset,
|
98
111
|
trustor: trustor,
|
99
112
|
authorize: authorize,
|
100
113
|
}).to_envelope(account)
|
@@ -110,11 +123,11 @@ module StellarCoreCommander
|
|
110
123
|
account = get_account account
|
111
124
|
|
112
125
|
if currencies.has_key?(:sell)
|
113
|
-
|
114
|
-
|
126
|
+
buying = make_asset currencies[:for]
|
127
|
+
selling = make_asset currencies[:sell]
|
115
128
|
else
|
116
|
-
|
117
|
-
|
129
|
+
buying = make_asset currencies[:buy]
|
130
|
+
selling = make_asset currencies[:with]
|
118
131
|
price = 1 / price
|
119
132
|
amount = (amount * price).floor
|
120
133
|
end
|
@@ -122,8 +135,8 @@ module StellarCoreCommander
|
|
122
135
|
Stellar::Transaction.manage_offer({
|
123
136
|
account: account,
|
124
137
|
sequence: next_sequence(account),
|
125
|
-
|
126
|
-
|
138
|
+
selling: selling,
|
139
|
+
buying: buying,
|
127
140
|
amount: amount,
|
128
141
|
price: price,
|
129
142
|
}).to_envelope(account)
|
@@ -134,11 +147,11 @@ module StellarCoreCommander
|
|
134
147
|
account = get_account account
|
135
148
|
|
136
149
|
if currencies.has_key?(:sell)
|
137
|
-
|
138
|
-
|
150
|
+
buying = make_asset currencies[:for]
|
151
|
+
selling = make_asset currencies[:sell]
|
139
152
|
else
|
140
|
-
|
141
|
-
|
153
|
+
buying = make_asset currencies[:buy]
|
154
|
+
selling = make_asset currencies[:with]
|
142
155
|
price = 1 / price
|
143
156
|
amount = (amount * price).floor
|
144
157
|
end
|
@@ -146,60 +159,103 @@ module StellarCoreCommander
|
|
146
159
|
Stellar::Transaction.create_passive_offer({
|
147
160
|
account: account,
|
148
161
|
sequence: next_sequence(account),
|
149
|
-
|
150
|
-
|
162
|
+
selling: selling,
|
163
|
+
buying: buying,
|
151
164
|
amount: amount,
|
152
165
|
price: price,
|
153
166
|
}).to_envelope(account)
|
154
167
|
end
|
155
168
|
|
156
|
-
|
157
|
-
|
158
|
-
def require_trust_auth(account)
|
159
|
-
set_flags account, [:auth_required_flag]
|
160
|
-
end
|
161
|
-
|
162
|
-
Contract Symbol, ArrayOf[Symbol] => Any
|
163
|
-
def set_flags(account, flags)
|
169
|
+
Contract Symbol, SetOptionsArgs => Any
|
170
|
+
def set_options(account, args)
|
164
171
|
account = get_account account
|
165
172
|
|
166
|
-
|
173
|
+
params = {
|
167
174
|
account: account,
|
168
175
|
sequence: next_sequence(account),
|
169
|
-
|
170
|
-
|
176
|
+
}
|
177
|
+
|
178
|
+
if args[:inflation_dest].present?
|
179
|
+
params[:inflation_dest] = get_account args[:inflation_dest]
|
180
|
+
end
|
171
181
|
|
182
|
+
if args[:set_flags].present?
|
183
|
+
params[:set] = make_account_flags(args[:set_flags])
|
184
|
+
end
|
185
|
+
|
186
|
+
if args[:clear_flags].present?
|
187
|
+
params[:clear] = make_account_flags(args[:clear_flags])
|
188
|
+
end
|
189
|
+
|
190
|
+
if args[:master_weight].present?
|
191
|
+
params[:master_weight] = args[:master_weight]
|
192
|
+
end
|
193
|
+
|
194
|
+
if args[:thresholds].present?
|
195
|
+
params[:low_threshold] = args[:thresholds][:low]
|
196
|
+
params[:med_threshold] = args[:thresholds][:medium]
|
197
|
+
params[:high_threshold] = args[:thresholds][:high]
|
198
|
+
end
|
199
|
+
|
200
|
+
if args[:home_domain].present?
|
201
|
+
params[:home_domain] = args[:home_domain]
|
202
|
+
end
|
203
|
+
|
204
|
+
if args[:signer].present?
|
205
|
+
params[:signer] = args[:signer]
|
206
|
+
end
|
207
|
+
|
208
|
+
tx = Stellar::Transaction.set_options(params)
|
172
209
|
tx.to_envelope(account)
|
173
210
|
end
|
174
211
|
|
175
212
|
|
213
|
+
Contract Symbol, ArrayOf[Symbol] => Any
|
214
|
+
def set_flags(account, flags)
|
215
|
+
set_options account, set_flags: flags
|
216
|
+
end
|
217
|
+
|
218
|
+
Contract Symbol, ArrayOf[Symbol] => Any
|
219
|
+
def clear_flags(account, flags)
|
220
|
+
set_options account, clear_flags: flags
|
221
|
+
end
|
222
|
+
|
223
|
+
Contract Symbol => Any
|
224
|
+
def require_trust_auth(account)
|
225
|
+
set_flags account, [:auth_required_flag]
|
226
|
+
end
|
227
|
+
|
176
228
|
Contract Symbol, Stellar::KeyPair, Num => Any
|
177
229
|
def add_signer(account, key, weight)
|
178
|
-
account
|
179
|
-
|
180
|
-
|
181
|
-
account: account,
|
182
|
-
sequence: next_sequence(account),
|
183
|
-
signer: Stellar::Signer.new({
|
184
|
-
pub_key: key.public_key,
|
185
|
-
weight: weight
|
186
|
-
}),
|
230
|
+
set_options account, signer: Stellar::Signer.new({
|
231
|
+
pub_key: key.public_key,
|
232
|
+
weight: weight
|
187
233
|
})
|
234
|
+
end
|
188
235
|
|
189
|
-
|
236
|
+
Contract Symbol, Stellar::KeyPair => Any
|
237
|
+
def remove_signer(account, key)
|
238
|
+
add_signer account, key, 0
|
239
|
+
end
|
240
|
+
|
241
|
+
Contract(Symbol, MasterWeightByte => Any)
|
242
|
+
def set_master_signer_weight(account, weight)
|
243
|
+
set_options account, master_weight: weight
|
190
244
|
end
|
191
245
|
|
192
246
|
Contract(Symbol, Thresholds => Any)
|
193
247
|
def set_thresholds(account, thresholds)
|
194
|
-
account
|
248
|
+
set_options account, thresholds: thresholds
|
249
|
+
end
|
195
250
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
})
|
251
|
+
Contract(Symbol, Symbol => Any)
|
252
|
+
def set_inflation_dest(account, destination)
|
253
|
+
set_options account, inflation_dest: destination
|
254
|
+
end
|
201
255
|
|
202
|
-
|
256
|
+
Contract(Symbol, String => Any)
|
257
|
+
def set_home_domain(account, domain)
|
258
|
+
set_options account, home_domain: domain
|
203
259
|
end
|
204
260
|
|
205
261
|
Contract Symbol, Symbol => Any
|
@@ -216,13 +272,26 @@ module StellarCoreCommander
|
|
216
272
|
tx.to_envelope(account)
|
217
273
|
end
|
218
274
|
|
275
|
+
|
276
|
+
Contract None => Any
|
277
|
+
def inflation(account=:master)
|
278
|
+
account = get_account account
|
279
|
+
|
280
|
+
tx = Stellar::Transaction.inflation({
|
281
|
+
account: account,
|
282
|
+
sequence: next_sequence(account),
|
283
|
+
})
|
284
|
+
|
285
|
+
tx.to_envelope(account)
|
286
|
+
end
|
287
|
+
|
219
288
|
private
|
220
289
|
|
221
290
|
delegate :get_account, to: :@transactor
|
222
291
|
delegate :next_sequence, to: :@transactor
|
223
292
|
|
224
|
-
Contract
|
225
|
-
def
|
293
|
+
Contract Asset => Or[[Symbol, String, Stellar::KeyPair], [:native]]
|
294
|
+
def make_asset(input)
|
226
295
|
if input == :native
|
227
296
|
return [:native]
|
228
297
|
end
|
@@ -230,7 +299,7 @@ module StellarCoreCommander
|
|
230
299
|
code, issuer = *input
|
231
300
|
issuer = get_account issuer
|
232
301
|
|
233
|
-
[:
|
302
|
+
[:alphanum4, code, issuer]
|
234
303
|
end
|
235
304
|
|
236
305
|
def make_account_flags(flags=nil)
|
@@ -247,7 +316,7 @@ module StellarCoreCommander
|
|
247
316
|
def normalize_amount(amount)
|
248
317
|
return amount if amount.first == :native
|
249
318
|
|
250
|
-
amount = [:
|
319
|
+
amount = [:alphanum4] + amount
|
251
320
|
amount[2] = get_account(amount[2]) # translate issuer to account
|
252
321
|
|
253
322
|
amount
|
@@ -1,8 +1,22 @@
|
|
1
1
|
module StellarCoreCommander
|
2
2
|
|
3
|
+
class UnexpectedDifference < StandardError
|
4
|
+
def initialize(kind, x, y)
|
5
|
+
@kind = kind
|
6
|
+
@x = x
|
7
|
+
@y = y
|
8
|
+
end
|
9
|
+
def message
|
10
|
+
"Unexpected difference in #{@kind}: #{@x} != #{@y}"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
3
14
|
class Process
|
4
15
|
include Contracts
|
5
16
|
|
17
|
+
class Crash < StandardError ; end
|
18
|
+
class AlreadyRunning < StandardError ; end
|
19
|
+
|
6
20
|
attr_reader :transactor
|
7
21
|
attr_reader :working_dir
|
8
22
|
attr_reader :name
|
@@ -10,40 +24,94 @@ module StellarCoreCommander
|
|
10
24
|
attr_reader :identity
|
11
25
|
attr_reader :server
|
12
26
|
attr_accessor :unverified
|
13
|
-
attr_reader :threshold
|
14
27
|
attr_reader :host
|
28
|
+
attr_reader :atlas
|
29
|
+
attr_reader :atlas_interval
|
30
|
+
attr_reader :network_passphrase
|
15
31
|
|
16
32
|
DEFAULT_HOST = '127.0.0.1'
|
17
33
|
|
34
|
+
SPECIAL_PEERS = {
|
35
|
+
:testnet1 => {
|
36
|
+
:dns => "core-testnet1.stellar.org",
|
37
|
+
:key => "GDKXE2OZMJIPOSLNA6N6F2BVCI3O777I2OOC4BV7VOYUEHYX7RTRYA7Y",
|
38
|
+
:name => "core-testnet-001",
|
39
|
+
:get => "wget -q https://s3-eu-west-1.amazonaws.com/history.stellar.org/prd/core-testnet/%s/{0} -O {1}"
|
40
|
+
},
|
41
|
+
:testnet2 => {
|
42
|
+
:dns => "core-testnet2.stellar.org",
|
43
|
+
:key => "GCUCJTIYXSOXKBSNFGNFWW5MUQ54HKRPGJUTQFJ5RQXZXNOLNXYDHRAP",
|
44
|
+
:name => "core-testnet-002",
|
45
|
+
:get => "wget -q https://s3-eu-west-1.amazonaws.com/history.stellar.org/prd/core-testnet/%s/{0} -O {1}"
|
46
|
+
},
|
47
|
+
:testnet3 => {
|
48
|
+
:dns => "core-testnet3.stellar.org",
|
49
|
+
:key => "GC2V2EFSXN6SQTWVYA5EPJPBWWIMSD2XQNKUOHGEKB535AQE2I6IXV2Z",
|
50
|
+
:name => "core-testnet-003",
|
51
|
+
:get => "wget -q https://s3-eu-west-1.amazonaws.com/history.stellar.org/prd/core-testnet/%s/{0} -O {1}"
|
52
|
+
}
|
53
|
+
}
|
54
|
+
|
18
55
|
Contract({
|
19
|
-
transactor:
|
20
|
-
working_dir:
|
21
|
-
name:
|
22
|
-
base_port:
|
23
|
-
identity:
|
24
|
-
quorum:
|
25
|
-
|
26
|
-
manual_close:
|
27
|
-
|
56
|
+
transactor: Transactor,
|
57
|
+
working_dir: String,
|
58
|
+
name: Symbol,
|
59
|
+
base_port: Num,
|
60
|
+
identity: Stellar::KeyPair,
|
61
|
+
quorum: ArrayOf[Symbol],
|
62
|
+
peers: Maybe[ArrayOf[Symbol]],
|
63
|
+
manual_close: Maybe[Bool],
|
64
|
+
await_sync: Maybe[Bool],
|
65
|
+
accelerate_time: Maybe[Bool],
|
66
|
+
catchup_complete: Maybe[Bool],
|
67
|
+
forcescp: Maybe[Bool],
|
68
|
+
validate: Maybe[Bool],
|
69
|
+
host: Maybe[String],
|
70
|
+
atlas: Maybe[String],
|
71
|
+
atlas_interval: Num,
|
72
|
+
use_s3: Bool,
|
73
|
+
s3_history_prefix: String,
|
74
|
+
s3_history_region: String,
|
75
|
+
database_url: Maybe[String],
|
76
|
+
keep_database: Maybe[Bool],
|
77
|
+
debug: Maybe[Bool],
|
78
|
+
network_passphrase: Maybe[String],
|
28
79
|
} => Any)
|
29
80
|
def initialize(params)
|
30
81
|
#config
|
31
|
-
@transactor
|
32
|
-
@working_dir
|
33
|
-
@name
|
34
|
-
@base_port
|
35
|
-
@identity
|
36
|
-
@quorum
|
37
|
-
@
|
38
|
-
@manual_close
|
39
|
-
@
|
82
|
+
@transactor = params[:transactor]
|
83
|
+
@working_dir = params[:working_dir]
|
84
|
+
@name = params[:name]
|
85
|
+
@base_port = params[:base_port]
|
86
|
+
@identity = params[:identity]
|
87
|
+
@quorum = params[:quorum]
|
88
|
+
@peers = params[:peers] || params[:quorum]
|
89
|
+
@manual_close = params[:manual_close] || false
|
90
|
+
@await_sync = params.fetch(:await_sync, true)
|
91
|
+
@accelerate_time = params[:accelerate_time] || false
|
92
|
+
@catchup_complete = params[:catchup_complete] || false
|
93
|
+
@forcescp = params.fetch(:forcescp, true)
|
94
|
+
@validate = params.fetch(:validate, true)
|
95
|
+
@host = params[:host]
|
96
|
+
@atlas = params[:atlas]
|
97
|
+
@atlas_interval = params[:atlas_interval]
|
98
|
+
@use_s3 = params[:use_s3]
|
99
|
+
@s3_history_region = params[:s3_history_region]
|
100
|
+
@s3_history_prefix = params[:s3_history_prefix]
|
101
|
+
@database_url = params[:database_url]
|
102
|
+
@keep_database = params[:keep_database]
|
103
|
+
@debug = params[:debug]
|
104
|
+
@network_passphrase = params[:network_passphrase] || Stellar::Networks::TESTNET
|
40
105
|
|
41
106
|
# state
|
42
107
|
@unverified = []
|
43
108
|
|
44
|
-
|
45
109
|
if not @quorum.include? @name
|
46
|
-
@quorum
|
110
|
+
@quorum = @quorum + [@name]
|
111
|
+
end
|
112
|
+
|
113
|
+
if not @peers.include? @name
|
114
|
+
@peers = @peers + [@name]
|
47
115
|
end
|
48
116
|
|
49
117
|
@server = Faraday.new(url: "http://#{hostname}:#{http_port}") do |conn|
|
@@ -52,16 +120,42 @@ module StellarCoreCommander
|
|
52
120
|
end
|
53
121
|
end
|
54
122
|
|
123
|
+
Contract None => Bool
|
124
|
+
def has_special_peers?
|
125
|
+
@peers.any? {|q| SPECIAL_PEERS.has_key? q}
|
126
|
+
end
|
127
|
+
|
128
|
+
Contract ArrayOf[Symbol], Symbol, Bool, Proc => ArrayOf[String]
|
129
|
+
def node_map_or_special_field(nodes, field, include_self)
|
130
|
+
specials = nodes.select {|q| SPECIAL_PEERS.has_key? q}
|
131
|
+
if specials.empty?
|
132
|
+
(nodes.map do |q|
|
133
|
+
if q != @name or include_self
|
134
|
+
yield q
|
135
|
+
end
|
136
|
+
end).compact
|
137
|
+
else
|
138
|
+
specials.map {|q| SPECIAL_PEERS[q][field]}
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
55
142
|
Contract None => ArrayOf[String]
|
56
143
|
def quorum
|
57
|
-
@quorum
|
144
|
+
node_map_or_special_field @quorum, :key, @validate do |q|
|
58
145
|
@transactor.get_process(q).identity.address
|
59
146
|
end
|
60
147
|
end
|
61
148
|
|
62
149
|
Contract None => ArrayOf[String]
|
63
|
-
def
|
64
|
-
@
|
150
|
+
def peer_names
|
151
|
+
node_map_or_special_field @peers, :name, true do |q|
|
152
|
+
q.to_s
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
Contract None => ArrayOf[String]
|
157
|
+
def peer_connections
|
158
|
+
node_map_or_special_field @peers, :dns, false do |q|
|
65
159
|
p = @transactor.get_process(q)
|
66
160
|
"#{p.hostname}:#{p.peer_port}"
|
67
161
|
end
|
@@ -72,32 +166,73 @@ module StellarCoreCommander
|
|
72
166
|
2
|
73
167
|
end
|
74
168
|
|
75
|
-
Contract None => Any
|
76
|
-
def rm_working_dir
|
77
|
-
FileUtils.rm_rf working_dir
|
78
|
-
end
|
79
|
-
|
80
169
|
Contract None => String
|
81
170
|
def idname
|
82
171
|
"#{@name}-#{@base_port}-#{@identity.address[0..5]}"
|
83
172
|
end
|
84
173
|
|
85
|
-
Contract None =>
|
86
|
-
def
|
87
|
-
|
174
|
+
Contract None => String
|
175
|
+
def database_url
|
176
|
+
if @database_url.present?
|
177
|
+
@database_url.strip
|
178
|
+
else
|
179
|
+
default_database_url
|
180
|
+
end
|
181
|
+
end
|
88
182
|
|
89
|
-
|
183
|
+
Contract None => URI::Generic
|
184
|
+
def database_uri
|
185
|
+
URI.parse(database_url)
|
186
|
+
end
|
90
187
|
|
91
|
-
|
92
|
-
|
188
|
+
Contract None => Maybe[String]
|
189
|
+
def database_host
|
190
|
+
database_uri.host
|
191
|
+
end
|
93
192
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
193
|
+
Contract None => String
|
194
|
+
def database_name
|
195
|
+
database_uri.path[1..-1]
|
196
|
+
end
|
197
|
+
|
198
|
+
Contract None => Sequel::Database
|
199
|
+
def database
|
200
|
+
@database ||= Sequel.connect(database_url)
|
201
|
+
end
|
202
|
+
|
203
|
+
Contract None => Maybe[String]
|
204
|
+
def database_user
|
205
|
+
database_uri.user
|
206
|
+
end
|
98
207
|
|
99
|
-
|
100
|
-
|
208
|
+
Contract None => Maybe[String]
|
209
|
+
def database_password
|
210
|
+
database_uri.password
|
211
|
+
end
|
212
|
+
|
213
|
+
Contract None => String
|
214
|
+
def database_port
|
215
|
+
database_uri.port || "5432"
|
216
|
+
end
|
217
|
+
|
218
|
+
Contract None => String
|
219
|
+
def dsn
|
220
|
+
base = "postgresql://dbname=#{database_name} "
|
221
|
+
base << " user=#{database_user}" if database_user.present?
|
222
|
+
base << " password=#{database_password}" if database_password.present?
|
223
|
+
base << " host=#{database_host} port=#{database_port}" if database_host.present?
|
224
|
+
|
225
|
+
base
|
226
|
+
end
|
227
|
+
|
228
|
+
Contract None => Any
|
229
|
+
def wait_for_ready
|
230
|
+
Timeout.timeout(sync_timeout) do
|
231
|
+
loop do
|
232
|
+
break if synced?
|
233
|
+
$stderr.puts "waiting until stellar-core #{idname} is synced (state: #{info_field 'state'}, quorum heard: #{scp_quorum_heard})"
|
234
|
+
sleep 1
|
235
|
+
end
|
101
236
|
end
|
102
237
|
end
|
103
238
|
|
@@ -134,6 +269,45 @@ module StellarCoreCommander
|
|
134
269
|
true
|
135
270
|
end
|
136
271
|
|
272
|
+
Contract Num, Symbol => Any
|
273
|
+
def catchup(ledger, mode)
|
274
|
+
server.get("/catchup?ledger=#{ledger}&mode=#{mode}")
|
275
|
+
end
|
276
|
+
|
277
|
+
Contract None => Hash
|
278
|
+
def info
|
279
|
+
response = server.get("/info")
|
280
|
+
body = ActiveSupport::JSON.decode(response.body)
|
281
|
+
body["info"]
|
282
|
+
rescue
|
283
|
+
{}
|
284
|
+
end
|
285
|
+
|
286
|
+
Contract String => Any
|
287
|
+
def info_field(k)
|
288
|
+
i = info
|
289
|
+
i[k]
|
290
|
+
rescue
|
291
|
+
false
|
292
|
+
end
|
293
|
+
|
294
|
+
Contract None => Bool
|
295
|
+
def synced?
|
296
|
+
(info_field "state") == "Synced!"
|
297
|
+
end
|
298
|
+
|
299
|
+
Contract None => Num
|
300
|
+
def ledger_num
|
301
|
+
(info_field "ledger")["num"]
|
302
|
+
rescue
|
303
|
+
0
|
304
|
+
end
|
305
|
+
|
306
|
+
Contract None => Bool
|
307
|
+
def await_sync?
|
308
|
+
@await_sync
|
309
|
+
end
|
310
|
+
|
137
311
|
Contract None => Hash
|
138
312
|
def metrics
|
139
313
|
response = server.get("/metrics")
|
@@ -143,13 +317,111 @@ module StellarCoreCommander
|
|
143
317
|
{}
|
144
318
|
end
|
145
319
|
|
146
|
-
Contract
|
147
|
-
def
|
148
|
-
metrics
|
320
|
+
Contract String => Num
|
321
|
+
def metrics_count(k)
|
322
|
+
m = metrics
|
323
|
+
m[k]["count"]
|
324
|
+
rescue
|
325
|
+
0
|
326
|
+
end
|
327
|
+
|
328
|
+
Contract String => Num
|
329
|
+
def metrics_1m_rate(k)
|
330
|
+
m = metrics
|
331
|
+
m[k]["1_min_rate"]
|
149
332
|
rescue
|
150
333
|
0
|
151
334
|
end
|
152
335
|
|
336
|
+
Contract String => Any
|
337
|
+
def dump_server_query(s)
|
338
|
+
fname = "#{working_dir}/#{s}-#{Time.now.to_i}-#{rand 100000}.json"
|
339
|
+
$stderr.puts "dumping server query #{fname}"
|
340
|
+
response = server.get("/#{s}")
|
341
|
+
File.open(fname, 'w') {|f| f.write(response.body) }
|
342
|
+
rescue
|
343
|
+
nil
|
344
|
+
end
|
345
|
+
|
346
|
+
Contract None => Any
|
347
|
+
def dump_metrics
|
348
|
+
dump_server_query("metrics")
|
349
|
+
end
|
350
|
+
|
351
|
+
Contract None => Any
|
352
|
+
def dump_info
|
353
|
+
dump_server_query("info")
|
354
|
+
end
|
355
|
+
|
356
|
+
Contract None => Any
|
357
|
+
def dump_scp_state
|
358
|
+
dump_server_query("scp")
|
359
|
+
end
|
360
|
+
|
361
|
+
Contract None => Num
|
362
|
+
def scp_ballots_prepared
|
363
|
+
metrics_count "scp.ballot.prepare"
|
364
|
+
end
|
365
|
+
|
366
|
+
Contract None => Num
|
367
|
+
def scp_quorum_heard
|
368
|
+
metrics_count "scp.quorum.heard"
|
369
|
+
end
|
370
|
+
|
371
|
+
Contract None => Bool
|
372
|
+
def check_no_error_metrics
|
373
|
+
m = metrics
|
374
|
+
for metric in ["scp.envelope.invalidsig",
|
375
|
+
"history.publish.failure",
|
376
|
+
"history.catchup.failure"]
|
377
|
+
c = m[metric]["count"] rescue 0
|
378
|
+
if c != 0
|
379
|
+
raise "nonzero metrics count for #{metric}: #{c}"
|
380
|
+
end
|
381
|
+
end
|
382
|
+
true
|
383
|
+
end
|
384
|
+
|
385
|
+
Contract Num, Num, Or[Symbol, Num] => Any
|
386
|
+
def start_load_generation(accounts, txs, txrate)
|
387
|
+
server.get("/generateload?accounts=#{accounts}&txs=#{txs}&txrate=#{txrate}")
|
388
|
+
end
|
389
|
+
|
390
|
+
Contract None => Num
|
391
|
+
def load_generation_runs
|
392
|
+
metrics_count "loadgen.run.complete"
|
393
|
+
end
|
394
|
+
|
395
|
+
Contract None => Num
|
396
|
+
def transactions_applied
|
397
|
+
metrics_count "ledger.transaction.apply"
|
398
|
+
end
|
399
|
+
|
400
|
+
Contract None => Num
|
401
|
+
def transactions_per_second
|
402
|
+
metrics_1m_rate "ledger.transaction.apply"
|
403
|
+
end
|
404
|
+
|
405
|
+
Contract None => Num
|
406
|
+
def operations_per_second
|
407
|
+
metrics_1m_rate "transaction.op.apply"
|
408
|
+
end
|
409
|
+
|
410
|
+
Contract None => Any
|
411
|
+
def start_checkdb
|
412
|
+
server.get("/checkdb")
|
413
|
+
end
|
414
|
+
|
415
|
+
Contract None => Num
|
416
|
+
def checkdb_runs
|
417
|
+
metrics_count "bucket.checkdb.execute"
|
418
|
+
end
|
419
|
+
|
420
|
+
Contract None => Num
|
421
|
+
def objects_checked
|
422
|
+
metrics_count "bucket.checkdb.object-compare"
|
423
|
+
end
|
424
|
+
|
153
425
|
Contract None => Num
|
154
426
|
def http_port
|
155
427
|
base_port
|
@@ -171,10 +443,21 @@ module StellarCoreCommander
|
|
171
443
|
|
172
444
|
end
|
173
445
|
|
446
|
+
Contract Stellar::KeyPair => Any
|
447
|
+
def account_row(account)
|
448
|
+
row = database[:accounts].where(:accountid => account.address).first
|
449
|
+
raise "Missing account in #{idname}'s database: #{account.address}" unless row
|
450
|
+
row
|
451
|
+
end
|
452
|
+
|
174
453
|
Contract Stellar::KeyPair => Num
|
175
454
|
def sequence_for(account)
|
176
|
-
|
177
|
-
|
455
|
+
(account_row account)[:seqnum]
|
456
|
+
end
|
457
|
+
|
458
|
+
Contract Stellar::KeyPair => Num
|
459
|
+
def balance_for(account)
|
460
|
+
(account_row account)[:balance]
|
178
461
|
end
|
179
462
|
|
180
463
|
Contract None => Num
|
@@ -182,6 +465,87 @@ module StellarCoreCommander
|
|
182
465
|
database[:ledgerheaders].max(:ledgerseq)
|
183
466
|
end
|
184
467
|
|
468
|
+
Contract String => Any
|
469
|
+
def db_store_state(name)
|
470
|
+
database.select(:state).from(:storestate).filter(statename: name).first[:state]
|
471
|
+
end
|
472
|
+
|
473
|
+
Contract None => String
|
474
|
+
def latest_ledger_hash
|
475
|
+
s_lcl = db_store_state("lastclosedledger")
|
476
|
+
t_lcl = database.select(:ledgerhash)
|
477
|
+
.from(:ledgerheaders)
|
478
|
+
.filter(:ledgerseq=>latest_ledger).first[:ledgerhash]
|
479
|
+
raise "inconsistent last-ledger hashes in db: #{t_lcl} vs. #{s_lcl}" if t_lcl != s_lcl
|
480
|
+
s_lcl
|
481
|
+
end
|
482
|
+
|
483
|
+
Contract None => Any
|
484
|
+
def history_archive_state
|
485
|
+
ActiveSupport::JSON.decode(db_store_state("historyarchivestate"))
|
486
|
+
end
|
487
|
+
|
488
|
+
Contract None => Num
|
489
|
+
def account_count
|
490
|
+
database.fetch("SELECT count(*) FROM accounts").first[:count]
|
491
|
+
end
|
492
|
+
|
493
|
+
Contract None => Num
|
494
|
+
def trustline_count
|
495
|
+
database.fetch("SELECT count(*) FROM trustlines").first[:count]
|
496
|
+
end
|
497
|
+
|
498
|
+
Contract None => Num
|
499
|
+
def offer_count
|
500
|
+
database.fetch("SELECT count(*) FROM offers").first[:count]
|
501
|
+
end
|
502
|
+
|
503
|
+
Contract None => ArrayOf[Any]
|
504
|
+
def ten_accounts
|
505
|
+
database.fetch("SELECT * FROM accounts ORDER BY accountid LIMIT 10").all
|
506
|
+
end
|
507
|
+
|
508
|
+
Contract None => ArrayOf[Any]
|
509
|
+
def ten_offers
|
510
|
+
database.fetch("SELECT * FROM offers ORDER BY sellerid LIMIT 10").all
|
511
|
+
end
|
512
|
+
|
513
|
+
Contract None => ArrayOf[Any]
|
514
|
+
def ten_trustlines
|
515
|
+
database.fetch("SELECT * FROM trustlines ORDER BY accountid, issuer, assetcode LIMIT 10").all
|
516
|
+
end
|
517
|
+
|
518
|
+
Contract String, Any, Any => nil
|
519
|
+
def check_equal(kind, x, y)
|
520
|
+
raise UnexpectedDifference.new(kind, x, y) if x != y
|
521
|
+
end
|
522
|
+
|
523
|
+
Contract Process => nil
|
524
|
+
def check_equal_ledger_objects(other)
|
525
|
+
check_equal "account count", account_count, other.account_count
|
526
|
+
check_equal "trustline count", trustline_count, other.trustline_count
|
527
|
+
check_equal "offer count", offer_count, other.offer_count
|
528
|
+
|
529
|
+
check_equal "ten accounts", ten_accounts, other.ten_accounts
|
530
|
+
check_equal "ten trustlines", ten_trustlines, other.ten_trustlines
|
531
|
+
check_equal "ten offers", ten_offers, other.ten_offers
|
532
|
+
end
|
533
|
+
|
534
|
+
Contract Process => Any
|
535
|
+
def check_ledger_sequence_is_prefix_of(other)
|
536
|
+
q = "SELECT ledgerseq, ledgerhash FROM ledgerheaders ORDER BY ledgerseq"
|
537
|
+
our_headers = other.database.fetch(q).all
|
538
|
+
other_headers = other.database.fetch(q).all
|
539
|
+
our_hash = {}
|
540
|
+
other_hash = {}
|
541
|
+
other_headers.each do |row|
|
542
|
+
other_hash[row[:ledgerseq]] = row[:ledgerhash]
|
543
|
+
end
|
544
|
+
our_headers.each do |row|
|
545
|
+
check_equal "ledger hashes", other_hash[row[:ledgerseq]], row[:ledgerhash]
|
546
|
+
end
|
547
|
+
end
|
548
|
+
|
185
549
|
Contract String => Maybe[String]
|
186
550
|
def transaction_result(hex_hash)
|
187
551
|
row = database[:txhistory].where(txid:hex_hash).first
|
@@ -196,14 +560,32 @@ module StellarCoreCommander
|
|
196
560
|
|
197
561
|
Contract None => Num
|
198
562
|
def close_timeout
|
199
|
-
|
563
|
+
150.0
|
564
|
+
end
|
565
|
+
|
566
|
+
Contract None => Num
|
567
|
+
def sync_timeout
|
568
|
+
if has_special_peers? and @catchup_complete
|
569
|
+
# catchup-complete can take quite a while on testnet; for now,
|
570
|
+
# give such tests an hour. May require a change in strategy later.
|
571
|
+
3600.0
|
572
|
+
else
|
573
|
+
# Checkpoints are made every 64 ledgers = 320s on a normal network,
|
574
|
+
# or every 8 ledgers = 8s on an accelerated-time network; we give you
|
575
|
+
# 3 checkpoints to make it to a sync (~16min) before giving up. The
|
576
|
+
# accelerated-time variant tends to need more tries due to S3 not
|
577
|
+
# admitting writes instantaneously, so we do not use a tighter bound
|
578
|
+
# for that case, just use the same 16min value, despite commonly
|
579
|
+
# succeeding in 20s or less.
|
580
|
+
320.0 * 3
|
581
|
+
end
|
200
582
|
end
|
201
583
|
|
202
584
|
Contract String, ArrayOf[String] => Maybe[Bool]
|
203
585
|
def run_cmd(cmd, args)
|
204
586
|
args += [{
|
205
|
-
out: "stellar-core.log",
|
206
|
-
err: "stellar-core.log",
|
587
|
+
out: ["stellar-core.log", "a"],
|
588
|
+
err: ["stellar-core.log", "a"],
|
207
589
|
}]
|
208
590
|
|
209
591
|
Dir.chdir working_dir do
|
@@ -211,5 +593,52 @@ module StellarCoreCommander
|
|
211
593
|
end
|
212
594
|
end
|
213
595
|
|
596
|
+
Contract None => Bool
|
597
|
+
def stopped?
|
598
|
+
!running?
|
599
|
+
end
|
600
|
+
|
601
|
+
Contract None => Bool
|
602
|
+
def launched?
|
603
|
+
!!@launched
|
604
|
+
end
|
605
|
+
|
606
|
+
Contract None => Bool
|
607
|
+
def crashed?
|
608
|
+
launched? && stopped?
|
609
|
+
end
|
610
|
+
|
611
|
+
Contract None => Any
|
612
|
+
def prepare
|
613
|
+
# noop by default, implement in subclass to customize behavior
|
614
|
+
nil
|
615
|
+
end
|
616
|
+
|
617
|
+
Contract None => Any
|
618
|
+
def run
|
619
|
+
raise Process::AlreadyRunning, "already running!" if running?
|
620
|
+
raise Process::Crash, "process #{name} has crashed. cannot run process again" if crashed?
|
621
|
+
|
622
|
+
setup
|
623
|
+
launch_process
|
624
|
+
@launched = true
|
625
|
+
end
|
626
|
+
|
627
|
+
# Dumps the database of the process to the working directory, returning the path to the file written to
|
628
|
+
Contract None => String
|
629
|
+
def dump_database
|
630
|
+
raise NotImplementedError, "implement in subclass"
|
631
|
+
end
|
632
|
+
|
633
|
+
private
|
634
|
+
Contract None => Any
|
635
|
+
def launch_process
|
636
|
+
raise NotImplementedError, "implement in subclass"
|
637
|
+
end
|
638
|
+
|
639
|
+
Contract None => Any
|
640
|
+
def setup
|
641
|
+
raise NotImplementedError, "implement in subclass"
|
642
|
+
end
|
214
643
|
end
|
215
644
|
end
|