From bf4aa546a21168a56959dd330aae6b7b58c1e086 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Fri, 28 Apr 2023 13:46:57 -0400 Subject: [PATCH 01/22] algod importer: Auto catchpoint label lookup. --- .../plugins/importers/algod/algod_importer.go | 77 +++++++++-- .../importers/algod/algod_importer_config.go | 2 + .../importers/algod/algod_importer_test.go | 129 ++++++++++++------ .../importers/algod/mock_algod_test.go | 1 + conduit/plugins/importers/algod/sample.yaml | 14 +- 5 files changed, 163 insertions(+), 60 deletions(-) diff --git a/conduit/plugins/importers/algod/algod_importer.go b/conduit/plugins/importers/algod/algod_importer.go index 2935f5e7..aed0812b 100644 --- a/conduit/plugins/importers/algod/algod_importer.go +++ b/conduit/plugins/importers/algod/algod_importer.go @@ -1,9 +1,12 @@ package algodimporter import ( + "bufio" "context" _ "embed" // used to embed config "fmt" + "io" + "net/http" "net/url" "reflect" "strconv" @@ -43,6 +46,8 @@ const ( retries = 5 ) +const catchpointsURL = "https://algorand-catchpoints.s3.us-east-2.amazonaws.com/consolidated/%s_catchpoints.txt" + type algodImporter struct { aclient *algod.Client logger *logrus.Logger @@ -96,7 +101,7 @@ func parseCatchpointRound(catchpoint string) (round sdk.Round, err error) { return } -func (algodImp *algodImporter) startCatchpointCatchup() error { +func (algodImp *algodImporter) startCatchpointCatchup(catchpoint string) error { // Run catchpoint catchup client, err := common.MakeClient(algodImp.cfg.NetAddr, "X-Algo-API-Token", algodImp.cfg.CatchupConfig.AdminToken) if err != nil { @@ -106,13 +111,13 @@ func (algodImp *algodImporter) startCatchpointCatchup() error { err = client.Post( algodImp.ctx, &resp, - fmt.Sprintf("/v2/catchup/%s", common.EscapeParams(algodImp.cfg.CatchupConfig.Catchpoint)...), + fmt.Sprintf("/v2/catchup/%s", common.EscapeParams(catchpoint)...), nil, nil, nil, ) if err != nil { - return fmt.Errorf("POST /v2/catchup/%s received unexpected error: %w", algodImp.cfg.CatchupConfig.Catchpoint, err) + return fmt.Errorf("POST /v2/catchup/%s received unexpected error: %w", catchpoint, err) } return nil } @@ -154,17 +159,54 @@ func (algodImp *algodImporter) monitorCatchpointCatchup() error { return nil } -func (algodImp *algodImporter) catchupNode(initProvider data.InitProvider) error { +func getMissingCatchpointLabel(URL string, nextRound uint64) (string, error) { + resp, err := http.Get(URL) + if err != nil { + return "", err + } + body, err := io.ReadAll(resp.Body) + if err != nil { + return "", fmt.Errorf("failed to read catchpoint label response: %w", err) + } + + if resp.StatusCode != 200 { + return "", fmt.Errorf("failed to lookup catchpoint label list (%d): %s", resp.StatusCode, string(body)) + } + + // look for best match without going over + var label string + labels := string(body) + scanner := bufio.NewScanner(strings.NewReader(labels)) + for scanner.Scan() { + line := scanner.Text() + round, err := parseCatchpointRound(line) + if err != nil { + return "", err + } + if uint64(round) > nextRound { + break + } + label = line + } + + // check if label is a valid catchpoint label + _, err = parseCatchpointRound(label) + if err != nil { + return "", fmt.Errorf("invalid catchpoint label: %s", label) + } + return label, nil +} + +func (algodImp *algodImporter) catchupNode(catchpoint string, nextRound uint64) error { if algodImp.mode == followerMode { // Set the sync round to the round provided by initProvider - _, err := algodImp.aclient.SetSyncRound(uint64(initProvider.NextDBRound())).Do(algodImp.ctx) + _, err := algodImp.aclient.SetSyncRound(nextRound).Do(algodImp.ctx) if err != nil { - return fmt.Errorf("received unexpected error setting sync round (%d): %w", initProvider.NextDBRound(), err) + return fmt.Errorf("received unexpected error setting sync round (%d): %w", nextRound, err) } } - // Run Catchpoint Catchup - if algodImp.cfg.CatchupConfig.Catchpoint != "" { + if catchpoint != "" { cpRound, err := parseCatchpointRound(algodImp.cfg.CatchupConfig.Catchpoint) if err != nil { return err @@ -180,7 +222,7 @@ func (algodImp *algodImporter) catchupNode(initProvider data.InitProvider) error nStatus.LastRound, ) } else { - err = algodImp.startCatchpointCatchup() + err = algodImp.startCatchpointCatchup(algodImp.cfg.CatchupConfig.Catchpoint) if err != nil { return err } @@ -192,7 +234,7 @@ func (algodImp *algodImporter) catchupNode(initProvider data.InitProvider) error } } - _, err := algodImp.aclient.StatusAfterBlock(uint64(initProvider.NextDBRound())).Do(algodImp.ctx) + _, err := algodImp.aclient.StatusAfterBlock(nextRound).Do(algodImp.ctx) if err != nil { err = fmt.Errorf("received unexpected error (StatusAfterBlock) waiting for node to catchup: %w", err) } @@ -253,7 +295,20 @@ func (algodImp *algodImporter) Init(ctx context.Context, initProvider data.InitP return nil, fmt.Errorf("unable to fetch genesis file from API at %s", algodImp.cfg.NetAddr) } - err = algodImp.catchupNode(initProvider) + catchpoint := "" + + // check for catchpoint to use. + if algodImp.cfg.CatchupConfig.Catchpoint != "" { + catchpoint = algodImp.cfg.CatchupConfig.Catchpoint + } else if algodImp.cfg.CatchupConfig.Auto { + URL := fmt.Sprintf(catchpointsURL, genesis.Network) + catchpoint, err = getMissingCatchpointLabel(URL, uint64(initProvider.NextDBRound())) + if err != nil { + return nil, err + } + } + + err = algodImp.catchupNode(catchpoint, uint64(initProvider.NextDBRound())) return &genesis, err } diff --git a/conduit/plugins/importers/algod/algod_importer_config.go b/conduit/plugins/importers/algod/algod_importer_config.go index 1a354ef2..9ace5618 100644 --- a/conduit/plugins/importers/algod/algod_importer_config.go +++ b/conduit/plugins/importers/algod/algod_importer_config.go @@ -19,6 +19,8 @@ type Config struct { // CatchupParams provides information required to sync a follower node to the pipeline round type CatchupParams struct { + // auto controls whether to automatically download an appropriate catchpoint label. + Auto bool `yaml:"auto"` // catchpoint is the catchpoint used to run fast catchup on startup when your node is behind the current pipeline round. Catchpoint string `yaml:"catchpoint"` // admin-token is the algod admin API token. diff --git a/conduit/plugins/importers/algod/algod_importer_test.go b/conduit/plugins/importers/algod/algod_importer_test.go index 5531cab1..06966c2d 100644 --- a/conduit/plugins/importers/algod/algod_importer_test.go +++ b/conduit/plugins/importers/algod/algod_importer_test.go @@ -99,60 +99,76 @@ func TestInitCatchup(t *testing.T) { tests := []struct { name string catchpoint string + auto bool algodServer *httptest.Server err string logs []string }{ - {"sync round failure", "", - NewAlgodServer( + { + name: "sync round failure", + algodServer: NewAlgodServer( GenesisResponder, MakeSyncRoundResponder(http.StatusBadRequest)), - "received unexpected error setting sync round (1): HTTP 400", - []string{}}, - {"catchpoint parse failure", "notvalid", - NewAlgodServer( + err: "received unexpected error setting sync round (1): HTTP 400", + logs: []string{}}, + { + name: "catchpoint parse failure", + catchpoint: "notvalid", + algodServer: NewAlgodServer( GenesisResponder, MakeSyncRoundResponder(http.StatusOK)), - "unable to parse catchpoint, invalid format", - []string{}}, - {"invalid catchpoint round uint parsing error", "abcd#abcd", - NewAlgodServer( + err: "unable to parse catchpoint, invalid format", + logs: []string{}}, + { + name: "invalid catchpoint round uint parsing error", + catchpoint: "abcd#abcd", + algodServer: NewAlgodServer( GenesisResponder, MakeSyncRoundResponder(http.StatusOK)), - "invalid syntax", - []string{}}, - {"node status failure", "1234#abcd", - NewAlgodServer( + err: "invalid syntax", + logs: []string{}}, + { + name: "node status failure", + catchpoint: "1234#abcd", + algodServer: NewAlgodServer( GenesisResponder, MakeSyncRoundResponder(http.StatusOK), MakeStatusResponder("/v2/status", http.StatusBadRequest, "")), - "received unexpected error failed to get node status: HTTP 400", - []string{}}, - {"catchpoint round before node round skips fast catchup", "1234#abcd", - NewAlgodServer( + err: "received unexpected error failed to get node status: HTTP 400", + logs: []string{}}, + { + name: "catchpoint round before node round skips fast catchup", + catchpoint: "1234#abcd", + algodServer: NewAlgodServer( GenesisResponder, MakeSyncRoundResponder(http.StatusOK), MakeNodeStatusResponder(models.NodeStatus{LastRound: 1235})), - "", - []string{"Skipping catchpoint catchup for 1234#abcd, since it's before node round 1235"}}, - {"start catchpoint catchup failure", "1236#abcd", - NewAlgodServer( + logs: []string{"Skipping catchpoint catchup for 1234#abcd, since it's before node round 1235"}}, + { + name: "start catchpoint catchup failure", + catchpoint: "1236#abcd", + algodServer: NewAlgodServer( GenesisResponder, MakeSyncRoundResponder(http.StatusOK), MakeNodeStatusResponder(models.NodeStatus{LastRound: 1235}), MakeStatusResponder("/v2/catchup/", http.StatusBadRequest, "")), - "POST /v2/catchup/1236#abcd received unexpected error: HTTP 400", - []string{}}, - {"monitor catchup node status failure", "1236#abcd", - NewAlgodServer( + err: "POST /v2/catchup/1236#abcd received unexpected error: HTTP 400", + logs: []string{}}, + { + name: "monitor catchup node status failure", + catchpoint: "1236#abcd", + algodServer: NewAlgodServer( GenesisResponder, MakeSyncRoundResponder(http.StatusOK), MakeJsonResponderSeries("/v2/status", []int{http.StatusOK, http.StatusBadRequest}, []interface{}{models.NodeStatus{LastRound: 1235}}), MakeStatusResponder("/v2/catchup/", http.StatusOK, "")), - "received unexpected error getting node status: HTTP 400", - []string{}}, - {"monitor catchup success", "1236#abcd", - NewAlgodServer( + err: "received unexpected error getting node status: HTTP 400", + logs: []string{}}, + { + name: "monitor catchup success", + catchpoint: "1236#abcd", + auto: true, // ignored + algodServer: NewAlgodServer( GenesisResponder, MakeSyncRoundResponder(http.StatusOK), MakeJsonResponderSeries("/v2/status", []int{http.StatusOK}, []interface{}{ @@ -164,35 +180,49 @@ func TestInitCatchup(t *testing.T) { models.NodeStatus{LastRound: 1236}, }), MakeStatusResponder("/v2/catchup/", http.StatusOK, "")), - "", - []string{ + logs: []string{ "catchup phase Processed Accounts: 1 / 1", "catchup phase Verified Accounts: 1 / 1", "catchup phase Acquired Blocks: 1 / 1", "catchup phase Verified Blocks", }}, - {"wait for node to catchup error", "1236#abcd", - NewAlgodServer( + { + name: "auto catchup used (even if the mocking isn't setup for it)", + catchpoint: "", + auto: true, + algodServer: NewAlgodServer( + GenesisResponder, + ), + err: "failed to lookup catchpoint label list", + }, + { + name: "wait for node to catchup error", + catchpoint: "1236#abcd", + algodServer: NewAlgodServer( GenesisResponder, MakeSyncRoundResponder(http.StatusOK), MakeJsonResponderSeries("/v2/status", []int{http.StatusOK, http.StatusOK, http.StatusBadRequest}, []interface{}{models.NodeStatus{LastRound: 1235}}), MakeStatusResponder("/v2/catchup/", http.StatusOK, "")), - "received unexpected error (StatusAfterBlock) waiting for node to catchup: HTTP 400", - []string{}}, + err: "received unexpected error (StatusAfterBlock) waiting for node to catchup: HTTP 400", + logs: []string{}}, } for _, ttest := range tests { ttest := ttest t.Run(ttest.name, func(t *testing.T) { t.Parallel() testLogger, hook := test.NewNullLogger() - testImporter := New() - cfgStr := fmt.Sprintf(`--- -mode: %s -netaddr: %s -catchup-config: - catchpoint: %s -`, "follower", ttest.algodServer.URL, ttest.catchpoint) - _, err := testImporter.Init(ctx, conduit.MakePipelineInitProvider(&pRound, nil), plugins.MakePluginConfig(cfgStr), testLogger) + testImporter := &algodImporter{} + cfg := Config{ + Mode: "follower", + NetAddr: ttest.algodServer.URL, + CatchupConfig: CatchupParams{ + Catchpoint: ttest.catchpoint, + Auto: ttest.auto, + }, + } + cfgStr, err := yaml.Marshal(cfg) + require.NoError(t, err) + _, err = testImporter.Init(ctx, conduit.MakePipelineInitProvider(&pRound, nil), plugins.MakePluginConfig(string(cfgStr)), testLogger) if ttest.err != "" { require.ErrorContains(t, err, ttest.err) } else { @@ -520,3 +550,14 @@ func TestGetBlockErrors(t *testing.T) { }) } } + +func TestMissingCatchpointLabel(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "1000#abcd\n1100#abcd\n1200#abcd") + })) + defer ts.Close() + label, err := getMissingCatchpointLabel(ts.URL, 1100) + require.NoError(t, err) + // closest without going over + require.Equal(t, "1100#abcd", label) +} diff --git a/conduit/plugins/importers/algod/mock_algod_test.go b/conduit/plugins/importers/algod/mock_algod_test.go index 0a7fdf75..dfe233b1 100644 --- a/conduit/plugins/importers/algod/mock_algod_test.go +++ b/conduit/plugins/importers/algod/mock_algod_test.go @@ -70,6 +70,7 @@ func MakeGenesisResponder(genesis types.Genesis) func(reqPath string, w http.Res // GenesisResponder handles /v2/genesis requests and returns an empty Genesis object var GenesisResponder = MakeGenesisResponder(types.Genesis{ Comment: "", + Network: "FAKE", DevMode: true, }) diff --git a/conduit/plugins/importers/algod/sample.yaml b/conduit/plugins/importers/algod/sample.yaml index 9637f6e0..86f3d713 100644 --- a/conduit/plugins/importers/algod/sample.yaml +++ b/conduit/plugins/importers/algod/sample.yaml @@ -16,11 +16,15 @@ # Algod catchpoint catchup arguments catchup-config: - # The catchpoint to use when running fast catchup. Select an appropriate catchpoint for your deployment. - # They are published in the following locations: - # mainnet: https://algorand-catchpoints.s3.us-east-2.amazonaws.com/consolidated/mainnet_catchpoints.txt - # betanet: https://algorand-catchpoints.s3.us-east-2.amazonaws.com/consolidated/betanet_catchpoints.txt - # testnet: https://algorand-catchpoints.s3.us-east-2.amazonaws.com/consolidated/testnet_catchpoints.txt + # Automatically download an appropriate catchpoint label. If false, you + # must specify a catchpoint to use fast catchup. + auto: false + # The catchpoint to use when running fast catchup. If this is set it + # overrides 'auto: true'. To select an appropriate catchpoint for your + # deployment, see the list of available catchpoints for each network: + # mainnet: https://algorand-catchpoints.s3.us-east-2.amazonaws.com/consolidated/mainnet_catchpoints.txt + # betanet: https://algorand-catchpoints.s3.us-east-2.amazonaws.com/consolidated/betanet_catchpoints.txt + # testnet: https://algorand-catchpoints.s3.us-east-2.amazonaws.com/consolidated/testnet_catchpoints.txt catchpoint: "" # Algod Admin API Token admin-token: "" From 719acabb03ff3f5da4333c2f3a0c413f5e21c0ae Mon Sep 17 00:00:00 2001 From: Will Winder Date: Fri, 28 Apr 2023 13:58:00 -0400 Subject: [PATCH 02/22] update docs --- conduit/plugins/importers/algod/README.md | 18 ++++++++----- docs/tutorials/IndexerMigration.md | 33 ++++++++++------------- docs/tutorials/IndexerWriter.md | 5 ++-- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/conduit/plugins/importers/algod/README.md b/conduit/plugins/importers/algod/README.md index cff60c27..301afca9 100644 --- a/conduit/plugins/importers/algod/README.md +++ b/conduit/plugins/importers/algod/README.md @@ -6,7 +6,7 @@ This plugin imports block data from an algod node. Fetch blocks data from the [a ### Automatic Fast Catchup -If an admin API token and catchpoint are set, the plugin will automatically run fast catchup on startup if the node is behind the current pipeline round. +If an admin API token and Auto (or a catchpoint label) are set, the plugin will automatically run fast catchup on startup if the node is behind the current pipeline round. ### Follower Node Orchestration @@ -34,12 +34,16 @@ When using a follower node, ledger state delta objects are provided to the proce # Algod catchpoint catchup arguments catchup-config: - # The catchpoint to use when running fast catchup. Select an appropriate catchpoint for your deployment. - # They are published in the following locations: - # mainnet: https://algorand-catchpoints.s3.us-east-2.amazonaws.com/consolidated/mainnet_catchpoints.txt - # betanet: https://algorand-catchpoints.s3.us-east-2.amazonaws.com/consolidated/betanet_catchpoints.txt - # testnet: https://algorand-catchpoints.s3.us-east-2.amazonaws.com/consolidated/testnet_catchpoints.txt + # Automatically download an appropriate catchpoint label. If false, you + # must specify a catchpoint to use fast catchup. + auto: false + # The catchpoint to use when running fast catchup. If this is set it + # overrides 'auto: true'. To select an appropriate catchpoint for your + # deployment, see the list of available catchpoints for each network: + # mainnet: https://algorand-catchpoints.s3.us-east-2.amazonaws.com/consolidated/mainnet_catchpoints.txt + # betanet: https://algorand-catchpoints.s3.us-east-2.amazonaws.com/consolidated/betanet_catchpoints.txt + # testnet: https://algorand-catchpoints.s3.us-east-2.amazonaws.com/consolidated/testnet_catchpoints.txt catchpoint: "" # Algod Admin API Token admin-token: "" -``` \ No newline at end of file +``` diff --git a/docs/tutorials/IndexerMigration.md b/docs/tutorials/IndexerMigration.md index 45e87bae..6200a4a6 100644 --- a/docs/tutorials/IndexerMigration.md +++ b/docs/tutorials/IndexerMigration.md @@ -26,7 +26,7 @@ graph LR; ledger["Local Ledger"] db["PostgreSQL DB"] restapi["REST API"] - + algod-->index; subgraph "Data Pipeline" index-->ledger; @@ -82,7 +82,6 @@ graph LR; ro2---psql; lb---ro3; ro3---psql; - ``` Because the database connection can only tolerate a single writer without having race conditions and/or deadlocks, Indexer offers a read-only mode which does not run the data pipeline and has no write access to the database. It's @@ -120,7 +119,6 @@ graph LR; ro2---psql; lb---ro3; ro3---psql; - ``` With this architecture you're free to do things like use filter processors to limit the size of your database--though @@ -133,7 +131,7 @@ it could fetch any block it needed, Conduit's algod importer is not required to what is called Follower mode in algod. You can take a look [here](https://github.com/algorand/go-algorand/blob/master/node/follower_node.go) if you're interested in looking at the source code for the follower node. -To run algod in Follower mode, it must be launched with `EnableFollowMode` set to `true` in the algod config. +To run algod in Follower mode, it must be launched with `EnableFollowMode` set to `true` in the algod config. Doing this will provide the new features listed below, but will remove the node's ability to participate in consensus (propose new blocks), or broadcast transactions to be included in new blocks. It will only, as hinted by its name, follow the chain. @@ -142,7 +140,7 @@ Follower mode algod provides 2 key features required by Conduit. ### Sync Rounds In order to remove the requirement of algod being Archival, we needed a way to ensure that algod would have the data available for the particular round that our Conduit pipeline was importing at any given time. We've done this through -the concept of a `sync` round. +the concept of a `sync` round. The sync round is a parameter which Conduit provides to algod. It specifies the particular round that we want algod to ensure is kept in the cache until we've finished running it through our Conduit pipeline. When a sync round is set on a @@ -183,7 +181,7 @@ fetch all required data directly from the algod node, and ensure that the node i ## Altering Architecture of Indexer Applications Let's take a look at a theoretical application more closely and see what in particular we need to do to alter it to use -Conduit. +Conduit. Here's a diagram of a containerized application (leaving out some details such as load balancers, and scaled out API nodes). @@ -196,7 +194,7 @@ graph LR; subgraph "Persistent Volume" ll["Local Ledger"]; end - + indexer-->algod; indexer-->psql; indexer-->ll; @@ -231,7 +229,7 @@ Follower nodes provide the same methods of catchup as regular algod nodes, but w recent ledger round upon startup. Thaht scenario will be covered in step 3. ### Step 2: Remove the Local Ledger -Because our Conduit pipeline will use the Follower node's state delta API, we no longer need our local ledger persistent +Because our Conduit pipeline will use the Follower node's state delta API, we no longer need our local ledger persistent volume. It can be removed. ### Step 3: Refactor our Indexer Writer to Conduit @@ -255,11 +253,12 @@ metrics: log-level: "INFO" importer: name: "algod", - config: + config: "netaddr": $ALGOD_ADDR, "token": $ALGOD_TOKEN, "mode": "follower", catchup-config: + auto: false catchpoint: "" admin-token: "" exporter: @@ -268,27 +267,23 @@ exporter: "connection-string": $PGSQL_CONNECTION_STRING, ``` -If your algod node needs to run fast catchup, you can fill in the catchup-config section. You'll need to first look up your Indexer round from the postgres database. The Indexer stores the latest round in the database, and you can read it via the `/health` endpoint. The result is formatted in json -so you can use jq to more easily see your Indexer's round (if your Indexer is listening locally on port 8980). -```bash -curl http://localhost:8980/health | jq '.round' -``` +If your algod node needs to run fast catchup, you can fill in the catchup-config section. You'll need to first look up your Admin API token from algod. It is in a file named `algod.admin.token`. To automatically lookup the appropriate catchpoint label, set `auto: true`. -Now that you can look up a catchpoint, conduit will run fast catchup on your node if a catchpoint is provided. Look up the closest catchpoint prior to the desired sync round. -For a list of catchpoints, you can reference the following: +Note: to manually lookup the catchpoint label, see the complete listings here: * [Mainnet](https://algorand-catchpoints.s3.us-east-2.amazonaws.com/consolidated/mainnet_catchpoints.txt) * [Testnet](https://algorand-catchpoints.s3.us-east-2.amazonaws.com/consolidated/testnet_catchpoints.txt) * [Betanet](https://algorand-catchpoints.s3.us-east-2.amazonaws.com/consolidated/betanet_catchpoints.txt) -For example, if your postgres database is on round 25001234, use the following configuration: +Your `catchup-config` section should be updated to the following: ```yaml catchup-config: - catchpoint: "25000000#EOX5UYQV4IXTGYQCIDR7ZLUK6WZGDC5EG6PYQGBG6PBYNAQUPN2Q" + auto: true admin-token: "$ALGOD_ADMIN_TOKEN" ``` -Then run Conduit, `conduit -d $CONDUIT_DATA_DIR`! +With configuration complete, run Conduit: `conduit -d $CONDUIT_DATA_DIR` You can separately run your Indexer with `--no-algod` to connect your API to the database. If you configured a catchpoint, Conduit will facilitate a fast catchup during initialization. Once the catchpoint has been reached the node will resume normal catchup to advance from the catchpoint round to target round defined in postgres. The fast-catchup and catchup process may take anywhere from 30 minutes to over an hour depending on hardware and disk configurations. + diff --git a/docs/tutorials/IndexerWriter.md b/docs/tutorials/IndexerWriter.md index ecd1cb5e..41f23241 100644 --- a/docs/tutorials/IndexerWriter.md +++ b/docs/tutorials/IndexerWriter.md @@ -119,8 +119,9 @@ Configuration: * `exporter.config.connection-string`: `host=localhost port=5555 user=algorand password=pgpass dbname=conduit` If you are connecting to an existing PostgreSQL database, you can also set a -catchpoint and the admin token. If those are set Conduit will automatically -initialize the node using fast catchup. +catchpoint and the admin token and enable `auto` catchup. You can optionally +specify the catchup label directly. If those are configured, Conduit will +automatically initialize the node using fast catchup. Review the inline documentation in `conduit.yml` and decide if there are any other settings you would like to update. From 40f24668a9c63b7a28254ca988cc94e19eb338d3 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Fri, 28 Apr 2023 14:46:39 -0400 Subject: [PATCH 03/22] Replace redundant check. --- conduit/plugins/importers/algod/algod_importer.go | 9 ++++----- .../plugins/importers/algod/algod_importer_test.go | 11 ++++++++++- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/conduit/plugins/importers/algod/algod_importer.go b/conduit/plugins/importers/algod/algod_importer.go index aed0812b..621cf1ee 100644 --- a/conduit/plugins/importers/algod/algod_importer.go +++ b/conduit/plugins/importers/algod/algod_importer.go @@ -177,7 +177,7 @@ func getMissingCatchpointLabel(URL string, nextRound uint64) (string, error) { var label string labels := string(body) scanner := bufio.NewScanner(strings.NewReader(labels)) - for scanner.Scan() { + for scanner.Scan() && scanner.Text() != "" { line := scanner.Text() round, err := parseCatchpointRound(line) if err != nil { @@ -189,11 +189,10 @@ func getMissingCatchpointLabel(URL string, nextRound uint64) (string, error) { label = line } - // check if label is a valid catchpoint label - _, err = parseCatchpointRound(label) - if err != nil { - return "", fmt.Errorf("invalid catchpoint label: %s", label) + if label == "" { + return "", fmt.Errorf("no catchpoint label found for round %d at: %s", nextRound, URL) } + return label, nil } diff --git a/conduit/plugins/importers/algod/algod_importer_test.go b/conduit/plugins/importers/algod/algod_importer_test.go index 06966c2d..37ab5493 100644 --- a/conduit/plugins/importers/algod/algod_importer_test.go +++ b/conduit/plugins/importers/algod/algod_importer_test.go @@ -551,7 +551,7 @@ func TestGetBlockErrors(t *testing.T) { } } -func TestMissingCatchpointLabel(t *testing.T) { +func TestGetMissingCatchpointLabel(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "1000#abcd\n1100#abcd\n1200#abcd") })) @@ -561,3 +561,12 @@ func TestMissingCatchpointLabel(t *testing.T) { // closest without going over require.Equal(t, "1100#abcd", label) } + +func TestGetMissingCatchpointLabelError(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "") + })) + defer ts.Close() + _, err := getMissingCatchpointLabel(ts.URL, 1100) + require.ErrorContains(t, err, "no catchpoint label found for round 1100 at:") +} From 2939d025f28eb6539d622aca7aa224f9a751c888 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Fri, 28 Apr 2023 15:25:23 -0400 Subject: [PATCH 04/22] Fix catchpoint variable. --- conduit/plugins/importers/algod/algod_importer.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/conduit/plugins/importers/algod/algod_importer.go b/conduit/plugins/importers/algod/algod_importer.go index 621cf1ee..0c81accb 100644 --- a/conduit/plugins/importers/algod/algod_importer.go +++ b/conduit/plugins/importers/algod/algod_importer.go @@ -206,7 +206,8 @@ func (algodImp *algodImporter) catchupNode(catchpoint string, nextRound uint64) } if catchpoint != "" { - cpRound, err := parseCatchpointRound(algodImp.cfg.CatchupConfig.Catchpoint) + algodImp.logger.Infof("Starting catchpoint catchup with label %s", catchpoint) + cpRound, err := parseCatchpointRound(catchpoint) if err != nil { return err } @@ -217,11 +218,11 @@ func (algodImp *algodImporter) catchupNode(catchpoint string, nextRound uint64) if cpRound <= sdk.Round(nStatus.LastRound) { algodImp.logger.Infof( "Skipping catchpoint catchup for %s, since it's before node round %d", - algodImp.cfg.CatchupConfig.Catchpoint, + catchpoint, nStatus.LastRound, ) } else { - err = algodImp.startCatchpointCatchup(algodImp.cfg.CatchupConfig.Catchpoint) + err = algodImp.startCatchpointCatchup(catchpoint) if err != nil { return err } @@ -303,11 +304,13 @@ func (algodImp *algodImporter) Init(ctx context.Context, initProvider data.InitP URL := fmt.Sprintf(catchpointsURL, genesis.Network) catchpoint, err = getMissingCatchpointLabel(URL, uint64(initProvider.NextDBRound())) if err != nil { - return nil, err + return nil, fmt.Errorf("unable to lookup catchpoint: %w", err) } } - err = algodImp.catchupNode(catchpoint, uint64(initProvider.NextDBRound())) + if catchpoint != "" { + err = algodImp.catchupNode(catchpoint, uint64(initProvider.NextDBRound())) + } return &genesis, err } From 0aed8a37662413aa74e42e5800040c2cd9963602 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Fri, 28 Apr 2023 15:38:52 -0400 Subject: [PATCH 05/22] Fix unit test. --- conduit/plugins/importers/algod/algod_importer.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/conduit/plugins/importers/algod/algod_importer.go b/conduit/plugins/importers/algod/algod_importer.go index 0c81accb..a2db568a 100644 --- a/conduit/plugins/importers/algod/algod_importer.go +++ b/conduit/plugins/importers/algod/algod_importer.go @@ -308,9 +308,7 @@ func (algodImp *algodImporter) Init(ctx context.Context, initProvider data.InitP } } - if catchpoint != "" { - err = algodImp.catchupNode(catchpoint, uint64(initProvider.NextDBRound())) - } + err = algodImp.catchupNode(catchpoint, uint64(initProvider.NextDBRound())) return &genesis, err } From 217b52bfc515b22cfa026e87b4c715e7848663f5 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Sat, 29 Apr 2023 07:34:02 -0400 Subject: [PATCH 06/22] Don't monitor unless we actually start the catchup --- conduit/plugins/importers/algod/algod_importer.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/conduit/plugins/importers/algod/algod_importer.go b/conduit/plugins/importers/algod/algod_importer.go index a2db568a..0e42e196 100644 --- a/conduit/plugins/importers/algod/algod_importer.go +++ b/conduit/plugins/importers/algod/algod_importer.go @@ -226,11 +226,12 @@ func (algodImp *algodImporter) catchupNode(catchpoint string, nextRound uint64) if err != nil { return err } - } - // Wait for algod to catchup - err = algodImp.monitorCatchpointCatchup() - if err != nil { - return err + + // Wait for algod to catchup + err = algodImp.monitorCatchpointCatchup() + if err != nil { + return err + } } } From 6c119b08c38f0259c8fb50f8475fe9015834b6a6 Mon Sep 17 00:00:00 2001 From: Zeph Grunschlag Date: Mon, 1 May 2023 01:06:29 -0500 Subject: [PATCH 07/22] wip --- .gitignore | 3 + examples/Justfile | 469 +++++++++++++++++++++++++++++++++++ examples/binary_polars.py | 111 +++++++++ examples/data/conduit.yml | 46 ++++ examples/pldb/blocks.feather | Bin 0 -> 1289712 bytes 5 files changed, 629 insertions(+) create mode 100644 examples/Justfile create mode 100644 examples/binary_polars.py create mode 100644 examples/data/conduit.yml create mode 100644 examples/pldb/blocks.feather diff --git a/.gitignore b/.gitignore index cc77112e..8211d0a4 100644 --- a/.gitignore +++ b/.gitignore @@ -55,3 +55,6 @@ coverage.txt # conduit example cmd/conduit/data + +# examples +examples/blocks \ No newline at end of file diff --git a/examples/Justfile b/examples/Justfile new file mode 100644 index 00000000..b9c4133b --- /dev/null +++ b/examples/Justfile @@ -0,0 +1,469 @@ +set export +set shell := ["zsh", "-cu"] + +NETWORKS := `echo $HOME` + "/networks" +# NAME := "indexerboxes" +NAME := "niftynetwork" +CURR_NETWORK := NETWORKS + "/" + NAME +# GO_ALGORAND := "../../.." +GO_ALGORAND := "/Users/zeph/github/algorand/go-algorand" +# NODE_TEMPLATE := GO_ALGORAND + "/test/testdata/nettemplates/OneNodeFuture.json" +NODE_TEMPLATE := GO_ALGORAND + "/test/testdata/nettemplates/TwoNodesFollower100Second.json" +PRIVATE_DATA_NODE := "Primary" +DATA_NODE := env_var_or_default("DATA_NODE", PRIVATE_DATA_NODE) +IS_PUBLIC_TRUTHY := env_var_or_default("DATA_NODE", "") +ALGORAND_DATA := CURR_NETWORK + "/" + DATA_NODE +ALGORAND_TOKEN_PATH := ALGORAND_DATA + "/algod.token" +ALGORAND_ALGOD_PATH := ALGORAND_DATA + "/algod.net" +ALGORAND_PID_PATH := ALGORAND_DATA + "/algod.pid" + +# Older: +BOXES_TEAL := "boxes.teal" + +# --- SUMMARY --- # + +# list all available commands +default: + just --list + +# echo all variables +@echo: + echo NETWORKS: $NETWORKS + echo NAME: $NAME + echo CURR_NETWORK: $CURR_NETWORK + echo GO_ALGORAND: $GO_ALGORAND + echo NODE_TEMPLATE: $NODE_TEMPLATE + echo PRIVATE_DATA_NODE: $PRIVATE_DATA_NODE + echo DATA_NODE: $DATA_NODE + echo IS_PUBLIC_TRUTHY: $IS_PUBLIC_TRUTHY + echo ALGORAND_DATA: $ALGORAND_DATA + echo ALGORAND_TOKEN_PATH: $ALGORAND_TOKEN_PATH + echo ALGORAND_ALGOD_PATH: $ALGORAND_ALGOD_PATH + echo ALGORAND_PID_PATH: $ALGORAND_PID_PATH + + echo BOXES_TEAL: $BOXES_TEAL + +# --- algod curl --- # +algod ENDPOINT="/v2/status" VERB="GET": + #! /usr/bin/env bash + # set -euxo pipefail + set -euo pipefail + ALGORAND_TOKEN=$(cat ${ALGORAND_TOKEN_PATH}) + ALGORAND_ALGOD=$(cat ${ALGORAND_ALGOD_PATH}) + ALGORAND_PID=$(cat ${ALGORAND_PID_PATH}) + curl -X ${VERB} "http://${ALGORAND_ALGOD}${ENDPOINT}" -H "Authorization: Bearer ${ALGORAND_TOKEN}" + + +# --- GENERATOR SCRIPT COMMANDS --- # + +# generate an arbitrary number of app and box scenarios, each with up to BOXES_PER_APP boxes +gen-mult-app-boxes NUM_APPS="10" BOXES_PER_APP="2048": + #!/usr/bin/env python3 + import subprocess + from subprocess import CalledProcessError + + num_apps = int({{NUM_APPS}}) + print(f"{num_apps=}") + for i in range(num_apps): + print("\n", "\n", "\n", f"gen-app-and-box-scenarios #{i+1}" ) + subprocess.run(["just", "gen-app-and-box-scenarios", "{{BOXES_PER_APP}}"]).check_returncode() + + +# create an app and add up to BOXES_PER_APP random boxes to it in a multi-threaded fashion +gen-app-and-box-scenarios BOXES_PER_APP="10": + #!/usr/bin/env python3 + from concurrent.futures import ThreadPoolExecutor + import json + import logging + import random + import string + import subprocess + from subprocess import CalledProcessError + import time + + CHARS = string.digits + string.ascii_letters + VAL_SIZE = 24 + BOXES_PER_APP = int({{BOXES_PER_APP}}) + NLS = "\n" * 3 + + subprocess.run(["just", "app-create_fund"]).check_returncode() + + def worker(thread_number): + logging.info(f"HELLO from {thread_number}!") + + create_cpe = set_cpe = test_cpe = del_cpe = None + + rand_key_size = random.randint(4, 64) + rand_key = "".join(random.choice(CHARS) for _ in range(rand_key_size)) + print(f"{NLS}{thread_number}: {rand_key=}") + try: + subprocess.run(["just", "box-create", rand_key]).check_returncode() + except CalledProcessError as cpe: + create_cpe = str(cpe) + + rand_val = "".join(random.choice(CHARS) for _ in range(VAL_SIZE)) + print(f"{NLS}{thread_number}: {rand_val=}") + try: + subprocess.run(["just", "box-set", rand_key, rand_val]).check_returncode() + except CalledProcessError as cpe: + set_cpe = str(cpe) + + print(f"{NLS}{thread_number}: checking {rand_val=}") + try: + subprocess.run(["just", "box-test", rand_key, rand_val]).check_returncode() + except CalledProcessError as cpe: + test_cpe = str(cpe) + + delete = random.choice([True, False]) + if delete: + print(f"{NLS}{thread_number}: deleting") + try: + subprocess.run(["just", "box-delete", rand_key]).check_returncode() + except CalledProcessError as cpe: + del_cpe = str(cpe) + + return { + "thread_number": thread_number, + "key_size": rand_key_size, + "key": rand_key, + "val": rand_val, + "deleted": delete, + "called_process_errors": { + "create_cpe": create_cpe, + "set_cpe": set_cpe, + "test_cpe": test_cpe, + "del_cpe": del_cpe, + }, + } + + format = "%(asctime)s: %(message)s" + logging.basicConfig(format=format, level=logging.INFO, + datefmt="%H:%M:%S") + + results = [] + with ThreadPoolExecutor() as executor: + for r in executor.map(worker, range(BOXES_PER_APP)): + results.append(r) + + print(json.dumps(results, indent=2)) + + for result in results: + for err in result["called_process_errors"].values(): + if err: + raise err + +# --- HIGHER LEVEL --- # + +# create and then start (error if already created) +@create_and_start: create start status + sleep 5 + just status + +# create an app and then fund it +@app-create_fund: app-create last-app-fund + + +# --- BOX PUT: HIGHER LEVEL --- # + +# create box[BOX] for last app with provided key variable BOX +@box-create $BOX: + just app-call-last '\"create\", \"{{BOX}}\"' + +# set box[BOX]=VAL for last app with key BOX and val VAL +@box-set $BOX $VAL: + just app-call-last '\"set\", \"{{BOX}}\", \"{{VAL}}\"' + +# set box[BOX]=VAL for last app with key BOX and val VAL +@box-test $BOX $VAL: + just app-call-last '\"check\", \"{{BOX}}\", \"{{VAL}}\"' + +# delete box[BOX] for last app +@box-delete $BOX: + just app-call-last '\"delete\", \"{{BOX}}\"' + +# stop and tear down the node network. WARNING: YOU WILL LOSE ALL YOUR NODE DATA FROM THE FILE SYSTEM. +@stop_and_nuke: stop nuke + +# --- PRE-REQUISITES --- # + +# calculate an app's address using the python SDK +app-address *ARGS: + #!/usr/bin/env python3 + from algosdk import logic + print(logic.get_application_address({{ ARGS }})) + +# --- NETWORKS / NODES --- # + +# Private vs. Public Networks. Typical workflow: +# 1. Create a directory for your network (CURR_NETWORK = NETWORKS/NAME) +# 2. Populate the node information under CURR_NETWORK. Branch on Public vs. Private. +# In either case the the data directory is ALGORAND_DATA == CURR_NETWORK/DATA_NODE == NETWORKS/NAME/DATA_NODE. +# a. Public: Use algocfg to configure a single node: see `just pub-create` +# b. Private: Use `goal` to configure a network nodes under CURR_NETWORK. See `just create` +# 3. Start the network: `just start` +# +# NOTE: To run a public network commands, you need to supply the env var `DATA_NODE`. EG: +# DATA_NODE=Follower just pub-validate-datadir +# Or, export and run: +# export DATA_NODE=Follower +# just pub-validate-datadir + + +# create a private network with one node (error if already created) +create: + mkdir -p $NETWORKS + goal network create -n $NAME -r $CURR_NETWORK -t $NODE_TEMPLATE + +@tree: + tree $CURR_NETWORK + +# start a the network (error if already running or not created) +@start: + goal node start + + +# PUBLIC NETWORKS BEGIN + +# check that is ready to connect to public network +pub-validate-datadir: + #! /usr/bin/env bash + set -euxo pipefail + [ -z "$IS_PUBLIC_TRUTHY" ] && { echo "Error: DATA_NODE env var required for public network" ; exit 1; } + echo "Ready for public network with node datadir: $ALGORAND_DATA" + +# list available profiles for configuring a network +@pub-cfg-list: + algocfg profile list + +# show the current network configuration@ +@pub-cfg-show: + echo "cat ${ALGORAND_DATA}/config.json" + cat ${ALGORAND_DATA}/config.json + +# prepare for connecting to public network +pub-prepare: pub-validate-datadir + mkdir -p $ALGORAND_DATA + +# configure a Conduit's network using `algocfg` +pub-create NODE_PROFILE="conduit" NETWORK="testnet": pub-prepare + algocfg profile set {{NODE_PROFILE}} -d $ALGORAND_DATA + cp ${GO_ALGORAND}/installer/genesis/{{NETWORK}}/genesis.json $ALGORAND_DATA + +# status of network node +@status: + goal node status && echo "RUNNING" || echo "NOT RUNNING" + +# stop the running node (error if not running) +@stop: + goal node stop + +# remove the node's data from the file system +@nuke: + echo "deleting $CURR_NETWORK" + rm -rf $CURR_NETWORK + +# --- ACCOUNTS --- # + +# list all associated accounts +@list: + goal account list + +# create a new account without renaming it to a human friendly local alias +@raw-new-account: + goal account new | awk '{print $NF}' + +# echo an account's alias +@account-alias $ACCOUNT: + just list | grep {{ACCOUNT}} | awk '{print $2}' + +# create a new locally aliased account +@new-account $ALIAS $ACCOUNT=`just raw-new-account`: + goal account rename `just account-alias {{ACCOUNT}}` {{ALIAS}} + +# create a new multisig account with threshold 1 using provided accounts (cannot handle aliases) +@raw-msig-account *ACCOUNTS: + goal account multisig new -T 1 {{ACCOUNTS}} | awk '{print $NF}' + +# create a new multisig account with given ALIAS and threshold 1 using provided accounts (cannot handle aliases) +@new-msig-account $ALIAS *ACCOUNTS: + goal account rename `just account-alias $(just raw-msig-account {{ACCOUNTS}})` {{ALIAS}} + +# funding account's address +@funder: + just list | awk '{print $2}' + +# provide information about a given account +@info $ACCOUNT=`just funder`: + goal account info --address {{ACCOUNT}} + +# provide an account's balance +@balance $ACCOUNT=`just funder`: + goal account balance --address {{ACCOUNT}} + +# funder's most recently created app-id +@last-app-id: + just info | grep ID | tail -n 1 | cut -d "," -f1 | awk '{print $2}' + +# the account address of the funders most recently created app-id +@last-app-address: + just app-address `just last-app-id` + +# --- ASSETS --- # + +# create a dummy asset for the provided FUNDER. Copy pasta from: https://dappradar.com/blog/algorand-dapp-development-2-standard-asset-management +@asset-create $FUNDER=`just funder`: + goal asset create --creator {{FUNDER}} --total 1000000 --unitname bUSD --name "Balgorand USD" --asseturl "https://b-usd.com" --decimals 9 + +# --- APPLICATIONS --- # + +# information about an application of given id +@app-info $APP_ID=`just last-app-id`: + goal app info --app-id {{APP_ID}} + +# print out the boxes teal program +@boxes_teal: + cat $BOXES_TEAL + +# shortcut for the approval and clear program `goal app create` params +@programs: + echo "--approval-prog $BOXES_TEAL --clear-prog clear.teal" + +# shortcut for the storage params of `goal app create` +@app-vars $GBS="0" $GI="0" $LBS="0" $LI="0": + echo "--global-byteslices $GBS --global-ints $GI --local-byteslices $LBS --local-ints $LI" + +# shortcut for creating the arguments of an app call +box-app-args *ARGS: + #!/usr/bin/env python3 + args = [] + box_arg = "" + i = 0 + for arg in {{ARGS}}: + try: + int(arg) + arg = f"int:{arg}" + except Exception: + arg = f"str:{arg}" + args.extend(["--app-arg", arg]) + if i == 1: + box_arg = f"--box {arg}" + i += 1 + if box_arg: + args = [box_arg] + args + print(*args) + +# create an app funded by funder account +@app-create $GBS="0" $GI="0" $LBS="0" $LI="0": + echo "goal app create --creator `just funder` `just programs` `just app-vars $GBS $GI $LBS $LI`" + goal app create --creator `just funder` `just programs` `just app-vars $GBS $GI $LBS $LI` + +# call the last app from the funder address using ARGS +@app-call-last *ARGS='\"create\", \"mybox\"': + (set -x; goal app call --app-id `just last-app-id` --from `just funder` `just box-app-args {{ARGS}}`) + +# --- BOX INFO --- # + +# get all the boxes associated a given app-id +app-box-list $APP_ID=`just last-app-id`: + goal app box list --app-id {{APP_ID}} --max 0 + +# get box information for agiven app-id and box anme +app-box-info $APP_ID=`just last-app-id` $BOX="str:mybox": + goal app box info --app-id {{APP_ID}} --name {{BOX}} + + +# --- CLERK --- # + +# send from one account to another a given amount +@send $FROM $TO $AMOUNT: + goal clerk send --from {{FROM}} --to {{TO}} --amount {{AMOUNT}} + +# fund the most recently created app +@last-app-fund $AMOUNT=`echo 133713371337`: + just send `just funder` `just last-app-address` {{AMOUNT}} + +# --- CONSENSUS PARAMS --- # + +# list all consensus param declarations +@consensus-params-list: + cat ${GO_ALGORAND}/config/consensus.go | egrep " (bool|byte|int$|uint|map\[|Duration|PaysetCommitType)" | egrep -v "type|=|func|,|//" + +# print out the value history of a consensus param +@consensus-param $CP="MaximumMinimumBalance": + cat ${GO_ALGORAND}/config/consensus.go | grep {{CP}} || echo "{{CP}} not found in consensus.go" + +# consensus params for program size +consensus-prog-size: + just consensus-param LogicSigMaxSize + just consensus-param MaxAppProgramLen + +# consensus param for LogicSigMaxCost +consensus-prog-cost: + just consensus-param MaxCost + just consensus-param MaxAppProgramCost + +# consensus params for program pages +@consensus-prog-pages: + just consensus-param MaxExtraAppProgramPages + +# consensus params for foreign refs: +consensus-foreign-refs: + just consensus-param MaxAppTxnAccounts + just consensus-param MaxAppTxnForeignApps + just consensus-param MaxAppTxnForeignAssets + just consensus-param MaxAppTotalTxnReferences + just consensus-param MaxAppBoxReferences + +# consensus params for local/global storage: +consensus-storage: + just consensus-param MaxAppKeyLen + just consensus-param MaxAppBytesValueLen + just consensus-param MaxAppSumKeyValueLens + just consensus-param MaxLocalSchemaEntries + just consensus-param MaxGlobalSchemaEntries + +# consensus params for min-balance calc: +consensus-minbal: + just consensus-param SchemaMinBalancePerEntry + just consensus-param SchemaUintMinBalance + just consensus-param SchemaBytesMinBalance + just consensus-param BoxFlatMinBalance + just consensus-param BoxByteMinBalance + +# consensus params for boxes: +consensus-boxes: + just consensus-param MaxAppKeyLen + just consensus-param MaxBoxSize + just consensus-param BoxFlatMinBalance + just consensus-param BoxByteMinBalance + just consensus-param MaxAppBoxReferences + just consensus-param BytesPerBoxReference + +# --- MISCELLANEOUS --- # + +# print out the network's algod & kmd token and network/process info +client-info: + #! /usr/bin/env bash + set -euo pipefail + ALGORAND_TOKEN=$(cat ${ALGORAND_TOKEN_PATH}) + ALGORAND_ALGOD=$(cat ${ALGORAND_ALGOD_PATH}) + ALGORAND_PID=$(cat ${ALGORAND_PID_PATH}) + echo "algod.token: ${ALGORAND_TOKEN}" + echo "algod.net: ${ALGORAND_ALGOD}" + echo "algod.pid: ${ALGORAND_PID}" + + if [[ -f "${ALGORAND_DATA}/kmd-v0.5/kmd.token" ]]; then + KMD=$(cat "${ALGORAND_DATA}/kmd-v0.5/kmd.token") + else + KMD="" + fi + echo "kmd.token: ${KMD}" + if [[ -n $KMD ]]; then + echo "kmd.net---->" + cat ${ALGORAND_DATA}/kmd-v0.5/kmd.log | grep 127.0.0.1 | head -n 1 | cut -d '"' -f4 + fi + +# print out broadcastQueueBulk's channel size ... the default is 100 which is too small for the example +@broadcast-queue-size: + echo "default is 100. What is it actually in network/wsNetwork.go ?" + cat {{GO_ALGORAND}}/network/wsNetwork.go | grep "wn.broadcastQueueBulk = make(chan broadcastRequest" | cut -d "," -f2 | cut -d ")" -f1 | awk '{print $1}' \ No newline at end of file diff --git a/examples/binary_polars.py b/examples/binary_polars.py new file mode 100644 index 00000000..deab3a79 --- /dev/null +++ b/examples/binary_polars.py @@ -0,0 +1,111 @@ +from dataclasses import dataclass +import json +import os +from pathlib import Path +import sys + +import polars as pl + +DEBUGGING = True + +STOP_SIGNAL = "!!!STOP!!!" + +EXAMPLES = Path.cwd() / "examples" +BLK_DIR = EXAMPLES / "blocks" +PLDB = EXAMPLES / "pldb" + +POLARS_TXNS = PLDB / "transactions.feather" +POLARS_BLKS = PLDB / "blocks.feather" + + +@dataclass +class PolarsDF: + file: Path + df: pl.LazyFrame | pl.DataFrame | None = None + + +@dataclass +class PolarsDB: + txns: PolarsDF + blks: PolarsDF + + +def setup(): + db = PolarsDB( + txns=PolarsDF(file=POLARS_TXNS), + blks=PolarsDF(file=POLARS_BLKS), + ) + + try: + db.blks.df = pl.scan_ipc(POLARS_BLKS) + db.txns.df = pl.scan_ipc(POLARS_TXNS) + except FileNotFoundError as fnfe: + print(f"File not found: {fnfe}") + + return db + + +import polars as pl + +# Assuming you have two Polars DataFrames `df1` and `df2` with the given structure +# Replace 'df1' and 'df2' with your actual DataFrame variable names + + +# Define a function to extract the 'rnd' field from the block dictionaries +def extract_rnd(blk: dict) -> int: + return blk["rnd"] + +def fix_missing_rnd(blk): + if "rnd" not in blk["block"]: + blk["block"]["rnd"] = 0 + return blk + +def merge(pdf: PolarsDF, blocks: list[dict]): + orig_len = 0 + if pdf.df is None: + pdf.df = pl.DataFrame([fix_missing_rnd(blk) for blk in blocks]) + else: + assert isinstance(pdf.df, pl.LazyFrame) + orig = pdf.df.collect() + orig_len = len(orig) + orig_rounds = set(orig["block"].apply(extract_rnd).to_list()) + + if blks := [ + fix_missing_rnd(blk) + for blk in blocks + if blk["block"].get("rnd", 0) not in orig_rounds + ]: + try: + pdf.df = orig.extend(pl.DataFrame(blks)) + except pl.ShapeError as se: + print(f"ShapeError: {se}") + bs, ds = (otd := orig.to_dict())['block'], otd['delta'] + collected_dicts = [{'block': b, 'delta': d} for b, d in zip(bs, ds)] + pdf.df = pl.DataFrame(collected_dicts + blks) + else: + print(f"No new blocks to add to {pdf.file}") + return + + pdf.df.write_ipc(pdf.file) + print( + f"{pdf.file} updated with {len(blocks)} blocks from original {orig_len} blocks" + ) + + +if __name__ == "__main__": + blocks_iter = sorted(os.listdir(BLK_DIR)) if DEBUGGING else sys.stdin + + db = setup() + blocks = [] + try: + for i, line in enumerate(blocks_iter): + if not (trimmed := line.strip()): + continue + + print(f"{i}. {trimmed=}") + with open(f"{BLK_DIR}/{trimmed}") as f: + blocks.append(json.loads(f.read())) + except KeyboardInterrupt: + print("TERMINATING PROGRAM") + finally: + merge(db.blks, blocks) diff --git a/examples/data/conduit.yml b/examples/data/conduit.yml new file mode 100644 index 00000000..d839b2a4 --- /dev/null +++ b/examples/data/conduit.yml @@ -0,0 +1,46 @@ +log-level: INFO + +# If no log file is provided logs are written to stdout. +#log-file: + +retry-count: 1 + +retry-delay: "1s" + +# Optional filepath to use for pidfile. +#pid-filepath: /path/to/pidfile + +# Whether or not to print the conduit banner on startup. +hide-banner: false + +# When enabled prometheus metrics are available on '/metrics' +metrics: + mode: OFF + addr: ":9999" + prefix: "conduit" + +importer: + name: algod + config: + mode: "follower" + # shouldn't we be able to specify the starting sync round? + # VOLATILE: + netaddr: "http://127.0.0.1:63135" + # VOLATILE: + token: "dc99e2f8e526dbcf2a6e362d7a3beab6db8b1b848b0268c2b269a86c96b6e1bd" + +processors: + - name: filter_processor + config: + filters: + - any: + - tag: txn.snd + expression-type: equal + expression: "NOTIFY2IJIXDNDCHF4EJZJOQYEQACIEVAIXBQMSZU4YTDKHWPLVYGTOU5Y" + +exporter: + name: "file_writer" + config: + block-dir: "/Users/zeph/github/algorand/conduit/examples/blocks" + filename-pattern: "block_%09d.json" + drop-certificate: true diff --git a/examples/pldb/blocks.feather b/examples/pldb/blocks.feather new file mode 100644 index 0000000000000000000000000000000000000000..56c2157f8339fcaac1ef6658a1a30becf538db3e GIT binary patch literal 1289712 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 z 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%0DQTzf=^{+Ut=yTMn3hOT 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 Y-4o_`GE?WfCYo1;vD2oVJZs2*09J9o<^TWy literal 0 HcmV?d00001 From 641d2b1ee2fa7f68483577ba081280e8b4418999 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Mon, 1 May 2023 12:33:50 -0400 Subject: [PATCH 08/22] Handle catching up to an earlier round. --- .../plugins/importers/algod/algod_importer.go | 54 ++++++++++++++----- 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/conduit/plugins/importers/algod/algod_importer.go b/conduit/plugins/importers/algod/algod_importer.go index 0e42e196..0284b15d 100644 --- a/conduit/plugins/importers/algod/algod_importer.go +++ b/conduit/plugins/importers/algod/algod_importer.go @@ -196,15 +196,35 @@ func getMissingCatchpointLabel(URL string, nextRound uint64) (string, error) { return label, nil } -func (algodImp *algodImporter) catchupNode(catchpoint string, nextRound uint64) error { - if algodImp.mode == followerMode { - // Set the sync round to the round provided by initProvider - _, err := algodImp.aclient.SetSyncRound(nextRound).Do(algodImp.ctx) - if err != nil { - return fmt.Errorf("received unexpected error setting sync round (%d): %w", nextRound, err) - } +// checkRounds to see if catchup is needed, an error is returned if a bad state +// is detected. +func checkRounds(logger *logrus.Logger, catchpointRound, nodeRound, targetRound uint64) (bool, error) { + // Make sure catchpoint round is not in the future + canCatchup := catchpointRound <= targetRound + mustCatchup := targetRound < nodeRound + shouldCatchup := nodeRound < catchpointRound + + if canCatchup && mustCatchup { + logger.Infof("Catchup required. Node round %d is ahead of target round %d", nodeRound, targetRound) + return true, nil + } + + if canCatchup && shouldCatchup { + logger.Infof("Catchup required. Node round %d and target round %d are behind catchpoint round %d", nodeRound, targetRound, catchpointRound) + } + if !canCatchup && mustCatchup { + err := fmt.Errorf("node round %d is ahead of target round %d", nodeRound, targetRound) + logger.Errorf("Catchup required but no valid catchpoint available: %s.", err.Error()) + return false, err + } + + logger.Infof("No catchup required. Node round %d is behind target round %d.", nodeRound, targetRound) + return false, nil +} + +func (algodImp *algodImporter) catchupNode(catchpoint string, targetRound uint64) error { if catchpoint != "" { algodImp.logger.Infof("Starting catchpoint catchup with label %s", catchpoint) cpRound, err := parseCatchpointRound(catchpoint) @@ -215,12 +235,9 @@ func (algodImp *algodImporter) catchupNode(catchpoint string, nextRound uint64) if err != nil { return fmt.Errorf("received unexpected error failed to get node status: %w", err) } - if cpRound <= sdk.Round(nStatus.LastRound) { - algodImp.logger.Infof( - "Skipping catchpoint catchup for %s, since it's before node round %d", - catchpoint, - nStatus.LastRound, - ) + + if runCatchup, err := checkRounds(algodImp.logger, uint64(cpRound), nStatus.LastRound, targetRound); !runCatchup || err != nil { + return err } else { err = algodImp.startCatchpointCatchup(catchpoint) if err != nil { @@ -235,7 +252,16 @@ func (algodImp *algodImporter) catchupNode(catchpoint string, nextRound uint64) } } - _, err := algodImp.aclient.StatusAfterBlock(nextRound).Do(algodImp.ctx) + // It is possible for the round to go backwards after a catchup. So sync must be called after fast catchup. + if algodImp.mode == followerMode { + // Set the sync round to the round provided by initProvider + _, err := algodImp.aclient.SetSyncRound(targetRound).Do(algodImp.ctx) + if err != nil { + return fmt.Errorf("received unexpected error setting sync round (%d): %w", targetRound, err) + } + } + + _, err := algodImp.aclient.StatusAfterBlock(targetRound).Do(algodImp.ctx) if err != nil { err = fmt.Errorf("received unexpected error (StatusAfterBlock) waiting for node to catchup: %w", err) } From c04c885b6f5ca8a0070a3eccacb68b4616fd3683 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Tue, 2 May 2023 19:37:27 -0400 Subject: [PATCH 09/22] Fix unit tests. --- .../plugins/importers/algod/algod_importer.go | 4 +- .../importers/algod/algod_importer_test.go | 54 +++++++++++-------- 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/conduit/plugins/importers/algod/algod_importer.go b/conduit/plugins/importers/algod/algod_importer.go index 0284b15d..1f3fde4b 100644 --- a/conduit/plugins/importers/algod/algod_importer.go +++ b/conduit/plugins/importers/algod/algod_importer.go @@ -211,7 +211,7 @@ func checkRounds(logger *logrus.Logger, catchpointRound, nodeRound, targetRound if canCatchup && shouldCatchup { logger.Infof("Catchup required. Node round %d and target round %d are behind catchpoint round %d", nodeRound, targetRound, catchpointRound) - + return true, nil } if !canCatchup && mustCatchup { @@ -220,7 +220,7 @@ func checkRounds(logger *logrus.Logger, catchpointRound, nodeRound, targetRound return false, err } - logger.Infof("No catchup required. Node round %d is behind target round %d.", nodeRound, targetRound) + logger.Infof("No catchup required. Node round %d, target round %d, catchpoint round %d.", nodeRound, targetRound, catchpointRound) return false, nil } diff --git a/conduit/plugins/importers/algod/algod_importer_test.go b/conduit/plugins/importers/algod/algod_importer_test.go index 37ab5493..2ebf8d6f 100644 --- a/conduit/plugins/importers/algod/algod_importer_test.go +++ b/conduit/plugins/importers/algod/algod_importer_test.go @@ -99,13 +99,15 @@ func TestInitCatchup(t *testing.T) { tests := []struct { name string catchpoint string + targetRound sdk.Round auto bool algodServer *httptest.Server err string logs []string }{ { - name: "sync round failure", + name: "sync round failure", + targetRound: 1, algodServer: NewAlgodServer( GenesisResponder, MakeSyncRoundResponder(http.StatusBadRequest)), @@ -137,37 +139,42 @@ func TestInitCatchup(t *testing.T) { err: "received unexpected error failed to get node status: HTTP 400", logs: []string{}}, { - name: "catchpoint round before node round skips fast catchup", - catchpoint: "1234#abcd", + name: "catchpoint round before node round skips fast catchup", + catchpoint: "1234#abcd", + targetRound: 1235, algodServer: NewAlgodServer( GenesisResponder, MakeSyncRoundResponder(http.StatusOK), MakeNodeStatusResponder(models.NodeStatus{LastRound: 1235})), - logs: []string{"Skipping catchpoint catchup for 1234#abcd, since it's before node round 1235"}}, - { - name: "start catchpoint catchup failure", - catchpoint: "1236#abcd", + logs: []string{"No catchup required. Node round 1235, target round 1235, catchpoint round 1234."}, + }, { + name: "start catchpoint catchup failure", + catchpoint: "1236#abcd", + targetRound: 1240, algodServer: NewAlgodServer( GenesisResponder, MakeSyncRoundResponder(http.StatusOK), MakeNodeStatusResponder(models.NodeStatus{LastRound: 1235}), MakeStatusResponder("/v2/catchup/", http.StatusBadRequest, "")), err: "POST /v2/catchup/1236#abcd received unexpected error: HTTP 400", - logs: []string{}}, + logs: []string{}, + }, { - name: "monitor catchup node status failure", - catchpoint: "1236#abcd", + name: "monitor catchup node status failure", + catchpoint: "1236#abcd", + targetRound: 1239, algodServer: NewAlgodServer( GenesisResponder, MakeSyncRoundResponder(http.StatusOK), MakeJsonResponderSeries("/v2/status", []int{http.StatusOK, http.StatusBadRequest}, []interface{}{models.NodeStatus{LastRound: 1235}}), MakeStatusResponder("/v2/catchup/", http.StatusOK, "")), err: "received unexpected error getting node status: HTTP 400", - logs: []string{}}, - { - name: "monitor catchup success", - catchpoint: "1236#abcd", - auto: true, // ignored + logs: []string{}, + }, { + name: "monitor catchup success", + catchpoint: "1236#abcd", + targetRound: 1240, + auto: true, // ignored algodServer: NewAlgodServer( GenesisResponder, MakeSyncRoundResponder(http.StatusOK), @@ -185,8 +192,8 @@ func TestInitCatchup(t *testing.T) { "catchup phase Verified Accounts: 1 / 1", "catchup phase Acquired Blocks: 1 / 1", "catchup phase Verified Blocks", - }}, - { + }, + }, { name: "auto catchup used (even if the mocking isn't setup for it)", catchpoint: "", auto: true, @@ -194,17 +201,18 @@ func TestInitCatchup(t *testing.T) { GenesisResponder, ), err: "failed to lookup catchpoint label list", - }, - { - name: "wait for node to catchup error", - catchpoint: "1236#abcd", + }, { + name: "wait for node to catchup error", + targetRound: 1240, + catchpoint: "1236#abcd", algodServer: NewAlgodServer( GenesisResponder, MakeSyncRoundResponder(http.StatusOK), MakeJsonResponderSeries("/v2/status", []int{http.StatusOK, http.StatusOK, http.StatusBadRequest}, []interface{}{models.NodeStatus{LastRound: 1235}}), MakeStatusResponder("/v2/catchup/", http.StatusOK, "")), err: "received unexpected error (StatusAfterBlock) waiting for node to catchup: HTTP 400", - logs: []string{}}, + logs: []string{}, + }, } for _, ttest := range tests { ttest := ttest @@ -222,7 +230,7 @@ func TestInitCatchup(t *testing.T) { } cfgStr, err := yaml.Marshal(cfg) require.NoError(t, err) - _, err = testImporter.Init(ctx, conduit.MakePipelineInitProvider(&pRound, nil), plugins.MakePluginConfig(string(cfgStr)), testLogger) + _, err = testImporter.Init(ctx, conduit.MakePipelineInitProvider(&ttest.targetRound, nil), plugins.MakePluginConfig(string(cfgStr)), testLogger) if ttest.err != "" { require.ErrorContains(t, err, ttest.err) } else { From 88a16f60a2a411f310bda718ad08bcc6633bc8f4 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Tue, 2 May 2023 20:17:30 -0400 Subject: [PATCH 10/22] More tests. --- .../plugins/importers/algod/algod_importer.go | 12 +-- .../importers/algod/algod_importer_test.go | 85 +++++++++++++++++++ 2 files changed, 92 insertions(+), 5 deletions(-) diff --git a/conduit/plugins/importers/algod/algod_importer.go b/conduit/plugins/importers/algod/algod_importer.go index 1f3fde4b..df8e55b6 100644 --- a/conduit/plugins/importers/algod/algod_importer.go +++ b/conduit/plugins/importers/algod/algod_importer.go @@ -204,23 +204,25 @@ func checkRounds(logger *logrus.Logger, catchpointRound, nodeRound, targetRound mustCatchup := targetRound < nodeRound shouldCatchup := nodeRound < catchpointRound + msg := fmt.Sprintf("Node round %d, target round %d, catchpoint round %d", nodeRound, targetRound, catchpointRound) + if canCatchup && mustCatchup { - logger.Infof("Catchup required. Node round %d is ahead of target round %d", nodeRound, targetRound) + logger.Infof("Catchup required, node round ahead of target round. %s.", msg) return true, nil } if canCatchup && shouldCatchup { - logger.Infof("Catchup required. Node round %d and target round %d are behind catchpoint round %d", nodeRound, targetRound, catchpointRound) + logger.Infof("Catchup requested. %s.", msg) return true, nil } if !canCatchup && mustCatchup { - err := fmt.Errorf("node round %d is ahead of target round %d", nodeRound, targetRound) - logger.Errorf("Catchup required but no valid catchpoint available: %s.", err.Error()) + err := fmt.Errorf("node round %d and catchpoint round %d are ahead of target round %d", nodeRound, catchpointRound, targetRound) + logger.Errorf("Catchup required but no valid catchpoint available, %s.", err.Error()) return false, err } - logger.Infof("No catchup required. Node round %d, target round %d, catchpoint round %d.", nodeRound, targetRound, catchpointRound) + logger.Infof("No catchup required. %s.", msg) return false, nil } diff --git a/conduit/plugins/importers/algod/algod_importer_test.go b/conduit/plugins/importers/algod/algod_importer_test.go index 2ebf8d6f..9fb5b6f0 100644 --- a/conduit/plugins/importers/algod/algod_importer_test.go +++ b/conduit/plugins/importers/algod/algod_importer_test.go @@ -95,6 +95,91 @@ netaddr: %s } } +func Test_checkRounds(t *testing.T) { + type args struct { + catchpointRound uint64 + nodeRound uint64 + targetRound uint64 + } + tests := []struct { + name string + args args + want bool + wantErr assert.ErrorAssertionFunc + wantLogLevel logrus.Level + wantLogMsg string + }{ + { + name: "Skip catchpoint", + args: args{ + catchpointRound: 1000, + nodeRound: 1001, + targetRound: 1002, + }, + want: false, + wantErr: assert.NoError, + wantLogLevel: logrus.InfoLevel, + wantLogMsg: "No catchup required. Node round 1001, target round 1002, catchpoint round 1000.", + }, + { + name: "Catchup requested.", + args: args{ + catchpointRound: 1002, + nodeRound: 1001, + targetRound: 1002, + }, + want: true, + wantErr: assert.NoError, + wantLogLevel: logrus.InfoLevel, + wantLogMsg: "Catchup requested. Node round 1001, target round 1002, catchpoint round 1002.", + }, + { + name: "Catchup required. Success.", + args: args{ + catchpointRound: 1000, + nodeRound: 5000, + targetRound: 1002, + }, + want: true, + wantErr: assert.NoError, + wantLogLevel: logrus.InfoLevel, + wantLogMsg: "Catchup required, node round ahead of target round. Node round 5000, target round 1002, catchpoint round 1000.", + }, + { + name: "Catchup required. Error.", + args: args{ + catchpointRound: 6000, + nodeRound: 5000, + targetRound: 1002, + }, + want: false, + wantErr: assert.Error, + wantLogLevel: logrus.ErrorLevel, + wantLogMsg: "Catchup required but no valid catchpoint available, node round 5000 and catchpoint round 6000 are ahead of target round 1002.", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + testLogger, hook := test.NewNullLogger() + got, err := checkRounds(testLogger, tt.args.catchpointRound, tt.args.nodeRound, tt.args.targetRound) + + // Write 1 line to the log. + require.Len(t, hook.Entries, 1) + require.Equal(t, tt.wantLogLevel, hook.Entries[0].Level) + require.Equal(t, tt.wantLogMsg, hook.Entries[0].Message) + + // Check the error + if !tt.wantErr(t, err, fmt.Sprintf("checkRounds(-, %v, %v, %v)", tt.args.catchpointRound, tt.args.nodeRound, tt.args.targetRound)) { + return + } + + // Check return values + assert.Equalf(t, tt.want, got, "checkRounds(-, %v, %v, %v)", tt.args.catchpointRound, tt.args.nodeRound, tt.args.targetRound) + + }) + } +} + func TestInitCatchup(t *testing.T) { tests := []struct { name string From ad0f9599cfa17fd392a921f1ec2e291e3e396751 Mon Sep 17 00:00:00 2001 From: Zeph Grunschlag Date: Tue, 2 May 2023 21:52:48 -0500 Subject: [PATCH 11/22] stable algod token and port --- examples/Justfile | 11 ++++++++++- examples/data/conduit.yml | 7 ++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/examples/Justfile b/examples/Justfile index b9c4133b..07c558ef 100644 --- a/examples/Justfile +++ b/examples/Justfile @@ -14,9 +14,13 @@ DATA_NODE := env_var_or_default("DATA_NODE", PRIVATE_DATA_NODE) IS_PUBLIC_TRUTHY := env_var_or_default("DATA_NODE", "") ALGORAND_DATA := CURR_NETWORK + "/" + DATA_NODE ALGORAND_TOKEN_PATH := ALGORAND_DATA + "/algod.token" +ALGORAND_ADTOKEN_PATH := ALGORAND_DATA + "/algod.admin.token" ALGORAND_ALGOD_PATH := ALGORAND_DATA + "/algod.net" ALGORAND_PID_PATH := ALGORAND_DATA + "/algod.pid" +PRESET_ALGOD_TOKEN := "16b29a0a2bbcc535f1e9e40f0c0888013f3789bf2bd34e7907c8fb1ae9d16024" +PRESET_ALGOD_ADMIN := "20064faacad1e590e757ac9492506c2d948633d7c458651b16a3991d26997695" + # Older: BOXES_TEAL := "boxes.teal" @@ -245,8 +249,11 @@ pub-prepare: pub-validate-datadir mkdir -p $ALGORAND_DATA # configure a Conduit's network using `algocfg` -pub-create NODE_PROFILE="conduit" NETWORK="testnet": pub-prepare +pub-create NODE_PROFILE="conduit" NETWORK="testnet" ENDPOINT="127.0.0.1:56765" PT="1" PAT="1": pub-prepare algocfg profile set {{NODE_PROFILE}} -d $ALGORAND_DATA + [ -n {{ENDPOINT}} ] && echo "setting ENDPOINT={{ENDPOINT}}" && algocfg set -p EndpointAddress -v {{ENDPOINT}} + [ {{PT}} = "1" ] && echo "setting ALGOD TOKEN=${PRESET_ALGOD_TOKEN}" && echo ${PRESET_ALGOD_TOKEN} > ${ALGORAND_TOKEN_PATH} + [ {{PAT}} = "1" ] && echo "setting ALGOD ADMIN TOKEN=${PRESET_ALGOD_ADMIN}" && echo ${PRESET_ALGOD_ADMIN} > ${ALGORAND_ADTOKEN_PATH} cp ${GO_ALGORAND}/installer/genesis/{{NETWORK}}/genesis.json $ALGORAND_DATA # status of network node @@ -446,9 +453,11 @@ client-info: #! /usr/bin/env bash set -euo pipefail ALGORAND_TOKEN=$(cat ${ALGORAND_TOKEN_PATH}) + ALGORAND_ADTOKEN=$(cat ${ALGORAND_ADTOKEN_PATH}) ALGORAND_ALGOD=$(cat ${ALGORAND_ALGOD_PATH}) ALGORAND_PID=$(cat ${ALGORAND_PID_PATH}) echo "algod.token: ${ALGORAND_TOKEN}" + echo "algod.admin.token: ${ALGORAND_ADTOKEN}" echo "algod.net: ${ALGORAND_ALGOD}" echo "algod.pid: ${ALGORAND_PID}" diff --git a/examples/data/conduit.yml b/examples/data/conduit.yml index d839b2a4..73e8b7d3 100644 --- a/examples/data/conduit.yml +++ b/examples/data/conduit.yml @@ -23,11 +23,8 @@ importer: name: algod config: mode: "follower" - # shouldn't we be able to specify the starting sync round? - # VOLATILE: - netaddr: "http://127.0.0.1:63135" - # VOLATILE: - token: "dc99e2f8e526dbcf2a6e362d7a3beab6db8b1b848b0268c2b269a86c96b6e1bd" + netaddr: "27.0.0.1:56765" + token: "16b29a0a2bbcc535f1e9e40f0c0888013f3789bf2bd34e7907c8fb1ae9d16024" processors: - name: filter_processor From 21b0f5a400b5699dc53b0a7458d58826e8f5d929 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Wed, 3 May 2023 08:12:35 -0400 Subject: [PATCH 12/22] Move starting catchup message to better location. --- conduit/plugins/importers/algod/algod_importer.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/conduit/plugins/importers/algod/algod_importer.go b/conduit/plugins/importers/algod/algod_importer.go index df8e55b6..3500c447 100644 --- a/conduit/plugins/importers/algod/algod_importer.go +++ b/conduit/plugins/importers/algod/algod_importer.go @@ -228,7 +228,6 @@ func checkRounds(logger *logrus.Logger, catchpointRound, nodeRound, targetRound func (algodImp *algodImporter) catchupNode(catchpoint string, targetRound uint64) error { if catchpoint != "" { - algodImp.logger.Infof("Starting catchpoint catchup with label %s", catchpoint) cpRound, err := parseCatchpointRound(catchpoint) if err != nil { return err @@ -241,6 +240,8 @@ func (algodImp *algodImporter) catchupNode(catchpoint string, targetRound uint64 if runCatchup, err := checkRounds(algodImp.logger, uint64(cpRound), nStatus.LastRound, targetRound); !runCatchup || err != nil { return err } else { + algodImp.logger.Infof("Starting catchpoint catchup with label %s", catchpoint) + err = algodImp.startCatchpointCatchup(catchpoint) if err != nil { return err From 91b858368000b47b396eacdee990465ce7464504 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Wed, 3 May 2023 08:33:31 -0400 Subject: [PATCH 13/22] Workaround for catching up to the exact round. --- conduit/plugins/importers/algod/algod_importer.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/conduit/plugins/importers/algod/algod_importer.go b/conduit/plugins/importers/algod/algod_importer.go index 3500c447..cf77a3ee 100644 --- a/conduit/plugins/importers/algod/algod_importer.go +++ b/conduit/plugins/importers/algod/algod_importer.go @@ -183,7 +183,8 @@ func getMissingCatchpointLabel(URL string, nextRound uint64) (string, error) { if err != nil { return "", err } - if uint64(round) > nextRound { + // TODO: Change >= to > after go-algorand#5352 is fixed. + if uint64(round) >= nextRound { break } label = line @@ -200,7 +201,8 @@ func getMissingCatchpointLabel(URL string, nextRound uint64) (string, error) { // is detected. func checkRounds(logger *logrus.Logger, catchpointRound, nodeRound, targetRound uint64) (bool, error) { // Make sure catchpoint round is not in the future - canCatchup := catchpointRound <= targetRound + // TODO: Change < to <= after go-algorand#5352 is fixed. + canCatchup := catchpointRound < targetRound mustCatchup := targetRound < nodeRound shouldCatchup := nodeRound < catchpointRound From 9ea6b6d02fc81b57ac68fd3705630b16405ecfca Mon Sep 17 00:00:00 2001 From: Will Winder Date: Wed, 3 May 2023 08:56:11 -0400 Subject: [PATCH 14/22] Update test. --- README.md | 8 -------- conduit/plugins/importers/algod/algod_importer_test.go | 4 ++-- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 59051a6c..99abc928 100644 --- a/README.md +++ b/README.md @@ -57,11 +57,3 @@ Indexer was built in a way that strongly coupled it to Postgresql, and the defin Going forward we will continue to maintain the Indexer application, however our main focus will be enabling and optimizing a multitude of use cases through the Conduit pipeline design rather the singular Indexer pipeline. For a more detailed look at the differences between Conduit and Indexer, see [our migration guide](./docs/tutorials/IndexerMigration.md). - -# Known Issues - -## Restarting Follower Nodes Multiple Times in a Row - -When a follower node is restarted, the sync round is advanced to the node's ledger round. This causes a chain reaction where the node's ledger round is then advanced by `MaxAcctLookback` rounds. When this happens, the node should temporarily have access to 2 * `MaxAcctLookback` ledger state delta responses because some had been previously persisted to disk. However, if the follower node is restarted a second time before conduit has consumed the temporary ledger state delta objects, the node will become desynchronized from Conduit. - -When this happens the follower node must be manually re-synchronized with Conduit. This is done by launching a new follower node or running fast catchup to move to an earlier round. diff --git a/conduit/plugins/importers/algod/algod_importer_test.go b/conduit/plugins/importers/algod/algod_importer_test.go index 9fb5b6f0..ada0cb5c 100644 --- a/conduit/plugins/importers/algod/algod_importer_test.go +++ b/conduit/plugins/importers/algod/algod_importer_test.go @@ -126,12 +126,12 @@ func Test_checkRounds(t *testing.T) { args: args{ catchpointRound: 1002, nodeRound: 1001, - targetRound: 1002, + targetRound: 1003, }, want: true, wantErr: assert.NoError, wantLogLevel: logrus.InfoLevel, - wantLogMsg: "Catchup requested. Node round 1001, target round 1002, catchpoint round 1002.", + wantLogMsg: "Catchup requested. Node round 1001, target round 1003, catchpoint round 1002.", }, { name: "Catchup required. Success.", From 8ca0460725bf9fba3fddf1efcad0f337906daa38 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Wed, 3 May 2023 09:06:36 -0400 Subject: [PATCH 15/22] Fix another test... --- conduit/plugins/importers/algod/algod_importer_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conduit/plugins/importers/algod/algod_importer_test.go b/conduit/plugins/importers/algod/algod_importer_test.go index ada0cb5c..1b41d230 100644 --- a/conduit/plugins/importers/algod/algod_importer_test.go +++ b/conduit/plugins/importers/algod/algod_importer_test.go @@ -649,7 +649,7 @@ func TestGetMissingCatchpointLabel(t *testing.T) { fmt.Fprintln(w, "1000#abcd\n1100#abcd\n1200#abcd") })) defer ts.Close() - label, err := getMissingCatchpointLabel(ts.URL, 1100) + label, err := getMissingCatchpointLabel(ts.URL, 1101) require.NoError(t, err) // closest without going over require.Equal(t, "1100#abcd", label) From 1d985f30a072292c87b3e137b7e9ff0ae91f7a0b Mon Sep 17 00:00:00 2001 From: Will Winder Date: Wed, 3 May 2023 15:16:22 -0400 Subject: [PATCH 16/22] Mention label and auto precedence. --- conduit/plugins/importers/algod/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conduit/plugins/importers/algod/README.md b/conduit/plugins/importers/algod/README.md index 301afca9..fcbf1a46 100644 --- a/conduit/plugins/importers/algod/README.md +++ b/conduit/plugins/importers/algod/README.md @@ -6,7 +6,7 @@ This plugin imports block data from an algod node. Fetch blocks data from the [a ### Automatic Fast Catchup -If an admin API token and Auto (or a catchpoint label) are set, the plugin will automatically run fast catchup on startup if the node is behind the current pipeline round. +If an admin API token and Auto (or a catchpoint label) are set, the plugin will automatically run fast catchup on startup if the node is behind the current pipeline round. If a catchpoint label is set, that takes precedent over auto being set to true. ### Follower Node Orchestration From 7fec4411fb257883383c9e7b692a364c8de06884 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Wed, 3 May 2023 15:19:46 -0400 Subject: [PATCH 17/22] Don't overuse the word automatic. --- conduit/plugins/importers/algod/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conduit/plugins/importers/algod/README.md b/conduit/plugins/importers/algod/README.md index fcbf1a46..7477708d 100644 --- a/conduit/plugins/importers/algod/README.md +++ b/conduit/plugins/importers/algod/README.md @@ -6,7 +6,7 @@ This plugin imports block data from an algod node. Fetch blocks data from the [a ### Automatic Fast Catchup -If an admin API token and Auto (or a catchpoint label) are set, the plugin will automatically run fast catchup on startup if the node is behind the current pipeline round. If a catchpoint label is set, that takes precedent over auto being set to true. +If an admin API token and Auto (or a catchpoint label) are set, and a fast catchup would help, the plugin will coordinate running fast catchup on the node. If a catchpoint label is set, that takes precedent over auto being set to true. ### Follower Node Orchestration From 59a4ada3553b30021b808caed7b131ae8d551410 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Wed, 3 May 2023 15:26:55 -0400 Subject: [PATCH 18/22] Clarify comment and tutorial. --- conduit/plugins/importers/algod/algod_importer.go | 3 ++- docs/tutorials/IndexerWriter.md | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/conduit/plugins/importers/algod/algod_importer.go b/conduit/plugins/importers/algod/algod_importer.go index cf77a3ee..7f1b2a0b 100644 --- a/conduit/plugins/importers/algod/algod_importer.go +++ b/conduit/plugins/importers/algod/algod_importer.go @@ -257,7 +257,8 @@ func (algodImp *algodImporter) catchupNode(catchpoint string, targetRound uint64 } } - // It is possible for the round to go backwards after a catchup. So sync must be called after fast catchup. + // Set the sync round after fast-catchup in case the node round is ahead of the target round. + // Trying to set it before would cause an error. if algodImp.mode == followerMode { // Set the sync round to the round provided by initProvider _, err := algodImp.aclient.SetSyncRound(targetRound).Do(algodImp.ctx) diff --git a/docs/tutorials/IndexerWriter.md b/docs/tutorials/IndexerWriter.md index 41f23241..c463bdd7 100644 --- a/docs/tutorials/IndexerWriter.md +++ b/docs/tutorials/IndexerWriter.md @@ -118,10 +118,10 @@ Configuration: * `importer.config.token`: the contents of `algod_data/algod.token` * `exporter.config.connection-string`: `host=localhost port=5555 user=algorand password=pgpass dbname=conduit` -If you are connecting to an existing PostgreSQL database, you can also set a -catchpoint and the admin token and enable `auto` catchup. You can optionally +If you are connecting to an existing PostgreSQL database, you can also set +the admin token and enable `auto`. You can optionally specify the catchup label directly. If those are configured, Conduit will -automatically initialize the node using fast catchup. +coordinate initializing the node by using fast catchup. Review the inline documentation in `conduit.yml` and decide if there are any other settings you would like to update. From a7bbaa105d2ed3cd15987a9156704574ff06099e Mon Sep 17 00:00:00 2001 From: Zeph Grunschlag Date: Fri, 5 May 2023 07:16:34 -0500 Subject: [PATCH 19/22] wip --- examples/data/conduit.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/data/conduit.yml b/examples/data/conduit.yml index 73e8b7d3..225ff4a6 100644 --- a/examples/data/conduit.yml +++ b/examples/data/conduit.yml @@ -23,8 +23,10 @@ importer: name: algod config: mode: "follower" - netaddr: "27.0.0.1:56765" + netaddr: "http://127.0.0.1:56765" token: "16b29a0a2bbcc535f1e9e40f0c0888013f3789bf2bd34e7907c8fb1ae9d16024" + catchup-config: + admin-token: "20064faacad1e590e757ac9492506c2d948633d7c458651b16a3991d26997695" processors: - name: filter_processor From 067be735db36ca815326046b0b34fd3b465fa0f1 Mon Sep 17 00:00:00 2001 From: Zeph Grunschlag Date: Mon, 8 May 2023 17:15:45 -0500 Subject: [PATCH 20/22] wip --- examples/Justfile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/Justfile b/examples/Justfile index 07c558ef..87fc1abd 100644 --- a/examples/Justfile +++ b/examples/Justfile @@ -18,6 +18,7 @@ ALGORAND_ADTOKEN_PATH := ALGORAND_DATA + "/algod.admin.token" ALGORAND_ALGOD_PATH := ALGORAND_DATA + "/algod.net" ALGORAND_PID_PATH := ALGORAND_DATA + "/algod.pid" +# These fake pre-set tokens make it easier to test against a local network PRESET_ALGOD_TOKEN := "16b29a0a2bbcc535f1e9e40f0c0888013f3789bf2bd34e7907c8fb1ae9d16024" PRESET_ALGOD_ADMIN := "20064faacad1e590e757ac9492506c2d948633d7c458651b16a3991d26997695" @@ -218,6 +219,7 @@ create: mkdir -p $NETWORKS goal network create -n $NAME -r $CURR_NETWORK -t $NODE_TEMPLATE +# print out the current network's data directory tree @tree: tree $CURR_NETWORK @@ -239,7 +241,7 @@ pub-validate-datadir: @pub-cfg-list: algocfg profile list -# show the current network configuration@ +# show the current network configuration @pub-cfg-show: echo "cat ${ALGORAND_DATA}/config.json" cat ${ALGORAND_DATA}/config.json @@ -374,7 +376,7 @@ box-app-args *ARGS: app-box-list $APP_ID=`just last-app-id`: goal app box list --app-id {{APP_ID}} --max 0 -# get box information for agiven app-id and box anme +# get box information for a given app-id and box name app-box-info $APP_ID=`just last-app-id` $BOX="str:mybox": goal app box info --app-id {{APP_ID}} --name {{BOX}} From fd724e30acf79856d508e7855bcc24c7c131d2c9 Mon Sep 17 00:00:00 2001 From: Zeph Grunschlag Date: Sun, 14 May 2023 01:13:21 -0500 Subject: [PATCH 21/22] make_just.py --- examples/Justfile | 6 +- examples/Justfile.tmpl | 480 +++++++++++++++++++++++++++++++++++++++++ examples/make_just.py | 45 ++++ 3 files changed, 528 insertions(+), 3 deletions(-) create mode 100644 examples/Justfile.tmpl create mode 100644 examples/make_just.py diff --git a/examples/Justfile b/examples/Justfile index 87fc1abd..6f2e04fb 100644 --- a/examples/Justfile +++ b/examples/Justfile @@ -2,12 +2,9 @@ set export set shell := ["zsh", "-cu"] NETWORKS := `echo $HOME` + "/networks" -# NAME := "indexerboxes" NAME := "niftynetwork" CURR_NETWORK := NETWORKS + "/" + NAME -# GO_ALGORAND := "../../.." GO_ALGORAND := "/Users/zeph/github/algorand/go-algorand" -# NODE_TEMPLATE := GO_ALGORAND + "/test/testdata/nettemplates/OneNodeFuture.json" NODE_TEMPLATE := GO_ALGORAND + "/test/testdata/nettemplates/TwoNodesFollower100Second.json" PRIVATE_DATA_NODE := "Primary" DATA_NODE := env_var_or_default("DATA_NODE", PRIVATE_DATA_NODE) @@ -58,6 +55,9 @@ algod ENDPOINT="/v2/status" VERB="GET": ALGORAND_PID=$(cat ${ALGORAND_PID_PATH}) curl -X ${VERB} "http://${ALGORAND_ALGOD}${ENDPOINT}" -H "Authorization: Bearer ${ALGORAND_TOKEN}" +# pass thru goal command but with the $ALGORAND_DATA set +goal *ARGS: + goal {{ARGS}} # --- GENERATOR SCRIPT COMMANDS --- # diff --git a/examples/Justfile.tmpl b/examples/Justfile.tmpl new file mode 100644 index 00000000..a8fd7a01 --- /dev/null +++ b/examples/Justfile.tmpl @@ -0,0 +1,480 @@ +set export +set shell := ["zsh", "-cu"] + +NETWORKS := $${NETWORKS} # parent directory of all networks +NAME := "$${NAME}" +CURR_NETWORK := NETWORKS + "/" + NAME +GO_ALGORAND := "$${GO_ALGORAND}" +NODE_TEMPLATE := GO_ALGORAND + "/test/testdata/nettemplates/$${NODE_TEMPLATE}" +PRIVATE_DATA_NODE := "$${PRIVATE_DATA_NODE}" +DATA_NODE := env_var_or_default("DATA_NODE", PRIVATE_DATA_NODE) +IS_PUBLIC_TRUTHY := env_var_or_default("DATA_NODE", "") +ALGORAND_DATA := CURR_NETWORK + "/" + DATA_NODE +ALGORAND_TOKEN_PATH := ALGORAND_DATA + "/algod.token" +ALGORAND_ADTOKEN_PATH := ALGORAND_DATA + "/algod.admin.token" +ALGORAND_ALGOD_PATH := ALGORAND_DATA + "/algod.net" +ALGORAND_PID_PATH := ALGORAND_DATA + "/algod.pid" + +# These fake pre-set tokens make it easier to test against a local network +PRESET_ALGOD_TOKEN := "16b29a0a2bbcc535f1e9e40f0c0888013f3789bf2bd34e7907c8fb1ae9d16024" +PRESET_ALGOD_ADMIN := "20064faacad1e590e757ac9492506c2d948633d7c458651b16a3991d26997695" + +# Older: +BOXES_TEAL := "boxes.teal" + +# --- SUMMARY --- # + +# list all available commands +default: + just --list + +# echo all variables +@echo: + echo NETWORKS: $NETWORKS + echo NAME: $NAME + echo CURR_NETWORK: $CURR_NETWORK + echo GO_ALGORAND: $GO_ALGORAND + echo NODE_TEMPLATE: $NODE_TEMPLATE + echo PRIVATE_DATA_NODE: $PRIVATE_DATA_NODE + echo DATA_NODE: $DATA_NODE + echo IS_PUBLIC_TRUTHY: $IS_PUBLIC_TRUTHY + echo ALGORAND_DATA: $ALGORAND_DATA + echo ALGORAND_TOKEN_PATH: $ALGORAND_TOKEN_PATH + echo ALGORAND_ALGOD_PATH: $ALGORAND_ALGOD_PATH + echo ALGORAND_PID_PATH: $ALGORAND_PID_PATH + + echo BOXES_TEAL: $BOXES_TEAL + +# --- algod curl --- # +algod ENDPOINT="/v2/status" VERB="GET": + #! /usr/bin/env bash + # set -euxo pipefail + set -euo pipefail + ALGORAND_TOKEN=$(cat ${ALGORAND_TOKEN_PATH}) + ALGORAND_ALGOD=$(cat ${ALGORAND_ALGOD_PATH}) + ALGORAND_PID=$(cat ${ALGORAND_PID_PATH}) + curl -X ${VERB} "http://${ALGORAND_ALGOD}${ENDPOINT}" -H "Authorization: Bearer ${ALGORAND_TOKEN}" + +# pass thru goal command but with the $ALGORAND_DATA set +goal *ARGS: + goal {{ARGS}} + +# --- GENERATOR SCRIPT COMMANDS --- # + +# generate an arbitrary number of app and box scenarios, each with up to BOXES_PER_APP boxes +gen-mult-app-boxes NUM_APPS="10" BOXES_PER_APP="2048": + #!/usr/bin/env python3 + import subprocess + from subprocess import CalledProcessError + + num_apps = int({{NUM_APPS}}) + print(f"{num_apps=}") + for i in range(num_apps): + print("\n", "\n", "\n", f"gen-app-and-box-scenarios #{i+1}" ) + subprocess.run(["just", "gen-app-and-box-scenarios", "{{BOXES_PER_APP}}"]).check_returncode() + + +# create an app and add up to BOXES_PER_APP random boxes to it in a multi-threaded fashion +gen-app-and-box-scenarios BOXES_PER_APP="10": + #!/usr/bin/env python3 + from concurrent.futures import ThreadPoolExecutor + import json + import logging + import random + import string + import subprocess + from subprocess import CalledProcessError + import time + + CHARS = string.digits + string.ascii_letters + VAL_SIZE = 24 + BOXES_PER_APP = int({{BOXES_PER_APP}}) + NLS = "\n" * 3 + + subprocess.run(["just", "app-create_fund"]).check_returncode() + + def worker(thread_number): + logging.info(f"HELLO from {thread_number}!") + + create_cpe = set_cpe = test_cpe = del_cpe = None + + rand_key_size = random.randint(4, 64) + rand_key = "".join(random.choice(CHARS) for _ in range(rand_key_size)) + print(f"{NLS}{thread_number}: {rand_key=}") + try: + subprocess.run(["just", "box-create", rand_key]).check_returncode() + except CalledProcessError as cpe: + create_cpe = str(cpe) + + rand_val = "".join(random.choice(CHARS) for _ in range(VAL_SIZE)) + print(f"{NLS}{thread_number}: {rand_val=}") + try: + subprocess.run(["just", "box-set", rand_key, rand_val]).check_returncode() + except CalledProcessError as cpe: + set_cpe = str(cpe) + + print(f"{NLS}{thread_number}: checking {rand_val=}") + try: + subprocess.run(["just", "box-test", rand_key, rand_val]).check_returncode() + except CalledProcessError as cpe: + test_cpe = str(cpe) + + delete = random.choice([True, False]) + if delete: + print(f"{NLS}{thread_number}: deleting") + try: + subprocess.run(["just", "box-delete", rand_key]).check_returncode() + except CalledProcessError as cpe: + del_cpe = str(cpe) + + return { + "thread_number": thread_number, + "key_size": rand_key_size, + "key": rand_key, + "val": rand_val, + "deleted": delete, + "called_process_errors": { + "create_cpe": create_cpe, + "set_cpe": set_cpe, + "test_cpe": test_cpe, + "del_cpe": del_cpe, + }, + } + + format = "%(asctime)s: %(message)s" + logging.basicConfig(format=format, level=logging.INFO, + datefmt="%H:%M:%S") + + results = [] + with ThreadPoolExecutor() as executor: + for r in executor.map(worker, range(BOXES_PER_APP)): + results.append(r) + + print(json.dumps(results, indent=2)) + + for result in results: + for err in result["called_process_errors"].values(): + if err: + raise err + +# --- HIGHER LEVEL --- # + +# create and then start (error if already created) +@create_and_start: create start status + sleep 5 + just status + +# create an app and then fund it +@app-create_fund: app-create last-app-fund + + +# --- BOX PUT: HIGHER LEVEL --- # + +# create box[BOX] for last app with provided key variable BOX +@box-create $BOX: + just app-call-last '\"create\", \"{{BOX}}\"' + +# set box[BOX]=VAL for last app with key BOX and val VAL +@box-set $BOX $VAL: + just app-call-last '\"set\", \"{{BOX}}\", \"{{VAL}}\"' + +# set box[BOX]=VAL for last app with key BOX and val VAL +@box-test $BOX $VAL: + just app-call-last '\"check\", \"{{BOX}}\", \"{{VAL}}\"' + +# delete box[BOX] for last app +@box-delete $BOX: + just app-call-last '\"delete\", \"{{BOX}}\"' + +# stop and tear down the node network. WARNING: YOU WILL LOSE ALL YOUR NODE DATA FROM THE FILE SYSTEM. +@stop_and_nuke: stop nuke + +# --- PRE-REQUISITES --- # + +# calculate an app's address using the python SDK +app-address *ARGS: + #!/usr/bin/env python3 + from algosdk import logic + print(logic.get_application_address({{ ARGS }})) + +# --- NETWORKS / NODES --- # + +# Private vs. Public Networks. Typical workflow: +# 1. Create a directory for your network (CURR_NETWORK = NETWORKS/NAME) +# 2. Populate the node information under CURR_NETWORK. Branch on Public vs. Private. +# In either case the the data directory is ALGORAND_DATA == CURR_NETWORK/DATA_NODE == NETWORKS/NAME/DATA_NODE. +# a. Public: Use algocfg to configure a single node: see `just pub-create` +# b. Private: Use `goal` to configure a network nodes under CURR_NETWORK. See `just create` +# 3. Start the network: `just start` +# +# NOTE: To run a public network commands, you need to supply the env var `DATA_NODE`. EG: +# DATA_NODE=Follower just pub-validate-datadir +# Or, export and run: +# export DATA_NODE=Follower +# just pub-validate-datadir + + +# create a private network with one node (error if already created) +create: + mkdir -p $NETWORKS + goal network create -n $NAME -r $CURR_NETWORK -t $NODE_TEMPLATE + +# print out the current network's data directory tree +@tree: + tree $CURR_NETWORK + +# start a the network (error if already running or not created) +@start: + goal node start + + +# PUBLIC NETWORKS BEGIN + +# check that is ready to connect to public network +pub-validate-datadir: + #! /usr/bin/env bash + set -euxo pipefail + [ -z "$IS_PUBLIC_TRUTHY" ] && { echo "Error: DATA_NODE env var required for public network" ; exit 1; } + echo "Ready for public network with node datadir: $ALGORAND_DATA" + +# list available profiles for configuring a network +@pub-cfg-list: + algocfg profile list + +# show the current network configuration +@pub-cfg-show: + echo "cat ${ALGORAND_DATA}/config.json" + cat ${ALGORAND_DATA}/config.json + +# prepare for connecting to public network +pub-prepare: pub-validate-datadir + mkdir -p $ALGORAND_DATA + +# configure a Conduit's network using `algocfg` +pub-create NODE_PROFILE="conduit" NETWORK="testnet" ENDPOINT="127.0.0.1:56765" PT="1" PAT="1": pub-prepare + algocfg profile set {{NODE_PROFILE}} -d $ALGORAND_DATA + [ -n {{ENDPOINT}} ] && echo "setting ENDPOINT={{ENDPOINT}}" && algocfg set -p EndpointAddress -v {{ENDPOINT}} + [ {{PT}} = "1" ] && echo "setting ALGOD TOKEN=${PRESET_ALGOD_TOKEN}" && echo ${PRESET_ALGOD_TOKEN} > ${ALGORAND_TOKEN_PATH} + [ {{PAT}} = "1" ] && echo "setting ALGOD ADMIN TOKEN=${PRESET_ALGOD_ADMIN}" && echo ${PRESET_ALGOD_ADMIN} > ${ALGORAND_ADTOKEN_PATH} + cp ${GO_ALGORAND}/installer/genesis/{{NETWORK}}/genesis.json $ALGORAND_DATA + +# status of network node +@status: + goal node status && echo "RUNNING" || echo "NOT RUNNING" + +# stop the running node (error if not running) +@stop: + goal node stop + +# remove the node's data from the file system +@nuke: + echo "deleting $CURR_NETWORK" + rm -rf $CURR_NETWORK + +# --- ACCOUNTS --- # + +# list all associated accounts +@list: + goal account list + +# create a new account without renaming it to a human friendly local alias +@raw-new-account: + goal account new | awk '{print $NF}' + +# echo an account's alias +@account-alias $ACCOUNT: + just list | grep {{ACCOUNT}} | awk '{print $2}' + +# create a new locally aliased account +@new-account $ALIAS $ACCOUNT=`just raw-new-account`: + goal account rename `just account-alias {{ACCOUNT}}` {{ALIAS}} + +# create a new multisig account with threshold 1 using provided accounts (cannot handle aliases) +@raw-msig-account *ACCOUNTS: + goal account multisig new -T 1 {{ACCOUNTS}} | awk '{print $NF}' + +# create a new multisig account with given ALIAS and threshold 1 using provided accounts (cannot handle aliases) +@new-msig-account $ALIAS *ACCOUNTS: + goal account rename `just account-alias $(just raw-msig-account {{ACCOUNTS}})` {{ALIAS}} + +# funding account's address +@funder: + just list | awk '{print $2}' + +# provide information about a given account +@info $ACCOUNT=`just funder`: + goal account info --address {{ACCOUNT}} + +# provide an account's balance +@balance $ACCOUNT=`just funder`: + goal account balance --address {{ACCOUNT}} + +# funder's most recently created app-id +@last-app-id: + just info | grep ID | tail -n 1 | cut -d "," -f1 | awk '{print $2}' + +# the account address of the funders most recently created app-id +@last-app-address: + just app-address `just last-app-id` + +# --- ASSETS --- # + +# create a dummy asset for the provided FUNDER. Copy pasta from: https://dappradar.com/blog/algorand-dapp-development-2-standard-asset-management +@asset-create $FUNDER=`just funder`: + goal asset create --creator {{FUNDER}} --total 1000000 --unitname bUSD --name "Balgorand USD" --asseturl "https://b-usd.com" --decimals 9 + +# --- APPLICATIONS --- # + +# information about an application of given id +@app-info $APP_ID=`just last-app-id`: + goal app info --app-id {{APP_ID}} + +# print out the boxes teal program +@boxes_teal: + cat $BOXES_TEAL + +# shortcut for the approval and clear program `goal app create` params +@programs: + echo "--approval-prog $BOXES_TEAL --clear-prog clear.teal" + +# shortcut for the storage params of `goal app create` +@app-vars $GBS="0" $GI="0" $LBS="0" $LI="0": + echo "--global-byteslices $GBS --global-ints $GI --local-byteslices $LBS --local-ints $LI" + +# shortcut for creating the arguments of an app call +box-app-args *ARGS: + #!/usr/bin/env python3 + args = [] + box_arg = "" + i = 0 + for arg in {{ARGS}}: + try: + int(arg) + arg = f"int:{arg}" + except Exception: + arg = f"str:{arg}" + args.extend(["--app-arg", arg]) + if i == 1: + box_arg = f"--box {arg}" + i += 1 + if box_arg: + args = [box_arg] + args + print(*args) + +# create an app funded by funder account +@app-create $GBS="0" $GI="0" $LBS="0" $LI="0": + echo "goal app create --creator `just funder` `just programs` `just app-vars $GBS $GI $LBS $LI`" + goal app create --creator `just funder` `just programs` `just app-vars $GBS $GI $LBS $LI` + +# call the last app from the funder address using ARGS +@app-call-last *ARGS='\"create\", \"mybox\"': + (set -x; goal app call --app-id `just last-app-id` --from `just funder` `just box-app-args {{ARGS}}`) + +# --- BOX INFO --- # + +# get all the boxes associated a given app-id +app-box-list $APP_ID=`just last-app-id`: + goal app box list --app-id {{APP_ID}} --max 0 + +# get box information for a given app-id and box name +app-box-info $APP_ID=`just last-app-id` $BOX="str:mybox": + goal app box info --app-id {{APP_ID}} --name {{BOX}} + + +# --- CLERK --- # + +# send from one account to another a given amount +@send $FROM $TO $AMOUNT: + goal clerk send --from {{FROM}} --to {{TO}} --amount {{AMOUNT}} + +# fund the most recently created app +@last-app-fund $AMOUNT=`echo 133713371337`: + just send `just funder` `just last-app-address` {{AMOUNT}} + +# --- CONSENSUS PARAMS --- # + +# list all consensus param declarations +@consensus-params-list: + cat ${GO_ALGORAND}/config/consensus.go | egrep " (bool|byte|int$|uint|map\[|Duration|PaysetCommitType)" | egrep -v "type|=|func|,|//" + +# print out the value history of a consensus param +@consensus-param $CP="MaximumMinimumBalance": + cat ${GO_ALGORAND}/config/consensus.go | grep {{CP}} || echo "{{CP}} not found in consensus.go" + +# consensus params for program size +consensus-prog-size: + just consensus-param LogicSigMaxSize + just consensus-param MaxAppProgramLen + +# consensus param for LogicSigMaxCost +consensus-prog-cost: + just consensus-param MaxCost + just consensus-param MaxAppProgramCost + +# consensus params for program pages +@consensus-prog-pages: + just consensus-param MaxExtraAppProgramPages + +# consensus params for foreign refs: +consensus-foreign-refs: + just consensus-param MaxAppTxnAccounts + just consensus-param MaxAppTxnForeignApps + just consensus-param MaxAppTxnForeignAssets + just consensus-param MaxAppTotalTxnReferences + just consensus-param MaxAppBoxReferences + +# consensus params for local/global storage: +consensus-storage: + just consensus-param MaxAppKeyLen + just consensus-param MaxAppBytesValueLen + just consensus-param MaxAppSumKeyValueLens + just consensus-param MaxLocalSchemaEntries + just consensus-param MaxGlobalSchemaEntries + +# consensus params for min-balance calc: +consensus-minbal: + just consensus-param SchemaMinBalancePerEntry + just consensus-param SchemaUintMinBalance + just consensus-param SchemaBytesMinBalance + just consensus-param BoxFlatMinBalance + just consensus-param BoxByteMinBalance + +# consensus params for boxes: +consensus-boxes: + just consensus-param MaxAppKeyLen + just consensus-param MaxBoxSize + just consensus-param BoxFlatMinBalance + just consensus-param BoxByteMinBalance + just consensus-param MaxAppBoxReferences + just consensus-param BytesPerBoxReference + +# --- MISCELLANEOUS --- # + +# print out the network's algod & kmd token and network/process info +client-info: + #! /usr/bin/env bash + set -euo pipefail + ALGORAND_TOKEN=$(cat ${ALGORAND_TOKEN_PATH}) + ALGORAND_ADTOKEN=$(cat ${ALGORAND_ADTOKEN_PATH}) + ALGORAND_ALGOD=$(cat ${ALGORAND_ALGOD_PATH}) + ALGORAND_PID=$(cat ${ALGORAND_PID_PATH}) + echo "algod.token: ${ALGORAND_TOKEN}" + echo "algod.admin.token: ${ALGORAND_ADTOKEN}" + echo "algod.net: ${ALGORAND_ALGOD}" + echo "algod.pid: ${ALGORAND_PID}" + + if [[ -f "${ALGORAND_DATA}/kmd-v0.5/kmd.token" ]]; then + KMD=$(cat "${ALGORAND_DATA}/kmd-v0.5/kmd.token") + else + KMD="" + fi + echo "kmd.token: ${KMD}" + if [[ -n $KMD ]]; then + echo "kmd.net---->" + cat ${ALGORAND_DATA}/kmd-v0.5/kmd.log | grep 127.0.0.1 | head -n 1 | cut -d '"' -f4 + fi + +# print out broadcastQueueBulk's channel size ... the default is 100 which is too small for the example +@broadcast-queue-size: + echo "default is 100. What is it actually in network/wsNetwork.go ?" + cat {{GO_ALGORAND}}/network/wsNetwork.go | grep "wn.broadcastQueueBulk = make(chan broadcastRequest" | cut -d "," -f2 | cut -d ")" -f1 | awk '{print $1}' \ No newline at end of file diff --git a/examples/make_just.py b/examples/make_just.py new file mode 100644 index 00000000..14678873 --- /dev/null +++ b/examples/make_just.py @@ -0,0 +1,45 @@ +import argparse +from string import Template + +# EG: +# +# ❯ python make_just.py --name blue-whale + + +class DblDollars(Template): + delimiter = '$$' # Use $$ as the delimiter + +# Define the substitutions +substitutions = { + "NAME": "niftynetwork", + "NETWORKS": '`echo $HOME` + "/networks"', + "GO_ALGORAND": "/Users/zeph/github/algorand/go-algorand", + "NODE_TEMPLATE": "OneNodeFuture.json", + "PRIVATE_DATA_NODE": "Primary", +} + +# Parse command-line arguments +parser = argparse.ArgumentParser() +parser.add_argument("--name") +parser.add_argument("--networks") +parser.add_argument("--go-algorand") +parser.add_argument("--node-template") +parser.add_argument("--private-data-node") + + +args = parser.parse_args() + +# Update the substitutions with the arguments +substitutions.update({ + key: vars(args)[k] for key in substitutions if vars(args)[(k := key.lower())] is not None +}) + +# Open the template and substitute the placeholders with actual values +with open('Justfile.tmpl', 'r') as f: + template = DblDollars(f.read()) + result = template.substitute(substitutions) + +# Write the result to the output file +with open('Justfile2', 'w') as f: + f.write(result) + From 41c341c8a3af9e2d6204ecc15a21f99035d88716 Mon Sep 17 00:00:00 2001 From: Zeph Grunschlag Date: Sun, 13 Aug 2023 15:00:03 -0500 Subject: [PATCH 22/22] wip --- examples/make_just.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/examples/make_just.py b/examples/make_just.py index 14678873..c62b6acf 100644 --- a/examples/make_just.py +++ b/examples/make_just.py @@ -20,6 +20,7 @@ class DblDollars(Template): # Parse command-line arguments parser = argparse.ArgumentParser() +parser.add_argument("--just-out-file", default="Justfile.tmp") parser.add_argument("--name") parser.add_argument("--networks") parser.add_argument("--go-algorand") @@ -28,10 +29,12 @@ class DblDollars(Template): args = parser.parse_args() +templ_args = vars(args) +file_out = templ_args.pop("just_out_file") # Update the substitutions with the arguments substitutions.update({ - key: vars(args)[k] for key in substitutions if vars(args)[(k := key.lower())] is not None + key: templ_args[k] for key in substitutions if templ_args[(k := key.lower())] is not None }) # Open the template and substitute the placeholders with actual values @@ -40,6 +43,6 @@ class DblDollars(Template): result = template.substitute(substitutions) # Write the result to the output file -with open('Justfile2', 'w') as f: +with open(file_out, 'w') as f: f.write(result)