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
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'uri'
|
2
|
+
require 'set'
|
2
3
|
require 'securerandom'
|
3
4
|
|
4
5
|
module StellarCoreCommander
|
@@ -6,15 +7,41 @@ module StellarCoreCommander
|
|
6
7
|
class DockerProcess < Process
|
7
8
|
include Contracts
|
8
9
|
|
10
|
+
attr_reader :docker_core_image
|
11
|
+
attr_reader :docker_state_image
|
12
|
+
|
13
|
+
Contract({
|
14
|
+
docker_state_image: String,
|
15
|
+
docker_core_image: String,
|
16
|
+
docker_pull: Bool
|
17
|
+
} => Any)
|
18
|
+
def initialize(params)
|
19
|
+
@docker_state_image = params[:docker_state_image]
|
20
|
+
@docker_core_image = params[:docker_core_image]
|
21
|
+
@docker_pull = params[:docker_pull]
|
22
|
+
super
|
23
|
+
end
|
24
|
+
|
9
25
|
Contract None => Num
|
10
26
|
def required_ports
|
11
27
|
3
|
12
28
|
end
|
13
29
|
|
30
|
+
Contract None => Any
|
31
|
+
def launch_heka_container
|
32
|
+
$stderr.puts "launching heka container #{heka_container_name}"
|
33
|
+
docker %W(run
|
34
|
+
--name #{heka_container_name}
|
35
|
+
--net container:#{container_name}
|
36
|
+
--volumes-from #{container_name}
|
37
|
+
-d stellar/heka
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
14
41
|
Contract None => Any
|
15
42
|
def launch_state_container
|
16
|
-
$stderr.puts "launching state container #{state_container_name}"
|
17
|
-
docker %W(run --name #{state_container_name} -p #{postgres_port}:5432 --env-file stellar-core.env -d
|
43
|
+
$stderr.puts "launching state container #{state_container_name} from image #{docker_state_image}"
|
44
|
+
docker %W(run --name #{state_container_name} -p #{postgres_port}:5432 --env-file stellar-core.env -d #{docker_state_image})
|
18
45
|
raise "Could not create state container" unless $?.success?
|
19
46
|
end
|
20
47
|
|
@@ -25,6 +52,13 @@ module StellarCoreCommander
|
|
25
52
|
raise "Could not drop db: #{database_name}" unless $?.success?
|
26
53
|
end
|
27
54
|
|
55
|
+
Contract None => Any
|
56
|
+
def shutdown_heka_container
|
57
|
+
return true unless heka_container_running?
|
58
|
+
docker %W(rm -f -v #{heka_container_name})
|
59
|
+
raise "Could not stop heka container: #{heka_container_name}" unless $?.success?
|
60
|
+
end
|
61
|
+
|
28
62
|
Contract None => Any
|
29
63
|
def write_config
|
30
64
|
IO.write("#{working_dir}/stellar-core.env", config)
|
@@ -35,66 +69,75 @@ module StellarCoreCommander
|
|
35
69
|
write_config
|
36
70
|
end
|
37
71
|
|
38
|
-
Contract None =>
|
39
|
-
def
|
40
|
-
raise "already running!" if running?
|
41
|
-
setup
|
72
|
+
Contract None => Any
|
73
|
+
def launch_process
|
42
74
|
launch_state_container
|
43
75
|
launch_stellar_core
|
76
|
+
launch_heka_container if atlas
|
44
77
|
end
|
45
78
|
|
46
79
|
Contract None => Bool
|
47
80
|
def running?
|
48
|
-
|
49
|
-
|
81
|
+
container_running? container_name
|
82
|
+
end
|
83
|
+
|
84
|
+
Contract None => Bool
|
85
|
+
def heka_container_running?
|
86
|
+
container_running? heka_container_name
|
50
87
|
end
|
51
88
|
|
52
89
|
Contract None => Bool
|
53
90
|
def state_container_running?
|
54
|
-
|
55
|
-
$?.success?
|
91
|
+
container_running? state_container_name
|
56
92
|
end
|
57
93
|
|
58
94
|
Contract None => Any
|
59
95
|
def shutdown
|
60
96
|
return true unless running?
|
97
|
+
docker %W(stop #{container_name})
|
98
|
+
docker %W(exec #{container_name} rm -rf /history)
|
61
99
|
docker %W(rm -f #{container_name})
|
62
100
|
end
|
63
101
|
|
64
102
|
Contract None => Any
|
65
103
|
def cleanup
|
66
104
|
database.disconnect
|
105
|
+
dump_database
|
106
|
+
dump_logs
|
107
|
+
dump_cores
|
108
|
+
dump_scp_state
|
109
|
+
dump_info
|
110
|
+
dump_metrics
|
67
111
|
shutdown
|
68
112
|
shutdown_state_container
|
69
|
-
|
113
|
+
shutdown_heka_container if atlas
|
70
114
|
end
|
71
115
|
|
72
116
|
Contract None => Any
|
73
|
-
def
|
74
|
-
|
75
|
-
host_args = "-H tcp://#{docker_host}:#{docker_port}" if host
|
76
|
-
`docker #{host_args} exec #{state_container_name} pg_dump -U #{database_user} --clean --no-owner #{database_name}`
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
Contract None => Sequel::Database
|
81
|
-
def database
|
82
|
-
@database ||= Sequel.postgres(database_name, host: docker_host, port: postgres_port, user: database_user, password: database_password)
|
117
|
+
def dump_logs
|
118
|
+
docker ["logs", container_name]
|
83
119
|
end
|
84
120
|
|
85
|
-
Contract None =>
|
86
|
-
def
|
87
|
-
|
121
|
+
Contract None => Any
|
122
|
+
def dump_cores
|
123
|
+
docker %W(run --volumes-from #{container_name} --rm -e MODE=local #{docker_core_image} /utils/core_file_processor.py)
|
124
|
+
docker %W(cp #{container_name}:/cores .)
|
88
125
|
end
|
89
126
|
|
90
|
-
Contract None =>
|
91
|
-
def
|
92
|
-
"
|
127
|
+
Contract None => Any
|
128
|
+
def dump_database
|
129
|
+
fname = "#{working_dir}/database-#{Time.now.to_i}-#{rand 100000}.sql"
|
130
|
+
$stderr.puts "dumping database to #{fname}"
|
131
|
+
host_args = "-H tcp://#{docker_host}:#{docker_port}" if host
|
132
|
+
sql = `docker #{host_args} exec #{state_container_name} pg_dump -U #{database_user} --clean --no-owner --no-privileges #{database_name}`
|
133
|
+
File.open(fname, 'w') {|f| f.write(sql) }
|
134
|
+
fname
|
93
135
|
end
|
94
136
|
|
95
137
|
Contract None => String
|
96
|
-
def
|
138
|
+
def default_database_url
|
97
139
|
@database_password ||= SecureRandom.hex
|
140
|
+
"postgres://postgres:#{@database_password}@#{docker_host}:#{postgres_port}/stellar"
|
98
141
|
end
|
99
142
|
|
100
143
|
Contract None => Num
|
@@ -112,6 +155,11 @@ module StellarCoreCommander
|
|
112
155
|
"scc-state-#{idname}"
|
113
156
|
end
|
114
157
|
|
158
|
+
Contract None => String
|
159
|
+
def heka_container_name
|
160
|
+
"scc-heka-#{idname}"
|
161
|
+
end
|
162
|
+
|
115
163
|
Contract None => String
|
116
164
|
def docker_host
|
117
165
|
return host if host
|
@@ -124,44 +172,167 @@ module StellarCoreCommander
|
|
124
172
|
docker_host
|
125
173
|
end
|
126
174
|
|
175
|
+
Contract None => Bool
|
176
|
+
def docker_pull?
|
177
|
+
@docker_pull
|
178
|
+
end
|
179
|
+
|
180
|
+
Contract None => ArrayOf[String]
|
181
|
+
def aws_credentials_volume
|
182
|
+
if use_s3 and File.exists?("#{ENV['HOME']}/.aws")
|
183
|
+
["-v", "#{ENV['HOME']}/.aws:/root/.aws:ro"]
|
184
|
+
else
|
185
|
+
[]
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
Contract None => Bool
|
190
|
+
def use_s3
|
191
|
+
if @use_s3
|
192
|
+
true
|
193
|
+
else
|
194
|
+
if host and (@quorum.size > 1)
|
195
|
+
$stderr.puts "WARNING: multi-peer with remote docker host, but no s3; history will not be shared"
|
196
|
+
end
|
197
|
+
false
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
Contract None => ArrayOf[String]
|
202
|
+
def shared_history_volume
|
203
|
+
if use_s3
|
204
|
+
[]
|
205
|
+
else
|
206
|
+
dir = File.expand_path("#{working_dir}/../history-archives")
|
207
|
+
Dir.mkdir(dir) unless File.exists?(dir)
|
208
|
+
["-v", "#{dir}:/history"]
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
Contract None => String
|
213
|
+
def history_get_command
|
214
|
+
cmds = Set.new
|
215
|
+
localget = "cp /history/%s/{0} {1}"
|
216
|
+
s3get = "aws s3 --region #{@s3_history_region} cp #{@s3_history_prefix}/%s/{0} {1}"
|
217
|
+
@quorum.each do |q|
|
218
|
+
if q == @name
|
219
|
+
next
|
220
|
+
end
|
221
|
+
if SPECIAL_PEERS.has_key? q
|
222
|
+
cmds.add SPECIAL_PEERS[q][:get]
|
223
|
+
elsif use_s3
|
224
|
+
cmds.add s3get
|
225
|
+
else
|
226
|
+
cmds.add localget
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
if cmds.size == 0
|
231
|
+
if use_s3
|
232
|
+
cmds.add s3get
|
233
|
+
else
|
234
|
+
cmds.add localget
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
if cmds.size != 1
|
239
|
+
raise "Conflicting get commands: #{cmds.to_a.inspect}"
|
240
|
+
end
|
241
|
+
<<-EOS.strip_heredoc
|
242
|
+
HISTORY_GET=#{cmds.to_a.first}
|
243
|
+
EOS
|
244
|
+
end
|
245
|
+
|
246
|
+
Contract None => String
|
247
|
+
def history_put_commands
|
248
|
+
if has_special_peers?
|
249
|
+
""
|
250
|
+
else
|
251
|
+
if use_s3
|
252
|
+
<<-EOS.strip_heredoc
|
253
|
+
HISTORY_PUT=aws s3 --region #{@s3_history_region} cp {0} #{@s3_history_prefix}/%s/{1}
|
254
|
+
EOS
|
255
|
+
else
|
256
|
+
<<-EOS.strip_heredoc
|
257
|
+
HISTORY_PUT=cp {0} /history/%s/{1}
|
258
|
+
HISTORY_MKDIR=mkdir -p /history/%s/{0}
|
259
|
+
EOS
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
def prepare
|
265
|
+
$stderr.puts "preparing #{idname} (dir:#{working_dir})"
|
266
|
+
return unless docker_pull?
|
267
|
+
docker %W(pull #{docker_state_image})
|
268
|
+
docker %W(pull #{docker_core_image})
|
269
|
+
docker %W(pull stellar/heka)
|
270
|
+
end
|
271
|
+
|
272
|
+
def crash
|
273
|
+
docker %W(exec #{container_name} pkill -ABRT stellar-core)
|
274
|
+
end
|
275
|
+
|
127
276
|
private
|
128
277
|
def launch_stellar_core
|
129
278
|
$stderr.puts "launching stellar-core container #{container_name}"
|
130
|
-
docker %W(run
|
279
|
+
docker (%W(run
|
131
280
|
--name #{container_name}
|
132
281
|
--net host
|
133
282
|
--volumes-from #{state_container_name}
|
283
|
+
) + aws_credentials_volume + shared_history_volume + %W(
|
134
284
|
--env-file stellar-core.env
|
135
|
-
-d
|
136
|
-
/
|
137
|
-
|
285
|
+
-d #{docker_core_image}
|
286
|
+
/start #{@name} fresh #{"forcescp" if @forcescp}
|
287
|
+
))
|
138
288
|
raise "Could not create stellar-core container" unless $?.success?
|
139
289
|
end
|
140
290
|
|
141
291
|
Contract None => String
|
142
292
|
def config
|
293
|
+
(
|
143
294
|
<<-EOS.strip_heredoc
|
144
295
|
POSTGRES_PASSWORD=#{database_password}
|
145
296
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
297
|
+
ENVIRONMENT=scc
|
298
|
+
CLUSTER_NAME=#{recipe_name}
|
299
|
+
HOSTNAME=#{idname}
|
300
|
+
|
301
|
+
#{@name}_POSTGRES_PORT=#{postgres_port}
|
302
|
+
#{@name}_PEER_PORT=#{peer_port}
|
303
|
+
#{@name}_HTTP_PORT=#{http_port}
|
304
|
+
#{@name}_PEER_SEED=#{identity.seed}
|
305
|
+
#{"#{@name}_VALIDATION_SEED=#{identity.seed}" if @validate}
|
151
306
|
|
152
307
|
#{"MANUAL_CLOSE=true" if manual_close?}
|
153
308
|
|
154
|
-
|
309
|
+
ARTIFICIALLY_GENERATE_LOAD_FOR_TESTING=true
|
310
|
+
#{"ARTIFICIALLY_ACCELERATE_TIME_FOR_TESTING=true" if @accelerate_time}
|
311
|
+
#{"CATCHUP_COMPLETE=true" if @catchup_complete}
|
155
312
|
|
156
|
-
|
157
|
-
QUORUM_SET=#{quorum}
|
313
|
+
#{"ATLAS_ADDRESS=" + atlas if atlas}
|
158
314
|
|
159
|
-
|
315
|
+
METRICS_INTERVAL=#{atlas_interval}
|
160
316
|
|
161
|
-
|
162
|
-
|
163
|
-
|
317
|
+
#{"COMMANDS=[\"ll?level=debug\"]" if @debug}
|
318
|
+
|
319
|
+
FAILURE_SAFETY=0
|
320
|
+
UNSAFE_QUORUM=true
|
321
|
+
|
322
|
+
PREFERRED_PEERS=#{peer_connections}
|
323
|
+
VALIDATORS=#{quorum}
|
324
|
+
|
325
|
+
HISTORY_PEERS=#{peer_names}
|
326
|
+
|
327
|
+
NETWORK_PASSPHRASE="#{network_passphrase}"
|
164
328
|
EOS
|
329
|
+
) + history_get_command + history_put_commands
|
330
|
+
end
|
331
|
+
|
332
|
+
def recipe_name
|
333
|
+
File.basename($opts[:recipe], '.rb')
|
334
|
+
rescue TypeError
|
335
|
+
'recipe_name_not_found'
|
165
336
|
end
|
166
337
|
|
167
338
|
def docker_port
|
@@ -183,5 +354,10 @@ module StellarCoreCommander
|
|
183
354
|
def docker(args)
|
184
355
|
run_cmd "docker", docker_args + args
|
185
356
|
end
|
357
|
+
|
358
|
+
def container_running?(name)
|
359
|
+
docker ['inspect', '-f', '{{.Name}} running: {{.State.Running}}', name]
|
360
|
+
$?.success?
|
361
|
+
end
|
186
362
|
end
|
187
363
|
end
|
@@ -4,12 +4,15 @@ module StellarCoreCommander
|
|
4
4
|
include Contracts
|
5
5
|
|
6
6
|
attr_reader :pid
|
7
|
-
attr_reader :wait
|
8
7
|
|
9
8
|
def initialize(params)
|
9
|
+
raise "`host` param is unsupported on LocalProcess, please use `-p docker` for this recipe." if params[:host]
|
10
|
+
$stderr.puts "Warning: Ignoring `atlas` param since LocalProcess doesn't support this." if params[:atlas]
|
11
|
+
|
10
12
|
super
|
11
13
|
@stellar_core_bin = params[:stellar_core_bin]
|
12
|
-
|
14
|
+
@database_url = params[:database].try(:strip)
|
15
|
+
|
13
16
|
setup_working_dir
|
14
17
|
end
|
15
18
|
|
@@ -21,7 +24,8 @@ module StellarCoreCommander
|
|
21
24
|
|
22
25
|
Contract None => Any
|
23
26
|
def initialize_history
|
24
|
-
|
27
|
+
Dir.mkdir(history_dir) unless File.exists?(history_dir)
|
28
|
+
run_cmd "./stellar-core", ["--newhist", @name.to_s]
|
25
29
|
raise "Could not initialize history" unless $?.success?
|
26
30
|
end
|
27
31
|
|
@@ -48,19 +52,22 @@ module StellarCoreCommander
|
|
48
52
|
IO.write("#{@working_dir}/stellar-core.cfg", config)
|
49
53
|
end
|
50
54
|
|
55
|
+
Contract None => String
|
56
|
+
def history_dir
|
57
|
+
File.expand_path("#{working_dir}/../history-archives")
|
58
|
+
end
|
59
|
+
|
51
60
|
Contract None => Any
|
52
61
|
def setup
|
53
62
|
write_config
|
54
|
-
create_database
|
63
|
+
create_database unless @keep_database
|
55
64
|
initialize_history
|
56
65
|
initialize_database
|
57
66
|
end
|
58
67
|
|
59
68
|
Contract None => Num
|
60
|
-
def
|
61
|
-
|
62
|
-
setup
|
63
|
-
forcescp
|
69
|
+
def launch_process
|
70
|
+
forcescp if @forcescp
|
64
71
|
launch_stellar_core
|
65
72
|
end
|
66
73
|
|
@@ -84,52 +91,50 @@ module StellarCoreCommander
|
|
84
91
|
::Process.kill "KILL", @pid
|
85
92
|
end
|
86
93
|
|
87
|
-
@
|
94
|
+
@wait_value == 0
|
88
95
|
end
|
89
96
|
|
90
97
|
Contract None => Any
|
91
98
|
def cleanup
|
92
99
|
database.disconnect
|
100
|
+
dump_database
|
101
|
+
dump_scp_state
|
102
|
+
dump_info
|
103
|
+
dump_metrics
|
93
104
|
shutdown
|
94
|
-
drop_database
|
95
|
-
rm_working_dir
|
105
|
+
drop_database unless @keep_database
|
96
106
|
end
|
97
107
|
|
98
108
|
Contract None => Any
|
99
109
|
def dump_database
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
Contract None => Sequel::Database
|
107
|
-
def database
|
108
|
-
@database ||= Sequel.postgres(database_name)
|
110
|
+
fname = "#{working_dir}/database-#{Time.now.to_i}-#{rand 100000}.sql"
|
111
|
+
$stderr.puts "dumping database to #{fname}"
|
112
|
+
sql = `pg_dump #{database_name} --clean --no-owner --no-privileges`
|
113
|
+
File.open(fname, 'w') {|f| f.write(sql) }
|
114
|
+
fname
|
109
115
|
end
|
110
116
|
|
111
117
|
Contract None => String
|
112
|
-
def
|
113
|
-
"
|
118
|
+
def default_database_url
|
119
|
+
"postgres:///#{idname}"
|
114
120
|
end
|
115
121
|
|
116
|
-
|
117
|
-
|
118
|
-
"postgresql://dbname=#{database_name}"
|
122
|
+
def crash
|
123
|
+
`kill -ABRT #{@pid}`
|
119
124
|
end
|
120
125
|
|
121
126
|
private
|
122
127
|
def launch_stellar_core
|
123
128
|
Dir.chdir @working_dir do
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
@pid = wait.pid
|
129
|
+
@pid = ::Process.spawn("./stellar-core",
|
130
|
+
:out => "stdout.txt",
|
131
|
+
:err => "stderr.txt")
|
132
|
+
@wait = Thread.new {
|
133
|
+
@wait_value = ::Process.wait(@pid);
|
134
|
+
$stderr.puts "stellar-core process exited: #{@wait_value}"
|
135
|
+
}
|
132
136
|
end
|
137
|
+
@pid
|
133
138
|
end
|
134
139
|
|
135
140
|
Contract None => String
|
@@ -139,25 +144,61 @@ module StellarCoreCommander
|
|
139
144
|
RUN_STANDALONE=false
|
140
145
|
HTTP_PORT=#{http_port}
|
141
146
|
PUBLIC_HTTP_PORT=false
|
142
|
-
|
143
|
-
|
147
|
+
NODE_SEED="#{@identity.seed}"
|
148
|
+
#{"NODE_IS_VALIDATOR=true" if @validate}
|
149
|
+
|
150
|
+
ARTIFICIALLY_GENERATE_LOAD_FOR_TESTING=true
|
151
|
+
#{"ARTIFICIALLY_ACCELERATE_TIME_FOR_TESTING=true" if @accelerate_time}
|
152
|
+
#{"CATCHUP_COMPLETE=true" if @catchup_complete}
|
144
153
|
|
145
154
|
DATABASE="#{dsn}"
|
146
|
-
PREFERRED_PEERS=#{
|
155
|
+
PREFERRED_PEERS=#{peer_connections}
|
147
156
|
|
148
157
|
#{"MANUAL_CLOSE=true" if manual_close?}
|
158
|
+
#{"COMMANDS=[\"ll?level=debug\"]" if @debug}
|
159
|
+
|
160
|
+
FAILURE_SAFETY=0
|
161
|
+
UNSAFE_QUORUM=true
|
162
|
+
|
163
|
+
NETWORK_PASSPHRASE="#{network_passphrase}"
|
149
164
|
|
150
165
|
[QUORUM_SET]
|
151
|
-
THRESHOLD=#{threshold}
|
152
166
|
VALIDATORS=#{quorum}
|
153
167
|
|
154
|
-
|
155
|
-
get="cp history/main/{0} {1}"
|
156
|
-
put="cp {0} history/main/{1}"
|
157
|
-
mkdir="mkdir -p history/main/{0}"
|
168
|
+
#{history_sources}
|
158
169
|
EOS
|
159
170
|
end
|
160
171
|
|
172
|
+
Contract Symbol => String
|
173
|
+
def one_history_source(n)
|
174
|
+
dir = "#{history_dir}/#{n}"
|
175
|
+
if n == @name
|
176
|
+
<<-EOS.strip_heredoc
|
177
|
+
[HISTORY.#{n}]
|
178
|
+
get="cp #{dir}/{0} {1}"
|
179
|
+
put="cp {0} #{dir}/{1}"
|
180
|
+
mkdir="mkdir -p #{dir}/{0}"
|
181
|
+
EOS
|
182
|
+
else
|
183
|
+
name = n.to_s
|
184
|
+
get = "cp #{history_dir}/%s/{0} {1}"
|
185
|
+
if SPECIAL_PEERS.has_key? n
|
186
|
+
name = SPECIAL_PEERS[n][:name]
|
187
|
+
get = SPECIAL_PEERS[n][:get]
|
188
|
+
end
|
189
|
+
get.sub!('%s', name)
|
190
|
+
<<-EOS.strip_heredoc
|
191
|
+
[HISTORY.#{name}]
|
192
|
+
get="#{get}"
|
193
|
+
EOS
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
Contract None => String
|
198
|
+
def history_sources
|
199
|
+
@quorum.map {|n| one_history_source n}.join("\n")
|
200
|
+
end
|
201
|
+
|
161
202
|
def setup_working_dir
|
162
203
|
if @stellar_core_bin.blank?
|
163
204
|
search = `which stellar-core`.strip
|