Skip to content

Commit

Permalink
graphql: treat verifyZkloginSignature inputs as transaction payload (#…
Browse files Browse the repository at this point in the history
…20694)

## Description

Treat `verifyZkloginSignature`'s `bytes` and `signature` arguments as
part of the "transaction payload" for the purposes of enforcing GraphQL
request size limits, as this endpoint can be used to verify the
signature on a transaction and without this change, it would not be
possible to verify signatures on larger transactions (over 5KiB by
default).

Fixes #20638

## Test plan

New unit tests:

```
sui$ cargo nextest run -p sui-graphql-rpc -p sui-mvr-graphql-rpc -- server::builder::tests
```

---

## Release notes

Check each box that your changes affect. If none of the boxes relate to
your changes, release notes aren't required.

For each box you select, include information after the relevant heading
that describes the impact of your changes that a user might notice and
any actions they must take to implement updates.

- [ ] Protocol: 
- [ ] Nodes (Validators and Full nodes): 
- [ ] Indexer: 
- [ ] JSON-RPC: 
- [x] GraphQL: Treat `bytes` and `signature` arguments to
`verifyZkloginSignature` as part of the transaction payload for the
purposes of imposing request payload size limits.
- [ ] CLI: 
- [ ] Rust SDK:
- [ ] REST API:
  • Loading branch information
amnn authored Dec 19, 2024
1 parent 4767a0b commit f7a2117
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 74 deletions.
15 changes: 12 additions & 3 deletions crates/sui-graphql-rpc/src/extensions/query_limits_checker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ use uuid::Uuid;
pub(crate) const CONNECTION_FIELDS: [&str; 2] = ["edges", "nodes"];
const DRY_RUN_TX_BLOCK: &str = "dryRunTransactionBlock";
const EXECUTE_TX_BLOCK: &str = "executeTransactionBlock";
const VERIFY_ZKLOGIN: &str = "verifyZkloginSignature";

/// The size of the query payload in bytes, as it comes from the request header: `Content-Length`.
#[derive(Clone, Copy, Debug)]
Expand Down Expand Up @@ -231,6 +232,14 @@ impl<'a> LimitsTraversal<'a> {
for (_name, value) in &f.node.arguments {
self.check_tx_arg(value)?;
}
} else if name == VERIFY_ZKLOGIN {
if let Some(value) = f.node.get_argument("bytes") {
self.check_tx_arg(value)?;
}

if let Some(value) = f.node.get_argument("signature") {
self.check_tx_arg(value)?;
}
}
}

Expand Down Expand Up @@ -607,9 +616,9 @@ impl<'a> Reporter<'a> {
self.graphql_error(
code::BAD_USER_INPUT,
format!(
"{message}. Requests are limited to {max_tx_payload} bytes or fewer on \
transaction payloads (all inputs to executeTransactionBlock or \
dryRunTransactionBlock) and the rest of the request (the query part) must be \
"{message}. Requests are limited to {max_tx_payload} bytes or fewer on transaction \
payloads (all inputs to executeTransactionBlock, dryRunTransactionBlock, or \
verifyZkloginSignature) and the rest of the request (the query part) must be \
{max_query_payload} bytes or fewer.",
max_tx_payload = self.limits.max_tx_payload_size,
max_query_payload = self.limits.max_query_payload_size,
Expand Down
104 changes: 70 additions & 34 deletions crates/sui-graphql-rpc/src/server/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1217,8 +1217,8 @@ pub mod tests {
)
.await,
"Query part too large: 354 bytes. Requests are limited to 400 bytes or fewer on \
transaction payloads (all inputs to executeTransactionBlock or \
dryRunTransactionBlock) and the rest of the request (the query part) must be 10 \
transaction payloads (all inputs to executeTransactionBlock, dryRunTransactionBlock, \
or verifyZkloginSignature) and the rest of the request (the query part) must be 10 \
bytes or fewer."
);
}
Expand Down Expand Up @@ -1248,8 +1248,8 @@ pub mod tests {
)
.await,
"Transaction payload too large. Requests are limited to 10 bytes or fewer on \
transaction payloads (all inputs to executeTransactionBlock or \
dryRunTransactionBlock) and the rest of the request (the query part) must be 400 \
transaction payloads (all inputs to executeTransactionBlock, dryRunTransactionBlock, \
or verifyZkloginSignature) and the rest of the request (the query part) must be 400 \
bytes or fewer."
);
}
Expand Down Expand Up @@ -1280,12 +1280,47 @@ pub mod tests {
)
.await,
"Transaction payload too large. Requests are limited to 10 bytes or fewer on \
transaction payloads (all inputs to executeTransactionBlock or \
dryRunTransactionBlock) and the rest of the request (the query part) must be 400 bytes \
transaction payloads (all inputs to executeTransactionBlock, dryRunTransactionBlock, \
or verifyZkloginSignature) and the rest of the request (the query part) must be 400 bytes \
or fewer."
);
}

#[tokio::test]
async fn test_payload_zklogin_exceeded() {
let cluster = prep_executor_cluster().await;
let db_url = cluster.graphql_connection_config.db_url.clone();
assert_eq!(
execute_for_error(
&db_url,
Limits {
max_tx_payload_size: 10,
max_query_payload_size: 600,
..Default::default()
},
r#"
query {
verifyZkloginSignature(
bytes: "AAABBBCCC",
signature: "DDD",
intentScope: TRANSACTION_DATA,
author: "0xeee",
) {
success
errors
}
}
"#
.into(),
)
.await,
"Transaction payload too large. Requests are limited to 10 bytes or fewer on \
transaction payloads (all inputs to executeTransactionBlock, dryRunTransactionBlock, \
or verifyZkloginSignature) and the rest of the request (the query part) must be 600 \
bytes or fewer."
);
}

#[tokio::test]
async fn test_payload_total_exceeded_impl() {
let cluster = prep_executor_cluster().await;
Expand All @@ -1312,8 +1347,9 @@ pub mod tests {
)
.await,
"Overall request too large: 380 bytes. Requests are limited to 10 bytes or fewer on \
transaction payloads (all inputs to executeTransactionBlock or dryRunTransactionBlock) \
and the rest of the request (the query part) must be 10 bytes or fewer."
transaction payloads (all inputs to executeTransactionBlock, dryRunTransactionBlock, \
or verifyZkloginSignature) and the rest of the request (the query part) must be 10 \
bytes or fewer."
);
}

Expand Down Expand Up @@ -1347,8 +1383,8 @@ pub mod tests {
)
.await,
"Transaction payload too large. Requests are limited to 10 bytes or fewer on \
transaction payloads (all inputs to executeTransactionBlock or \
dryRunTransactionBlock) and the rest of the request (the query part) must be 500 \
transaction payloads (all inputs to executeTransactionBlock, dryRunTransactionBlock, \
or verifyZkloginSignature) and the rest of the request (the query part) must be 500 \
bytes or fewer."
);
}
Expand Down Expand Up @@ -1383,9 +1419,9 @@ pub mod tests {
)
.await,
"Query part too large: 409 bytes. Requests are limited to 500 bytes or fewer on \
transaction payloads (all inputs to executeTransactionBlock or \
dryRunTransactionBlock) and the rest of the request (the query part) must be 10 bytes \
or fewer."
transaction payloads (all inputs to executeTransactionBlock, dryRunTransactionBlock, \
or verifyZkloginSignature) and the rest of the request (the query part) must be 10 \
bytes or fewer."
);
}

Expand Down Expand Up @@ -1419,8 +1455,8 @@ pub mod tests {
)
.await,
"Transaction payload too large. Requests are limited to 10 bytes or fewer on \
transaction payloads (all inputs to executeTransactionBlock or \
dryRunTransactionBlock) and the rest of the request (the query part) must be 400 \
transaction payloads (all inputs to executeTransactionBlock, dryRunTransactionBlock, \
or verifyZkloginSignature) and the rest of the request (the query part) must be 400 \
bytes or fewer."
);
}
Expand Down Expand Up @@ -1455,9 +1491,9 @@ pub mod tests {
)
.await,
"Query part too large: 398 bytes. Requests are limited to 400 bytes or fewer on \
transaction payloads (all inputs to executeTransactionBlock or \
dryRunTransactionBlock) and the rest of the request (the query part) must be 10 bytes \
or fewer."
transaction payloads (all inputs to executeTransactionBlock, dryRunTransactionBlock, \
or verifyZkloginSignature) and the rest of the request (the query part) must be 10 \
bytes or fewer."
);
}

