Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
64016b0
Implement dry run in the past
Dentosal Feb 3, 2025
312077b
Add changelog entry
Dentosal Feb 3, 2025
883a51b
Fix typo
Dentosal Feb 3, 2025
b7dfc6e
Require --debug flag for setting block height to past
Dentosal Feb 3, 2025
3a0d381
Merge branch 'master' into dento/2062_dry_run_in_the_past
Dentosal Feb 5, 2025
07013c7
Fix broken merge
Dentosal Feb 5, 2025
0dd8f64
Workaround linux pthread issue of rocksdb
Dentosal Feb 6, 2025
52a981a
Merge branch 'master' into dento/2062_dry_run_in_the_past
Dentosal Feb 6, 2025
77e0b89
Merge branch 'master' into dento/2062_dry_run_in_the_past
Dentosal Feb 7, 2025
1dd6847
Move from --debug to --historical-execution, force off for InMemory db
Dentosal Feb 7, 2025
8345f46
fmt
Dentosal Feb 7, 2025
2245cc1
Properly use --historical-execution
Dentosal Feb 7, 2025
d3984ca
Enable --historical-execution for dry run in past test
Dentosal Feb 7, 2025
4599802
Add API endpoints to read the storage information
xgreenx Feb 1, 2025
046e902
Add support for storage balances as well
xgreenx Feb 1, 2025
0d62baa
Merge branch 'master' into dento/graphql-storage-api
Dentosal Feb 18, 2025
bfecd5f
Fix issues after merge
Dentosal Feb 18, 2025
ab176bf
fmt
Dentosal Feb 18, 2025
9916070
clippy
Dentosal Feb 19, 2025
58e5312
Do not log full byte arrays on "schema not up to date" test
Dentosal Feb 19, 2025
be86870
Update schema
Dentosal Feb 19, 2025
a9ec886
fmt
Dentosal Feb 19, 2025
a28be05
Merge branch 'master' into dento/graphql-storage-api
Dentosal Feb 19, 2025
5fe40cb
Fix merge of CHANGELOG.md
Dentosal Feb 21, 2025
1f001ed
Merge branch 'master' into dento/graphql-storage-api
Dentosal Feb 21, 2025
8aac38a
Merge branch 'master' into dento/graphql-storage-api
Dentosal Feb 26, 2025
2a3c62b
Fix some lifetime bounds
Dentosal Feb 26, 2025
2c0b8fc
Add changelog
Dentosal Feb 26, 2025
d2a58c4
Merge branch 'master' into dento/graphql-storage-api
Dentosal Feb 26, 2025
33b2883
Return back caching of the `ReadView` but now after the required heig…
xgreenx Feb 27, 2025
7e4b9bd
Fixed the issue with the subscription and non-working required block …
xgreenx Feb 27, 2025
2e4e005
Updated CHANGELOG
xgreenx Feb 27, 2025
c194340
Merge branch 'master' into dento/graphql-storage-api
xgreenx Feb 27, 2025
49b3f7c
Condense storage tests
Dentosal Feb 28, 2025
df92693
Add storage tests
Dentosal Feb 28, 2025
b615bda
Document that this is using historical execution flag
Dentosal Feb 28, 2025
48dfd68
Update schema.sdl
Dentosal Feb 28, 2025
809534b
Improve changelog
Dentosal Feb 28, 2025
1ee14ee
fmt
Dentosal Feb 28, 2025
dc031e1
Update tests/tests/balances.rs
Dentosal Feb 28, 2025
756a8b6
Merge branch 'master' into dento/graphql-storage-api
Dentosal Feb 28, 2025
47cc999
Merge branch 'master' into dento/graphql-storage-api
Dentosal Mar 3, 2025
1d45cbc
Merge branch 'master' into dento/graphql-storage-api
xgreenx Mar 4, 2025
3760f03
Merge branch 'master' into dento/graphql-storage-api
AurelienFT Mar 4, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .changes/added/2682.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added GraphQL APIs to get contract storage and balances for current and past blocks.
1 change: 1 addition & 0 deletions .changes/fixed/2682.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed the issue with RPC consistency feature for the subscriptions(without the fix first we perform the logic of the query, and only after verify the required height).
19 changes: 19 additions & 0 deletions crates/client/assets/schema.sdl
Original file line number Diff line number Diff line change
Expand Up @@ -1034,6 +1034,18 @@ type Query {
consensusParameters(version: Int!): ConsensusParameters!
stateTransitionBytecodeByVersion(version: Int!): StateTransitionBytecode
stateTransitionBytecodeByRoot(root: HexString!): StateTransitionBytecode!
"""
Get storage slot values for a contract at a specific block height.
Use the latest block height if not provided.
Requires historical execution config to be enabled.
"""
contractSlotValues(contractId: ContractId!, blockHeight: U32, storageSlots: [Bytes32!]!): [StorageSlot!]!
"""
Get balance values for a contract at a specific block height.
Use the latest block height if not provided.
Requires historical execution config to be enabled.
"""
contractBalanceValues(contractId: ContractId!, blockHeight: U32, assets: [AssetId!]!): [ContractBalance!]!
}

