Skip to content

Commit 55f7b85

Browse files
Merge pull request #135 from coinbase/patrick/support-return-funds
Support Automated Fund Return
2 parents cdc7644 + 8a5624c commit 55f7b85

File tree

11 files changed

+459
-64
lines changed

11 files changed

+459
-64
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ deps:
2020
go get ./...
2121

2222
lint:
23-
golangci-lint run -v \
23+
golangci-lint run --timeout 2m0s -v \
2424
-E golint,misspell,gocyclo,whitespace,goconst,gocritic,gocognit,bodyclose,unconvert,lll,unparam,gomnd;
2525

2626
format:

README.md

Lines changed: 135 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,123 @@ Use "rosetta-cli [command] --help" for more information about a command.
8080
All `rosetta-cli` parameters are populated from a configuration file (`--configuration-file`)
8181
provided at runtime. If a configuration file is not provided, the default
8282
configuration is used. This default configuration can be viewed
83-
[here](examples/configuration/default.json).
83+
[here](examples/configuration/default.json). Note, there is no default
84+
configuration for running `check:construction` as this is very network-specific.
85+
You can view a full list of all configuration options [here](https://pkg.go.dev/github.com/coinbase/rosetta-cli/configuration).
8486

8587
In the `examples/configuration` directory, you can find examples configuration
8688
files for running tests against a Bitcoin Rosetta implementation
8789
([config](examples/configuration/bitcoin.json)) and an Ethereum Rosetta
8890
implementation ([config](examples/configuration/ethereum.json)).
8991

92+
#### Writing check:construction Tests
93+
The new Construction API testing framework (first released in `rosetta-cli@v0.5.0`) uses
94+
a new design pattern to allow for complex transaction construction orchestration.
95+
You can read more about the design goals [here](https://community.rosetta-api.org/t/feedback-request-automated-construction-api-testing-improvements/146).
96+
97+
##### Terminology
98+
When first learning about a new topic, it is often useful to understand the
99+
hierarchy of concerns. In the automated Construction API tester, this
100+
"hierarchy" is as follows:
101+
```text
102+
Workflows -> Jobs
103+
Scenarios
104+
Actions
105+
```
106+
107+
`Workflows` contain collections of `Scenarios` to execute. `Scenarios` are
108+
executed atomically in database transactions (rolled back if execution fails)
109+
and culminate in an optional broadcast. This means that a single `Workflow`
110+
could contain multiple broadcasts (which can be useful for orchestrating
111+
staking-related transactions that affect a single account).
112+
113+
To perform a `Workflow`, we create a `Job`. This `Job` has a unique identifier
114+
and stores state for all `Scenarios` in the `Workflow`. State is shared across
115+
an entire `Job` so `Actions` in a `Scenario` can access the output of `Actions`
116+
in other `Scenarios`. The syntax for accessing this shared state can be found
117+
[here](https://github.com/tidwall/gjson/blob/master/SYNTAX.md).
118+
119+
`Actions` are discrete operations that can be performed in the context of a
120+
`Scenario`. A full list of all `Actions` that can be performed can be found
121+
[here](https://pkg.go.dev/github.com/coinbase/rosetta-sdk-go/constructor/job#ActionType).
122+
123+
If you have suggestions for more actions, please
124+
[open an issue in `rosetta-sdk-go`](https://github.com/coinbase/rosetta-sdk-go/issues)!
125+
126+
##### Workflows
127+
To use the automated Construction API tester, you must implement 2 required `Workflows`:
128+
* `create_account`
129+
* `request_funds`
130+
131+
Please note that `create_account` can contain a transaction broadcast if
132+
on-chain origination is required for new accounts on your blockchain.
133+
134+
If you plan to run the automated Construction API tester in CI, you may wish to
135+
provide [`prefunded accounts`](https://pkg.go.dev/github.com/coinbase/rosetta-cli/configuration#ConstructionConfiguration)
136+
when running the tester (otherwise you would need to manually fund generated
137+
accounts).
138+
139+
Optionally, you can also provide a `return_funds` workflow that will be invoked
140+
when exiting `check:construction`. This can be useful in CI when you want to return
141+
all funds to a single accout or faucet (instead of black-holing them in all the addresses
142+
created during testing).
143+
144+
##### Broadcast Invocation
145+
If you'd like to broadcast a transaction at the end of a `Scenario`,
146+
you must populate the following fields:
147+
* `<scenario>.network`
148+
* `<scenario>.operations`
149+
* `<scenario>.confirmation_depth` (allows for stake-related transactions to complete before marking as a success)
150+
151+
Optionally, you can populate the following field:
152+
* `<scenario>.preprocess_metadata`
153+
154+
Once a transaction is confirmed on-chain (after the provided
155+
`<scenario>.confirmation_depth`, it is stored by the tester at
156+
`<scenario>.transaction` for access by other `Scenarios` in the same `Job`.
157+
158+
##### Dry Runs
159+
In UTXO-based blockchains, it may be necessary to amend the `operations` stored
160+
in `<scenario>.operations` based on the `suggested_fee` returned in
161+
`/construction/metadata`. The automated Construction API tester supports
162+
running a "dry run" of a transaction broadcast if you set the follow field:
163+
* `<scenario>.dry_run = true`
164+
165+
The suggested fee will then be stored as `<scenario>.suggested_fee` for use by
166+
other `Scenarios` in the same `Job`. You can find an example of this in the
167+
Bitcoin [config](examples/configuration/bitcoin.json).
168+
169+
*If this field is not populated or set to `false`, the transaction
170+
will be constructed, signed, and broadcast.*
171+
172+
##### Future Work
173+
* DSL for writing `Workflows` (if anyone in the community has ideas for
174+
this, we are all ears!)
175+
* `Workflow` testing tool (to mock `Workflow` before running on network)
176+
* Re-usable components (pre-defined logic that can be used in any `Workflow` -
177+
both user-defined and provided by `rosetta-cli`
178+
179+
#### End Conditions
180+
When running the `rosetta-cli` in a CI job, it is usually desired to exit
181+
when certain conditions are met (or before then with an exit code of 1). We
182+
provide this functionality through the use of "end conditions" which can be
183+
specified in your configuration file.
184+
185+
##### check:data
186+
A full list of `check:data` end conditions can be found [here](https://pkg.go.dev/github.com/coinbase/rosetta-cli/configuration#DataEndConditions).
187+
If any end condition is satisifed, we will exit and output the
188+
results in `results_output_file` (if it is populated).
189+
190+
##### check:construction
191+
The `check:construction` end condition is a map of
192+
workflow:count that indicates how many of each workflow
193+
should be performed before `check:construction` should stop.
194+
For example, `{"create_account": 5}` indicates that 5 `create_account`
195+
workflows should be performed before stopping.
196+
197+
Unlike `check:data`, all `check:construction` end conditions
198+
must be satisifed before the `rosetta-cli` will exit.
199+
90200
#### Disable Complex Checks
91201
If you are just getting started with your implementation, you may want
92202
to disable balance tracking (did any address balance go below zero?) and
@@ -95,15 +205,6 @@ by the `/account/balance` endpoint?). Take a look at the
95205
[simple configuration](examples/configuration/simple.json) for an example of
96206
how to do this.
97207

98-
#### Future Work
99-
In the near future, we will add support for providing complex exit conditions
100-
(i.e. did we reach tip? did we reconcile every account?) for both
101-
`check:construction` and `check:data` so that the `rosetta-cli`
102-
can be integrated into a CI flow. Currently, the only way to exit with a
103-
successful status in the `rosetta-cli` is to provide an `--end` flag
104-
when running `check:data` (returns 0 if no errors up to a block index
105-
are observed).
106-
107208
### Commands
108209
#### version
109210
```
@@ -385,31 +486,6 @@ Global Flags:
385486
default values.
386487
```
387488

388-
## Development
389-
* `make deps` to install dependencies
390-
* `make test` to run tests
391-
* `make lint` to lint the source code (included generated code)
392-
* `make release` to run one last check before opening a PR
393-
* `make compile version=RELEASE_TAG` to generate binaries
394-
395-
### Helper/Handler
396-
Many of the packages use a `Helper/Handler` interface pattern to acquire
397-
required information or to send events to some client implementation. An example
398-
of this is in the `reconciler` package where a `Helper` is used to get
399-
the account balance and the `Handler` is called to incidate whether the
400-
reconciliation of an account was successful.
401-
402-
### Repo Structure
403-
```
404-
cmd
405-
examples // examples of different config files
406-
internal
407-
logger // logic to write syncing information to stdout/files
408-
processor // Helper/Handler implementations for reconciler, storage, and syncer
409-
storage // persists block to temporary storage and allows for querying balances
410-
utils // useful functions
411-
```
412-
413489
## Correctness Checks
414490
This tool performs a variety of correctness checks using the Rosetta Server. If
415491
any correctness check fails, the CLI will exit and print out a detailed
@@ -442,6 +518,30 @@ on the blockchain node without being included in an operation
442518
returned by the Rosetta Data API. Recall that all balance-changing
443519
operations should be returned by the Rosetta Data API.
444520

521+
## Development
522+
* `make deps` to install dependencies
523+
* `make test` to run tests
524+
* `make lint` to lint the source code (included generated code)
525+
* `make release` to run one last check before opening a PR
526+
* `make compile version=RELEASE_TAG` to generate binaries
527+
528+
### Helper/Handler
529+
Many of the packages use a `Helper/Handler` interface pattern to acquire
530+
required information or to send events to some client implementation. An example
531+
of this is in the `reconciler` package where a `Helper` is used to get
532+
the account balance and the `Handler` is called to incidate whether the
533+
reconciliation of an account was successful.
534+
535+
### Repo Structure
536+
```
537+
cmd
538+
examples // examples of different config files
539+
pkg
540+
logger // logic to write syncing information to stdout/files
541+
processor // Helper/Handler implementations for reconciler, storage, and syncer
542+
tester // test orchestrators
543+
```
544+
445545
## License
446546
This project is available open source under the terms of the [Apache 2.0 License](https://opensource.org/licenses/Apache-2.0).
447547

cmd/check_construction.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,8 @@ func runCheckConstructionCmd(cmd *cobra.Command, args []string) {
132132
})
133133

134134
sigListeners := []context.CancelFunc{cancel}
135-
go handleSignals(sigListeners)
135+
go handleSignals(&sigListeners)
136136

137137
err = g.Wait()
138-
constructionTester.HandleErr(err)
138+
constructionTester.HandleErr(err, &sigListeners)
139139
}

cmd/check_data.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ func runCheckDataCmd(cmd *cobra.Command, args []string) {
138138
})
139139

140140
sigListeners := []context.CancelFunc{cancel}
141-
go handleSignals(sigListeners)
141+
go handleSignals(&sigListeners)
142142

143143
err = g.Wait()
144144

@@ -148,5 +148,5 @@ func runCheckDataCmd(cmd *cobra.Command, args []string) {
148148

149149
// HandleErr will exit if we should not attempt
150150
// to find missing operations.
151-
dataTester.HandleErr(ctx, err, sigListeners)
151+
dataTester.HandleErr(ctx, err, &sigListeners)
152152
}

cmd/root.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,14 +115,14 @@ func ensureDataDirectoryExists() {
115115
// handleSignals handles OS signals so we can ensure we close database
116116
// correctly. We call multiple sigListeners because we
117117
// may need to cancel more than 1 context.
118-
func handleSignals(listeners []context.CancelFunc) {
118+
func handleSignals(listeners *[]context.CancelFunc) {
119119
sigs := make(chan os.Signal, 1)
120120
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
121121
go func() {
122122
sig := <-sigs
123123
color.Red("Received signal: %s", sig)
124124
SignalReceived = true
125-
for _, listener := range listeners {
125+
for _, listener := range *listeners {
126126
listener()
127127
}
128128
}()
@@ -132,6 +132,6 @@ var versionCmd = &cobra.Command{
132132
Use: "version",
133133
Short: "Print rosetta-cli version",
134134
Run: func(cmd *cobra.Command, args []string) {
135-
fmt.Println("v0.5.1")
135+
fmt.Println("v0.5.2")
136136
},
137137
}

0 commit comments

Comments
 (0)