Skip to content

graphql-alt: Epoch.coinDenyList #22005

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: amnn/gql-protocol
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

//# init --protocol-version 70 --accounts A --addresses P=0x0 --simulator

//# advance-epoch

//# publish --sender A
module P::coin {
use sui::coin::{Self, CoinMetadata, DenyCapV2, TreasuryCap};
use sui::deny_list::DenyList;

public struct COIN() has drop;

public struct Bundle has key, store {
id: UID,
treasury: TreasuryCap<COIN>,
deny: DenyCapV2<COIN>,
metadata: CoinMetadata<COIN>,
}

fun init(otw: COIN, ctx: &mut TxContext) {
let (treasury, deny, metadata) = coin::create_regulated_currency_v2(
otw,
9,
b"COIN",
b"Coin",
b"A test coin",
option::none(),
true,
ctx,
);

transfer::public_share_object(Bundle {
id: object::new(ctx),
treasury,
deny,
metadata,
});
}

public fun poke_deny_list(
deny_list: &mut DenyList,
bundle: &mut Bundle,
ctx: &mut TxContext,
) {
coin::deny_list_v2_add(deny_list, &mut bundle.deny, @0x1234, ctx)
}
}

//# programmable --sender A --inputs object(0x403) object(2,0)
//> P::coin::poke_deny_list(Input(0), Input(1))

//# create-checkpoint

//# advance-epoch

//# create-checkpoint

