diff --git a/.markdownlint.yml b/.markdownlint.yml index bb8ca9d84a..dbf917c50a 100644 --- a/.markdownlint.yml +++ b/.markdownlint.yml @@ -5,6 +5,7 @@ MD013: false # Line length (Material MkDocs handles this) MD033: false # Allow inline HTML (needed for admonitions) MD041: false # First line in file should be a top level header +MD060: false # Table column style (existing tables use various styles) # Configure list formatting rules (these would have caught our issues!) MD004: diff --git a/daemon/daemon_test.go b/daemon/daemon_test.go index 8f9fa26aab..27e008f57b 100644 --- a/daemon/daemon_test.go +++ b/daemon/daemon_test.go @@ -232,7 +232,7 @@ func TestDaemon_Start_AllServices(t *testing.T) { // Configure settings - this will now pick up KAFKA_PORT and persister URLs from gocore.Config appSettings := settings.NewSettings() - appSettings.LocalTestStartFromState = "RUNNING" + appSettings.LocalTestStartFromState = "LAUNCHING" appSettings.P2P.Port = p2pPort appSettings.Asset.HTTPPort = assetPort appSettings.Asset.HTTPListenAddress = fmt.Sprintf(":%d", assetPort) diff --git a/daemon/test_daemon.go b/daemon/test_daemon.go index 679431b400..423ab69631 100644 --- a/daemon/test_daemon.go +++ b/daemon/test_daemon.go @@ -378,7 +378,7 @@ func NewTestDaemon(t *testing.T, opts TestOptions) *TestDaemon { require.NoError(t, err) appSettings.Asset.CentrifugeDisable = true appSettings.UtxoStore.DBTimeout = 500 * time.Second - appSettings.LocalTestStartFromState = "RUNNING" + appSettings.LocalTestStartFromState = "LAUNCHING" appSettings.SubtreeValidation.TxMetaCacheEnabled = false appSettings.ProfilerAddr = "" appSettings.RPC.CacheEnabled = false diff --git a/docs/howto/developersHowToTeranodeCLI.md b/docs/howto/developersHowToTeranodeCLI.md index 5ed5df4dc6..fc2fce2e3a 100644 --- a/docs/howto/developersHowToTeranodeCLI.md +++ b/docs/howto/developersHowToTeranodeCLI.md @@ -68,22 +68,27 @@ SETTINGS_CONTEXT=dev.[YOUR_CONTEXT] ./teranode-cli setfsmstate --fsmstate and use the FSM State controls to transition to **RUNNING** or **LEGACYSYNCING**. +Access the dashboard at and use the FSM State controls to **LAUNCH** the node or transition to **LEGACYSYNCING**. -**Option 2: Using teranode-cli** +##### Option 2: Using teranode-cli ```bash -# Transition to Run mode -docker exec -it blockchain teranode-cli setfsmstate --fsmstate running +# Launch the node (recommended - performs sync check before running) +docker exec -it blockchain teranode-cli setfsmstate --fsmstate launching -# Or transition to LegacySync mode +# Or transition to LegacySync mode (for syncing from legacy BSV nodes) docker exec -it blockchain teranode-cli setfsmstate --fsmstate legacysyncing ``` +> **Note:** The LAUNCHING state performs a sync check and will automatically transition to RUNNING (if synced) or CATCHINGBLOCKS (if behind peers). + You can verify the current state with: ```bash @@ -589,11 +591,11 @@ docker exec -it blockchain teranode-cli setfsmstate --fsmstate LEGACYSYNCING # Or via Admin Dashboard at http://localhost:8090/admin ``` -**Set FSM State to Running**: +**Launch the Node** (recommended from IDLE): ```bash -# Via teranode-cli -docker exec -it blockchain teranode-cli setfsmstate --fsmstate RUNNING +# Via teranode-cli (performs sync check before running) +docker exec -it blockchain teranode-cli setfsmstate --fsmstate LAUNCHING # Or via Admin Dashboard at http://localhost:8090/admin ``` diff --git a/docs/howto/miners/docker/minersHowToResetTeranode.md b/docs/howto/miners/docker/minersHowToResetTeranode.md index c0f9b670ba..2356adcbef 100644 --- a/docs/howto/miners/docker/minersHowToResetTeranode.md +++ b/docs/howto/miners/docker/minersHowToResetTeranode.md @@ -160,8 +160,8 @@ Teranode starts in IDLE state. You need to transition to the appropriate state: # Or via teranode-cli docker exec -it blockchain teranode-cli setfsmstate --fsmstate LEGACYSYNCING -# Or for direct operation: -docker exec -it blockchain teranode-cli setfsmstate --fsmstate RUNNING +# Or launch the node (performs sync check before running): +docker exec -it blockchain teranode-cli setfsmstate --fsmstate LAUNCHING ``` ### Monitor Synchronization diff --git a/docs/howto/miners/kubernetes/minersHowToInstallation.md b/docs/howto/miners/kubernetes/minersHowToInstallation.md index 66a54c512b..16cce8a327 100644 --- a/docs/howto/miners/kubernetes/minersHowToInstallation.md +++ b/docs/howto/miners/kubernetes/minersHowToInstallation.md @@ -208,12 +208,14 @@ By default, this configuration deploys Teranode to connect to the **teratestnet* #### Start Syncing Process -A fresh Teranode starts up in IDLE state by default. To start syncing from the network, you can run: +A fresh Teranode starts up in IDLE state by default. To launch the node and start syncing from the network, run: ```bash -kubectl exec -it $(kubectl get pods -n teranode-operator -l app=blockchain -o jsonpath='{.items[0].metadata.name}') -n teranode-operator -- teranode-cli setfsmstate -fsmstate running +kubectl exec -it $(kubectl get pods -n teranode-operator -l app=blockchain -o jsonpath='{.items[0].metadata.name}') -n teranode-operator -- teranode-cli setfsmstate -fsmstate launching ``` +> **Note:** The LAUNCHING state performs a sync check and will automatically transition to RUNNING (if synced) or CATCHINGBLOCKS (if behind peers). + To know more about the syncing process, please refer to the [Teranode Sync Guide](minersHowToSyncTheNode.md) ## Verifying the Deployment diff --git a/docs/howto/miners/kubernetes/minersHowToInstallationDeprecated.md b/docs/howto/miners/kubernetes/minersHowToInstallationDeprecated.md index 34a7e1599f..ebf7cc16fe 100644 --- a/docs/howto/miners/kubernetes/minersHowToInstallationDeprecated.md +++ b/docs/howto/miners/kubernetes/minersHowToInstallationDeprecated.md @@ -2,7 +2,7 @@ Last modified: 29-January-2025 -# Index +## Index - [Introduction](#introduction) - [Prerequisites](#prerequisites) @@ -10,13 +10,13 @@ Last modified: 29-January-2025 - [Software Requirements](#software-requirements) - [Network Considerations](#network-considerations) - [Installation Process](#installation-process) - - [Teranode Initial Synchronization](#teranode-initial-synchronization) - - [Full P2P Download](#full-p2p-download) - - [Initial Data Set Installation](#initial-data-set-installation) - - [Teranode Installation - Introduction to the Kubernetes Operator](#teranode-installation-introduction-to-the-kubernetes-operator) - - [Installing Teranode with the Custom Kubernetes Operator](#installing-teranode-with-the-custom-kubernetes-operator) + - [Teranode Initial Synchronization](#teranode-initial-synchronization) + - [Full P2P Download](#full-p2p-download) + - [Initial Data Set Installation](#initial-data-set-installation) + - [Teranode Installation - Introduction to the Kubernetes Operator](#teranode-installation---introduction-to-the-kubernetes-operator) + - [Installing Teranode with the Custom Kubernetes Operator](#installing-teranode-with-the-custom-kubernetes-operator) - [Optimizations](#optimizations) -- [Reference - Settings](#reference-settings) +- [Reference - Settings](#reference---settings) ## Introduction @@ -153,7 +153,7 @@ data: utxostore: 'aerospike://...' ``` -To review the list of settings you could configure in the ConfigMap, please refer to the list [here](https://github.com/bsv-blockchain/teranode/blob/main/settings.conf). +To review the list of settings you could configure in the ConfigMap, please refer to [the settings configuration file](https://github.com/bsv-blockchain/teranode/blob/main/settings.conf). #### Storage Requirements @@ -300,15 +300,17 @@ Standard Kubernetes logging and troubleshooting approaches apply. Users can use - The various Teranode services will be accessible through the configured ingress or service endpoints. - Refer to your specific ingress or network configuration for exact URLs and ports. -### Step 9: Change the node status to Run or LegacySync +### Step 9: Launch the Node or Enter LegacySync -1. **Force the node to transition to Run mode:** +1. **Launch the node (recommended - performs sync check before running):** ```bash - kubectl exec -it $(kubectl get pods -n teranode-operator -l app=blockchain -o jsonpath='{.items[0].metadata.name}') -n teranode-operator -- teranode-cli setfsmstate --fsmstate RUNNING + kubectl exec -it $(kubectl get pods -n teranode-operator -l app=blockchain -o jsonpath='{.items[0].metadata.name}') -n teranode-operator -- teranode-cli setfsmstate --fsmstate LAUNCHING ``` -2. **Or LegacySync mode:** + > **Note:** The LAUNCHING state performs a sync check and will automatically transition to RUNNING (if synced) or CATCHINGBLOCKS (if behind peers). + +2. **Or LegacySync mode (for syncing from legacy BSV nodes):** ```bash kubectl exec -it $(kubectl get pods -n teranode-operator -l app=blockchain -o jsonpath='{.items[0].metadata.name}') -n teranode-operator -- teranode-cli setfsmstate --fsmstate LEGACYSYNCING @@ -371,4 +373,4 @@ If you have local access to SV Nodes, you can use them to speed up the initial b ## Reference - Settings -You can find the pre-configured settings file [here](https://github.com/bsv-blockchain/teranode/blob/main/settings.conf). You can refer to this document in order to identify the current system behaviour and in order to override desired settings in your `settings_local.conf`. +You can find the [pre-configured settings file](https://github.com/bsv-blockchain/teranode/blob/main/settings.conf) in the repository. You can refer to this document in order to identify the current system behaviour and in order to override desired settings in your `settings_local.conf`. diff --git a/docs/howto/miners/kubernetes/minersHowToStopStartKubernetesTeranode.md b/docs/howto/miners/kubernetes/minersHowToStopStartKubernetesTeranode.md index e5a28543a1..489902f033 100644 --- a/docs/howto/miners/kubernetes/minersHowToStopStartKubernetesTeranode.md +++ b/docs/howto/miners/kubernetes/minersHowToStopStartKubernetesTeranode.md @@ -41,12 +41,14 @@ kubectl wait --for=condition=ready pod -l app=blockchain -n teranode-operator -- kubectl logs -n teranode-operator -l app=blockchain -f ``` -### 3. Start Syncing (if needed) +### 3. Launch the Node (if needed) ```bash -kubectl exec -it $(kubectl get pods -n teranode-operator -l app=blockchain -o jsonpath='{.items[0].metadata.name}') -n teranode-operator -- teranode-cli setfsmstate -fsmstate running +kubectl exec -it $(kubectl get pods -n teranode-operator -l app=blockchain -o jsonpath='{.items[0].metadata.name}') -n teranode-operator -- teranode-cli setfsmstate -fsmstate launching ``` +> **Note:** The LAUNCHING state performs a sync check and will automatically transition to RUNNING (if synced) or CATCHINGBLOCKS (if behind peers). + ## Stopping Teranode ### 1. Graceful Shutdown diff --git a/docs/howto/miners/minersHowToInteractWithFSM.md b/docs/howto/miners/minersHowToInteractWithFSM.md index a77f85b413..3ea3dea492 100644 --- a/docs/howto/miners/minersHowToInteractWithFSM.md +++ b/docs/howto/miners/minersHowToInteractWithFSM.md @@ -48,6 +48,7 @@ The `teranode-cli` is recommended for scripting and automation. It provides a co ### Docker Compose Environment #### 1. Check Current State + ```bash docker exec -it blockchain teranode-cli getfsmstate ``` @@ -55,7 +56,8 @@ docker exec -it blockchain teranode-cli getfsmstate #### 2. Set New State ```bash -docker exec -it blockchain teranode-cli setfsmstate --fsmstate RUNNING +# Launch the node (recommended from IDLE - performs sync check) +docker exec -it blockchain teranode-cli setfsmstate --fsmstate LAUNCHING ``` ### Kubernetes Environment @@ -78,18 +80,31 @@ kubectl exec -it $(kubectl get pods -n teranode-operator -l app=blockchain -o js #### 2. Set New State ```bash -# Change state to RUNNING -kubectl exec -it $(kubectl get pods -n teranode-operator -l app=blockchain -o jsonpath='{.items[0].metadata.name}') -n teranode-operator -- teranode-cli setfsmstate --fsmstate RUNNING +# Launch the node (recommended from IDLE - performs sync check) +kubectl exec -it $(kubectl get pods -n teranode-operator -l app=blockchain -o jsonpath='{.items[0].metadata.name}') -n teranode-operator -- teranode-cli setfsmstate --fsmstate LAUNCHING ``` ## Valid FSM States The following states are valid for all environments: -- IDLE -- RUNNING -- LEGACYSYNCING -- CATCHINGBLOCKS +- **IDLE** - Node is stopped and not processing +- **LAUNCHING** - Node is performing initial sync check (auto-transitions to RUNNING or CATCHINGBLOCKS) +- **RUNNING** - Node is fully operational +- **CATCHINGBLOCKS** - Node is catching up with the network +- **LEGACYSYNCING** - Node is syncing using legacy Bitcoin protocol + +## State Transitions + +**Starting the node (from IDLE):** + +- Use `LAUNCH` event to transition IDLE → LAUNCHING +- The node will automatically transition to RUNNING (if synced) or CATCHINGBLOCKS (if behind peers) +- **Note:** The `RUN` event is NOT valid from IDLE state. You must use `LAUNCH`. + +**Legacy sync mode:** + +- Use `LEGACYSYNC` event to transition IDLE → LEGACYSYNCING (bypasses LAUNCHING state) ## Validation @@ -118,16 +133,20 @@ grpcurl -plaintext blockchain:18087 blockchain_api.BlockchainAPI.GetFSMCurrentSt **Trigger State Transitions:** ```bash -# Transition to RUNNING state +# Start the node (IDLE -> LAUNCHING -> RUNNING/CATCHINGBLOCKS) +# This is the recommended way to start the node +grpcurl -plaintext blockchain:18087 blockchain_api.BlockchainAPI.Launch + +# Transition to RUNNING state (only valid from LAUNCHING, LEGACYSYNCING, or CATCHINGBLOCKS) grpcurl -plaintext blockchain:18087 blockchain_api.BlockchainAPI.Run -# Transition to LEGACYSYNCING state +# Transition to LEGACYSYNCING state (from IDLE) grpcurl -plaintext blockchain:18087 blockchain_api.BlockchainAPI.LegacySync -# Transition to CATCHINGBLOCKS state +# Transition to CATCHINGBLOCKS state (from RUNNING or LAUNCHING) grpcurl -plaintext blockchain:18087 blockchain_api.BlockchainAPI.CatchUpBlocks -# Transition to IDLE state +# Transition to IDLE state (from RUNNING or LEGACYSYNCING) grpcurl -plaintext blockchain:18087 blockchain_api.BlockchainAPI.Idle ``` @@ -157,16 +176,20 @@ Expected output: **Trigger State Transitions:** ```bash -# Transition to RUNNING state +# Start the node (IDLE -> LAUNCHING -> RUNNING/CATCHINGBLOCKS) +# This is the recommended way to start the node +grpcurl -plaintext localhost:18087 blockchain_api.BlockchainAPI.Launch + +# Transition to RUNNING state (only valid from LAUNCHING, LEGACYSYNCING, or CATCHINGBLOCKS) grpcurl -plaintext localhost:18087 blockchain_api.BlockchainAPI.Run -# Transition to LEGACYSYNCING state +# Transition to LEGACYSYNCING state (from IDLE) grpcurl -plaintext localhost:18087 blockchain_api.BlockchainAPI.LegacySync -# Transition to CATCHINGBLOCKS state +# Transition to CATCHINGBLOCKS state (from RUNNING or LAUNCHING) grpcurl -plaintext localhost:18087 blockchain_api.BlockchainAPI.CatchUpBlocks -# Transition to IDLE state +# Transition to IDLE state (from RUNNING or LEGACYSYNCING) grpcurl -plaintext localhost:18087 blockchain_api.BlockchainAPI.Idle ``` diff --git a/docs/references/services/blockchain_reference.md b/docs/references/services/blockchain_reference.md index a701ea8efc..673e623cd1 100644 --- a/docs/references/services/blockchain_reference.md +++ b/docs/references/services/blockchain_reference.md @@ -532,13 +532,21 @@ func (b *Blockchain) SendFSMEvent(ctx context.Context, eventReq *blockchain_api. Sends an event to the finite state machine. +### Launch + +```go +func (b *Blockchain) Launch(ctx context.Context, _ *emptypb.Empty) (*emptypb.Empty, error) +``` + +Transitions the FSM from IDLE to the LAUNCHING state. This is the **recommended way to start the node** as it performs an initial sync check before processing transactions. The P2P sync coordinator will automatically transition the node to RUNNING (if synced) or CATCHINGBLOCKS (if behind peers). + ### Run ```go func (b *Blockchain) Run(ctx context.Context, _ *emptypb.Empty) (*emptypb.Empty, error) ``` -Transitions the FSM to the RUNNING state. +Transitions the FSM to the RUNNING state. **Note:** This is only valid from LAUNCHING, LEGACYSYNCING, or CATCHINGBLOCKS states - not from IDLE. Use Launch() to start the node from IDLE. ### CatchUpBlocks diff --git a/docs/state-machine.diagram.md b/docs/state-machine.diagram.md index 5f7f55c7b6..4a6dec3cd3 100644 --- a/docs/state-machine.diagram.md +++ b/docs/state-machine.diagram.md @@ -7,11 +7,21 @@ The mermaid diagram outlined below represents the various states and events that ```mermaid stateDiagram-v2 [*] --> IDLE - CATCHINGBLOCKS --> RUNNING: RUN + IDLE --> LAUNCHING: LAUNCH IDLE --> LEGACYSYNCING: LEGACYSYNC - IDLE --> RUNNING: RUN + LAUNCHING --> RUNNING: RUN + LAUNCHING --> CATCHINGBLOCKS: CATCHUPBLOCKS + CATCHINGBLOCKS --> RUNNING: RUN LEGACYSYNCING --> RUNNING: RUN LEGACYSYNCING --> IDLE: STOP RUNNING --> CATCHINGBLOCKS: CATCHUPBLOCKS RUNNING --> IDLE: STOP ``` + +## States + +- **IDLE**: Node is stopped and not processing. Operator must trigger LAUNCH to start. +- **LAUNCHING**: Node is performing initial sync check before processing. Auto-transitions to RUNNING (if synced) or CATCHINGBLOCKS (if behind peers). +- **RUNNING**: Node is fully operational and processing transactions/blocks. +- **CATCHINGBLOCKS**: Node is catching up with the network by downloading blocks from peers. +- **LEGACYSYNCING**: Node is syncing using the legacy Bitcoin protocol (for connecting to legacy nodes). diff --git a/docs/topics/architecture/stateManagement.md b/docs/topics/architecture/stateManagement.md index 24a2a00f12..8878760d8e 100644 --- a/docs/topics/architecture/stateManagement.md +++ b/docs/topics/architecture/stateManagement.md @@ -10,14 +10,16 @@ - [3.2.3. Access via gRPC](#323-access-via-grpc) - [3.3. State Machine States](#33-state-machine-states) - [3.3.1. FSM: Idle State](#331-fsm-idle-state) - - [3.3.2. FSM: Legacy Syncing State](#332-fsm-legacy-syncing-state) - - [3.3.3. FSM: Running State](#333-fsm-running-state) - - [3.3.4. FSM: Catching Blocks State](#334-fsm-catching-blocks-state) + - [3.3.2. FSM: Launching State](#332-fsm-launching-state) + - [3.3.3. FSM: Legacy Syncing State](#333-fsm-legacy-syncing-state) + - [3.3.4. FSM: Running State](#334-fsm-running-state) + - [3.3.5. FSM: Catching Blocks State](#335-fsm-catching-blocks-state) - [3.4. State Machine Events](#34-state-machine-events) - - [3.4.1. FSM Event: Legacy Sync](#341-fsm-event-legacy-sync) - - [3.4.2. FSM Event: Run](#342-fsm-event-run) - - [3.4.3. FSM Event: Catch up Blocks](#343-fsm-event-catch-up-blocks) - - [3.4.4. FSM Event: Stop](#344-fsm-event-stop) + - [3.4.1. FSM Event: Launch](#341-fsm-event-launch) + - [3.4.2. FSM Event: Legacy Sync](#342-fsm-event-legacy-sync) + - [3.4.3. FSM Event: Run](#343-fsm-event-run) + - [3.4.4. FSM Event: Catch up Blocks](#344-fsm-event-catch-up-blocks) + - [3.4.5. FSM Event: Stop](#345-fsm-event-stop) - [3.5. Waiting on State Machine Transitions](#35-waiting-on-state-machine-transitions) 4. [Other Resources](#4-other-resources) @@ -39,17 +41,19 @@ The Teranode blockchain service uses a Finite State Machine (FSM) to manage the The FSM has the following **states**: -- **Idle** -- **LegacySyncing** -- **Running** -- **CatchingBlocks** +- **Idle** - Node is stopped and not processing +- **Launching** - Node is performing initial sync check before starting operations +- **LegacySyncing** - Node is syncing using the legacy Bitcoin protocol +- **Running** - Node is fully operational and processing transactions/blocks +- **CatchingBlocks** - Node is catching up with the network by downloading blocks The FSM responds to the following **events**: -- **LegacySync** -- **Run** -- **CatchupBlocks** -- **Stop** +- **Launch** - Start the node with sync check (IDLE → LAUNCHING) +- **LegacySync** - Start legacy sync mode (IDLE → LEGACYSYNCING) +- **Run** - Transition to running state (from LAUNCHING, LEGACYSYNCING, or CATCHINGBLOCKS) +- **CatchupBlocks** - Start catching up blocks (from RUNNING or LAUNCHING) +- **Stop** - Stop the node (from RUNNING or LEGACYSYNCING) The diagram below represents the relationships between the states and events in the FSM (as defined in `services/blockchain/fsm.go`): @@ -57,11 +61,14 @@ The diagram below represents the relationships between the states and events in The FSM handles the following state **transitions**: +- **Launch**: Transitions to _Launching_ from _Idle_ (recommended way to start the node) - **LegacySync**: Transitions to _LegacySyncing_ from _Idle_ -- **Run**: Transitions to _Running_ from _Idle_, _LegacySyncing_ or _CatchingBlocks_ -- **CatchupBlocks**: Transitions to _CatchingBlocks_ from _Running_ +- **Run**: Transitions to _Running_ from _Launching_, _LegacySyncing_ or _CatchingBlocks_ (NOT from _Idle_) +- **CatchupBlocks**: Transitions to _CatchingBlocks_ from _Running_ or _Launching_ - **Stop**: Transitions to _Idle_ from _LegacySyncing_ or _Running_ +> **Important**: The `Run` event is NOT valid from the `Idle` state. To start the node, use the `Launch` event which will perform a sync check and automatically transition to either `Running` (if synced) or `CatchingBlocks` (if behind peers). + Teranode provides a visualizer tool to generate and visualize the state machine diagram. To run the visualizer, use the command `go run services/blockchain/fsm_visualizer/main.go`. The generated `docs/state-machine.diagram.md` can be visualized using . ## 3. Functionality @@ -102,6 +109,7 @@ The Blockchain service also exposes the following gRPC methods to interact with - **GetFSMCurrentState** - Returns the current state of the FSM - **SendFSMEvent** - Sends an event to the FSM to trigger a state transition +- **Launch** - Transitions the FSM to the Launching state (recommended way to start the node) - **LegacySync** - Transitions the FSM to the LegacySyncing state (delegates on the SendFSMEvent method) - **Run** - Transitions the FSM to the Running state (delegates on the SendFSMEvent method) - **CatchUpBlocks** - Transitions the FSM to the CatchingBlocks state (delegates on the SendFSMEvent method) @@ -115,7 +123,7 @@ The Blockchain service always starts in an `Idle` state. In this state: - No operations are permitted - All services are inactive - The node is not participating in the network in any way -- Must be manually triggered to transition to another state +- Must be manually triggered to transition to another state via the `Launch` event Allowed Operations in Idle State: @@ -130,13 +138,41 @@ Allowed Operations in Idle State: - ❌ Create subtrees (or propagate them) - ❌ Create blocks (mine candidates) -All services will wait for the FSM to transition to the `Running` state (either directly or after going through a `Legacy Sync` step) before starting their operations. As such, the node should see no activity until the FSM transitions to the `Running` state. +All services will wait for the FSM to transition to the `Running` state before starting their operations. As such, the node should see no activity until the FSM transitions to the `Running` state. The node can also return back to the `Idle` state from `Running` or `LegacySyncing`, however this can only be triggered by a manual / external request. -#### 3.3.2. FSM: Legacy Syncing State +> **Note**: To start the node from `Idle`, use the `Launch` event (not `Run`). The `Run` event is not valid from the `Idle` state. + +#### 3.3.2. FSM: Launching State + +The `Launching` state is a transitional state that performs an initial sync check before the node starts processing. This state ensures that: + +- The node checks its sync status with peers before processing transactions +- Subtree validation is blocked (preventing processing of future block transactions) +- The node automatically transitions to the appropriate state based on sync status + +Allowed Operations in Launching State: + +- ❌ Process external transactions +- ❌ Legacy relay transactions +- ❌ Queue subtrees +- ❌ Process subtrees +- ❌ Queue blocks +- ❌ Process blocks +- ❌ Relay blocks +- ❌ Speedy process blocks +- ❌ Create subtrees (or propagate them) +- ❌ Create blocks (mine candidates) + +The P2P sync coordinator monitors this state and automatically: + +- Transitions to `Running` if the node is synced with peers (or has no peers) +- Transitions to `CatchingBlocks` if the node is behind peers -When a node is starting up, it may need to perform a legacy sync. This is a full block sync performed against legacy BSV nodes. In this state: +#### 3.3.3. FSM: Legacy Syncing State + +When a node is starting up, it may need to perform a legacy sync (an alternative to the normal `Launch` path). This is a full block sync performed against legacy BSV nodes. In this state: Allowed Operations in Legacy Syncing State: @@ -151,7 +187,7 @@ Allowed Operations in Legacy Syncing State: - ❌ Create subtrees (or propagate them) - ❌ Create blocks (mine candidates) -#### 3.3.3. FSM: Running State +#### 3.3.4. FSM: Running State The `Running` state represents the node actively participating in the network. In this state: @@ -172,16 +208,16 @@ If `fsm_state_restore` setting is enabled, and if the node was previously in the If the node was previously in any other state, the Block Assembler would now start mining blocks. The Block Assembler will never mine blocks under any other node state. -#### 3.3.4. FSM: Catching Blocks State +#### 3.3.5. FSM: Catching Blocks State -The `CatchingBlocks` state represents the node catching up on blocks. This state is triggered by BlockValidation when the node needs to catch up with the network. In this state: +The `CatchingBlocks` state represents the node catching up on blocks. This state is triggered when the node needs to catch up with the network (either from LAUNCHING during startup sync check, or from RUNNING when falling behind). In this state: Allowed Operations in Catching Blocks State: - ✅ Process external transactions - ✅ Legacy relay transactions - ✅ Queue subtrees -- ✅ Process subtrees +- ❌ Process subtrees - ✅ Queue blocks - ✅ Process blocks - ✅ Relay blocks @@ -207,7 +243,20 @@ Key points about error handling: ### 3.4. State Machine Events -#### 3.4.1. FSM Event: Legacy Sync +#### 3.4.1. FSM Event: Launch + +The gRPC `Launch` method triggers the FSM to transition from `Idle` to the `Launching` state. This is the **recommended way to start the node** as it performs an initial sync check before processing transactions. + +When the `Launch` event is triggered: + +1. The FSM transitions to the `Launching` state +2. The P2P sync coordinator checks if the node is synced with peers +3. If synced (or no peers available), the node automatically transitions to `Running` +4. If behind peers, the node transitions to `CatchingBlocks` to catch up first + +This ensures that subtree validation doesn't process transactions for future blocks before the node is synced. + +#### 3.4.2. FSM Event: Legacy Sync The gRPC `LegacySync` method triggers the FSM to transition to the `LegacySyncing` state. This event is used to indicate that the node is syncing from legacy BSV nodes. @@ -215,19 +264,23 @@ The Legacy service triggers this event when the node is starting up and needs to ![fsm_legacy_symc.svg](img/plantuml/fsm_legacy_sync.svg) -#### 3.4.2. FSM Event: Run +#### 3.4.3. FSM Event: Run + +The gRPC `Run` method triggers the FSM to transition to the `Running` state. This event is only valid from `Launching`, `LegacySyncing`, or `CatchingBlocks` states - **NOT from `Idle`**. -The gRPC `Run` method triggers the FSM to transition to the `Running` state. This event is used to indicate that the node is ready to start participating in the network and processing transactions and blocks. +To start the node from `Idle`, use the `Launch` event instead. ![fsm_run.svg](img/plantuml/fsm_run.svg) -#### 3.4.3. FSM Event: Catch up Blocks +#### 3.4.4. FSM Event: Catch up Blocks The gRPC `CatchUpBlocks` method triggers the FSM to transition to the `CatchingBlocks` state. This event is used to indicate that the node is catching up on blocks and needs to process the latest blocks before resuming full operations. +This event is valid from both `Running` and `Launching` states. + ![fsm_catchup_blocks.svg](img/plantuml/fsm_catchup_blocks.svg) -#### 3.4.4. FSM Event: Stop +#### 3.4.5. FSM Event: Stop The gRPC `Idle` method sends a `Stop` event to the FSM, which triggers a transition to the `Idle` state. This event is used to stop the node from participating in the network and halt all operations. diff --git a/docs/topics/dashboard.md b/docs/topics/dashboard.md index 58ec4c4d5c..075e079c7f 100644 --- a/docs/topics/dashboard.md +++ b/docs/topics/dashboard.md @@ -248,16 +248,17 @@ The admin page (`/admin`) provides administrative operations. **This page requir **Current State Display:** -- Shows blockchain state: IDLE, RUNNING, CATCHING BLOCKS, LEGACY SYNCING, DISCONNECTED +- Shows blockchain state: IDLE, LAUNCHING, RUNNING, CATCHING BLOCKS, LEGACY SYNCING, DISCONNECTED **State Transitions:** | Event | Description | |-------|-------------| -| RUN | Start blockchain processing | +| LAUNCH | Start node with sync check (recommended from IDLE) | +| RUN | Transition to running state (from LAUNCHING, LEGACYSYNCING, or CATCHINGBLOCKS) | | STOP | Stop blockchain (transition to IDLE) | | CATCHUPBLOCKS | Enter block catchup mode | -| LEGACYSYNC | Enter legacy sync mode | +| LEGACYSYNC | Enter legacy sync mode (from IDLE) | - Dynamic button UI showing available events for current state - Custom event submission diff --git a/docs/tutorials/developers/developerSetup.md b/docs/tutorials/developers/developerSetup.md index 9c5c62b653..62370e1153 100644 --- a/docs/tutorials/developers/developerSetup.md +++ b/docs/tutorials/developers/developerSetup.md @@ -243,7 +243,7 @@ SETTINGS_CONTEXT=dev.[YOUR_CONTEXT] go run . If no errors are seen, you have successfully installed the project and are ready to start working on the project or running the node. -Note that the node is initialized in IDLE mode by default. You'll need to transition it to RUNNING mode to start processing transactions. +Note that the node is initialized in IDLE mode by default. You'll need to launch the node to start processing transactions. The recommended approach is to use the `LAUNCH` event, which performs a sync check before transitioning to RUNNING. ### 7.1. Executing the Teranode-CLI as a Developer @@ -265,8 +265,8 @@ Once built, you can run commands directly: # Get current FSM state SETTINGS_CONTEXT=dev.[YOUR_CONTEXT] ./teranode-cli getfsmstate -# Set FSM state to RUNNING -SETTINGS_CONTEXT=dev.[YOUR_CONTEXT] ./teranode-cli setfsmstate --fsmstate running +# Launch the node (recommended way to start from IDLE) +SETTINGS_CONTEXT=dev.[YOUR_CONTEXT] ./teranode-cli setfsmstate --fsmstate launching ``` #### Available Commands @@ -301,24 +301,33 @@ For general help and a list of available commands: The CLI will use your development settings as specified by your `SETTINGS_CONTEXT` environment variable. -#### Transitioning the Node to RUNNING Mode +#### Launching the Node -To transition the node from IDLE to RUNNING mode, use: +To start the node from IDLE mode, use the `LAUNCHING` state which performs a sync check before processing: ```bash # Get current FSM state SETTINGS_CONTEXT=dev ./teranode-cli getfsmstate -# Set FSM state to RUNNING -SETTINGS_CONTEXT=dev ./teranode-cli setfsmstate --fsmstate running +# Launch the node (recommended way to start from IDLE) +SETTINGS_CONTEXT=dev ./teranode-cli setfsmstate --fsmstate launching ``` +The node will: + +1. Transition to LAUNCHING state +2. Perform a sync check with peers (if P2P is enabled) +3. Automatically transition to RUNNING (if synced) or CATCHINGBLOCKS (if behind peers) + After executing these commands, your log should show a successful transition: ```bash -[Blockchain Client] FSM successfully transitioned from IDLE to state:RUNNING +[Blockchain Client] FSM successfully transitioned from IDLE to state:LAUNCHING +[Blockchain Client] FSM successfully transitioned from LAUNCHING to state:RUNNING ``` +> **Note:** In development mode with P2P disabled, the node will immediately transition from LAUNCHING to RUNNING. + ### 7.2. Debugging Teranode Teranode supports debugging using Delve, the Go debugger. You can use any IDE that supports Delve, including VS Code, GoLand, or the Delve CLI directly. diff --git a/docs/tutorials/miners/minersGettingStarted.md b/docs/tutorials/miners/minersGettingStarted.md index 721c747d6e..9778b76d7c 100644 --- a/docs/tutorials/miners/minersGettingStarted.md +++ b/docs/tutorials/miners/minersGettingStarted.md @@ -130,13 +130,15 @@ Force the node to transition to Run mode: #### Option 2: Using teranode-cli ```bash -# Transition to Run mode -docker exec -it blockchain teranode-cli setfsmstate --fsmstate RUNNING +# Launch the node (recommended - performs sync check before running) +docker exec -it blockchain teranode-cli setfsmstate --fsmstate LAUNCHING -# Or transition to LegacySync mode +# Or transition to LegacySync mode (for syncing from legacy BSV nodes) docker exec -it blockchain teranode-cli setfsmstate --fsmstate LEGACYSYNCING ``` +> **Note:** The LAUNCHING state performs a sync check and will automatically transition to RUNNING (if synced) or CATCHINGBLOCKS (if behind peers). + - Verify services are running: ```bash diff --git a/errors/error.pb.go b/errors/error.pb.go index a2d617462b..02ac479b1a 100644 --- a/errors/error.pb.go +++ b/errors/error.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11 -// protoc v6.33.2 +// protoc v6.33.4 // source: errors/error.proto package errors diff --git a/model/model.pb.go b/model/model.pb.go index 805cb8f2b6..3fbc316302 100644 --- a/model/model.pb.go +++ b/model/model.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11 -// protoc v6.33.2 +// protoc v6.33.4 // source: model/model.proto package model diff --git a/services/alert/alert_api/alert_api.pb.go b/services/alert/alert_api/alert_api.pb.go index daacd07741..3b5479bbed 100644 --- a/services/alert/alert_api/alert_api.pb.go +++ b/services/alert/alert_api/alert_api.pb.go @@ -3,7 +3,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11 -// protoc v6.33.2 +// protoc v6.33.4 // source: services/alert/alert_api/alert_api.proto package alert_api diff --git a/services/alert/alert_api/alert_api_grpc.pb.go b/services/alert/alert_api/alert_api_grpc.pb.go index 7342c1b10a..777a04f9ee 100644 --- a/services/alert/alert_api/alert_api_grpc.pb.go +++ b/services/alert/alert_api/alert_api_grpc.pb.go @@ -3,7 +3,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.6.0 -// - protoc v6.33.2 +// - protoc v6.33.4 // source: services/alert/alert_api/alert_api.proto package alert_api diff --git a/services/asset/asset_api/asset_api.pb.go b/services/asset/asset_api/asset_api.pb.go index 9f9aad3483..3f2ece0533 100644 --- a/services/asset/asset_api/asset_api.pb.go +++ b/services/asset/asset_api/asset_api.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11 -// protoc v6.33.2 +// protoc v6.33.4 // source: services/asset/asset_api/asset_api.proto package asset_api diff --git a/services/asset/httpimpl/fsm_handler.go b/services/asset/httpimpl/fsm_handler.go index f60d063e8d..498dac0862 100644 --- a/services/asset/httpimpl/fsm_handler.go +++ b/services/asset/httpimpl/fsm_handler.go @@ -75,9 +75,12 @@ func (h *FSMHandler) GetFSMEvents(c echo.Context) error { switch *state { case blockchain_api.FSMStateType_IDLE: events = []string{ - blockchain_api.FSMEventType_RUN.String(), + blockchain_api.FSMEventType_LAUNCH.String(), // Primary - safe startup with sync check blockchain_api.FSMEventType_LEGACYSYNC.String(), } + // NOTE: RUN is NOT available from IDLE - must use LAUNCH + case blockchain_api.FSMStateType_LAUNCHING: + events = []string{} // No manual events - auto-transitions only case blockchain_api.FSMStateType_RUNNING: events = []string{ blockchain_api.FSMEventType_STOP.String(), diff --git a/services/blockassembly/blockassembly_api/blockassembly_api.pb.go b/services/blockassembly/blockassembly_api/blockassembly_api.pb.go index f0c463b7ac..e1400578a9 100644 --- a/services/blockassembly/blockassembly_api/blockassembly_api.pb.go +++ b/services/blockassembly/blockassembly_api/blockassembly_api.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11 -// protoc v6.33.2 +// protoc v6.33.4 // source: services/blockassembly/blockassembly_api/blockassembly_api.proto package blockassembly_api diff --git a/services/blockassembly/blockassembly_api/blockassembly_api_grpc.pb.go b/services/blockassembly/blockassembly_api/blockassembly_api_grpc.pb.go index 5c36e329a0..8d69c136ba 100644 --- a/services/blockassembly/blockassembly_api/blockassembly_api_grpc.pb.go +++ b/services/blockassembly/blockassembly_api/blockassembly_api_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.6.0 -// - protoc v6.33.2 +// - protoc v6.33.4 // source: services/blockassembly/blockassembly_api/blockassembly_api.proto package blockassembly_api diff --git a/services/blockchain/Client.go b/services/blockchain/Client.go index 966d795d06..9ca9a1a9f8 100644 --- a/services/blockchain/Client.go +++ b/services/blockchain/Client.go @@ -76,11 +76,13 @@ const ( FSMStateRUNNING = blockchain_api.FSMStateType_RUNNING FSMStateCATCHINGBLOCKS = blockchain_api.FSMStateType_CATCHINGBLOCKS FSMStateLEGACYSYNCING = blockchain_api.FSMStateType_LEGACYSYNCING + FSMStateLAUNCHING = blockchain_api.FSMStateType_LAUNCHING FSMEventIDLE = blockchain_api.FSMEventType_STOP FSMEventRUN = blockchain_api.FSMEventType_RUN FSMEventCATCHUPBLOCKS = blockchain_api.FSMEventType_CATCHUPBLOCKS FSMEventLEGACYSYNC = blockchain_api.FSMEventType_LEGACYSYNC + FSMEventLAUNCH = blockchain_api.FSMEventType_LAUNCH ) // NewClient creates a new blockchain client with default address settings. @@ -1713,6 +1715,44 @@ func (c *Client) Run(ctx context.Context, source string) error { return nil } +// Launch sends a launch FSM event to the blockchain service. +// This method transitions the blockchain service's finite state machine to the +// LAUNCHING state, which performs an initial sync check before starting normal +// operations. This ensures subtree validation doesn't process transactions +// before the node is synchronized with peers. +// +// The method first checks if the FSM is already in the LAUNCHING state +// to avoid unnecessary state transitions. +// +// Parameters: +// - ctx: Context for the operation with timeout and cancellation support +// - source: Identifier for the source of the event (for logging) +// +// Returns: +// - error: Any error encountered during the FSM event transmission +func (c *Client) Launch(ctx context.Context, source string) error { + currentState := "" + + state, _ := c.GetFSMCurrentState(ctx) + if state != nil { + // check whether the current state is the same as the target state + if *state == FSMStateLAUNCHING { + return nil + } + + currentState = state.String() + } + + c.logger.Infof("[Blockchain Client] Sending Launch event %s (%s => Launch)", source, currentState) + + _, err := c.client.Launch(ctx, &emptypb.Empty{}) + if err != nil { + return errors.UnwrapGRPC(err) + } + + return nil +} + // CatchUpBlocks sends a catchup blocks FSM event to the blockchain service. // This method initiates a blockchain synchronization process by transitioning the // blockchain service's finite state machine to the CATCHING_BLOCKS state, which diff --git a/services/blockchain/Interface.go b/services/blockchain/Interface.go index f9afdf38ad..90f05cf240 100644 --- a/services/blockchain/Interface.go +++ b/services/blockchain/Interface.go @@ -849,6 +849,21 @@ type ClientI interface { // - Error if the readiness check fails IsFullyReady(ctx context.Context) (bool, error) + // Launch initiates the node startup with sync check. + // + // This method transitions the FSM from IDLE to LAUNCHING state, which allows + // block validation to perform a sync check before transitioning to RUNNING or + // CATCHINGBLOCKS state. This prevents subtree validation from processing messages + // with the wrong block height context during catchup. + // + // Parameters: + // - ctx: Context for the operation with timeout and cancellation support + // - source: Identifier for the source requesting the launch + // + // Returns: + // - Error if the launch fails + Launch(ctx context.Context, source string) error + // Run initiates the normal operation of the blockchain service. // // This method starts the blockchain service in its standard operational mode, diff --git a/services/blockchain/LocalClient.go b/services/blockchain/LocalClient.go index 8d4317c2d7..acf560fbf8 100644 --- a/services/blockchain/LocalClient.go +++ b/services/blockchain/LocalClient.go @@ -447,6 +447,10 @@ func (c *LocalClient) Run(ctx context.Context, source string) error { return nil } +func (c *LocalClient) Launch(ctx context.Context, source string) error { + return nil +} + func (c *LocalClient) Idle(ctx context.Context) error { return nil } diff --git a/services/blockchain/Server.go b/services/blockchain/Server.go index f802db3d43..2841ab9fab 100644 --- a/services/blockchain/Server.go +++ b/services/blockchain/Server.go @@ -351,6 +351,14 @@ func (b *Blockchain) Init(ctx context.Context) error { if b.localTestStartState != "" { b.finiteStateMachine.SetState(b.localTestStartState) + // If starting in LAUNCHING state (test mode), auto-transition to RUNNING + // This simulates what happens in production after sync check passes + if b.localTestStartState == blockchain_api.FSMStateType_LAUNCHING.String() { + if err := b.finiteStateMachine.Event(ctx, blockchain_api.FSMEventType_RUN.String()); err != nil { + b.logger.Errorf("[Blockchain][Init] Error transitioning from LAUNCHING to RUNNING: %v", err) + } + } + err := b.store.SetFSMState(ctx, b.finiteStateMachine.Current()) if err != nil { b.logger.Errorf("[Blockchain][Init] Error setting FSM state in blockchain store: %v", err) @@ -2586,6 +2594,26 @@ func (b *Blockchain) Idle(ctx context.Context, _ *emptypb.Empty) (*emptypb.Empty return &emptypb.Empty{}, nil } +// Launch transitions the blockchain service to the LAUNCHING state for initial sync check. +func (b *Blockchain) Launch(ctx context.Context, _ *emptypb.Empty) (*emptypb.Empty, error) { + // check whether the FSM is already in the LAUNCHING state + if b.finiteStateMachine.Is(blockchain_api.FSMStateType_LAUNCHING.String()) { + return &emptypb.Empty{}, nil + } + + req := &blockchain_api.SendFSMEventRequest{ + Event: blockchain_api.FSMEventType_LAUNCH, + } + + _, err := b.SendFSMEvent(ctx, req) + if err != nil { + // unable to send the event, no need to update the state. + return nil, err + } + + return &emptypb.Empty{}, nil +} + // Legacy endpoints // GetBlockLocator retrieves a block locator for synchronization purposes. diff --git a/services/blockchain/blockchain_api/blockchain_api.pb.go b/services/blockchain/blockchain_api/blockchain_api.pb.go index 483b9fee8b..6bdde6b58b 100644 --- a/services/blockchain/blockchain_api/blockchain_api.pb.go +++ b/services/blockchain/blockchain_api/blockchain_api.pb.go @@ -34,6 +34,7 @@ const ( FSMEventType_RUN FSMEventType = 1 FSMEventType_CATCHUPBLOCKS FSMEventType = 2 FSMEventType_LEGACYSYNC FSMEventType = 3 + FSMEventType_LAUNCH FSMEventType = 4 // Trigger initial sync check before running ) // Enum value maps for FSMEventType. @@ -43,12 +44,14 @@ var ( 1: "RUN", 2: "CATCHUPBLOCKS", 3: "LEGACYSYNC", + 4: "LAUNCH", } FSMEventType_value = map[string]int32{ "STOP": 0, "RUN": 1, "CATCHUPBLOCKS": 2, "LEGACYSYNC": 3, + "LAUNCH": 4, } ) @@ -87,6 +90,7 @@ const ( FSMStateType_RUNNING FSMStateType = 1 // Service is running normally FSMStateType_CATCHINGBLOCKS FSMStateType = 2 // Service is catching up blocks FSMStateType_LEGACYSYNCING FSMStateType = 3 // Service is in legacy sync mode + FSMStateType_LAUNCHING FSMStateType = 4 // Service is performing initial sync check before running ) // Enum value maps for FSMStateType. @@ -96,12 +100,14 @@ var ( 1: "RUNNING", 2: "CATCHINGBLOCKS", 3: "LEGACYSYNCING", + 4: "LAUNCHING", } FSMStateType_value = map[string]int32{ "IDLE": 0, "RUNNING": 1, "CATCHINGBLOCKS": 2, "LEGACYSYNCING": 3, + "LAUNCHING": 4, } ) @@ -5546,18 +5552,21 @@ const file_services_blockchain_blockchain_api_blockchain_api_proto_rawDesc = "" "\vacquired_at\x18\x02 \x01(\x03R\n" + "acquiredAt\x12%\n" + "\x0edeletion_count\x18\x03 \x01(\x05R\rdeletionCount\x12!\n" + - "\fdeletion_ids\x18\x04 \x03(\x03R\vdeletionIds*D\n" + + "\fdeletion_ids\x18\x04 \x03(\x03R\vdeletionIds*P\n" + "\fFSMEventType\x12\b\n" + "\x04STOP\x10\x00\x12\a\n" + "\x03RUN\x10\x01\x12\x11\n" + "\rCATCHUPBLOCKS\x10\x02\x12\x0e\n" + "\n" + - "LEGACYSYNC\x10\x03*L\n" + + "LEGACYSYNC\x10\x03\x12\n" + + "\n" + + "\x06LAUNCH\x10\x04*[\n" + "\fFSMStateType\x12\b\n" + "\x04IDLE\x10\x00\x12\v\n" + "\aRUNNING\x10\x01\x12\x12\n" + "\x0eCATCHINGBLOCKS\x10\x02\x12\x11\n" + - "\rLEGACYSYNCING\x10\x032\xdf4\n" + + "\rLEGACYSYNCING\x10\x03\x12\r\n" + + "\tLAUNCHING\x10\x042\x9b5\n" + "\rBlockchainAPI\x12F\n" + "\n" + "HealthGRPC\x12\x16.google.protobuf.Empty\x1a\x1e.blockchain_api.HealthResponse\"\x00\x12E\n" + @@ -5614,7 +5623,8 @@ const file_services_blockchain_blockchain_api_blockchain_api_proto_rawDesc = "" "\rCatchUpBlocks\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\"\x00\x12>\n" + "\n" + "LegacySync\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\"\x00\x128\n" + - "\x04Idle\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\"\x00\x12W\n" + + "\x04Idle\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\"\x00\x12:\n" + + "\x06Launch\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\"\x00\x12W\n" + "\x11ReportPeerFailure\x12(.blockchain_api.ReportPeerFailureRequest\x1a\x16.google.protobuf.Empty\"\x00\x12d\n" + "\x0fGetBlockLocator\x12&.blockchain_api.GetBlockLocatorRequest\x1a'.blockchain_api.GetBlockLocatorResponse\"\x00\x12m\n" + "\x12LocateBlockHeaders\x12).blockchain_api.LocateBlockHeadersRequest\x1a*.blockchain_api.LocateBlockHeadersResponse\"\x00\x12^\n" + @@ -5821,88 +5831,90 @@ var file_services_blockchain_blockchain_api_blockchain_api_proto_depIdxs = []int 103, // 66: blockchain_api.BlockchainAPI.CatchUpBlocks:input_type -> google.protobuf.Empty 103, // 67: blockchain_api.BlockchainAPI.LegacySync:input_type -> google.protobuf.Empty 103, // 68: blockchain_api.BlockchainAPI.Idle:input_type -> google.protobuf.Empty - 75, // 69: blockchain_api.BlockchainAPI.ReportPeerFailure:input_type -> blockchain_api.ReportPeerFailureRequest - 69, // 70: blockchain_api.BlockchainAPI.GetBlockLocator:input_type -> blockchain_api.GetBlockLocatorRequest - 71, // 71: blockchain_api.BlockchainAPI.LocateBlockHeaders:input_type -> blockchain_api.LocateBlockHeadersRequest - 103, // 72: blockchain_api.BlockchainAPI.GetBestHeightAndTime:input_type -> google.protobuf.Empty - 79, // 73: blockchain_api.BlockchainAPI.ScheduleBlobDeletion:input_type -> blockchain_api.ScheduleBlobDeletionRequest - 81, // 74: blockchain_api.BlockchainAPI.CancelBlobDeletion:input_type -> blockchain_api.CancelBlobDeletionRequest - 83, // 75: blockchain_api.BlockchainAPI.ListScheduledDeletions:input_type -> blockchain_api.ListScheduledDeletionsRequest - 86, // 76: blockchain_api.BlockchainAPI.GetPendingBlobDeletions:input_type -> blockchain_api.GetPendingBlobDeletionsRequest - 88, // 77: blockchain_api.BlockchainAPI.RemoveBlobDeletion:input_type -> blockchain_api.RemoveBlobDeletionRequest - 89, // 78: blockchain_api.BlockchainAPI.IncrementBlobDeletionRetry:input_type -> blockchain_api.IncrementBlobDeletionRetryRequest - 91, // 79: blockchain_api.BlockchainAPI.CompleteBlobDeletions:input_type -> blockchain_api.CompleteBlobDeletionsRequest - 93, // 80: blockchain_api.BlockchainAPI.AcquireBlobDeletionBatch:input_type -> blockchain_api.AcquireBlobDeletionBatchRequest - 95, // 81: blockchain_api.BlockchainAPI.CompleteBlobDeletionBatch:input_type -> blockchain_api.CompleteBlobDeletionBatchRequest - 2, // 82: blockchain_api.BlockchainAPI.HealthGRPC:output_type -> blockchain_api.HealthResponse - 103, // 83: blockchain_api.BlockchainAPI.AddBlock:output_type -> google.protobuf.Empty - 11, // 84: blockchain_api.BlockchainAPI.GetBlock:output_type -> blockchain_api.GetBlockResponse - 6, // 85: blockchain_api.BlockchainAPI.GetBlocks:output_type -> blockchain_api.GetBlocksResponse - 11, // 86: blockchain_api.BlockchainAPI.GetBlockByHeight:output_type -> blockchain_api.GetBlockResponse - 11, // 87: blockchain_api.BlockchainAPI.GetBlockByID:output_type -> blockchain_api.GetBlockResponse - 9, // 88: blockchain_api.BlockchainAPI.GetNextBlockID:output_type -> blockchain_api.GetNextBlockIDResponse - 104, // 89: blockchain_api.BlockchainAPI.GetBlockStats:output_type -> model.BlockStats - 105, // 90: blockchain_api.BlockchainAPI.GetBlockGraphData:output_type -> model.BlockDataPoints - 49, // 91: blockchain_api.BlockchainAPI.GetLastNBlocks:output_type -> blockchain_api.GetLastNBlocksResponse - 51, // 92: blockchain_api.BlockchainAPI.GetLastNInvalidBlocks:output_type -> blockchain_api.GetLastNInvalidBlocksResponse - 53, // 93: blockchain_api.BlockchainAPI.GetSuitableBlock:output_type -> blockchain_api.GetSuitableBlockResponse - 57, // 94: blockchain_api.BlockchainAPI.GetHashOfAncestorBlock:output_type -> blockchain_api.GetHashOfAncestorBlockResponse - 36, // 95: blockchain_api.BlockchainAPI.GetLatestBlockHeaderFromBlockLocator:output_type -> blockchain_api.GetBlockHeaderResponse - 19, // 96: blockchain_api.BlockchainAPI.GetBlockHeadersFromOldest:output_type -> blockchain_api.GetBlockHeadersResponse - 59, // 97: blockchain_api.BlockchainAPI.GetNextWorkRequired:output_type -> blockchain_api.GetNextWorkRequiredResponse - 14, // 98: blockchain_api.BlockchainAPI.GetBlockExists:output_type -> blockchain_api.GetBlockExistsResponse - 19, // 99: blockchain_api.BlockchainAPI.GetBlockHeaders:output_type -> blockchain_api.GetBlockHeadersResponse - 19, // 100: blockchain_api.BlockchainAPI.GetBlockHeadersToCommonAncestor:output_type -> blockchain_api.GetBlockHeadersResponse - 19, // 101: blockchain_api.BlockchainAPI.GetBlockHeadersFromCommonAncestor:output_type -> blockchain_api.GetBlockHeadersResponse - 19, // 102: blockchain_api.BlockchainAPI.GetBlockHeadersFromTill:output_type -> blockchain_api.GetBlockHeadersResponse - 22, // 103: blockchain_api.BlockchainAPI.GetBlockHeadersFromHeight:output_type -> blockchain_api.GetBlockHeadersFromHeightResponse - 24, // 104: blockchain_api.BlockchainAPI.GetBlockHeadersByHeight:output_type -> blockchain_api.GetBlockHeadersByHeightResponse - 26, // 105: blockchain_api.BlockchainAPI.GetBlocksByHeight:output_type -> blockchain_api.GetBlocksByHeightResponse - 28, // 106: blockchain_api.BlockchainAPI.FindBlocksContainingSubtree:output_type -> blockchain_api.FindBlocksContainingSubtreeResponse - 29, // 107: blockchain_api.BlockchainAPI.GetBlockHeaderIDs:output_type -> blockchain_api.GetBlockHeaderIDsResponse - 36, // 108: blockchain_api.BlockchainAPI.GetBestBlockHeader:output_type -> blockchain_api.GetBlockHeaderResponse - 37, // 109: blockchain_api.BlockchainAPI.CheckBlockIsInCurrentChain:output_type -> blockchain_api.CheckBlockIsCurrentChainResponse - 39, // 110: blockchain_api.BlockchainAPI.CheckBlockIsAncestorOfBlock:output_type -> blockchain_api.CheckBlockIsAncestorOfBlockResponse - 74, // 111: blockchain_api.BlockchainAPI.GetChainTips:output_type -> blockchain_api.GetChainTipsResponse - 36, // 112: blockchain_api.BlockchainAPI.GetBlockHeader:output_type -> blockchain_api.GetBlockHeaderResponse - 34, // 113: blockchain_api.BlockchainAPI.InvalidateBlock:output_type -> blockchain_api.InvalidateBlockResponse - 103, // 114: blockchain_api.BlockchainAPI.RevalidateBlock:output_type -> google.protobuf.Empty - 41, // 115: blockchain_api.BlockchainAPI.Subscribe:output_type -> blockchain_api.Notification - 103, // 116: blockchain_api.BlockchainAPI.SendNotification:output_type -> google.protobuf.Empty - 44, // 117: blockchain_api.BlockchainAPI.GetState:output_type -> blockchain_api.StateResponse - 103, // 118: blockchain_api.BlockchainAPI.SetState:output_type -> google.protobuf.Empty - 47, // 119: blockchain_api.BlockchainAPI.GetBlockIsMined:output_type -> blockchain_api.GetBlockIsMinedResponse - 103, // 120: blockchain_api.BlockchainAPI.SetBlockMinedSet:output_type -> google.protobuf.Empty - 103, // 121: blockchain_api.BlockchainAPI.ClearBlockMinedSet:output_type -> google.protobuf.Empty - 62, // 122: blockchain_api.BlockchainAPI.GetBlocksMinedNotSet:output_type -> blockchain_api.GetBlocksMinedNotSetResponse - 103, // 123: blockchain_api.BlockchainAPI.SetBlockSubtreesSet:output_type -> google.protobuf.Empty - 64, // 124: blockchain_api.BlockchainAPI.GetBlocksSubtreesNotSet:output_type -> blockchain_api.GetBlocksSubtreesNotSetResponse - 103, // 125: blockchain_api.BlockchainAPI.SetBlockProcessedAt:output_type -> google.protobuf.Empty - 103, // 126: blockchain_api.BlockchainAPI.SetBlockPersistedAt:output_type -> google.protobuf.Empty - 78, // 127: blockchain_api.BlockchainAPI.GetBlocksNotPersisted:output_type -> blockchain_api.GetBlocksNotPersistedResponse - 66, // 128: blockchain_api.BlockchainAPI.SendFSMEvent:output_type -> blockchain_api.GetFSMStateResponse - 66, // 129: blockchain_api.BlockchainAPI.GetFSMCurrentState:output_type -> blockchain_api.GetFSMStateResponse - 103, // 130: blockchain_api.BlockchainAPI.WaitFSMToTransitionToGivenState:output_type -> google.protobuf.Empty - 103, // 131: blockchain_api.BlockchainAPI.WaitUntilFSMTransitionFromIdleState:output_type -> google.protobuf.Empty - 103, // 132: blockchain_api.BlockchainAPI.Run:output_type -> google.protobuf.Empty - 103, // 133: blockchain_api.BlockchainAPI.CatchUpBlocks:output_type -> google.protobuf.Empty - 103, // 134: blockchain_api.BlockchainAPI.LegacySync:output_type -> google.protobuf.Empty - 103, // 135: blockchain_api.BlockchainAPI.Idle:output_type -> google.protobuf.Empty - 103, // 136: blockchain_api.BlockchainAPI.ReportPeerFailure:output_type -> google.protobuf.Empty - 70, // 137: blockchain_api.BlockchainAPI.GetBlockLocator:output_type -> blockchain_api.GetBlockLocatorResponse - 72, // 138: blockchain_api.BlockchainAPI.LocateBlockHeaders:output_type -> blockchain_api.LocateBlockHeadersResponse - 73, // 139: blockchain_api.BlockchainAPI.GetBestHeightAndTime:output_type -> blockchain_api.GetBestHeightAndTimeResponse - 80, // 140: blockchain_api.BlockchainAPI.ScheduleBlobDeletion:output_type -> blockchain_api.ScheduleBlobDeletionResponse - 82, // 141: blockchain_api.BlockchainAPI.CancelBlobDeletion:output_type -> blockchain_api.CancelBlobDeletionResponse - 85, // 142: blockchain_api.BlockchainAPI.ListScheduledDeletions:output_type -> blockchain_api.ListScheduledDeletionsResponse - 87, // 143: blockchain_api.BlockchainAPI.GetPendingBlobDeletions:output_type -> blockchain_api.GetPendingBlobDeletionsResponse - 103, // 144: blockchain_api.BlockchainAPI.RemoveBlobDeletion:output_type -> google.protobuf.Empty - 90, // 145: blockchain_api.BlockchainAPI.IncrementBlobDeletionRetry:output_type -> blockchain_api.IncrementBlobDeletionRetryResponse - 92, // 146: blockchain_api.BlockchainAPI.CompleteBlobDeletions:output_type -> blockchain_api.CompleteBlobDeletionsResponse - 94, // 147: blockchain_api.BlockchainAPI.AcquireBlobDeletionBatch:output_type -> blockchain_api.AcquireBlobDeletionBatchResponse - 103, // 148: blockchain_api.BlockchainAPI.CompleteBlobDeletionBatch:output_type -> google.protobuf.Empty - 82, // [82:149] is the sub-list for method output_type - 15, // [15:82] is the sub-list for method input_type + 103, // 69: blockchain_api.BlockchainAPI.Launch:input_type -> google.protobuf.Empty + 75, // 70: blockchain_api.BlockchainAPI.ReportPeerFailure:input_type -> blockchain_api.ReportPeerFailureRequest + 69, // 71: blockchain_api.BlockchainAPI.GetBlockLocator:input_type -> blockchain_api.GetBlockLocatorRequest + 71, // 72: blockchain_api.BlockchainAPI.LocateBlockHeaders:input_type -> blockchain_api.LocateBlockHeadersRequest + 103, // 73: blockchain_api.BlockchainAPI.GetBestHeightAndTime:input_type -> google.protobuf.Empty + 79, // 74: blockchain_api.BlockchainAPI.ScheduleBlobDeletion:input_type -> blockchain_api.ScheduleBlobDeletionRequest + 81, // 75: blockchain_api.BlockchainAPI.CancelBlobDeletion:input_type -> blockchain_api.CancelBlobDeletionRequest + 83, // 76: blockchain_api.BlockchainAPI.ListScheduledDeletions:input_type -> blockchain_api.ListScheduledDeletionsRequest + 86, // 77: blockchain_api.BlockchainAPI.GetPendingBlobDeletions:input_type -> blockchain_api.GetPendingBlobDeletionsRequest + 88, // 78: blockchain_api.BlockchainAPI.RemoveBlobDeletion:input_type -> blockchain_api.RemoveBlobDeletionRequest + 89, // 79: blockchain_api.BlockchainAPI.IncrementBlobDeletionRetry:input_type -> blockchain_api.IncrementBlobDeletionRetryRequest + 91, // 80: blockchain_api.BlockchainAPI.CompleteBlobDeletions:input_type -> blockchain_api.CompleteBlobDeletionsRequest + 93, // 81: blockchain_api.BlockchainAPI.AcquireBlobDeletionBatch:input_type -> blockchain_api.AcquireBlobDeletionBatchRequest + 95, // 82: blockchain_api.BlockchainAPI.CompleteBlobDeletionBatch:input_type -> blockchain_api.CompleteBlobDeletionBatchRequest + 2, // 83: blockchain_api.BlockchainAPI.HealthGRPC:output_type -> blockchain_api.HealthResponse + 103, // 84: blockchain_api.BlockchainAPI.AddBlock:output_type -> google.protobuf.Empty + 11, // 85: blockchain_api.BlockchainAPI.GetBlock:output_type -> blockchain_api.GetBlockResponse + 6, // 86: blockchain_api.BlockchainAPI.GetBlocks:output_type -> blockchain_api.GetBlocksResponse + 11, // 87: blockchain_api.BlockchainAPI.GetBlockByHeight:output_type -> blockchain_api.GetBlockResponse + 11, // 88: blockchain_api.BlockchainAPI.GetBlockByID:output_type -> blockchain_api.GetBlockResponse + 9, // 89: blockchain_api.BlockchainAPI.GetNextBlockID:output_type -> blockchain_api.GetNextBlockIDResponse + 104, // 90: blockchain_api.BlockchainAPI.GetBlockStats:output_type -> model.BlockStats + 105, // 91: blockchain_api.BlockchainAPI.GetBlockGraphData:output_type -> model.BlockDataPoints + 49, // 92: blockchain_api.BlockchainAPI.GetLastNBlocks:output_type -> blockchain_api.GetLastNBlocksResponse + 51, // 93: blockchain_api.BlockchainAPI.GetLastNInvalidBlocks:output_type -> blockchain_api.GetLastNInvalidBlocksResponse + 53, // 94: blockchain_api.BlockchainAPI.GetSuitableBlock:output_type -> blockchain_api.GetSuitableBlockResponse + 57, // 95: blockchain_api.BlockchainAPI.GetHashOfAncestorBlock:output_type -> blockchain_api.GetHashOfAncestorBlockResponse + 36, // 96: blockchain_api.BlockchainAPI.GetLatestBlockHeaderFromBlockLocator:output_type -> blockchain_api.GetBlockHeaderResponse + 19, // 97: blockchain_api.BlockchainAPI.GetBlockHeadersFromOldest:output_type -> blockchain_api.GetBlockHeadersResponse + 59, // 98: blockchain_api.BlockchainAPI.GetNextWorkRequired:output_type -> blockchain_api.GetNextWorkRequiredResponse + 14, // 99: blockchain_api.BlockchainAPI.GetBlockExists:output_type -> blockchain_api.GetBlockExistsResponse + 19, // 100: blockchain_api.BlockchainAPI.GetBlockHeaders:output_type -> blockchain_api.GetBlockHeadersResponse + 19, // 101: blockchain_api.BlockchainAPI.GetBlockHeadersToCommonAncestor:output_type -> blockchain_api.GetBlockHeadersResponse + 19, // 102: blockchain_api.BlockchainAPI.GetBlockHeadersFromCommonAncestor:output_type -> blockchain_api.GetBlockHeadersResponse + 19, // 103: blockchain_api.BlockchainAPI.GetBlockHeadersFromTill:output_type -> blockchain_api.GetBlockHeadersResponse + 22, // 104: blockchain_api.BlockchainAPI.GetBlockHeadersFromHeight:output_type -> blockchain_api.GetBlockHeadersFromHeightResponse + 24, // 105: blockchain_api.BlockchainAPI.GetBlockHeadersByHeight:output_type -> blockchain_api.GetBlockHeadersByHeightResponse + 26, // 106: blockchain_api.BlockchainAPI.GetBlocksByHeight:output_type -> blockchain_api.GetBlocksByHeightResponse + 28, // 107: blockchain_api.BlockchainAPI.FindBlocksContainingSubtree:output_type -> blockchain_api.FindBlocksContainingSubtreeResponse + 29, // 108: blockchain_api.BlockchainAPI.GetBlockHeaderIDs:output_type -> blockchain_api.GetBlockHeaderIDsResponse + 36, // 109: blockchain_api.BlockchainAPI.GetBestBlockHeader:output_type -> blockchain_api.GetBlockHeaderResponse + 37, // 110: blockchain_api.BlockchainAPI.CheckBlockIsInCurrentChain:output_type -> blockchain_api.CheckBlockIsCurrentChainResponse + 39, // 111: blockchain_api.BlockchainAPI.CheckBlockIsAncestorOfBlock:output_type -> blockchain_api.CheckBlockIsAncestorOfBlockResponse + 74, // 112: blockchain_api.BlockchainAPI.GetChainTips:output_type -> blockchain_api.GetChainTipsResponse + 36, // 113: blockchain_api.BlockchainAPI.GetBlockHeader:output_type -> blockchain_api.GetBlockHeaderResponse + 34, // 114: blockchain_api.BlockchainAPI.InvalidateBlock:output_type -> blockchain_api.InvalidateBlockResponse + 103, // 115: blockchain_api.BlockchainAPI.RevalidateBlock:output_type -> google.protobuf.Empty + 41, // 116: blockchain_api.BlockchainAPI.Subscribe:output_type -> blockchain_api.Notification + 103, // 117: blockchain_api.BlockchainAPI.SendNotification:output_type -> google.protobuf.Empty + 44, // 118: blockchain_api.BlockchainAPI.GetState:output_type -> blockchain_api.StateResponse + 103, // 119: blockchain_api.BlockchainAPI.SetState:output_type -> google.protobuf.Empty + 47, // 120: blockchain_api.BlockchainAPI.GetBlockIsMined:output_type -> blockchain_api.GetBlockIsMinedResponse + 103, // 121: blockchain_api.BlockchainAPI.SetBlockMinedSet:output_type -> google.protobuf.Empty + 103, // 122: blockchain_api.BlockchainAPI.ClearBlockMinedSet:output_type -> google.protobuf.Empty + 62, // 123: blockchain_api.BlockchainAPI.GetBlocksMinedNotSet:output_type -> blockchain_api.GetBlocksMinedNotSetResponse + 103, // 124: blockchain_api.BlockchainAPI.SetBlockSubtreesSet:output_type -> google.protobuf.Empty + 64, // 125: blockchain_api.BlockchainAPI.GetBlocksSubtreesNotSet:output_type -> blockchain_api.GetBlocksSubtreesNotSetResponse + 103, // 126: blockchain_api.BlockchainAPI.SetBlockProcessedAt:output_type -> google.protobuf.Empty + 103, // 127: blockchain_api.BlockchainAPI.SetBlockPersistedAt:output_type -> google.protobuf.Empty + 78, // 128: blockchain_api.BlockchainAPI.GetBlocksNotPersisted:output_type -> blockchain_api.GetBlocksNotPersistedResponse + 66, // 129: blockchain_api.BlockchainAPI.SendFSMEvent:output_type -> blockchain_api.GetFSMStateResponse + 66, // 130: blockchain_api.BlockchainAPI.GetFSMCurrentState:output_type -> blockchain_api.GetFSMStateResponse + 103, // 131: blockchain_api.BlockchainAPI.WaitFSMToTransitionToGivenState:output_type -> google.protobuf.Empty + 103, // 132: blockchain_api.BlockchainAPI.WaitUntilFSMTransitionFromIdleState:output_type -> google.protobuf.Empty + 103, // 133: blockchain_api.BlockchainAPI.Run:output_type -> google.protobuf.Empty + 103, // 134: blockchain_api.BlockchainAPI.CatchUpBlocks:output_type -> google.protobuf.Empty + 103, // 135: blockchain_api.BlockchainAPI.LegacySync:output_type -> google.protobuf.Empty + 103, // 136: blockchain_api.BlockchainAPI.Idle:output_type -> google.protobuf.Empty + 103, // 137: blockchain_api.BlockchainAPI.Launch:output_type -> google.protobuf.Empty + 103, // 138: blockchain_api.BlockchainAPI.ReportPeerFailure:output_type -> google.protobuf.Empty + 70, // 139: blockchain_api.BlockchainAPI.GetBlockLocator:output_type -> blockchain_api.GetBlockLocatorResponse + 72, // 140: blockchain_api.BlockchainAPI.LocateBlockHeaders:output_type -> blockchain_api.LocateBlockHeadersResponse + 73, // 141: blockchain_api.BlockchainAPI.GetBestHeightAndTime:output_type -> blockchain_api.GetBestHeightAndTimeResponse + 80, // 142: blockchain_api.BlockchainAPI.ScheduleBlobDeletion:output_type -> blockchain_api.ScheduleBlobDeletionResponse + 82, // 143: blockchain_api.BlockchainAPI.CancelBlobDeletion:output_type -> blockchain_api.CancelBlobDeletionResponse + 85, // 144: blockchain_api.BlockchainAPI.ListScheduledDeletions:output_type -> blockchain_api.ListScheduledDeletionsResponse + 87, // 145: blockchain_api.BlockchainAPI.GetPendingBlobDeletions:output_type -> blockchain_api.GetPendingBlobDeletionsResponse + 103, // 146: blockchain_api.BlockchainAPI.RemoveBlobDeletion:output_type -> google.protobuf.Empty + 90, // 147: blockchain_api.BlockchainAPI.IncrementBlobDeletionRetry:output_type -> blockchain_api.IncrementBlobDeletionRetryResponse + 92, // 148: blockchain_api.BlockchainAPI.CompleteBlobDeletions:output_type -> blockchain_api.CompleteBlobDeletionsResponse + 94, // 149: blockchain_api.BlockchainAPI.AcquireBlobDeletionBatch:output_type -> blockchain_api.AcquireBlobDeletionBatchResponse + 103, // 150: blockchain_api.BlockchainAPI.CompleteBlobDeletionBatch:output_type -> google.protobuf.Empty + 83, // [83:151] is the sub-list for method output_type + 15, // [15:83] is the sub-list for method input_type 15, // [15:15] is the sub-list for extension type_name 15, // [15:15] is the sub-list for extension extendee 0, // [0:15] is the sub-list for field type_name diff --git a/services/blockchain/blockchain_api/blockchain_api.proto b/services/blockchain/blockchain_api/blockchain_api.proto index f29863684e..ff0e837e83 100644 --- a/services/blockchain/blockchain_api/blockchain_api.proto +++ b/services/blockchain/blockchain_api/blockchain_api.proto @@ -177,6 +177,9 @@ service BlockchainAPI { // Idle marks the service as idle. rpc Idle(google.protobuf.Empty) returns (google.protobuf.Empty) {} + // Launch initiates the node startup with sync check. + rpc Launch(google.protobuf.Empty) returns (google.protobuf.Empty) {} + // ReportPeerFailure notifies about peer download failures (catchup, subtree, block, etc). rpc ReportPeerFailure(ReportPeerFailureRequest) returns (google.protobuf.Empty) {} @@ -629,6 +632,7 @@ enum FSMEventType { RUN = 1; CATCHUPBLOCKS = 2; LEGACYSYNC = 3; + LAUNCH = 4; // Trigger initial sync check before running } // FSMStateType defines possible states of the blockchain FSM. @@ -637,6 +641,7 @@ enum FSMStateType { RUNNING = 1; // Service is running normally CATCHINGBLOCKS = 2; // Service is catching up blocks LEGACYSYNCING = 3; // Service is in legacy sync mode + LAUNCHING = 4; // Service is performing initial sync check before running } // GetBlockLocatorRequest requests a block locator. diff --git a/services/blockchain/blockchain_api/blockchain_api_grpc.pb.go b/services/blockchain/blockchain_api/blockchain_api_grpc.pb.go index bf7d00eca8..15e58232e2 100644 --- a/services/blockchain/blockchain_api/blockchain_api_grpc.pb.go +++ b/services/blockchain/blockchain_api/blockchain_api_grpc.pb.go @@ -77,6 +77,7 @@ const ( BlockchainAPI_CatchUpBlocks_FullMethodName = "/blockchain_api.BlockchainAPI/CatchUpBlocks" BlockchainAPI_LegacySync_FullMethodName = "/blockchain_api.BlockchainAPI/LegacySync" BlockchainAPI_Idle_FullMethodName = "/blockchain_api.BlockchainAPI/Idle" + BlockchainAPI_Launch_FullMethodName = "/blockchain_api.BlockchainAPI/Launch" BlockchainAPI_ReportPeerFailure_FullMethodName = "/blockchain_api.BlockchainAPI/ReportPeerFailure" BlockchainAPI_GetBlockLocator_FullMethodName = "/blockchain_api.BlockchainAPI/GetBlockLocator" BlockchainAPI_LocateBlockHeaders_FullMethodName = "/blockchain_api.BlockchainAPI/LocateBlockHeaders" @@ -208,6 +209,8 @@ type BlockchainAPIClient interface { LegacySync(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) // Idle marks the service as idle. Idle(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) + // Launch initiates the node startup with sync check. + Launch(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) // ReportPeerFailure notifies about peer download failures (catchup, subtree, block, etc). ReportPeerFailure(ctx context.Context, in *ReportPeerFailureRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) // GetBlockLocator retrieves a block locator for chain synchronization. @@ -796,6 +799,16 @@ func (c *blockchainAPIClient) Idle(ctx context.Context, in *emptypb.Empty, opts return out, nil } +func (c *blockchainAPIClient) Launch(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, BlockchainAPI_Launch_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *blockchainAPIClient) ReportPeerFailure(ctx context.Context, in *ReportPeerFailureRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) @@ -1042,6 +1055,8 @@ type BlockchainAPIServer interface { LegacySync(context.Context, *emptypb.Empty) (*emptypb.Empty, error) // Idle marks the service as idle. Idle(context.Context, *emptypb.Empty) (*emptypb.Empty, error) + // Launch initiates the node startup with sync check. + Launch(context.Context, *emptypb.Empty) (*emptypb.Empty, error) // ReportPeerFailure notifies about peer download failures (catchup, subtree, block, etc). ReportPeerFailure(context.Context, *ReportPeerFailureRequest) (*emptypb.Empty, error) // GetBlockLocator retrieves a block locator for chain synchronization. @@ -1243,6 +1258,9 @@ func (UnimplementedBlockchainAPIServer) LegacySync(context.Context, *emptypb.Emp func (UnimplementedBlockchainAPIServer) Idle(context.Context, *emptypb.Empty) (*emptypb.Empty, error) { return nil, status.Error(codes.Unimplemented, "method Idle not implemented") } +func (UnimplementedBlockchainAPIServer) Launch(context.Context, *emptypb.Empty) (*emptypb.Empty, error) { + return nil, status.Error(codes.Unimplemented, "method Launch not implemented") +} func (UnimplementedBlockchainAPIServer) ReportPeerFailure(context.Context, *ReportPeerFailureRequest) (*emptypb.Empty, error) { return nil, status.Error(codes.Unimplemented, "method ReportPeerFailure not implemented") } @@ -2268,6 +2286,24 @@ func _BlockchainAPI_Idle_Handler(srv interface{}, ctx context.Context, dec func( return interceptor(ctx, in, info, handler) } +func _BlockchainAPI_Launch_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(emptypb.Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BlockchainAPIServer).Launch(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: BlockchainAPI_Launch_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BlockchainAPIServer).Launch(ctx, req.(*emptypb.Empty)) + } + return interceptor(ctx, in, info, handler) +} + func _BlockchainAPI_ReportPeerFailure_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ReportPeerFailureRequest) if err := dec(in); err != nil { @@ -2721,6 +2757,10 @@ var BlockchainAPI_ServiceDesc = grpc.ServiceDesc{ MethodName: "Idle", Handler: _BlockchainAPI_Idle_Handler, }, + { + MethodName: "Launch", + Handler: _BlockchainAPI_Launch_Handler, + }, { MethodName: "ReportPeerFailure", Handler: _BlockchainAPI_ReportPeerFailure_Handler, diff --git a/services/blockchain/fsm.go b/services/blockchain/fsm.go index aa9a107357..9677b071e6 100644 --- a/services/blockchain/fsm.go +++ b/services/blockchain/fsm.go @@ -13,8 +13,8 @@ import ( // NewFiniteStateMachine creates a new finite state machine for the blockchain service. // -// States: IDLE, RUNNING, CATCHINGBLOCKS, LEGACYSYNCING -// Events: RUN, CATCHUPBLOCKS, LEGACYSYNC, STOP +// States: IDLE, RUNNING, CATCHINGBLOCKS, LEGACYSYNCING, LAUNCHING +// Events: RUN, CATCHUPBLOCKS, LEGACYSYNC, STOP, LAUNCH // // Automatically sends notifications on state transitions and updates Prometheus metrics. func (b *Blockchain) NewFiniteStateMachine(opts ...func(*fsm.FSM)) *fsm.FSM { @@ -45,10 +45,19 @@ func (b *Blockchain) NewFiniteStateMachine(opts ...func(*fsm.FSM)) *fsm.FSM { finiteStateMachine := fsm.NewFSM( blockchain_api.FSMStateType_IDLE.String(), fsm.Events{ + // LAUNCH: IDLE -> LAUNCHING (safe startup with sync check) { - Name: blockchain_api.FSMEventType_RUN.String(), + Name: blockchain_api.FSMEventType_LAUNCH.String(), Src: []string{ blockchain_api.FSMStateType_IDLE.String(), + }, + Dst: blockchain_api.FSMStateType_LAUNCHING.String(), + }, + // RUN: only from LAUNCHING, LEGACYSYNCING, or CATCHINGBLOCKS (NOT from IDLE) + { + Name: blockchain_api.FSMEventType_RUN.String(), + Src: []string{ + blockchain_api.FSMStateType_LAUNCHING.String(), blockchain_api.FSMStateType_LEGACYSYNCING.String(), blockchain_api.FSMStateType_CATCHINGBLOCKS.String(), }, @@ -61,10 +70,12 @@ func (b *Blockchain) NewFiniteStateMachine(opts ...func(*fsm.FSM)) *fsm.FSM { }, Dst: blockchain_api.FSMStateType_LEGACYSYNCING.String(), }, + // CATCHUPBLOCKS: from RUNNING or LAUNCHING { Name: blockchain_api.FSMEventType_CATCHUPBLOCKS.String(), Src: []string{ blockchain_api.FSMStateType_RUNNING.String(), + blockchain_api.FSMStateType_LAUNCHING.String(), }, Dst: blockchain_api.FSMStateType_CATCHINGBLOCKS.String(), }, @@ -114,6 +125,8 @@ func CheckFSM(blockchainClient ClientI) func(ctx context.Context, checkLiveness status = http.StatusOK case blockchain_api.FSMStateType_IDLE: status = http.StatusOK + case blockchain_api.FSMStateType_LAUNCHING: + status = http.StatusOK default: status = http.StatusServiceUnavailable } diff --git a/services/blockchain/fsm_test.go b/services/blockchain/fsm_test.go index b41df192bd..759ad06b27 100644 --- a/services/blockchain/fsm_test.go +++ b/services/blockchain/fsm_test.go @@ -24,10 +24,19 @@ func Test_NewFiniteStateMachine(t *testing.T) { fsm := blockchainClient.NewFiniteStateMachine() require.NotNil(t, fsm) require.Equal(t, "IDLE", fsm.Current()) - require.True(t, fsm.Can(blockchain_api.FSMEventType_RUN.String())) + require.True(t, fsm.Can(blockchain_api.FSMEventType_LAUNCH.String())) + require.False(t, fsm.Can(blockchain_api.FSMEventType_RUN.String())) // RUN not valid from IDLE // Test transitions - t.Run("Transition from Idle to Running", func(t *testing.T) { + t.Run("Transition from Idle to Launching", func(t *testing.T) { + err := fsm.Event(ctx, blockchain_api.FSMEventType_LAUNCH.String()) + require.NoError(t, err) + require.Equal(t, "LAUNCHING", fsm.Current()) + require.True(t, fsm.Can(blockchain_api.FSMEventType_RUN.String())) + require.True(t, fsm.Can(blockchain_api.FSMEventType_CATCHUPBLOCKS.String())) + }) + + t.Run("Transition from Launching to Running", func(t *testing.T) { err := fsm.Event(ctx, blockchain_api.FSMEventType_RUN.String()) require.NoError(t, err) require.Equal(t, "RUNNING", fsm.Current()) @@ -76,6 +85,19 @@ func Test_GetSetFSMStateFromStore(t *testing.T) { require.Equal(t, "IDLE", state) }) + t.Run("Alter current state to Launching", func(t *testing.T) { + _, err = blockchainClient.Launch(ctx, &emptypb.Empty{}) + require.NoError(t, err) + + resp, err := blockchainClient.GetFSMCurrentState(ctx, &emptypb.Empty{}) + require.NoError(t, err) + require.Equal(t, "LAUNCHING", resp.State.String()) + + state, err := blockchainClient.GetStoreFSMState(ctx) + require.NoError(t, err) + require.Equal(t, "LAUNCHING", state) + }) + t.Run("Alter current state to Running", func(t *testing.T) { _, err = blockchainClient.Run(ctx, &emptypb.Empty{}) require.NoError(t, err) diff --git a/services/blockchain/mock.go b/services/blockchain/mock.go index e404ebbf2d..0120d7939d 100644 --- a/services/blockchain/mock.go +++ b/services/blockchain/mock.go @@ -502,6 +502,12 @@ func (m *Mock) Run(ctx context.Context, source string) error { return args.Error(0) } +// Launch mocks the Launch method +func (m *Mock) Launch(ctx context.Context, source string) error { + args := m.Called(ctx, source) + return args.Error(0) +} + // CatchUpBlocks mocks the CatchUpBlocks method func (m *Mock) CatchUpBlocks(ctx context.Context) error { args := m.Called(ctx) diff --git a/services/blockpersister/Server_test.go b/services/blockpersister/Server_test.go index defd21b98c..b4ec0418fe 100644 --- a/services/blockpersister/Server_test.go +++ b/services/blockpersister/Server_test.go @@ -791,11 +791,12 @@ func (m *MockBlockchainClient) GetFSMCurrentStateForE2ETestMode() blockchain.FSM defer m.mu.RUnlock() return m.fsmState } -func (m *MockBlockchainClient) IsFullyReady(ctx context.Context) (bool, error) { return true, nil } -func (m *MockBlockchainClient) Run(ctx context.Context, source string) error { return nil } -func (m *MockBlockchainClient) CatchUpBlocks(ctx context.Context) error { return nil } -func (m *MockBlockchainClient) LegacySync(ctx context.Context) error { return nil } -func (m *MockBlockchainClient) Idle(ctx context.Context) error { return nil } +func (m *MockBlockchainClient) IsFullyReady(ctx context.Context) (bool, error) { return true, nil } +func (m *MockBlockchainClient) Run(ctx context.Context, source string) error { return nil } +func (m *MockBlockchainClient) Launch(ctx context.Context, source string) error { return nil } +func (m *MockBlockchainClient) CatchUpBlocks(ctx context.Context) error { return nil } +func (m *MockBlockchainClient) LegacySync(ctx context.Context) error { return nil } +func (m *MockBlockchainClient) Idle(ctx context.Context) error { return nil } func (m *MockBlockchainClient) SendFSMEvent(ctx context.Context, event blockchain.FSMEventType) error { return nil } diff --git a/services/blockvalidation/InvalidBlocksKafkaProducer_test.go b/services/blockvalidation/InvalidBlocksKafkaProducer_test.go index ae2f76b33c..9e44ce689d 100644 --- a/services/blockvalidation/InvalidBlocksKafkaProducer_test.go +++ b/services/blockvalidation/InvalidBlocksKafkaProducer_test.go @@ -393,6 +393,12 @@ func (m *MockBlockchainClient) Run(ctx context.Context, source string) error { return args.Error(0) } +// Launch implements the blockchain.ClientI interface +func (m *MockBlockchainClient) Launch(ctx context.Context, source string) error { + args := m.Called(ctx, source) + return args.Error(0) +} + // SendFSMEvent implements the blockchain.ClientI interface func (m *MockBlockchainClient) SendFSMEvent(ctx context.Context, event blockchain.FSMEventType) error { args := m.Called(ctx, event) diff --git a/services/blockvalidation/blockvalidation_api/blockvalidation_api.pb.go b/services/blockvalidation/blockvalidation_api/blockvalidation_api.pb.go index 5cd89e8cbd..13073e2a4c 100644 --- a/services/blockvalidation/blockvalidation_api/blockvalidation_api.pb.go +++ b/services/blockvalidation/blockvalidation_api/blockvalidation_api.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11 -// protoc v6.33.2 +// protoc v6.33.4 // source: services/blockvalidation/blockvalidation_api/blockvalidation_api.proto package blockvalidation_api diff --git a/services/blockvalidation/blockvalidation_api/blockvalidation_api_grpc.pb.go b/services/blockvalidation/blockvalidation_api/blockvalidation_api_grpc.pb.go index 722c107b57..f949a48970 100644 --- a/services/blockvalidation/blockvalidation_api/blockvalidation_api_grpc.pb.go +++ b/services/blockvalidation/blockvalidation_api/blockvalidation_api_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.6.0 -// - protoc v6.33.2 +// - protoc v6.33.4 // source: services/blockvalidation/blockvalidation_api/blockvalidation_api.proto package blockvalidation_api diff --git a/services/legacy/peer_api/peer_api.pb.go b/services/legacy/peer_api/peer_api.pb.go index 8b31374e4b..e8f084ea90 100644 --- a/services/legacy/peer_api/peer_api.pb.go +++ b/services/legacy/peer_api/peer_api.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11 -// protoc v6.33.2 +// protoc v6.33.4 // source: services/legacy/peer_api/peer_api.proto package peer_api diff --git a/services/legacy/peer_api/peer_api_grpc.pb.go b/services/legacy/peer_api/peer_api_grpc.pb.go index 9ecc97a2c4..8809dd5d67 100644 --- a/services/legacy/peer_api/peer_api_grpc.pb.go +++ b/services/legacy/peer_api/peer_api_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.6.0 -// - protoc v6.33.2 +// - protoc v6.33.4 // source: services/legacy/peer_api/peer_api.proto package peer_api diff --git a/services/p2p/p2p_api/p2p_api.pb.go b/services/p2p/p2p_api/p2p_api.pb.go index 515cbbf18e..e3ef6f78b1 100644 --- a/services/p2p/p2p_api/p2p_api.pb.go +++ b/services/p2p/p2p_api/p2p_api.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11 -// protoc v6.33.2 +// protoc v6.33.4 // source: services/p2p/p2p_api/p2p_api.proto package p2p_api diff --git a/services/p2p/p2p_api/p2p_api_grpc.pb.go b/services/p2p/p2p_api/p2p_api_grpc.pb.go index f2a89179c5..a02293349a 100644 --- a/services/p2p/p2p_api/p2p_api_grpc.pb.go +++ b/services/p2p/p2p_api/p2p_api_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.6.0 -// - protoc v6.33.2 +// - protoc v6.33.4 // source: services/p2p/p2p_api/p2p_api.proto package p2p_api diff --git a/services/p2p/sync_coordinator.go b/services/p2p/sync_coordinator.go index ddd6a324f8..eb4dffac84 100644 --- a/services/p2p/sync_coordinator.go +++ b/services/p2p/sync_coordinator.go @@ -326,6 +326,12 @@ func (sc *SyncCoordinator) checkFSMState(ctx context.Context) { return // Transition handled, no further action needed } + // When FSM is LAUNCHING, perform initial sync check and transition appropriately + if *currentState == blockchain_api.FSMStateType_LAUNCHING { + sc.handleLaunchingState(ctx) + return + } + // When FSM is RUNNING, we need to find a new sync peer and trigger catchup if *currentState == blockchain_api.FSMStateType_RUNNING { // Check if we should attempt reputation recovery @@ -335,6 +341,46 @@ func (sc *SyncCoordinator) checkFSMState(ctx context.Context) { } } +// handleLaunchingState handles the FSM LAUNCHING state - performs initial sync check +// and transitions to RUNNING (if synced) or triggers catchup (if behind peers) +func (sc *SyncCoordinator) handleLaunchingState(ctx context.Context) { + localHeight := sc.getLocalHeightSafe() + + // Get max peer height + maxPeerHeight := uint32(0) + hasPeers := false + peers := sc.registry.GetAll() + + for _, peer := range peers { + hasPeers = true + if peer.Height > maxPeerHeight { + maxPeerHeight = peer.Height + } + } + + sc.logger.Infof("[SyncCoordinator] LAUNCHING state check: localHeight=%d, maxPeerHeight=%d, hasPeers=%v", + localHeight, maxPeerHeight, hasPeers) + + // If we're behind peers, trigger catchup + if hasPeers && localHeight < maxPeerHeight { + sc.logger.Infof("[SyncCoordinator] Behind peers (local=%d, max=%d), triggering catchup", localHeight, maxPeerHeight) + if err := sc.blockchainClient.CatchUpBlocks(ctx); err != nil { + sc.logger.Errorf("[SyncCoordinator] Failed to trigger catchup from LAUNCHING: %v", err) + return + } + // Now trigger the actual sync by selecting a peer and sending sync message + sc.logger.Infof("[SyncCoordinator] Selecting sync peer and triggering block download") + sc.selectAndActivateNewPeer(localHeight, "") + return + } + + // If synced with peers or no peers, transition to RUNNING + sc.logger.Infof("[SyncCoordinator] Synced with peers or no peers, transitioning to RUNNING") + if err := sc.blockchainClient.Run(ctx, "p2p/sync_coordinator"); err != nil { + sc.logger.Errorf("[SyncCoordinator] Failed to transition from LAUNCHING to RUNNING: %v", err) + } +} + // handleFSMTransition checks for FSM state transitions and handles them func (sc *SyncCoordinator) handleFSMTransition(currentState *blockchain_api.FSMStateType) bool { if *currentState == blockchain_api.FSMStateType_RUNNING { diff --git a/services/propagation/propagation_api/propagation_api.pb.go b/services/propagation/propagation_api/propagation_api.pb.go index 83535f2eae..2292f24e91 100644 --- a/services/propagation/propagation_api/propagation_api.pb.go +++ b/services/propagation/propagation_api/propagation_api.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11 -// protoc v6.33.2 +// protoc v6.33.4 // source: services/propagation/propagation_api/propagation_api.proto package propagation_api diff --git a/services/propagation/propagation_api/propagation_api_grpc.pb.go b/services/propagation/propagation_api/propagation_api_grpc.pb.go index a7a910b969..15784efbd3 100644 --- a/services/propagation/propagation_api/propagation_api_grpc.pb.go +++ b/services/propagation/propagation_api/propagation_api_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.6.0 -// - protoc v6.33.2 +// - protoc v6.33.4 // source: services/propagation/propagation_api/propagation_api.proto package propagation_api diff --git a/services/pruner/pruner_api/pruner_api.pb.go b/services/pruner/pruner_api/pruner_api.pb.go index 6761df4c45..0b599bc804 100644 --- a/services/pruner/pruner_api/pruner_api.pb.go +++ b/services/pruner/pruner_api/pruner_api.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11 -// protoc v6.33.2 +// protoc v6.33.4 // source: services/pruner/pruner_api/pruner_api.proto package pruner_api diff --git a/services/pruner/pruner_api/pruner_api_grpc.pb.go b/services/pruner/pruner_api/pruner_api_grpc.pb.go index 8225de9a72..f71313b2ea 100644 --- a/services/pruner/pruner_api/pruner_api_grpc.pb.go +++ b/services/pruner/pruner_api/pruner_api_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.6.0 -// - protoc v6.33.2 +// - protoc v6.33.4 // source: services/pruner/pruner_api/pruner_api.proto package pruner_api diff --git a/services/subtreevalidation/subtreeHandler.go b/services/subtreevalidation/subtreeHandler.go index 6407f7d2f8..c1fd814b29 100644 --- a/services/subtreevalidation/subtreeHandler.go +++ b/services/subtreevalidation/subtreeHandler.go @@ -55,7 +55,8 @@ func (u *Server) subtreeMessageHandler(ctx context.Context) func(msg *kafka.Kafk return errors.NewProcessingError("[subtreeMessageHandler] failed to get FSM current state", err) } - if *state == blockchain.FSMStateCATCHINGBLOCKS { + // Only process subtrees when RUNNING - skip all other states + if *state != blockchain.FSMStateRUNNING { return nil } diff --git a/services/subtreevalidation/subtreevalidation_api/subtreevalidation_api.pb.go b/services/subtreevalidation/subtreevalidation_api/subtreevalidation_api.pb.go index a71be48392..b106987407 100644 --- a/services/subtreevalidation/subtreevalidation_api/subtreevalidation_api.pb.go +++ b/services/subtreevalidation/subtreevalidation_api/subtreevalidation_api.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11 -// protoc v6.33.2 +// protoc v6.33.4 // source: services/subtreevalidation/subtreevalidation_api/subtreevalidation_api.proto package subtreevalidation_api diff --git a/services/subtreevalidation/subtreevalidation_api/subtreevalidation_api_grpc.pb.go b/services/subtreevalidation/subtreevalidation_api/subtreevalidation_api_grpc.pb.go index c007b8227a..274e4b17ab 100644 --- a/services/subtreevalidation/subtreevalidation_api/subtreevalidation_api_grpc.pb.go +++ b/services/subtreevalidation/subtreevalidation_api/subtreevalidation_api_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.6.0 -// - protoc v6.33.2 +// - protoc v6.33.4 // source: services/subtreevalidation/subtreevalidation_api/subtreevalidation_api.proto package subtreevalidation_api diff --git a/services/validator/validator_api/validator_api.pb.go b/services/validator/validator_api/validator_api.pb.go index 2300e60ce0..d261a98d26 100644 --- a/services/validator/validator_api/validator_api.pb.go +++ b/services/validator/validator_api/validator_api.pb.go @@ -6,7 +6,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11 -// protoc v6.33.2 +// protoc v6.33.4 // source: services/validator/validator_api/validator_api.proto package validator_api diff --git a/services/validator/validator_api/validator_api_grpc.pb.go b/services/validator/validator_api/validator_api_grpc.pb.go index 13bec907c6..aecac0c622 100644 --- a/services/validator/validator_api/validator_api_grpc.pb.go +++ b/services/validator/validator_api/validator_api_grpc.pb.go @@ -6,7 +6,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.6.0 -// - protoc v6.33.2 +// - protoc v6.33.4 // source: services/validator/validator_api/validator_api.proto package validator_api diff --git a/stores/utxo/status.pb.go b/stores/utxo/status.pb.go index 04047a199b..8b7ea22495 100644 --- a/stores/utxo/status.pb.go +++ b/stores/utxo/status.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11 -// protoc v6.33.2 +// protoc v6.33.4 // source: stores/utxo/status.proto package utxo diff --git a/test/nodeHelpers/blockchainDaemon.go b/test/nodeHelpers/blockchainDaemon.go index 15b6f3b5aa..d3d1a4367d 100644 --- a/test/nodeHelpers/blockchainDaemon.go +++ b/test/nodeHelpers/blockchainDaemon.go @@ -138,7 +138,12 @@ func (m *BlockchainDaemon) StartBlockchainService() error { } m.Logger.Infof("Initial FSM state: %v", initialState) - // Run the blockchain FSM to transition from IDLE to RUNNING + // Launch the blockchain FSM to transition from IDLE to LAUNCHING + if err := m.BlockchainClient.Launch(m.ctx, "test"); err != nil { + return errors.NewProcessingError("failed to launch blockchain FSM", err) + } + + // Run the blockchain FSM to transition from LAUNCHING to RUNNING if err := m.BlockchainClient.Run(m.ctx, "test"); err != nil { return errors.NewProcessingError("failed to run blockchain FSM", err) } diff --git a/ui/dashboard/src/internal/api/index.ts b/ui/dashboard/src/internal/api/index.ts index f294dfe37b..e2669231be 100644 --- a/ui/dashboard/src/internal/api/index.ts +++ b/ui/dashboard/src/internal/api/index.ts @@ -362,6 +362,9 @@ export function getFSMEvents(): Promise> { case 'LEGACYSYNC': value = 4 break + case 'LAUNCH': + value = 5 + break default: // Try to extract a numeric ID from the event name if it exists const match = eventName.match(/[_-]?(\d+)$/) diff --git a/ui/dashboard/src/internal/components/page/network/connected-nodes-card/data.ts b/ui/dashboard/src/internal/components/page/network/connected-nodes-card/data.ts index 7744c30753..707850d495 100644 --- a/ui/dashboard/src/internal/components/page/network/connected-nodes-card/data.ts +++ b/ui/dashboard/src/internal/components/page/network/connected-nodes-card/data.ts @@ -18,7 +18,7 @@ const fieldKey = `${pageKey}.fields` // Function to calculate chainwork scores export function calculateChainworkScores(nodes: any[]): Map { const scoreMap = new Map() - + // Collect unique chainwork values and filter out empty/invalid ones const chainworkSet = new Set() nodes.forEach(node => { @@ -26,7 +26,7 @@ export function calculateChainworkScores(nodes: any[]): Map { chainworkSet.add(node.chain_work) } }) - + // Convert to array and sort in descending order (higher chainwork = lower score number) const sortedChainworks = Array.from(chainworkSet).sort((a, b) => { // Compare hex strings as big integers (reversed for descending) @@ -41,7 +41,7 @@ export function calculateChainworkScores(nodes: any[]): Map { sortedChainworks.forEach((chainwork, index) => { chainworkToScore.set(chainwork, index + 1) }) - + // Map each node to its score nodes.forEach(node => { const key = node.peer_id @@ -51,7 +51,7 @@ export function calculateChainworkScores(nodes: any[]): Map { scoreMap.set(key, 0) // No chainwork = score 0 } }) - + return scoreMap } @@ -186,10 +186,10 @@ export const renderCells = { const url = item.base_url || '-' const peerId = item.peer_id || '-' const isCurrentNode = item.isCurrentNode === true - + // Build tooltip with base URL and peer ID const tooltip = `${url}\n${peerId}` - + return { component: RenderSpanWithTooltip, props: { @@ -203,17 +203,17 @@ export const renderCells = { version: (idField, item, colId) => { const fullVersion = item.version || '-' const commitHash = item.commit_hash || '' - + // Try to extract semantic version (e.g., "v1.2.3" from "v1.2.3-abc123") const semverMatch = fullVersion.match(/^(v?\d+\.\d+\.\d+)/) const displayVersion = semverMatch ? semverMatch[1] : fullVersion - + // Build tooltip with full version and commit let tooltipText = fullVersion if (commitHash) { tooltipText = `${fullVersion} (commit: ${commitHash})` } - + return { component: RenderSpanWithTooltip, props: { @@ -242,6 +242,9 @@ export const renderCells = { } else if (state === 'IDLE') { emoji = '⏸️' tooltip = 'IDLE' + } else if (state === 'LAUNCHING') { + emoji = '🚀' + tooltip = 'LAUNCHING' } return { @@ -473,13 +476,13 @@ export const renderCells = { // Support both best_block_hash (from node_status) and hash (from mining_on) const hash = item[colId] || item.hash let miner = item.miner_name || item.miner || '' - + // If miner is not available, lookup from block hash -> miner cache if (!miner && hash) { const minerCache = get(blockHashToMiner) miner = minerCache.get(hash) || '' } - + return { component: hash ? RenderHashWithMiner : null, props: { diff --git a/ui/dashboard/src/routes/admin/+page.svelte b/ui/dashboard/src/routes/admin/+page.svelte index 3d6e2384b2..b68cc19c01 100644 --- a/ui/dashboard/src/routes/admin/+page.svelte +++ b/ui/dashboard/src/routes/admin/+page.svelte @@ -61,14 +61,17 @@ return error instanceof Error ? error.message : String(error) } - // duplicates the FSM transition logic from the backend (fsm_handler.go - // If the backend state machine changes, the UI will become out of sync. + // duplicates the FSM transition logic from the backend (fsm_handler.go + // If the backend state machine changes, the UI will become out of sync. function isEventAllowedForState(state: string | undefined, eventName: string): boolean { if (!state || !eventName) return false switch (state) { case 'IDLE': - return eventName === 'RUN' || eventName === 'LEGACYSYNC' + return eventName === 'LAUNCH' || eventName === 'LEGACYSYNC' + // NOTE: RUN is NOT available from IDLE - must use LAUNCH + case 'LAUNCHING': + return false // No manual events - auto-transitions only case 'RUNNING': return eventName === 'STOP' || eventName === 'CATCHUPBLOCKS' case 'LEGACYSYNCING': @@ -221,6 +224,7 @@ 1: 'RUNNING', 2: 'CATCHING BLOCKS', 3: 'LEGACY SYNCING', + 4: 'LAUNCHING', '-1': 'DISCONNECTED', } @@ -230,6 +234,7 @@ 1: 'green', 2: 'blue', 3: 'purple', + 4: 'orange', // LAUNCHING - orange like a rocket '-1': 'red', } @@ -253,6 +258,8 @@ return 'fas fa-fast-forward' case 'LEGACYSYNC': return 'fas fa-sync' + case 'LAUNCH': + return 'fas fa-rocket' default: return 'fas fa-question' } @@ -270,6 +277,8 @@ return 'Catch Up' case 'LEGACYSYNC': return 'Legacy Sync' + case 'LAUNCH': + return 'Launch' default: return eventName } diff --git a/util/kafka/kafka_message/kafka_messages.pb.go b/util/kafka/kafka_message/kafka_messages.pb.go index e7842d998c..14b350a9bb 100644 --- a/util/kafka/kafka_message/kafka_messages.pb.go +++ b/util/kafka/kafka_message/kafka_messages.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11 -// protoc v6.33.2 +// protoc v6.33.4 // source: util/kafka/kafka_message/kafka_messages.proto package kafkamessage