Skip to content
Merged
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

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

//# init --protocol-version 51 --addresses Test=0x0 --accounts A B C --simulator

// This test involves address A being affected by transactions in various ways:
//
// 1. A splits off a coin from its gas and sends it to B.
//
// 2. A sponsors a transaction where B splits off the a coin from the gas coin
// and sends it to C.
//
// 3. (A splits off a coin to use as gas in future transactions where the gas
// coin will be consumed).
//
// 4. (A splits off a coin to use as gas in future transactions where the gas
// coin will be consumed).
//
// 5. A sends its gas coin to B.
//
// 6. A sponsors a transaction where B sends the gas coin to C.
//
// Then we run a number of GraphQL queries to see whether various addresses are
// considered the sender, recipient or affected by a transaction.

//# programmable --sender A --inputs 1000000 @B
//> SplitCoins(Gas, [Input(0)]);
//> TransferObjects([Result(0)], Input(1))

//# programmable --sponsor A --sender B --inputs 2000000 @C
//> SplitCoins(Gas, [Input(0)]);
//> TransferObjects([Result(0)], Input(1))

//# programmable --sender A --inputs 3000000 @A
//> SplitCoins(Gas, [Input(0)]);
//> TransferObjects([Result(0)], Input(1))

//# programmable --sender A --inputs 4000000 @A
//> SplitCoins(Gas, [Input(0)]);
//> TransferObjects([Result(0)], Input(1))

//# programmable --sender A --inputs @B --gas-payment 3,0 --gas-budget 3000000
//> TransferObjects([Gas], Input(0))

//# programmable --sponsor A --sender B --inputs @C --gas-payment 4,0 --gas-budget 4000000
//> TransferObjects([Gas], Input(0))

//# create-checkpoint

//# run-graphql
{ # A should be affected by all the transactions above
affectsA: transactionBlocks(filter: { kind: PROGRAMMABLE_TX, affectedAddress: "@{A}" }, scanLimit: 1000) {
nodes { ...CoinBalances }
}

# Same as above, but nesting the transaction block query inside an address query
affectsAViaAddress: address(address: "@{A}") {
transactionBlocks(filter: { kind: PROGRAMMABLE_TX }, relation: AFFECTED, scanLimit: 1000) {
nodes { ...CoinBalances }
}
}

# For contrast, the transactions that A is the sender for
sentByA: transactionBlocks(filter: { kind: PROGRAMMABLE_TX, signAddress: "@{A}" }) {
nodes { ...CoinBalances }
}

# ...and the transactions that A was a recipient for
receivedByA: transactionBlocks(filter: { kind: PROGRAMMABLE_TX, recvAddress: "@{A}" }, scanLimit: 1000) {
nodes { ...CoinBalances }
}

# B was not affected by all the transactions
affectsB: transactionBlocks(filter: { kind: PROGRAMMABLE_TX, affectedAddress: "@{B}" }, scanLimit: 1000) {
nodes { ...CoinBalances }
}

# And neither was C
affectsC: transactionBlocks(filter: { kind: PROGRAMMABLE_TX, affectedAddress: "@{C}" }, scanLimit: 1000) {
nodes { ...CoinBalances }
}
}

fragment CoinBalances on TransactionBlock {
effects {
objectChanges {
nodes {
inputState { ...CoinBalance }
outputState { ...CoinBalance }
}
}
}
}

fragment CoinBalance on Object {
asMoveObject { asCoin { coinBalance } }
}
10 changes: 10 additions & 0 deletions crates/sui-graphql-rpc/src/types/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ pub(crate) enum AddressTransactionBlockRelationship {
Sign,
/// Transactions that sent objects to this address.
Recv,
/// Transactions that this address was involved in, either as the sender, sponsor, or as the
/// owner of some object that was created, modified or transfered.
#[cfg(feature = "staging")]
Affected,
}

/// The 32-byte address that is an account address (corresponding to a public key).
Expand Down Expand Up @@ -181,6 +185,12 @@ impl Address {
recv_address: Some(self.address),
..Default::default()
},