Expand Down Expand Up @@ -1514,8 +1550,8 @@ pub mod tests {
)
.await,
"Transaction payload too large. Requests are limited to 30 bytes or fewer on \
transaction payloads (all inputs to executeTransactionBlock or \
dryRunTransactionBlock) and the rest of the request (the query part) must be 800 \
transaction payloads (all inputs to executeTransactionBlock, dryRunTransactionBlock, \
or verifyZkloginSignature) and the rest of the request (the query part) must be 800 \
bytes or fewer."
);
}
Expand Down Expand Up @@ -1576,8 +1612,8 @@ pub mod tests {
)
.await,
"Transaction payload too large. Requests are limited to 20 bytes or fewer on \
transaction payloads (all inputs to executeTransactionBlock or \
dryRunTransactionBlock) and the rest of the request (the query part) must be 800 \
transaction payloads (all inputs to executeTransactionBlock, dryRunTransactionBlock, \
or verifyZkloginSignature) and the rest of the request (the query part) must be 800 \
bytes or fewer."
);
}
Expand Down Expand Up @@ -1634,8 +1670,8 @@ pub mod tests {
)
.await,
"Transaction payload too large. Requests are limited to 30 bytes or fewer on \
transaction payloads (all inputs to executeTransactionBlock or \
dryRunTransactionBlock) and the rest of the request (the query part) must be 500 \
transaction payloads (all inputs to executeTransactionBlock, dryRunTransactionBlock, \
or verifyZkloginSignature) and the rest of the request (the query part) must be 500 \
bytes or fewer.",
)
}
Expand Down Expand Up @@ -1672,8 +1708,8 @@ pub mod tests {
)
.await,
"Transaction payload too large. Requests are limited to 10 bytes or fewer on \
transaction payloads (all inputs to executeTransactionBlock or \
dryRunTransactionBlock) and the rest of the request (the query part) must be 500 \
transaction payloads (all inputs to executeTransactionBlock, dryRunTransactionBlock, \
or verifyZkloginSignature) and the rest of the request (the query part) must be 500 \
bytes or fewer."
);
}
Expand Down Expand Up @@ -1923,8 +1959,8 @@ pub mod tests {
)
.await,
"Transaction payload too large. Requests are limited to 10 bytes or fewer on \
transaction payloads (all inputs to executeTransactionBlock or \
dryRunTransactionBlock) and the rest of the request (the query part) must be 500 \
transaction payloads (all inputs to executeTransactionBlock, dryRunTransactionBlock, \
or verifyZkloginSignature) and the rest of the request (the query part) must be 500 \
bytes or fewer."
);
}
Expand Down Expand Up @@ -1956,8 +1992,8 @@ pub mod tests {
)
.await,
"Transaction payload too large. Requests are limited to 10 bytes or fewer on \
transaction payloads (all inputs to executeTransactionBlock or \
dryRunTransactionBlock) and the rest of the request (the query part) must be 500 \
transaction payloads (all inputs to executeTransactionBlock, dryRunTransactionBlock, \
or verifyZkloginSignature) and the rest of the request (the query part) must be 500 \
bytes or fewer."
);
}
Expand Down Expand Up @@ -1992,8 +2028,8 @@ pub mod tests {
)
.await,
"Transaction payload too large. Requests are limited to 10 bytes or fewer on \
transaction payloads (all inputs to executeTransactionBlock or \
dryRunTransactionBlock) and the rest of the request (the query part) must be 500 \
transaction payloads (all inputs to executeTransactionBlock, dryRunTransactionBlock, \
or verifyZkloginSignature) and the rest of the request (the query part) must be 500 \
bytes or fewer."
);
}
Expand Down Expand Up @@ -2026,8 +2062,8 @@ pub mod tests {
)
.await,
"Transaction payload too large. Requests are limited to 10 bytes or fewer on \
transaction payloads (all inputs to executeTransactionBlock or \
dryRunTransactionBlock) and the rest of the request (the query part) must be 500 \
transaction payloads (all inputs to executeTransactionBlock, dryRunTransactionBlock, \
or verifyZkloginSignature) and the rest of the request (the query part) must be 500 \
bytes or fewer."
);
}
Expand Down
15 changes: 12 additions & 3 deletions crates/sui-mvr-graphql-rpc/src/extensions/query_limits_checker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ use uuid::Uuid;
pub(crate) const CONNECTION_FIELDS: [&str; 2] = ["edges", "nodes"];
const DRY_RUN_TX_BLOCK: &str = "dryRunTransactionBlock";
const EXECUTE_TX_BLOCK: &str = "executeTransactionBlock";
const VERIFY_ZKLOGIN: &str = "verifyZkloginSignature";

/// The size of the query payload in bytes, as it comes from the request header: `Content-Length`.
#[derive(Clone, Copy, Debug)]
Expand Down Expand Up @@ -231,6 +232,14 @@ impl<'a> LimitsTraversal<'a> {
for (_name, value) in &f.node.arguments {
self.check_tx_arg(value)?;
}
} else if name == VERIFY_ZKLOGIN {
if let Some(value) = f.node.get_argument("bytes") {
self.check_tx_arg(value)?;
}

if let Some(value) = f.node.get_argument("signature") {
self.check_tx_arg(value)?;
}
}
}

Expand Down Expand Up @@ -607,9 +616,9 @@ impl<'a> Reporter<'a> {
self.graphql_error(
code::BAD_USER_INPUT,
format!(
"{message}. Requests are limited to {max_tx_payload} bytes or fewer on \
transaction payloads (all inputs to executeTransactionBlock or \
dryRunTransactionBlock) and the rest of the request (the query part) must be \
"{message}. Requests are limited to {max_tx_payload} bytes or fewer on transaction \
payloads (all inputs to executeTransactionBlock, dryRunTransactionBlock, or \
verifyZkloginSignature) and the rest of the request (the query part) must be \
{max_query_payload} bytes or fewer.",
max_tx_payload = self.limits.max_tx_payload_size,
max_query_payload = self.limits.max_query_payload_size,
Expand Down
Loading

0 comments on commit f7a2117

Please sign in to comment.