Skip to content

Conversation

@MitchTurner
Copy link
Member

@MitchTurner MitchTurner commented Sep 16, 2025

Linked Issues/PRs

#3089

Description

Checklist

  • Breaking changes are clearly marked as such in the PR description and changelog
  • New behavior is reflected in tests
  • The specification matches the implemented behavior (link update PR if changes are needed)

Before requesting review

  • I have reviewed the code myself
  • I have created follow-up issues caused by this PR and linked them here

After merging, notify other teams

[Add or remove entries as needed]


Note

Introduces a gRPC Protobuf Block Aggregator API with storage and serialization, integrates it into Fuel Core as an RPC service with CLI config, and adds tests and build support.

  • RPC/API:
    • Add new crates/services/block_aggregator_api with Protobuf definitions (proto/api.proto), tonic server, and adapters (ProtobufAPI).
    • Implement block serialization/deserialization to/from Protobuf (full coverage of block/tx variants).
  • Node Integration:
    • Wire Block Aggregator service into fuel-core sub-services; add CLI args --rpc_ip/--rpc_port and rpc feature.
    • Extend Config with rpc_config; start service alongside GraphQL/others.
  • Storage:
    • Add block_aggregation DB to CombinedDatabase; define BlockAggregatorDatabase and storage tables for Protobuf Blocks.
    • Implement BlockAggregatorDB with range streaming and height tracking.
  • Serialization/Types:
    • Use Protobuf types instead of opaque bytes; add serializer adapter and header helpers in fuel-core-types.
  • Tests:
    • Add RPC integration tests and proptests for (de)serialization; extend test helpers.
  • Build/CI:
    • Install protoc in CI; add prost/tonic deps; ignore RUSTSEC-2025-0118; update .gitignore.

Written by Cursor Bugbot for commit 0da8556. This will update automatically on new commits. Configure here.

thiserror = { workspace = true }
tokio = { workspace = true }
tokio-stream = { workspace = true }
tonic = { workspace = true }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

think we want to support some form of compression. what you think between deflate and zstd ?

Copy link
Member Author

@MitchTurner MitchTurner Oct 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I add some naive serialization here:
#3101

But you're right, we could do some additional compression.

Maybe we need to version the serialization too....

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is compression at the rpc level, so it will be negotiated elsewhere, not talking about the serialized bytes of the block here :)

Comment on lines 156 to 162
let _server_task_handle = tokio::spawn(async move {
tonic::transport::Server::builder()
.add_service(block_aggregator_server::BlockAggregatorServer::new(server))
.serve(addr)
.await
.unwrap();
});
Copy link
Member Author

@MitchTurner MitchTurner Oct 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I add handling for this error as well as adding a drop impl for this type so the thread doesn't get orphaned here:
7f917ba
(in the next PR)

Comment on lines +177 to +178
- name: Install Protoc
uses: arduino/setup-protoc@v3
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to not require additional dependency? For example, maybe tonic could use Rust-based implementation of the protobuf?

If not, I think we should follow @rymnc's suggestion and move Protobuf definition into a separate repository which will publish a Rust crate which we can use later in fuel-core, t avoid additional dependency for SDK and Sway

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. That makes sense to me.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to remove dependency from protoc before we merge this change to the master=)

uint32 end = 2;
}

