Skip to content

Commit

Permalink
Add ability to index move functions by package, module and function (M…
Browse files Browse the repository at this point in the history
…ystenLabs#2669)

* Add ability to index move functions by package, module and function identifier

* Add ability to index move functions by package, module and function identifier

* addressing comments

* Auto stash before rebase of "origin/sadhan/move-function-index"

* Auto stash before rebase of "origin/sadhan/move-function-index"

* Auto stash before rebase of "origin/sadhan/move-function-index"

* delete .idea

* delete .idea

* delete .idea
  • Loading branch information
sadhansood authored Jun 29, 2022
1 parent 2e7ebeb commit c9a438a
Show file tree
Hide file tree
Showing 7 changed files with 203 additions and 0 deletions.
15 changes: 15 additions & 0 deletions crates/sui-core/src/authority.rs
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,10 @@ impl AuthorityState {
cert.sender_address(),
cert.data.input_objects()?.iter().map(|o| o.object_id()),
effects.effects.mutated_and_created(),
cert.data
.move_calls()?
.iter()
.map(|mc| (mc.package.0, mc.module.clone(), mc.function.clone())),
seq,
digest,
timestamp_ms,
Expand Down Expand Up @@ -1275,6 +1279,17 @@ impl AuthorityState {
}
}

pub async fn get_transactions_by_move_function(
&self,
package: ObjectID,
module: Option<String>,
function: Option<String>,
) -> Result<Vec<(TxSequenceNumber, TransactionDigest)>, anyhow::Error> {
Ok(self
.get_indexes()?
.get_transactions_by_move_function(package, module, function)?)
}

pub async fn get_timestamp_ms(
&self,
digest: &TransactionDigest,
Expand Down
8 changes: 8 additions & 0 deletions crates/sui-json-rpc-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,14 @@ pub trait RpcFullNodeReadApi {
object: ObjectID,
) -> RpcResult<Vec<(GatewayTxSeqNumber, TransactionDigest)>>;

#[method(name = "getTransactionsByMoveFunction")]
async fn get_transactions_by_move_function(
&self,
package: ObjectID,
module: Option<String>,
function: Option<String>,
) -> RpcResult<Vec<(GatewayTxSeqNumber, TransactionDigest)>>;

#[method(name = "getTransactionsFromAddress")]
async fn get_transactions_from_addr(
&self,
Expand Down
12 changes: 12 additions & 0 deletions crates/sui-json-rpc/src/read_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,18 @@ impl RpcFullNodeReadApiServer for FullNodeApi {
.await?)
}

async fn get_transactions_by_move_function(
&self,
package: ObjectID,
module: Option<String>,
function: Option<String>,
) -> RpcResult<Vec<(GatewayTxSeqNumber, TransactionDigest)>> {
Ok(self
.state
.get_transactions_by_move_function(package, module, function)
.await?)
}

async fn get_transactions_from_addr(
&self,
addr: SuiAddress,
Expand Down
51 changes: 51 additions & 0 deletions crates/sui-open-rpc/spec/openrpc.json
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,57 @@
}
}
},
{
"name": "sui_getTransactionsByMoveFunction",
"tags": [
{
"name": "Full Node API"
}
],
"params": [
{
"name": "package",
"required": true,
"schema": {
"$ref": "#/components/schemas/ObjectID"
}
},
{
"name": "module",
"schema": {
"type": "string"
}
},
{
"name": "function",
"schema": {
"type": "string"
}
}
],
"result": {
"name": "Vec<(GatewayTxSeqNumber,TransactionDigest)>",
"required": true,
"schema": {
"type": "array",
"items": {
"type": "array",
"items": [
{
"type": "integer",
"format": "uint64",
"minimum": 0.0
},
{
"$ref": "#/components/schemas/TransactionDigest"
}
],
"maxItems": 2,
"minItems": 2
}
}
}
},
{
"name": "sui_getTransactionsByMutatedObject",
"tags": [
Expand Down
44 changes: 44 additions & 0 deletions crates/sui-storage/src/indexes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use sui_types::error::SuiResult;
use sui_types::base_types::ObjectRef;
use sui_types::object::Owner;

use move_core_types::identifier::Identifier;
use typed_store::rocks::DBMap;
use typed_store::{reopen, traits::Map};

Expand All @@ -32,6 +33,10 @@ pub struct IndexStore {
/// Index from object id to transactions that modified/created that object id.
transactions_by_mutated_object_id: DBMap<(ObjectID, TxSequenceNumber), TransactionDigest>,

/// Index from package id, module and function identifier to transactions that used that moce function call as input.
transactions_by_move_function:
DBMap<(ObjectID, String, String, TxSequenceNumber), TransactionDigest>,

/// This is a map between the transaction digest and its timestamp (UTC timestamp in
/// **milliseconds** since epoch 1/1/1970). A transaction digest is subjectively time stamped
/// on a node according to the local machine time, so it varies across nodes.
Expand All @@ -51,6 +56,7 @@ impl IndexStore {
("transactions_to_addr", &options),
("transactions_by_input_object_id", &options),
("transactions_by_mutated_object_id", &options),
("transactions_by_move_function", &options),
("timestamps", &point_lookup),
];
typed_store::rocks::open_cf_opts(path, db_options, opt_cfs)
Expand All @@ -62,13 +68,15 @@ impl IndexStore {
transactions_to_addr,
transactions_by_input_object_id,
transactions_by_mutated_object_id,
transactions_by_move_function,
timestamps,
) = reopen!(
&db,
"transactions_from_addr"; <(SuiAddress, TxSequenceNumber), TransactionDigest>,
"transactions_to_addr"; <(SuiAddress, TxSequenceNumber), TransactionDigest>,
"transactions_by_input_object_id"; <(ObjectID, TxSequenceNumber), TransactionDigest>,
"transactions_by_mutated_object_id"; <(ObjectID, TxSequenceNumber), TransactionDigest>,
"transactions_by_move_function"; <(ObjectID, String, String, TxSequenceNumber), TransactionDigest>,
"timestamps";<TransactionDigest, u64>
);

Expand All @@ -77,6 +85,7 @@ impl IndexStore {
transactions_to_addr,
transactions_by_input_object_id,
transactions_by_mutated_object_id,
transactions_by_move_function,
timestamps,
}
}
Expand All @@ -86,6 +95,7 @@ impl IndexStore {
sender: SuiAddress,
active_inputs: impl Iterator<Item = ObjectID>,
mutated_objects: impl Iterator<Item = &'a (ObjectRef, Owner)> + Clone,
move_functions: impl Iterator<Item = (ObjectID, Identifier, Identifier)> + Clone,
sequence: TxSequenceNumber,
digest: &TransactionDigest,
timestamp_ms: u64,
Expand All @@ -109,6 +119,16 @@ impl IndexStore {
.map(|(obj_ref, _)| ((obj_ref.0, sequence), *digest)),
)?;

let batch = batch.insert_batch(
&self.transactions_by_move_function,
move_functions.map(|(obj_id, module, function)| {
(
(obj_id, module.to_string(), function.to_string(), sequence),
*digest,
)
}),
)?;

let batch = batch.insert_batch(
&self.transactions_to_addr,
mutated_objects.filter_map(|(_, owner)| {
Expand Down Expand Up @@ -171,6 +191,30 @@ impl IndexStore {
Self::get_transactions_by_object(&self.transactions_from_addr, addr)
}

pub fn get_transactions_by_move_function(
&self,
package: ObjectID,
module: Option<String>,
function: Option<String>,
) -> SuiResult<Vec<(TxSequenceNumber, TransactionDigest)>> {
Ok(self
.transactions_by_move_function
.iter()
.skip_to(&(
package,
module.clone().unwrap_or_else(|| "".to_string()),
function.clone().unwrap_or_else(|| "".to_string()),
TxSequenceNumber::MIN,
))?
.take_while(|((id, m, f, _), _)| {
*id == package
&& module.as_ref().map(|x| x == m).unwrap_or(true)
&& function.as_ref().map(|x| x == f).unwrap_or(true)
})
.map(|((_, _, _, seq), digest)| (seq, digest))
.collect())
}

pub fn get_transactions_to_addr(
&self,
addr: SuiAddress,
Expand Down
28 changes: 28 additions & 0 deletions crates/sui-types/src/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,13 @@ impl SingleTransactionKind {
}
}

pub fn move_call(&self) -> Option<&MoveCall> {
match &self {
Self::Call(call @ MoveCall { .. }) => Some(call),
_ => None,
}
}

/// Return the metadata of each of the input objects for the transaction.
/// For a Move object, we attach the object reference;
/// for a Move package, we provide the object id only since they never change on chain.
Expand Down Expand Up @@ -437,6 +444,27 @@ where
&self.gas_payment
}

pub fn move_calls(&self) -> SuiResult<Vec<&MoveCall>> {
let move_calls = match &self.kind {
TransactionKind::Single(s) => s.move_call().into_iter().collect(),
TransactionKind::Batch(b) => {
let mut result = vec![];
for kind in b {
fp_ensure!(
!matches!(kind, &SingleTransactionKind::Publish(..)),
SuiError::InvalidBatchTransaction {
error: "Publish transaction is not allowed in Batch Transaction"
.to_owned(),
}
);
result.extend(kind.move_call().into_iter());
}
result
}
};
Ok(move_calls)
}

pub fn input_objects(&self) -> SuiResult<Vec<InputObjectKind>> {
let mut inputs = match &self.kind {
TransactionKind::Single(s) => s.input_objects()?,
Expand Down
45 changes: 45 additions & 0 deletions crates/sui/tests/full_node_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,51 @@ async fn test_full_node_shared_objects() -> Result<(), anyhow::Error> {
Ok(())
}

#[tokio::test]
async fn test_full_node_move_function_index() -> Result<(), anyhow::Error> {
let (swarm, context, _) = setup_network_and_wallet().await?;

let config = swarm.config().generate_fullnode_config();
let node = SuiNode::start(&config).await?;
let sender = context.config.accounts.get(0).cloned().unwrap();
let (package_ref, counter_id) = publish_package_and_make_counter(&context, sender).await;
let digest = increment_counter(&context, sender, None, package_ref, counter_id).await;

wait_for_tx(digest, node.state().clone()).await;
let txes = node
.state()
.get_transactions_by_move_function(
package_ref.0,
Some("counter".to_string()),
Some("increment".to_string()),
)
.await?;

assert_eq!(txes.len(), 1);
assert_eq!(txes[0].1, digest);

let txes = node
.state()
.get_transactions_by_move_function(package_ref.0, None, None)
.await?;

// 2 transactions in the package i.e create and increment counter
assert_eq!(txes.len(), 2);
assert_eq!(txes[1].1, digest);

eprint!("start...");
let txes = node
.state()
.get_transactions_by_move_function(package_ref.0, Some("counter".to_string()), None)
.await?;

// 2 transactions in the package i.e publish and increment
assert_eq!(txes.len(), 2);
assert_eq!(txes[1].1, digest);

Ok(())
}

#[tokio::test]
async fn test_full_node_indexes() -> Result<(), anyhow::Error> {
let (swarm, mut context, _) = setup_network_and_wallet().await?;
Expand Down

0 comments on commit c9a438a

Please sign in to comment.