type Receipt {
Expand Down Expand Up @@ -1166,6 +1178,11 @@ type StorageReadReplayEvent {
value: HexString
}

type StorageSlot {
key: Bytes32!
value: HexString!
}


scalar SubId

Expand Down Expand Up @@ -1204,6 +1221,8 @@ type Subscription {
SubmittedStatus` as an intermediate state.
"""
submitAndAwaitStatus(tx: HexString!): TransactionStatus!
contractStorageSlots(contractId: ContractId!): StorageSlot!
contractStorageBalances(contractId: ContractId!): ContractBalance!
}

type SuccessStatus {
Expand Down
97 changes: 97 additions & 0 deletions crates/client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -850,6 +850,103 @@ impl FuelClient {
Ok(stream)
}

/// Requests all storage slots for the `contract_id`.
#[cfg(feature = "subscriptions")]
pub async fn contract_storage_slots<'a>(
&'a self,
contract_id: &'a ContractId,
) -> io::Result<impl Stream<Item = io::Result<(Bytes32, Vec<u8>)>> + 'a> {
use cynic::SubscriptionBuilder;
use schema::storage::ContractStorageSlotsArgs;
let s = schema::storage::ContractStorageSlots::build(ContractStorageSlotsArgs {
contract_id: (*contract_id).into(),
});

let stream = self.subscribe(s).await?.map(
|result: io::Result<schema::storage::ContractStorageSlots>| {
let result: (Bytes32, Vec<u8>) = result?.contract_storage_slots.into();
Result::<_, io::Error>::Ok(result)
},
);

Ok(stream)
}

/// Requests all storage balances for the `contract_id`.
#[cfg(feature = "subscriptions")]
pub async fn contract_storage_balances<'a>(
&'a self,
contract_id: &'a ContractId,
) -> io::Result<impl Stream<Item = io::Result<schema::contract::ContractBalance>> + 'a>
{
use cynic::SubscriptionBuilder;
use schema::{
contract::ContractBalance,
storage::ContractStorageBalancesArgs,
};
let s = schema::storage::ContractStorageBalances::build(
ContractStorageBalancesArgs {
contract_id: (*contract_id).into(),
},
);

let stream = self.subscribe(s).await?.map(
|result: io::Result<schema::storage::ContractStorageBalances>| {
let result: ContractBalance = result?.contract_storage_balances;
Result::<_, io::Error>::Ok(result)
},
);

Ok(stream)
}

pub async fn contract_slots_values(
&self,
contract_id: &ContractId,
block_height: Option<BlockHeight>,
requested_storage_slots: Vec<Bytes32>,
) -> io::Result<Vec<(Bytes32, Vec<u8>)>> {
let query = schema::storage::ContractSlotValues::build(
schema::storage::ContractSlotValuesArgs {
contract_id: (*contract_id).into(),
block_height: block_height.map(|b| (*b).into()),
storage_slots: requested_storage_slots
.into_iter()
.map(Into::into)
.collect(),
},
);

self.query(query)
.await
.map(|r| r.contract_slot_values.into_iter().map(Into::into).collect())
}

pub async fn contract_balance_values(
&self,
contract_id: &ContractId,
block_height: Option<BlockHeight>,
requested_storage_slots: Vec<AssetId>,
) -> io::Result<Vec<schema::contract::ContractBalance>> {
let query = schema::storage::ContractBalanceValues::build(
schema::storage::ContractBalanceValuesArgs {
contract_id: (*contract_id).into(),
block_height: block_height.map(|b| (*b).into()),
assets: requested_storage_slots
.into_iter()
.map(Into::into)
.collect(),
},
);

self.query(query).await.map(|r| {
r.contract_balance_values
.into_iter()
.map(Into::into)
.collect()
})
}

pub async fn start_session(&self) -> io::Result<String> {
let query = schema::StartSession::build(());

Expand Down
1 change: 1 addition & 0 deletions crates/client/src/client/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ pub mod primitives;
pub mod tx;

pub mod relayed_tx;
pub mod storage;

#[derive(cynic::QueryFragment, Clone, Debug)]
#[cynic(schema_path = "./assets/schema.sdl", graphql_type = "Query")]
Expand Down
93 changes: 93 additions & 0 deletions crates/client/src/client/schema/storage.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
use crate::client::schema::{
contract::ContractBalance,
schema,
AssetId,
Bytes32,
ContractId,
HexString,
U32,
};
use fuel_core_types::fuel_tx;

#[derive(cynic::QueryFragment, Clone, Debug)]
#[cynic(
schema_path = "./assets/schema.sdl",
graphql_type = "Subscription",
variables = "ContractStorageSlotsArgs"
)]
pub struct ContractStorageSlots {
#[arguments(contractId: $contract_id)]
pub contract_storage_slots: StorageSlot,
}

#[derive(cynic::QueryVariables, Debug)]
pub struct ContractStorageSlotsArgs {
/// The ID of the contract.
pub contract_id: ContractId,
}

#[derive(cynic::QueryFragment, Clone, Debug)]
#[cynic(
schema_path = "./assets/schema.sdl",
graphql_type = "Query",
variables = "ContractSlotValuesArgs"
)]
pub struct ContractSlotValues {
#[arguments(contractId: $contract_id, blockHeight: $block_height, storageSlots: $storage_slots)]
pub contract_slot_values: Vec<StorageSlot>,
}

#[derive(cynic::QueryVariables, Debug)]
pub struct ContractSlotValuesArgs {
pub contract_id: ContractId,
pub block_height: Option<U32>,
pub storage_slots: Vec<Bytes32>,
}

#[derive(cynic::QueryFragment, Clone, Debug)]
#[cynic(schema_path = "./assets/schema.sdl")]
pub struct StorageSlot {
pub key: Bytes32,
pub value: HexString,
}

impl From<StorageSlot> for (fuel_tx::Bytes32, Vec<u8>) {
fn from(value: StorageSlot) -> Self {
(value.key.into(), value.value.into())
}
}

#[derive(cynic::QueryFragment, Clone, Debug)]
#[cynic(
schema_path = "./assets/schema.sdl",
graphql_type = "Subscription",
variables = "ContractStorageBalancesArgs"
)]
pub struct ContractStorageBalances {
#[arguments(contractId: $contract_id)]
pub contract_storage_balances: ContractBalance,
}

#[derive(cynic::QueryVariables, Debug)]
pub struct ContractStorageBalancesArgs {
/// The ID of the contract.
pub contract_id: ContractId,
}

#[derive(cynic::QueryFragment, Clone, Debug)]
#[cynic(
schema_path = "./assets/schema.sdl",
graphql_type = "Query",
variables = "ContractBalanceValuesArgs"
)]
pub struct ContractBalanceValues {
#[arguments(contractId: $contract_id, blockHeight: $block_height, assets: $assets)]
pub contract_balance_values: Vec<ContractBalance>,
}

#[derive(cynic::QueryVariables, Debug)]
pub struct ContractBalanceValuesArgs {
pub contract_id: ContractId,
pub block_height: Option<U32>,
pub assets: Vec<AssetId>,
}
5 changes: 5 additions & 0 deletions crates/client/src/client/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,3 +318,8 @@ impl TryFrom<TransactionType> for Transaction {
}
}
}

pub struct StorageSlot {
pub key: primitives::Bytes32,
pub value: primitives::Bytes,
}
3 changes: 2 additions & 1 deletion crates/fuel-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ hex = { workspace = true }
hyper = { workspace = true }
indicatif = { workspace = true, default-features = true }
itertools = { workspace = true }
mockall = { workspace = true, optional = true }
num_cpus = { version = "1.16.0", optional = true }
parking_lot = { workspace = true }
paste = { workspace = true }
Expand Down Expand Up @@ -85,7 +86,6 @@ fuel-core-types = { path = "./../types", features = ["test-helpers"] }
fuel-core-upgradable-executor = { workspace = true, features = [
"test-helpers",
] }
mockall = { workspace = true }
proptest = { workspace = true }
test-case = { workspace = true }
test-strategy = { workspace = true }
Expand Down Expand Up @@ -114,6 +114,7 @@ test-helpers = [
"fuel-core-services/test-helpers",
"fuel-core-shared-sequencer?/test-helpers",
"fuel-core-importer/test-helpers",
"dep:mockall",
]
# features to enable in production, but increase build times
rocksdb-production = ["rocksdb", "rocksdb/jemalloc"]
Expand Down
2 changes: 2 additions & 0 deletions crates/fuel-core/src/database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,10 @@ where

pub type Database<Description = OnChain, Stage = RegularStage<Description>> =
GenericDatabase<DataSource<Description, Stage>, Empty>;
pub type OnChainKeyValueView = KeyValueView<ColumnType<OnChain>, HeightType<OnChain>>;
pub type OnChainIterableKeyValueView =
IterableKeyValueView<ColumnType<OnChain>, HeightType<OnChain>>;
pub type OffChainKeyValueView = KeyValueView<ColumnType<OffChain>, HeightType<OffChain>>;
pub type OffChainIterableKeyValueView =
IterableKeyValueView<ColumnType<OffChain>, HeightType<OffChain>>;
pub type RelayerIterableKeyValueView =
Expand Down
13 changes: 13 additions & 0 deletions crates/fuel-core/src/graphql_api.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use async_graphql::Context;
use fuel_core_storage::{
Error as StorageError,
IsNotFound,
Expand Down Expand Up @@ -168,3 +169,15 @@ impl<T> IntoApiResult<T> for Result<T, StorageError> {
}
}
}

pub fn require_historical_execution(ctx: &Context<'_>) -> async_graphql::Result<()> {
let config = ctx.data_unchecked::<Config>();

if config.historical_execution {
Ok(())
} else {
Err(async_graphql::Error::new(
"`--historical-execution` is required for this operation",
))
}
}
Loading
Loading