#[cfg(feature = "staging")]
Some(R::Affected) => TransactionBlockFilter {
affected_address: Some(self.address),
..Default::default()
},
}) else {
return Ok(ScanConnection::new(false, false));
};
Expand Down
3 changes: 3 additions & 0 deletions crates/sui-graphql-rpc/src/types/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -647,7 +647,10 @@ impl ObjectImpl<'_> {
let Some(filter) = filter
.unwrap_or_default()
.intersect(TransactionBlockFilter {
#[cfg(not(feature = "staging"))]
recv_address: Some(self.0.address),
#[cfg(feature = "staging")]
affected_address: Some(self.0.address),
..Default::default()
})
else {
Expand Down
37 changes: 27 additions & 10 deletions crates/sui-graphql-rpc/src/types/transaction_block/filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ pub(crate) struct TransactionBlockFilter {
/// Limit to transaction that occured strictly before the given checkpoint.
pub before_checkpoint: Option<UInt53>,

/// Limit to transactions that interacted with the given address. The address could be a
/// sender, sponsor, or recipient of the transaction.
#[cfg(feature = "staging")]
pub affected_address: Option<SuiAddress>,

/// Limit to transactions that were signed by the given address.
pub sign_address: Option<SuiAddress>,

Expand Down Expand Up @@ -62,6 +67,8 @@ impl TransactionBlockFilter {
at_checkpoint: intersect!(at_checkpoint, intersect::by_eq)?,
before_checkpoint: intersect!(before_checkpoint, intersect::by_min)?,

#[cfg(feature = "staging")]
affected_address: intersect!(affected_address, intersect::by_eq)?,
sign_address: intersect!(sign_address, intersect::by_eq)?,
recv_address: intersect!(recv_address, intersect::by_eq)?,
input_object: intersect!(input_object, intersect::by_eq)?,
Expand All @@ -82,6 +89,8 @@ impl TransactionBlockFilter {
[
self.function.is_some(),
self.kind.is_some(),
#[cfg(feature = "staging")]
self.affected_address.is_some(),
self.recv_address.is_some(),
self.input_object.is_some(),
self.changed_object.is_some(),
Expand All @@ -94,30 +103,38 @@ impl TransactionBlockFilter {
}

/// If we don't query a lookup table that has a denormalized sender column, we need to
/// explicitly sp
/// explicitly specify the sender with a query on `tx_sender`. This function returns the sender
/// we need to add an explicit query for if one is required, or `None` otherwise.
pub(crate) fn explicit_sender(&self) -> Option<SuiAddress> {
if self.function.is_none()
let missing_implicit_sender = self.function.is_none()
&& self.kind.is_none()
&& self.recv_address.is_none()
&& self.input_object.is_none()
&& self.changed_object.is_none()
{
self.sign_address
} else {
None
}
&& self.changed_object.is_none();

#[cfg(feature = "staging")]
let missing_implicit_sender = missing_implicit_sender && self.affected_address.is_none();

missing_implicit_sender
.then_some(self.sign_address)
.flatten()
}

/// A TransactionBlockFilter is considered not to have any filters if no filters are specified,
/// or if the only filters are on `checkpoint`.
pub(crate) fn has_filters(&self) -> bool {
self.function.is_some()
let has_filters = self.function.is_some()
|| self.kind.is_some()
|| self.sign_address.is_some()
|| self.recv_address.is_some()
|| self.input_object.is_some()
|| self.changed_object.is_some()
|| self.transaction_ids.is_some()
|| self.transaction_ids.is_some();

#[cfg(feature = "staging")]
let has_filters = has_filters || self.affected_address.is_some();

has_filters
}

pub(crate) fn is_empty(&self) -> bool {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -323,24 +323,38 @@ pub(crate) fn subqueries(filter: &TransactionBlockFilter, tx_bounds: TxBounds) -
),
});
}

if let Some(kind) = &filter.kind {
subqueries.push(("tx_kinds", select_kind(*kind, sender, tx_bounds)));
}

#[cfg(feature = "staging")]
if let Some(affected) = &filter.affected_address {
subqueries.push((
"tx_affected_addresses",
select_affected_address(affected, sender, tx_bounds),
));
}

if let Some(recv) = &filter.recv_address {
subqueries.push(("tx_recipients", select_recipient(recv, sender, tx_bounds)));
}

if let Some(input) = &filter.input_object {
subqueries.push(("tx_input_objects", select_input(input, sender, tx_bounds)));
}

if let Some(changed) = &filter.changed_object {
subqueries.push((
"tx_changed_objects",
select_changed(changed, sender, tx_bounds),
));
}

if let Some(sender) = &filter.explicit_sender() {
subqueries.push(("tx_senders", select_sender(sender, tx_bounds)));
}

if let Some(txs) = &filter.transaction_ids {
subqueries.push(("tx_digests", select_ids(txs, tx_bounds)));
}
Expand Down Expand Up @@ -441,6 +455,18 @@ fn select_kind(
}
}

#[cfg(feature = "staging")]
fn select_affected_address(
affected: &SuiAddress,
sender: Option<SuiAddress>,
bound: TxBounds,
) -> RawQuery {
filter!(
select_tx(sender, bound, "tx_affected_addresses"),
format!("affected = {}", bytea_literal(affected.as_slice()))
)
}

fn select_sender(sender: &SuiAddress, bound: TxBounds) -> RawQuery {
select_tx(Some(*sender), bound, "tx_senders")
}
Expand Down
10 changes: 10 additions & 0 deletions crates/sui-graphql-rpc/staging.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,11 @@ enum AddressTransactionBlockRelationship {
Transactions that sent objects to this address.
"""
RECV
"""
Transactions that this address was involved in, either as the sender, sponsor, or as the
owner of some object that was created, modified or transfered.
"""
AFFECTED
}

"""
Expand Down Expand Up @@ -4313,6 +4318,11 @@ input TransactionBlockFilter {
"""
beforeCheckpoint: UInt53
"""
Limit to transactions that interacted with the given address. The address could be a
sender, sponsor, or recipient of the transaction.
"""
affectedAddress: SuiAddress
"""
Limit to transactions that were signed by the given address.
"""
signAddress: SuiAddress
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,11 @@ enum AddressTransactionBlockRelationship {
Transactions that sent objects to this address.
"""
RECV
"""
Transactions that this address was involved in, either as the sender, sponsor, or as the
owner of some object that was created, modified or transfered.
"""
AFFECTED
}

"""
Expand Down Expand Up @@ -4317,6 +4322,11 @@ input TransactionBlockFilter {
"""
beforeCheckpoint: UInt53
"""
Limit to transactions that interacted with the given address. The address could be a
sender, sponsor, or recipient of the transaction.
"""
affectedAddress: SuiAddress
"""
Limit to transactions that were signed by the given address.
"""
signAddress: SuiAddress
Expand Down
Loading