diff --git a/crates/sui-bridge-indexer/src/sui_transaction_handler.rs b/crates/sui-bridge-indexer/src/sui_transaction_handler.rs index 92a4f1c9ebdf0..c33650eb0b7f1 100644 --- a/crates/sui-bridge-indexer/src/sui_transaction_handler.rs +++ b/crates/sui-bridge-indexer/src/sui_transaction_handler.rs @@ -94,7 +94,7 @@ pub fn into_token_transfers( "TokenDepositedEvent" => { info!("Observed Sui Deposit {:?}", ev); metrics.total_sui_token_deposited.inc(); - let move_event: MoveTokenDepositedEvent = bcs::from_bytes(&ev.bcs)?; + let move_event: MoveTokenDepositedEvent = bcs::from_bytes(ev.bcs.bytes())?; transfers.push(ProcessedTxnData::TokenTransfer(TokenTransfer { chain_id: move_event.source_chain, nonce: move_event.seq_num, @@ -119,7 +119,7 @@ pub fn into_token_transfers( "TokenTransferApproved" => { info!("Observed Sui Approval {:?}", ev); metrics.total_sui_token_transfer_approved.inc(); - let event: MoveTokenTransferApproved = bcs::from_bytes(&ev.bcs)?; + let event: MoveTokenTransferApproved = bcs::from_bytes(ev.bcs.bytes())?; transfers.push(ProcessedTxnData::TokenTransfer(TokenTransfer { chain_id: event.message_key.source_chain, nonce: event.message_key.bridge_seq_num, @@ -137,7 +137,7 @@ pub fn into_token_transfers( "TokenTransferClaimed" => { info!("Observed Sui Claim {:?}", ev); metrics.total_sui_token_transfer_claimed.inc(); - let event: MoveTokenTransferClaimed = bcs::from_bytes(&ev.bcs)?; + let event: MoveTokenTransferClaimed = bcs::from_bytes(ev.bcs.bytes())?; transfers.push(ProcessedTxnData::TokenTransfer(TokenTransfer { chain_id: event.message_key.source_chain, nonce: event.message_key.bridge_seq_num, diff --git a/crates/sui-bridge/src/events.rs b/crates/sui-bridge/src/events.rs index 57011978c098c..968bd22941839 100644 --- a/crates/sui-bridge/src/events.rs +++ b/crates/sui-bridge/src/events.rs @@ -391,7 +391,7 @@ macro_rules! declare_events { // Unwrap safe: we inited above $( if &event.type_ == $variant.get().unwrap() { - let event_struct: $event_struct = bcs::from_bytes(&event.bcs).map_err(|e| BridgeError::InternalError(format!("Failed to deserialize event to {}: {:?}", stringify!($event_struct), e)))?; + let event_struct: $event_struct = bcs::from_bytes(event.bcs.bytes()).map_err(|e| BridgeError::InternalError(format!("Failed to deserialize event to {}: {:?}", stringify!($event_struct), e)))?; return Ok(Some(SuiBridgeEvent::$variant(event_struct.try_into()?))); } )* @@ -443,6 +443,7 @@ pub mod tests { use crate::types::BridgeAction; use crate::types::SuiToEthBridgeAction; use ethers::types::Address as EthAddress; + use sui_json_rpc_types::BcsEvent; use sui_json_rpc_types::SuiEvent; use sui_types::base_types::ObjectID; use sui_types::base_types::SuiAddress; @@ -484,7 +485,7 @@ pub mod tests { }); let event = SuiEvent { type_: SuiToEthTokenBridgeV1.get().unwrap().clone(), - bcs: bcs::to_bytes(&emitted_event).unwrap(), + bcs: BcsEvent::new(bcs::to_bytes(&emitted_event).unwrap()), id: EventID { tx_digest, event_seq: event_idx as u64, diff --git a/crates/sui-bridge/src/server/handler.rs b/crates/sui-bridge/src/server/handler.rs index d82beb3896041..9bab4c9343e21 100644 --- a/crates/sui-bridge/src/server/handler.rs +++ b/crates/sui-bridge/src/server/handler.rs @@ -360,7 +360,7 @@ mod tests { types::{EmergencyAction, EmergencyActionType, LimitUpdateAction}, }; use ethers::types::{Address as EthAddress, TransactionReceipt}; - use sui_json_rpc_types::SuiEvent; + use sui_json_rpc_types::{BcsEvent, SuiEvent}; use sui_types::bridge::{BridgeChainId, TOKEN_ID_USDC}; use sui_types::{base_types::SuiAddress, crypto::get_key_pair}; @@ -456,12 +456,12 @@ mod tests { let mut sui_event_1 = SuiEvent::random_for_testing(); sui_event_1.type_ = SuiToEthTokenBridgeV1.get().unwrap().clone(); - sui_event_1.bcs = bcs::to_bytes(&emitted_event_1).unwrap(); + sui_event_1.bcs = BcsEvent::new(bcs::to_bytes(&emitted_event_1).unwrap()); let sui_tx_digest = sui_event_1.id.tx_digest; let mut sui_event_2 = SuiEvent::random_for_testing(); sui_event_2.type_ = SuiToEthTokenBridgeV1.get().unwrap().clone(); - sui_event_2.bcs = bcs::to_bytes(&emitted_event_1).unwrap(); + sui_event_2.bcs = BcsEvent::new(bcs::to_bytes(&emitted_event_1).unwrap()); let sui_event_idx_2 = 1; sui_client_mock.add_events_by_tx_digest(sui_tx_digest, vec![sui_event_2.clone()]); diff --git a/crates/sui-bridge/src/sui_client.rs b/crates/sui-bridge/src/sui_client.rs index d906960a4475e..cc143afa36ec9 100644 --- a/crates/sui-bridge/src/sui_client.rs +++ b/crates/sui-bridge/src/sui_client.rs @@ -658,6 +658,7 @@ mod tests { use move_core_types::account_address::AccountAddress; use serde::{Deserialize, Serialize}; use std::str::FromStr; + use sui_json_rpc_types::BcsEvent; use sui_types::bridge::{BridgeChainId, TOKEN_ID_SUI, TOKEN_ID_USDC}; use sui_types::crypto::get_key_pair; @@ -698,7 +699,7 @@ mod tests { let mut sui_event_1 = SuiEvent::random_for_testing(); sui_event_1.type_ = SuiToEthTokenBridgeV1.get().unwrap().clone(); - sui_event_1.bcs = bcs::to_bytes(&emitted_event_1).unwrap(); + sui_event_1.bcs = BcsEvent::new(bcs::to_bytes(&emitted_event_1).unwrap()); #[derive(Serialize, Deserialize)] struct RandomStruct {} @@ -708,7 +709,7 @@ mod tests { let mut sui_event_2 = SuiEvent::random_for_testing(); sui_event_2.type_ = SuiToEthTokenBridgeV1.get().unwrap().clone(); sui_event_2.type_.module = Identifier::from_str("unrecognized_module").unwrap(); - sui_event_2.bcs = bcs::to_bytes(&event_2).unwrap(); + sui_event_2.bcs = BcsEvent::new(bcs::to_bytes(&event_2).unwrap()); // Event 3 is defined in non-bridge package let mut sui_event_3 = sui_event_1.clone(); diff --git a/crates/sui-graphql-rpc/src/mutation.rs b/crates/sui-graphql-rpc/src/mutation.rs index 033655565ca2e..36746f79c35c8 100644 --- a/crates/sui-graphql-rpc/src/mutation.rs +++ b/crates/sui-graphql-rpc/src/mutation.rs @@ -119,7 +119,7 @@ impl Mutation { transaction_module: e.transaction_module, sender: e.sender, type_: e.type_, - contents: e.bcs, + contents: e.bcs.into_bytes(), }) .collect(); diff --git a/crates/sui-indexer/src/apis/indexer_api.rs b/crates/sui-indexer/src/apis/indexer_api.rs index 7c3dbf0308f16..ed7beb304fdac 100644 --- a/crates/sui-indexer/src/apis/indexer_api.rs +++ b/crates/sui-indexer/src/apis/indexer_api.rs @@ -228,7 +228,7 @@ impl IndexerApiServer for IndexerApi { results.truncate(limit); let next_cursor = results.last().map(|o| o.object_id); Ok(Page { - data: results, + data: results.into_iter().map(Into::into).collect(), next_cursor, has_next_page, }) diff --git a/crates/sui-indexer/src/models/events.rs b/crates/sui-indexer/src/models/events.rs index 6b9c5044c3ba5..00fe8613298cb 100644 --- a/crates/sui-indexer/src/models/events.rs +++ b/crates/sui-indexer/src/models/events.rs @@ -7,7 +7,7 @@ use std::sync::Arc; use diesel::prelude::*; use move_core_types::identifier::Identifier; -use sui_json_rpc_types::{type_and_fields_from_move_event_data, SuiEvent}; +use sui_json_rpc_types::{type_and_fields_from_move_event_data, BcsEvent, SuiEvent}; use sui_package_resolver::{PackageStore, Resolver}; use sui_types::base_types::{ObjectID, SuiAddress}; use sui_types::digests::TransactionDigest; @@ -115,7 +115,7 @@ impl StoredEvent { transaction_module: Identifier::from_str(&self.module)?, sender, type_, - bcs: self.bcs, + bcs: BcsEvent::new(self.bcs), parsed_json, timestamp_ms: Some(self.timestamp_ms as u64), }) diff --git a/crates/sui-json-rpc-types/src/lib.rs b/crates/sui-json-rpc-types/src/lib.rs index 8d1818dd7b35c..3e4b17f242417 100644 --- a/crates/sui-json-rpc-types/src/lib.rs +++ b/crates/sui-json-rpc-types/src/lib.rs @@ -1,11 +1,13 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 +use fastcrypto::encoding::{Base58, Base64}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; pub use balance_changes::*; pub use object_changes::*; +use serde_with::serde_as; pub use sui_checkpoint::*; pub use sui_coin::*; pub use sui_event::*; @@ -16,7 +18,6 @@ pub use sui_object::*; pub use sui_protocol::*; pub use sui_transaction::*; use sui_types::base_types::ObjectID; -use sui_types::dynamic_field::DynamicFieldInfo; #[cfg(test)] #[path = "unit_tests/rpc_types_tests.rs"] @@ -56,3 +57,160 @@ impl Page { } } } + +#[serde_with::serde_as] +#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug)] +#[serde(rename_all = "camelCase")] +pub struct DynamicFieldInfo { + pub name: sui_types::dynamic_field::DynamicFieldName, + #[serde(flatten)] + pub bcs_name: BcsName, + pub type_: sui_types::dynamic_field::DynamicFieldType, + pub object_type: String, + pub object_id: ObjectID, + pub version: sui_types::base_types::SequenceNumber, + pub digest: sui_types::digests::ObjectDigest, +} + +impl From for DynamicFieldInfo { + fn from( + sui_types::dynamic_field::DynamicFieldInfo { + name, + bcs_name, + type_, + object_type, + object_id, + version, + digest, + }: sui_types::dynamic_field::DynamicFieldInfo, + ) -> Self { + Self { + name, + bcs_name: BcsName::new(bcs_name), + type_, + object_type, + object_id, + version, + digest, + } + } +} + +#[serde_as] +#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "camelCase", tag = "bcsEncoding")] +#[serde(from = "MaybeTaggedBcsName")] +pub enum BcsName { + Base64 { + #[serde_as(as = "Base64")] + #[schemars(with = "Base64")] + bcs_name: Vec, + }, + Base58 { + #[serde_as(as = "Base58")] + #[schemars(with = "Base58")] + bcs_name: Vec, + }, +} + +impl BcsName { + pub fn new(bytes: Vec) -> Self { + Self::Base64 { bcs_name: bytes } + } + + pub fn bytes(&self) -> &[u8] { + match self { + BcsName::Base64 { bcs_name } => bcs_name.as_ref(), + BcsName::Base58 { bcs_name } => bcs_name.as_ref(), + } + } + + pub fn into_bytes(self) -> Vec { + match self { + BcsName::Base64 { bcs_name } => bcs_name, + BcsName::Base58 { bcs_name } => bcs_name, + } + } +} + +#[allow(unused)] +#[serde_as] +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase", untagged)] +enum MaybeTaggedBcsName { + Tagged(TaggedBcsName), + Base58 { + #[serde_as(as = "Base58")] + #[serde(rename = "bcsName")] + bcs_name: Vec, + }, +} + +#[serde_as] +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase", tag = "bcsEncoding")] +enum TaggedBcsName { + Base64 { + #[serde_as(as = "Base64")] + #[serde(rename = "bcsName")] + bcs_name: Vec, + }, + Base58 { + #[serde_as(as = "Base58")] + #[serde(rename = "bcsName")] + bcs_name: Vec, + }, +} + +impl From for BcsName { + fn from(name: MaybeTaggedBcsName) -> BcsName { + let bcs_name = match name { + MaybeTaggedBcsName::Tagged(TaggedBcsName::Base58 { bcs_name }) + | MaybeTaggedBcsName::Base58 { bcs_name } => bcs_name, + MaybeTaggedBcsName::Tagged(TaggedBcsName::Base64 { bcs_name }) => bcs_name, + }; + + // Bytes are already decoded, force into Base64 variant to avoid serializing to base58 + Self::Base64 { bcs_name } + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn bcs_name_test() { + let bytes = vec![0, 1, 2, 3, 4]; + let untagged_base58 = r#"{"bcsName":"12VfUX"}"#; + let tagged_base58 = r#"{"bcsEncoding":"base58","bcsName":"12VfUX"}"#; + let tagged_base64 = r#"{"bcsEncoding":"base64","bcsName":"AAECAwQ="}"#; + + println!( + "{}", + serde_json::to_string(&TaggedBcsName::Base64 { + bcs_name: bytes.clone() + }) + .unwrap() + ); + + assert_eq!( + bytes, + serde_json::from_str::(untagged_base58) + .unwrap() + .into_bytes() + ); + assert_eq!( + bytes, + serde_json::from_str::(tagged_base58) + .unwrap() + .into_bytes() + ); + assert_eq!( + bytes, + serde_json::from_str::(tagged_base64) + .unwrap() + .into_bytes() + ); + } +} diff --git a/crates/sui-json-rpc-types/src/sui_event.rs b/crates/sui-json-rpc-types/src/sui_event.rs index d1016a45c3ec4..eb9aace4ac409 100644 --- a/crates/sui-json-rpc-types/src/sui_event.rs +++ b/crates/sui-json-rpc-types/src/sui_event.rs @@ -1,6 +1,7 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 +use fastcrypto::encoding::Base58; use fastcrypto::encoding::Base64; use move_core_types::annotated_value::MoveDatatypeLayout; use move_core_types::identifier::Identifier; @@ -51,10 +52,9 @@ pub struct SuiEvent { pub type_: StructTag, /// Parsed json value of the event pub parsed_json: Value, - #[serde_as(as = "sui_types::sui_serde::Base64orBase58")] - #[schemars(with = "Base64")] /// Base64 encoded bcs bytes of the move event - pub bcs: Vec, + #[serde(flatten)] + pub bcs: BcsEvent, /// UTC timestamp in milliseconds since epoch (1/1/1970) #[serde(skip_serializing_if = "Option::is_none")] #[schemars(with = "Option>")] @@ -62,6 +62,82 @@ pub struct SuiEvent { pub timestamp_ms: Option, } +#[serde_as] +#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "camelCase", tag = "bcsEncoding")] +#[serde(from = "MaybeTaggedBcsEvent")] +pub enum BcsEvent { + Base64 { + #[serde_as(as = "Base64")] + #[schemars(with = "Base64")] + bcs: Vec, + }, + Base58 { + #[serde_as(as = "Base58")] + #[schemars(with = "Base58")] + bcs: Vec, + }, +} + +impl BcsEvent { + pub fn new(bytes: Vec) -> Self { + Self::Base64 { bcs: bytes } + } + + pub fn bytes(&self) -> &[u8] { + match self { + BcsEvent::Base64 { bcs } => bcs.as_ref(), + BcsEvent::Base58 { bcs } => bcs.as_ref(), + } + } + + pub fn into_bytes(self) -> Vec { + match self { + BcsEvent::Base64 { bcs } => bcs, + BcsEvent::Base58 { bcs } => bcs, + } + } +} + +#[allow(unused)] +#[serde_as] +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase", untagged)] +enum MaybeTaggedBcsEvent { + Tagged(TaggedBcsEvent), + Base58 { + #[serde_as(as = "Base58")] + bcs: Vec, + }, +} + +#[serde_as] +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase", tag = "bcsEncoding")] +enum TaggedBcsEvent { + Base64 { + #[serde_as(as = "Base64")] + bcs: Vec, + }, + Base58 { + #[serde_as(as = "Base58")] + bcs: Vec, + }, +} + +impl From for BcsEvent { + fn from(event: MaybeTaggedBcsEvent) -> BcsEvent { + let bcs = match event { + MaybeTaggedBcsEvent::Tagged(TaggedBcsEvent::Base58 { bcs }) + | MaybeTaggedBcsEvent::Base58 { bcs } => bcs, + MaybeTaggedBcsEvent::Tagged(TaggedBcsEvent::Base64 { bcs }) => bcs, + }; + + // Bytes are already decoded, force into Base64 variant to avoid serializing to base58 + Self::Base64 { bcs } + } +} + impl From for SuiEvent { fn from(ev: EventEnvelope) -> Self { Self { @@ -74,7 +150,9 @@ impl From for SuiEvent { sender: ev.event.sender, type_: ev.event.type_, parsed_json: ev.parsed_json, - bcs: ev.event.contents, + bcs: BcsEvent::Base64 { + bcs: ev.event.contents, + }, timestamp_ms: Some(ev.timestamp), } } @@ -87,7 +165,7 @@ impl From for Event { transaction_module: val.transaction_module, sender: val.sender, type_: val.type_, - contents: val.bcs, + contents: val.bcs.into_bytes(), } } } @@ -108,7 +186,9 @@ impl SuiEvent { contents, } = event; - let bcs = contents.to_vec(); + let bcs = BcsEvent::Base64 { + bcs: contents.to_vec(), + }; let move_value = Event::move_event_to_move_value(&contents, layout)?; let (type_, fields) = type_and_fields_from_move_event_data(move_value)?; @@ -165,7 +245,7 @@ impl SuiEvent { sender: SuiAddress::random_for_testing_only(), type_: StructTag::from_str("0x6666::random_for_testing::RandomForTesting").unwrap(), parsed_json: json!({}), - bcs: vec![], + bcs: BcsEvent::new(vec![]), timestamp_ms: None, } } @@ -293,3 +373,35 @@ impl Filter for EventFilter { pub trait Filter { fn matches(&self, item: &T) -> bool; } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn bcs_event_test() { + let bytes = vec![0, 1, 2, 3, 4]; + let untagged_base58 = r#"{"bcs":"12VfUX"}"#; + let tagged_base58 = r#"{"bcsEncoding":"base58","bcs":"12VfUX"}"#; + let tagged_base64 = r#"{"bcsEncoding":"base64","bcs":"AAECAwQ="}"#; + + assert_eq!( + bytes, + serde_json::from_str::(untagged_base58) + .unwrap() + .into_bytes() + ); + assert_eq!( + bytes, + serde_json::from_str::(tagged_base58) + .unwrap() + .into_bytes() + ); + assert_eq!( + bytes, + serde_json::from_str::(tagged_base64) + .unwrap() + .into_bytes() + ); + } +} diff --git a/crates/sui-json-rpc/src/indexer_api.rs b/crates/sui-json-rpc/src/indexer_api.rs index 53709116966b4..ff15f1a88e3de 100644 --- a/crates/sui-json-rpc/src/indexer_api.rs +++ b/crates/sui-json-rpc/src/indexer_api.rs @@ -348,7 +348,7 @@ impl IndexerApiServer for IndexerApi { .get_dynamic_fields_result_size_total .inc_by(data.len() as u64); Ok(DynamicFieldPage { - data: data.into_iter().map(|(_, w)| w).collect(), + data: data.into_iter().map(|(_, w)| w.into()).collect(), next_cursor, has_next_page, }) diff --git a/crates/sui-json-rpc/src/read_api.rs b/crates/sui-json-rpc/src/read_api.rs index 5687b3f215cfc..3982acc9c5b09 100644 --- a/crates/sui-json-rpc/src/read_api.rs +++ b/crates/sui-json-rpc/src/read_api.rs @@ -1139,7 +1139,7 @@ async fn get_display_object_by_type( // If there's any recent version of Display, give it to the client. // TODO: add support for version query. if let Some(event) = events.pop() { - let display: DisplayVersionUpdatedEvent = bcs::from_bytes(&event.bcs[..])?; + let display: DisplayVersionUpdatedEvent = bcs::from_bytes(&event.bcs.into_bytes())?; Ok(Some(display)) } else { Ok(None) diff --git a/crates/sui-mvr-graphql-rpc/src/mutation.rs b/crates/sui-mvr-graphql-rpc/src/mutation.rs index 033655565ca2e..36746f79c35c8 100644 --- a/crates/sui-mvr-graphql-rpc/src/mutation.rs +++ b/crates/sui-mvr-graphql-rpc/src/mutation.rs @@ -119,7 +119,7 @@ impl Mutation { transaction_module: e.transaction_module, sender: e.sender, type_: e.type_, - contents: e.bcs, + contents: e.bcs.into_bytes(), }) .collect(); diff --git a/crates/sui-mvr-indexer/src/apis/indexer_api.rs b/crates/sui-mvr-indexer/src/apis/indexer_api.rs index 7c3dbf0308f16..ed7beb304fdac 100644 --- a/crates/sui-mvr-indexer/src/apis/indexer_api.rs +++ b/crates/sui-mvr-indexer/src/apis/indexer_api.rs @@ -228,7 +228,7 @@ impl IndexerApiServer for IndexerApi { results.truncate(limit); let next_cursor = results.last().map(|o| o.object_id); Ok(Page { - data: results, + data: results.into_iter().map(Into::into).collect(), next_cursor, has_next_page, }) diff --git a/crates/sui-mvr-indexer/src/models/events.rs b/crates/sui-mvr-indexer/src/models/events.rs index 6b9c5044c3ba5..00fe8613298cb 100644 --- a/crates/sui-mvr-indexer/src/models/events.rs +++ b/crates/sui-mvr-indexer/src/models/events.rs @@ -7,7 +7,7 @@ use std::sync::Arc; use diesel::prelude::*; use move_core_types::identifier::Identifier; -use sui_json_rpc_types::{type_and_fields_from_move_event_data, SuiEvent}; +use sui_json_rpc_types::{type_and_fields_from_move_event_data, BcsEvent, SuiEvent}; use sui_package_resolver::{PackageStore, Resolver}; use sui_types::base_types::{ObjectID, SuiAddress}; use sui_types::digests::TransactionDigest; @@ -115,7 +115,7 @@ impl StoredEvent { transaction_module: Identifier::from_str(&self.module)?, sender, type_, - bcs: self.bcs, + bcs: BcsEvent::new(self.bcs), parsed_json, timestamp_ms: Some(self.timestamp_ms as u64), }) diff --git a/crates/sui-open-rpc/spec/openrpc.json b/crates/sui-open-rpc/spec/openrpc.json index bfeda57b7ca15..027082a9bc8df 100644 --- a/crates/sui-open-rpc/spec/openrpc.json +++ b/crates/sui-open-rpc/spec/openrpc.json @@ -792,6 +792,7 @@ "parsedJson": { "test": "example value" }, + "bcsEncoding": "base64", "bcs": "" } ], @@ -3555,7 +3556,8 @@ "type": "0x0000000000000000000000000000000000000000000000000000000000000009::test::TestField", "value": "some_value" }, - "bcsName": "FDB4OTo6dGVzdDo6VGVzdEZpZWxk", + "bcsEncoding": "base64", + "bcs_name": "FDB4OTo6dGVzdDo6VGVzdEZpZWxk", "type": "DynamicField", "objectType": "test", "objectId": "0x82b2fd67344691abd0efc771941b948ad35360b08e449fbbc28b0641175bf60b", @@ -3567,7 +3569,8 @@ "type": "0x0000000000000000000000000000000000000000000000000000000000000009::test::TestField", "value": "some_value" }, - "bcsName": "FDB4OTo6dGVzdDo6VGVzdEZpZWxk", + "bcsEncoding": "base64", + "bcs_name": "FDB4OTo6dGVzdDo6VGVzdEZpZWxk", "type": "DynamicField", "objectType": "test", "objectId": "0x21564fc5a68ace997461b098c1d1f3ccbde241d8fdf562db36bc1423ee10cecb", @@ -3579,7 +3582,8 @@ "type": "0x0000000000000000000000000000000000000000000000000000000000000009::test::TestField", "value": "some_value" }, - "bcsName": "FDB4OTo6dGVzdDo6VGVzdEZpZWxk", + "bcsEncoding": "base64", + "bcs_name": "FDB4OTo6dGVzdDo6VGVzdEZpZWxk", "type": "DynamicField", "objectType": "test", "objectId": "0x7e00acf5386662fa062483ba507b1e9e3039750f0a270f2e12441ad7f611a5f7", @@ -4115,6 +4119,7 @@ "sender": "0x84bd999f9ff7a1804872957fafa528628a24386298faa98850887f64da841b87", "type": "0x3::test::Test<0x3::test::Test>", "parsedJson": "some_value", + "bcsEncoding": "base64", "bcs": "" }, { @@ -4127,6 +4132,7 @@ "sender": "0x279efd098d59a66a3d9adc87cce81fe9ec69dc8105b2b60140589ec8be44c29f", "type": "0x3::test::Test<0x3::test::Test>", "parsedJson": "some_value", + "bcsEncoding": "base64", "bcs": "" }, { @@ -4139,6 +4145,7 @@ "sender": "0x289be027d2a94f744b4c59fda7b528f9c59f430eaba84b8bee9b43a30f9cc83f", "type": "0x3::test::Test<0x3::test::Test>", "parsedJson": "some_value", + "bcsEncoding": "base64", "bcs": "" }, { @@ -4151,6 +4158,7 @@ "sender": "0xa395759ca37c6e1ffc179184e98a6f9a2da5d78f6e34b0e5044ed52a6bc0a1bc", "type": "0x3::test::Test<0x3::test::Test>", "parsedJson": "some_value", + "bcsEncoding": "base64", "bcs": "" } ], @@ -5951,8 +5959,45 @@ }, "DynamicFieldInfo": { "type": "object", + "oneOf": [ + { + "type": "object", + "required": [ + "bcsEncoding", + "bcs_name" + ], + "properties": { + "bcsEncoding": { + "type": "string", + "enum": [ + "base64" + ] + }, + "bcs_name": { + "$ref": "#/components/schemas/Base64" + } + } + }, + { + "type": "object", + "required": [ + "bcsEncoding", + "bcs_name" + ], + "properties": { + "bcsEncoding": { + "type": "string", + "enum": [ + "base58" + ] + }, + "bcs_name": { + "$ref": "#/components/schemas/Base58" + } + } + } + ], "required": [ - "bcsName", "digest", "name", "objectId", @@ -5961,9 +6006,6 @@ "version" ], "properties": { - "bcsName": { - "$ref": "#/components/schemas/Base64" - }, "digest": { "$ref": "#/components/schemas/ObjectDigest" }, @@ -6070,8 +6112,45 @@ }, "Event": { "type": "object", + "oneOf": [ + { + "type": "object", + "required": [ + "bcs", + "bcsEncoding" + ], + "properties": { + "bcs": { + "$ref": "#/components/schemas/Base64" + }, + "bcsEncoding": { + "type": "string", + "enum": [ + "base64" + ] + } + } + }, + { + "type": "object", + "required": [ + "bcs", + "bcsEncoding" + ], + "properties": { + "bcs": { + "$ref": "#/components/schemas/Base58" + }, + "bcsEncoding": { + "type": "string", + "enum": [ + "base58" + ] + } + } + } + ], "required": [ - "bcs", "id", "packageId", "parsedJson", @@ -6080,14 +6159,6 @@ "type" ], "properties": { - "bcs": { - "description": "Base64 encoded bcs bytes of the move event", - "allOf": [ - { - "$ref": "#/components/schemas/Base64" - } - ] - }, "id": { "description": "Sequential event ID, ie (transaction seq number, event seq number). 1) Serves as a unique event ID for each fullnode 2) Also serves to sequence events for the purposes of pagination and querying. A higher id is an event seen later by that fullnode. This ID is the \"cursor\" for event querying.", "allOf": [ diff --git a/crates/sui-open-rpc/src/examples.rs b/crates/sui-open-rpc/src/examples.rs index 702841b2f7f9a..3cb3188f10806 100644 --- a/crates/sui-open-rpc/src/examples.rs +++ b/crates/sui-open-rpc/src/examples.rs @@ -18,6 +18,7 @@ use serde_json::json; use sui_json::SuiJsonValue; use sui_json_rpc::error::Error; +use sui_json_rpc_types::BcsEvent; use sui_json_rpc_types::DevInspectArgs; use sui_json_rpc_types::{ Balance, Checkpoint, CheckpointId, CheckpointPage, Coin, CoinPage, DelegatedStake, @@ -780,7 +781,7 @@ impl RpcExampleProvider { sender: SuiAddress::from(ObjectID::new(self.rng.gen())), type_: parse_sui_struct_tag("0x9::test::TestEvent").unwrap(), parsed_json: json!({"test": "example value"}), - bcs: vec![], + bcs: BcsEvent::new(vec![]), timestamp_ms: None, }; @@ -1163,6 +1164,7 @@ impl RpcExampleProvider { version: SequenceNumber::from_u64(1), digest: ObjectDigest::new(self.rng.gen()), }) + .map(Into::into) .collect::>(); let next_cursor = ObjectID::new(self.rng.gen()); @@ -1316,7 +1318,7 @@ impl RpcExampleProvider { sender: SuiAddress::from(ObjectID::new(self.rng.gen())), type_: StructTag::from_str("0x3::test::Test<0x3::test::Test>").unwrap(), parsed_json: serde_json::Value::String("some_value".to_string()), - bcs: vec![], + bcs: BcsEvent::new(vec![]), timestamp_ms: None, }) .collect(); diff --git a/crates/sui-types/src/dynamic_field.rs b/crates/sui-types/src/dynamic_field.rs index 8374989f23634..fc808abfe8d5e 100644 --- a/crates/sui-types/src/dynamic_field.rs +++ b/crates/sui-types/src/dynamic_field.rs @@ -61,12 +61,11 @@ where } #[serde_as] -#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug)] +#[derive(Clone, Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase")] pub struct DynamicFieldInfo { pub name: DynamicFieldName, - #[schemars(with = "Base64")] - #[serde_as(as = "Readable")] + #[serde_as(as = "Readable")] pub bcs_name: Vec, pub type_: DynamicFieldType, pub object_type: String, diff --git a/crates/sui-types/src/sui_serde.rs b/crates/sui-types/src/sui_serde.rs index ea2d1017f613f..714c28e198d72 100644 --- a/crates/sui-types/src/sui_serde.rs +++ b/crates/sui-types/src/sui_serde.rs @@ -366,36 +366,3 @@ impl<'de> DeserializeAs<'de, ProtocolVersion> for AsProtocolVersion { Ok(ProtocolVersion::from(*b)) } } - -/// Always serialize as base64, but deserialize from either Base64 or Base58 -pub struct Base64orBase58; - -impl SerializeAs for Base64orBase58 -where - T: AsRef<[u8]>, -{ - fn serialize_as(value: &T, serializer: S) -> Result - where - S: Serializer, - { - use fastcrypto::encoding::Encoding; - - let encoded_string = fastcrypto::encoding::Base64::encode(value); - encoded_string.serialize(serializer) - } -} - -impl<'de> DeserializeAs<'de, Vec> for Base64orBase58 { - fn deserialize_as(deserializer: D) -> Result, D::Error> - where - D: Deserializer<'de>, - { - use fastcrypto::encoding::Encoding; - - let s = String::deserialize(deserializer)?; - - fastcrypto::encoding::Base64::decode(&s) - .or_else(|_| fastcrypto::encoding::Base58::decode(&s)) - .map_err(|_| Error::custom("Deserialization failed")) - } -} diff --git a/crates/sui/src/client_commands.rs b/crates/sui/src/client_commands.rs index 8c36028994a3c..4a7736645b6e6 100644 --- a/crates/sui/src/client_commands.rs +++ b/crates/sui/src/client_commands.rs @@ -42,11 +42,11 @@ use sui_source_validation::{BytecodeSourceVerifier, ValidationMode}; use shared_crypto::intent::Intent; use sui_json::SuiJsonValue; use sui_json_rpc_types::{ - Coin, DevInspectArgs, DevInspectResults, DryRunTransactionBlockResponse, DynamicFieldPage, - SuiCoinMetadata, SuiData, SuiExecutionStatus, SuiObjectData, SuiObjectDataOptions, - SuiObjectResponse, SuiObjectResponseQuery, SuiParsedData, SuiProtocolConfigValue, SuiRawData, - SuiTransactionBlockEffects, SuiTransactionBlockEffectsAPI, SuiTransactionBlockResponse, - SuiTransactionBlockResponseOptions, + Coin, DevInspectArgs, DevInspectResults, DryRunTransactionBlockResponse, DynamicFieldInfo, + DynamicFieldPage, SuiCoinMetadata, SuiData, SuiExecutionStatus, SuiObjectData, + SuiObjectDataOptions, SuiObjectResponse, SuiObjectResponseQuery, SuiParsedData, + SuiProtocolConfigValue, SuiRawData, SuiTransactionBlockEffects, SuiTransactionBlockEffectsAPI, + SuiTransactionBlockResponse, SuiTransactionBlockResponseOptions, }; use sui_keys::keystore::AccountKeystore; use sui_move_build::{ @@ -66,7 +66,6 @@ use sui_types::{ base_types::{ObjectID, SequenceNumber, SuiAddress}, crypto::{EmptySignInfo, SignatureScheme}, digests::TransactionDigest, - dynamic_field::DynamicFieldInfo, error::SuiError, gas::GasCostSummary, gas_coin::GasCoin,