message Block {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea it to convert FuelBlock into a protobuf block, where protobuf block has all fields of FuelBlock. We don't plan to use any other serialization, so it is not clear to me what is data field here.

Copy link
Member Author

@MitchTurner MitchTurner Oct 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh. That's slightly different from what I understood. I'm just assuming it's getting serialized to bytes from an arbitrary serializer. The protobuf type is just accepting those bytes. I don't think it will be too hard to change it and still maintain dependency inversion.

@@ -0,0 +1,33 @@
syntax = "proto3";

package blockaggregator;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to generate protobuf schema from the rust type?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It all seems to be .proto -> Rust. That's fine from a clean-architecture perspective.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tokio-rs/prost#1350

Let's see what they will suggest

cursor[bot]

This comment was marked as outdated.

MitchTurner added a commit that referenced this pull request Nov 12, 2025
## Linked Issues/PRs
<!-- List of related issues/PRs -->
followup to feedback from
#3100

## Description
<!-- List of detailed changes -->

## Checklist
- [ ] Breaking changes are clearly marked as such in the PR description
and changelog
- [ ] New behavior is reflected in tests
- [ ] [The specification](https://github.com/FuelLabs/fuel-specs/)
matches the implemented behavior (link update PR if changes are needed)

### Before requesting review
- [ ] I have reviewed the code myself
- [ ] I have created follow-up issues caused by this PR and linked them
here

### After merging, notify other teams

[Add or remove entries as needed]

- [ ] [Rust SDK](https://github.com/FuelLabs/fuels-rs/)
- [ ] [Sway compiler](https://github.com/FuelLabs/sway/)
- [ ] [Platform
documentation](https://github.com/FuelLabs/devrel-requests/issues/new?assignees=&labels=new+request&projects=&template=NEW-REQUEST.yml&title=%5BRequest%5D%3A+)
(for out-of-organization contributors, the person merging the PR will do
this)
- [ ] Someone else?

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> <sup>[Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) is
generating a summary for commit
c0a6540. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## Linked Issues/PRs
<!-- List of related issues/PRs -->
Closes #3090

## Description
<!-- List of detailed changes -->

## Checklist
- [ ] Breaking changes are clearly marked as such in the PR description
and changelog
- [ ] New behavior is reflected in tests
- [ ] [The specification](https://github.com/FuelLabs/fuel-specs/)
matches the implemented behavior (link update PR if changes are needed)

### Before requesting review
- [ ] I have reviewed the code myself
- [ ] I have created follow-up issues caused by this PR and linked them
here

### After merging, notify other teams

[Add or remove entries as needed]

- [ ] [Rust SDK](https://github.com/FuelLabs/fuels-rs/)
- [ ] [Sway compiler](https://github.com/FuelLabs/sway/)
- [ ] [Platform
documentation](https://github.com/FuelLabs/devrel-requests/issues/new?assignees=&labels=new+request&projects=&template=NEW-REQUEST.yml&title=%5BRequest%5D%3A+)
(for out-of-organization contributors, the person merging the PR will do
this)
- [ ] Someone else?

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> <sup>[Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) is
generating a summary for commit
4bb648d. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is being reviewed by Cursor Bugbot

Details

Your team is on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle for each member of your team.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

Bug: Incomplete Backups Cause Data Loss

The backup_databases method doesn't back up the newly added block_aggregation database, causing data loss during backup operations.

crates/fuel-core/src/combined_database.rs#L116-L137

#[cfg(feature = "backup")]
fn backup_databases(
db_dir: &std::path::Path,
temp_dir: &std::path::Path,
) -> crate::database::Result<()> {
crate::state::rocks_db::RocksDb::<OnChain>::backup(db_dir, temp_dir)
.trace_err("Failed to backup on-chain database")?;
crate::state::rocks_db::RocksDb::<OffChain>::backup(db_dir, temp_dir)
.trace_err("Failed to backup off-chain database")?;
crate::state::rocks_db::RocksDb::<Relayer>::backup(db_dir, temp_dir)
.trace_err("Failed to backup relayer database")?;
crate::state::rocks_db::RocksDb::<GasPriceDatabase>::backup(db_dir, temp_dir)
.trace_err("Failed to backup gas-price database")?;
crate::state::rocks_db::RocksDb::<CompressionDatabase>::backup(db_dir, temp_dir)
.trace_err("Failed to backup compression database")?;
Ok(())
}

Fix in Cursor Fix in Web


Bug: New Database Causes Shutdown Failure

The shutdown method doesn't call shutdown on the newly added block_aggregation database, potentially leaving resources uncleaned and file handles open.

crates/fuel-core/src/combined_database.rs#L611-L618

pub fn shutdown(self) {
self.on_chain.shutdown();
self.off_chain.shutdown();
self.relayer.shutdown();
self.gas_price.shutdown();
self.compression.shutdown();
}

Fix in Cursor Fix in Web


&self.compression
}

pub fn block_aggregation(&self) -> &Database<BlockAggregatorDatabase> {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Incomplete Database Version Validation Risks Corruption

The check_version method doesn't validate the newly added block_aggregation database, which could allow version mismatches to go undetected and cause data corruption or compatibility issues at runtime.

Fix in Cursor Fix in Web

@MitchTurner MitchTurner mentioned this pull request Nov 14, 2025
31 tasks
@MitchTurner MitchTurner changed the base branch from master to chore/rustsec-2025-0118 November 17, 2025 17:33
@MitchTurner MitchTurner changed the base branch from chore/rustsec-2025-0118 to master November 17, 2025 18:30
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Block aggregator database not shut down properly

The shutdown() method does not call shutdown() on the block_aggregation database. This means the block aggregator database is not properly closed when the combined database shuts down, potentially leaving resources unfreed and causing data consistency issues if the service is restarted without proper cleanup.

crates/fuel-core/src/combined_database.rs#L490-L497

"compression database height({compression_db_height}) \
is less than target height({target_block_height})"
));
}
if on_chain_height > target_block_height {
self.on_chain().rollback_last_block()?;
}

Fix in Cursor Fix in Web


relayer,
gas_price,
compression,
block_aggregation,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Block aggregator database not included in backup/restore operations

The newly added block_aggregation database field is not being backed up or restored in the backup_databases() and restore_database() methods. When backup/restore is called, the block aggregator database is silently skipped, which causes data loss if the backup is ever used to restore a node's state. This can result in missing block data that was previously stored.

Fix in Cursor Fix in Web

.echo_delegation_interval,
};

let rpc_config = rpc_args.into_config();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Unconditional RPC config creation when feature disabled

The rpc_config variable is created unconditionally at line 460, but it's only used conditionally when the rpc feature is enabled (line 787-788). When compiling without the rpc feature, this creates an unused variable that will trigger a compiler warning or error. The line should be wrapped in #[cfg(feature = "rpc")] to match where it's consumed.

Fix in Cursor Fix in Web

xgreenx pushed a commit that referenced this pull request Nov 25, 2025
## Linked Issues/PRs

Complete feedback from:
#3100
#3101
#3112
#3116

As they have all been merged into 
#3100

### TODOs

#### Completed
- [x] Move proto types to new repo (or at least remove dep on protoc)
FuelLabs/fuel-core-protobuf#1
- [x] Use ipv4 instead of ipv4 `let listener =
TcpListener::bind("[::1]:0").unwrap();`
- [x] make sure rpc is optional (may already be good with feature)
- [x] "If we add a new database, we should include it in all places
where we already interact with databases, like check_version,
rollback_to, and so on(check where do we use databa liek relayer)"
- [x] "The CombinedDatabase::check_version() method is missing a version
check for the new block_aggregation database. This omission is
inconsistent with other database checks and could lead to version
compatibility issues."
- [x] "I would like to mention the reason why this database doesn't
force a monotonic increase in height."
- [x] "Also, becuase we don't force monotonic height increase, we can't
actually re-use rollback feature from historicat database. But we still
need the ability to reset the state of the block aggregator to height X.
So you can add another function, which we can call in rollback_to."
- [x] "I know that ServiceRunner here is an overkill, but we have
GraphQL that looks the same, and it was fiiiiine=D Maybe for consistency
plus some logging we could reuse it here as well. It's just an internal
service for your main service"
- [x] `// TODO: Should this be owned to begin with?` Yeah, we should
work with reference in proto_header_from_header and in proto_tx_from_tx
- [x] maybe "If you used BoxStream, then you could avoid usage of the
tokio::spawn( below. You can just do inner.map({...}).into_boxed()" in
protobuf_adapater impl of protobuf trait: `type GetBlockRangeStream =
BoxStream<Result<ProtoBlockResponse, Status>>;`
- [x] `#[allow(unused)] fn arb_inputs()`
- [x] in `serializer_adapter.rs`: "We should work with reference to
everywhere in this file. And we should also avoid usage of
unswap_or_default. All fields are always set, so it is strange why it
can be None`
- [x] `if let Some(value) = policies.get(PolicyType::Owner) {
    values[5] = value;
}`
- [x] in `serializer_adapter.rs`: "Why values for policies in an empty
array? If the policy is not set, we shouldn't include them. If no policy
is set, then it will be empty vector."
- [x] in `crates/types/src/blockchain/header.rs`: "We don't need to
expose the application header. You can find an example on how to create
a header here:
https://github.com/FuelLabs/fuel-rust-indexer/blob/main/crates/receipts_manager/adapters/graphql_event_adapter.rs#L250",
"And the same for the block, you don't need manually calculate it, you
can do that from the Block::new."
- [x] in `types/src/test_helpers.rs`: "Block::new should be enough for
you to create a header and a block. You don't need to implement all the
logic by yourself.You can clean up getters and setters which you've
added, after you udpated code here=)
"
- [x] Maybe remove `self.go_to_sleep_before_continuing().await;`

#### Need Feedback
- [ ] in `serializer_adapter.rs`: "If we use Rust definition to define
variants inside of enums, then we can remove useless fields like data
for MessageCoinPredicate and witness_index for MessageCoinPredicate"
(@xgreenx I don't understand this. What "Rust definition"?)

#### Noop
- [ ] ~in `serializer_adapter.rs`: "I think instead of creating many
procedure fucntions, we could implement TryFrom and From for types,
plus, we could split them into its own folders by transactions and some
common folder."~ **(Since this is in a separate repo now, the
TryFrom/From impls wouldn't be valid due to the orphan rule)**
- [ ] ~in `api.proto`: "I think all txs have these, maybe we could move
them to the top-level Transaction?" DRY up chargeable txs?~**( I don't
really care for this abstraction, but I could be convinced to DRY this
up. Just doesn't seem like much gain)**
- [ ] ~I think we can move fetching of the full blocks to be a part of
default functionality provided by FuelClient along with old
functionality (in `test-helpers/src/client_ext.rs`)~ **(I don't want to
add new functionality in this work, we could do a followup)**
- [ ] ~"[nit]
https://github.com/FuelLabs/fuel-rust-indexer/blob/main/crates/receipts_manager/service.rs#L584
We can replace the whole service with stream of joined events(old +
new)"~ **(WE could do this as a followup)**


## Description
<!-- List of detailed changes -->

## Checklist
- [ ] Breaking changes are clearly marked as such in the PR description
and changelog
- [ ] New behavior is reflected in tests
- [ ] [The specification](https://github.com/FuelLabs/fuel-specs/)
matches the implemented behavior (link update PR if changes are needed)

### Before requesting review
- [ ] I have reviewed the code myself
- [ ] I have created follow-up issues caused by this PR and linked them
here

### After merging, notify other teams

[Add or remove entries as needed]

- [ ] [Rust SDK](https://github.com/FuelLabs/fuels-rs/)
- [ ] [Sway compiler](https://github.com/FuelLabs/sway/)
- [ ] [Platform
documentation](https://github.com/FuelLabs/devrel-requests/issues/new?assignees=&labels=new+request&projects=&template=NEW-REQUEST.yml&title=%5BRequest%5D%3A+)
(for out-of-organization contributors, the person merging the PR will do
this)
- [ ] Someone else?
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants