@@ -22,11 +22,13 @@ import (
22
22
"github.com/spf13/cobra"
23
23
24
24
sdk "github.com/stellar/go/clients/horizonclient"
25
+ "github.com/stellar/go/clients/stellarcore"
25
26
"github.com/stellar/go/keypair"
26
27
proto "github.com/stellar/go/protocols/horizon"
27
28
horizon "github.com/stellar/go/services/horizon/internal"
28
29
"github.com/stellar/go/support/db/dbtest"
29
30
"github.com/stellar/go/support/errors"
31
+ "github.com/stellar/go/support/log"
30
32
"github.com/stellar/go/txnbuild"
31
33
"github.com/stellar/go/xdr"
32
34
"github.com/stretchr/testify/assert"
@@ -60,6 +62,7 @@ type Test struct {
60
62
config Config
61
63
cli client.APIClient
62
64
hclient * sdk.Client
65
+ cclient * stellarcore.Client
63
66
container container.ContainerCreateCreatedBody
64
67
app * horizon.App
65
68
}
@@ -143,13 +146,23 @@ func NewTest(t *testing.T, config Config) *Test {
143
146
t .Fatal (errors .Wrap (err , "error starting docker container" ))
144
147
}
145
148
149
+ ctx , cancel = context .WithTimeout (context .Background (), 5 * time .Second )
150
+ defer cancel ()
151
+
152
+ containerInfo , err := i .cli .ContainerInspect (ctx , i .container .ID )
153
+ if err != nil {
154
+ i .t .Fatal (errors .Wrap (err , "error inspecting container" ))
155
+ }
156
+ stellarCoreBinding := containerInfo .NetworkSettings .Ports [stellarCorePort ][0 ]
157
+ coreURL := fmt .Sprintf ("http://%s:%s" , stellarCoreBinding .HostIP , stellarCoreBinding .HostPort )
146
158
// only use horizon from quickstart container when testing captive core
147
159
if os .Getenv ("HORIZON_INTEGRATION_ENABLE_CAPTIVE_CORE" ) == "" {
148
- i .startHorizon ()
160
+ i .startHorizon (containerInfo , coreURL )
149
161
}
150
162
151
163
doCleanup = false
152
164
i .hclient = & sdk.Client {HorizonURL : "http://localhost:8000" }
165
+ i .cclient = & stellarcore.Client {URL : coreURL }
153
166
154
167
// Register cleanup handlers (on panic and ctrl+c) so the container is
155
168
// removed even if ingestion or testing fails.
@@ -162,6 +175,7 @@ func NewTest(t *testing.T, config Config) *Test {
162
175
os .Exit (0 )
163
176
}()
164
177
178
+ i .waitForCore ()
165
179
i .waitForIngestionAndUpgrade ()
166
180
return i
167
181
}
@@ -208,26 +222,16 @@ func (i *Test) setupHorizonBinary() {
208
222
}
209
223
}
210
224
211
- func (i * Test ) startHorizon () {
212
- ctx , cancel := context .WithTimeout (context .Background (), 5 * time .Second )
213
- defer cancel ()
214
-
215
- info , err := i .cli .ContainerInspect (ctx , i .container .ID )
216
- if err != nil {
217
- i .t .Fatal (errors .Wrap (err , "error inspecting container" ))
218
- }
219
-
220
- stellarCore := info .NetworkSettings .Ports [stellarCorePort ][0 ]
221
-
222
- stellarCorePostgres := info .NetworkSettings .Ports [stellarCorePostgresPort ][0 ]
225
+ func (i * Test ) startHorizon (containerInfo types.ContainerJSON , coreURL string ) {
226
+ stellarCorePostgres := containerInfo .NetworkSettings .Ports [stellarCorePostgresPort ][0 ]
223
227
stellarCorePostgresURL := fmt .Sprintf (
224
228
"postgres://stellar:%s@%s:%s/core" ,
225
229
stellarCorePostgresPassword ,
226
230
stellarCorePostgres .HostIP ,
227
231
stellarCorePostgres .HostPort ,
228
232
)
229
233
230
- historyArchive := info .NetworkSettings .Ports [historyArchivePort ][0 ]
234
+ historyArchive := containerInfo .NetworkSettings .Ports [historyArchivePort ][0 ]
231
235
232
236
horizonPostgresURL := dbtest .Postgres (i .t ).DSN
233
237
@@ -243,7 +247,7 @@ func (i *Test) startHorizon() {
243
247
}
244
248
cmd .SetArgs ([]string {
245
249
"--stellar-core-url" ,
246
- fmt . Sprintf ( "http://%s:%s" , stellarCore . HostIP , stellarCore . HostPort ) ,
250
+ coreURL ,
247
251
"--history-archive-urls" ,
248
252
fmt .Sprintf ("http://%s:%s" , historyArchive .HostIP , historyArchive .HostPort ),
249
253
"--ingest" ,
@@ -259,17 +263,48 @@ func (i *Test) startHorizon() {
259
263
})
260
264
configOpts .Init (cmd )
261
265
262
- if err = cmd .Execute (); err != nil {
266
+ if err : = cmd .Execute (); err != nil {
263
267
i .t .Fatalf ("cannot initialize horizon: %s" , err )
264
268
}
265
269
266
- if err = i .app .Ingestion ().BuildGenesisState (); err != nil {
270
+ if err : = i .app .Ingestion ().BuildGenesisState (); err != nil {
267
271
i .t .Fatalf ("cannot build genesis state: %s" , err )
268
272
}
269
273
270
274
go i .app .Serve ()
271
275
}
272
276
277
+ // Wait for core to be up and manually close the first ledger
278
+ func (i * Test ) waitForCore () {
279
+ for t := 30 * time .Second ; t >= 0 ; t -= time .Second {
280
+ i .t .Log ("Waiting for core to be up..." )
281
+ ctx , cancel := context .WithTimeout (context .Background (), time .Second )
282
+ _ , err := i .cclient .Info (ctx )
283
+ cancel ()
284
+ if err == nil {
285
+ break
286
+ }
287
+ time .Sleep (time .Second )
288
+ }
289
+
290
+ // We need to wait for Core to be armed before closing the first ledger
291
+ // Otherwise, for some reason, the protocol version of the ledger stays at 0
292
+ // TODO: instead of sleeping we should ensure Core's status (in GET /info) is "Armed"
293
+ // but, to do so, we should first expose it in Core's client.
294
+ time .Sleep (time .Second )
295
+ if err := i .CloseCoreLedger (); err != nil {
296
+ i .t .Fatalf ("Failed to manually close the second ledger: %s" , err )
297
+ }
298
+
299
+ // Make sure that the Sleep above was successful
300
+ ctx , cancel := context .WithTimeout (context .Background (), time .Second )
301
+ info , err := i .cclient .Info (ctx )
302
+ cancel ()
303
+ if err != nil || ! info .IsSynced () {
304
+ log .Fatal ("failed to wait for Core to be synced" )
305
+ }
306
+ }
307
+
273
308
func (i * Test ) waitForIngestionAndUpgrade () {
274
309
for t := 30 * time .Second ; t >= 0 ; t -= time .Second {
275
310
i .t .Log ("Waiting for ingestion and protocol upgrade..." )
@@ -380,6 +415,7 @@ func createTestContainer(i *Test, image string) error {
380
415
Cmd : []string {
381
416
"--standalone" ,
382
417
"--protocol-version" , strconv .FormatInt (int64 (i .config .ProtocolVersion ), 10 ),
418
+ "--enable-core-manual-close" ,
383
419
},
384
420
}
385
421
hostConfig := & container.HostConfig {}
@@ -602,11 +638,32 @@ func (i *Test) CreateSignedTransaction(
602
638
return tx , nil
603
639
}
604
640
641
+ func (i * Test ) CloseCoreLedger () error {
642
+ ctx , cancel := context .WithTimeout (context .Background (), time .Second )
643
+ defer cancel ()
644
+ return i .cclient .ManualClose (ctx )
645
+ }
646
+
605
647
func (i * Test ) SubmitTransaction (tx * txnbuild.Transaction ) (proto.Transaction , error ) {
606
648
txb64 , err := tx .Base64 ()
607
649
if err != nil {
608
650
return proto.Transaction {}, err
609
651
}
652
+ return i .SubmitTransactionXDR (txb64 )
653
+ }
654
+
655
+ func (i * Test ) SubmitTransactionXDR (txb64 string ) (proto.Transaction , error ) {
656
+ // Core runs in manual-close mode, so we need to close ledgers explicitly
657
+ // We need to close the ledger in parallel because Horizon's submission endpoint
658
+ // blocks until the transaction is in a closed ledger
659
+ go func () {
660
+ // This sleep is ugly, but a better approach would probably require
661
+ // instrumenting Horizon to tell us when the transaction was sent to core.
662
+ time .Sleep (time .Millisecond * 100 )
663
+ if err := i .CloseCoreLedger (); err != nil {
664
+ log .Fatalf ("failed to CloseCoreLedger(): %s" , err )
665
+ }
666
+ }()
610
667
611
668
return i .Client ().SubmitTransactionXDR (txb64 )
612
669
}
0 commit comments