//# run-graphql
{
e0: epoch(epochId: 0) {
coinDenyList { address version }
}

e1: epoch(epochId: 1) {
coinDenyList { address version }
}

e2: epoch(epochId: 2) {
coinDenyList { address version }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
source: external-crates/move/crates/move-transactional-test-runner/src/framework.rs
---
processed 8 tasks

init:
A: object(0,0)

task 1, line 6:
//# advance-epoch
Epoch advanced: 0

task 2, lines 8-49:
//# publish --sender A
created: object(2,0), object(2,1), object(2,2)
mutated: object(0,0)
gas summary: computation_cost: 1000000, storage_cost: 13862400, storage_rebate: 0, non_refundable_storage_fee: 0

task 3, lines 51-52:
//# programmable --sender A --inputs object(0x403) object(2,0)
//> P::coin::poke_deny_list(Input(0), Input(1))
events: Event { package_id: P, transaction_module: Identifier("coin"), sender: A, type_: StructTag { address: sui, module: Identifier("deny_list"), name: Identifier("PerTypeConfigCreated"), type_params: [] }, contents: [0, 0, 0, 0, 0, 0, 0, 0, 76, 102, 102, 53, 56, 51, 56, 100, 56, 52, 102, 49, 50, 48, 50, 100, 53, 53, 56, 99, 100, 101, 98, 102, 51, 51, 55, 50, 102, 100, 102, 98, 53, 50, 55, 57, 51, 55, 100, 54, 100, 101, 57, 52, 49, 57, 57, 53, 49, 53, 56, 100, 50, 98, 48, 52, 102, 100, 53, 53, 49, 98, 48, 52, 51, 58, 58, 99, 111, 105, 110, 58, 58, 67, 79, 73, 78, 42, 225, 19, 155, 21, 61, 71, 196, 117, 247, 51, 253, 36, 246, 86, 170, 118, 162, 71, 45, 111, 211, 99, 199, 93, 84, 154, 150, 42, 206, 127, 221] }
created: object(3,0), object(3,1), object(3,2)
mutated: 0x0000000000000000000000000000000000000000000000000000000000000403, object(0,0), object(2,0)
gas summary: computation_cost: 1000000, storage_cost: 12502000, storage_rebate: 3205224, non_refundable_storage_fee: 32376

task 4, line 54:
//# create-checkpoint
Checkpoint created: 2

task 5, line 56:
//# advance-epoch
Epoch advanced: 1

task 6, line 58:
//# create-checkpoint
Checkpoint created: 4

task 7, lines 60-73:
//# run-graphql
Response: {
"data": {
"e0": {
"coinDenyList": {
"address": "0x0000000000000000000000000000000000000000000000000000000000000403",
"version": 1
}
},
"e1": {
"coinDenyList": {
"address": "0x0000000000000000000000000000000000000000000000000000000000000403",
"version": 1
}
},
"e2": {
"coinDenyList": {
"address": "0x0000000000000000000000000000000000000000000000000000000000000403",
"version": 3
}
}
}
}
6 changes: 6 additions & 0 deletions crates/sui-indexer-alt-graphql/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ type Epoch {
"""
epochId: UInt53!
"""
State of the Coin DenyList object (0x403) at the start of this epoch.

The DenyList controls access to Regulated Coins. Writes to the DenyList are accumulated and only take effect on the next epoch boundary. Subsequently, it's possible to determine the state of the DenyList for a transaction by reading it at the start of the epoch the transaction is in.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
The DenyList controls access to Regulated Coins. Writes to the DenyList are accumulated and only take effect on the next epoch boundary. Subsequently, it's possible to determine the state of the DenyList for a transaction by reading it at the start of the epoch the transaction is in.
The DenyList controls access to Regulated Coins. Writes to the DenyList are accumulated and only take effect on the next epoch boundary. Consequently, it's possible to determine the state of the DenyList for a transaction by reading it at the start of the epoch the transaction is in.

"""
coinDenyList: Object
"""
The epoch's corresponding protocol configuration, including the feature flags and the configuration options.
"""
protocolConfigs: ProtocolConfigs
Expand Down
57 changes: 38 additions & 19 deletions crates/sui-indexer-alt-graphql/src/api/types/epoch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,27 @@ use sui_indexer_alt_reader::{
pg_reader::PgReader,
};
use sui_indexer_alt_schema::epochs::{StoredEpochEnd, StoredEpochStart};
use sui_types::SUI_DENY_LIST_OBJECT_ID;

use crate::{
api::scalars::{big_int::BigInt, date_time::DateTime, uint53::UInt53},
error::RpcError,
scope::Scope,
};

use super::protocol_configs::ProtocolConfigs;
use super::{
object::{self, Object},
protocol_configs::ProtocolConfigs,
};

pub(crate) struct Epoch {
pub(crate) epoch_id: u64,
pub(crate) scope: Scope,
Copy link
Contributor

Choose a reason for hiding this comment

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

Now that I'm revisiting this, why not have

Epoch {
epoch_id
scope
}

fn start -> EpochStart::fetch
fn end -> EpochEnd::fetch

Not sure if we need the EpochStart because it's basically the scope and epoch_id before fetching contents, and the contents from db are only used on EpochStart

start: EpochStart,
}

#[derive(Clone)]
struct EpochStart {
scope: Scope,
contents: Option<Arc<StoredEpochStart>>,
}

Expand Down Expand Up @@ -54,17 +58,37 @@ impl Epoch {

#[graphql(flatten)]
async fn start(&self, ctx: &Context<'_>) -> Result<EpochStart, RpcError> {
self.start.fetch(ctx, &self.scope, self.epoch_id).await
self.start.fetch(ctx, self.epoch_id).await
}

#[graphql(flatten)]
async fn end(&self, ctx: &Context<'_>) -> Result<EpochEnd, RpcError> {
EpochEnd::fetch(ctx, &self.scope, self.epoch_id).await
EpochEnd::fetch(ctx, &self.start.scope, self.epoch_id).await
}
}

#[Object]
impl EpochStart {
/// State of the Coin DenyList object (0x403) at the start of this epoch.
///
/// The DenyList controls access to Regulated Coins. Writes to the DenyList are accumulated and only take effect on the next epoch boundary. Subsequently, it's possible to determine the state of the DenyList for a transaction by reading it at the start of the epoch the transaction is in.
async fn coin_deny_list(
&self,
ctx: &Context<'_>,
) -> Result<Option<Object>, RpcError<object::Error>> {
let Some(contents) = &self.contents else {
return Ok(None);
};

Object::checkpoint_bounded(
ctx,
self.scope.clone(),
SUI_DENY_LIST_OBJECT_ID.into(),
(contents.cp_lo as u64).saturating_sub(1).into(),
)
.await
}

/// The epoch's corresponding protocol configuration, including the feature flags and the configuration options.
async fn protocol_configs(&self) -> Option<ProtocolConfigs> {
let Some(contents) = &self.contents else {
Expand Down Expand Up @@ -114,8 +138,7 @@ impl Epoch {
pub(crate) fn with_id(scope: Scope, epoch_id: u64) -> Self {
Self {
epoch_id,
scope,
start: EpochStart::empty(),
start: EpochStart::empty(scope),
}
}

Expand All @@ -127,37 +150,32 @@ impl Epoch {
scope: Scope,
epoch_id: UInt53,
) -> Result<Option<Self>, RpcError> {
let start = EpochStart::empty()
.fetch(ctx, &scope, epoch_id.into())
.await?;
let start = EpochStart::empty(scope).fetch(ctx, epoch_id.into()).await?;

let Some(contents) = &start.contents else {
return Ok(None);
};

Ok(Some(Self {
epoch_id: contents.epoch as u64,
scope,
start,
}))
}
}

impl EpochStart {
fn empty() -> Self {
Self { contents: None }
fn empty(scope: Scope) -> Self {
Self {
scope,
contents: None,
}
}

/// Attempt to fill the contents. If the contents are already filled, returns a clone,
/// otherwise attempts to fetch from the store. The resulting value may still have an empty
/// contents field, because it could not be found in the store, or the epoch started after the
/// checkpoint being viewed.
async fn fetch(
&self,
ctx: &Context<'_>,
scope: &Scope,
epoch_id: u64,
) -> Result<Self, RpcError> {
async fn fetch(&self, ctx: &Context<'_>, epoch_id: u64) -> Result<Self, RpcError> {
if self.contents.is_some() {
return Ok(self.clone());
}
Expand All @@ -171,11 +189,12 @@ impl EpochStart {
return Ok(self.clone());
};

if stored.cp_lo as u64 > scope.checkpoint_viewed_at() {
if stored.cp_lo as u64 > self.scope.checkpoint_viewed_at() {
return Ok(self.clone());
}

Ok(Self {
scope: self.scope.clone(),
contents: Some(Arc::new(stored)),
})
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ type Epoch {
"""
epochId: UInt53!
"""
State of the Coin DenyList object (0x403) at the start of this epoch.

The DenyList controls access to Regulated Coins. Writes to the DenyList are accumulated and only take effect on the next epoch boundary. Subsequently, it's possible to determine the state of the DenyList for a transaction by reading it at the start of the epoch the transaction is in.
"""
coinDenyList: Object
"""
The epoch's corresponding protocol configuration, including the feature flags and the configuration options.
"""
protocolConfigs: ProtocolConfigs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ type Epoch {
"""
epochId: UInt53!
"""
State of the Coin DenyList object (0x403) at the start of this epoch.

The DenyList controls access to Regulated Coins. Writes to the DenyList are accumulated and only take effect on the next epoch boundary. Subsequently, it's possible to determine the state of the DenyList for a transaction by reading it at the start of the epoch the transaction is in.
"""
coinDenyList: Object
"""
The epoch's corresponding protocol configuration, including the feature flags and the configuration options.
"""
protocolConfigs: ProtocolConfigs
Expand Down
6 changes: 6 additions & 0 deletions crates/sui-indexer-alt-graphql/staging.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ type Epoch {
"""
epochId: UInt53!
"""
State of the Coin DenyList object (0x403) at the start of this epoch.

The DenyList controls access to Regulated Coins. Writes to the DenyList are accumulated and only take effect on the next epoch boundary. Subsequently, it's possible to determine the state of the DenyList for a transaction by reading it at the start of the epoch the transaction is in.
"""
coinDenyList: Object
"""
The epoch's corresponding protocol configuration, including the feature flags and the configuration options.
"""
protocolConfigs: ProtocolConfigs
Expand Down
Loading