diff --git a/crates/rooch-open-rpc-spec/schemas/openrpc.json b/crates/rooch-open-rpc-spec/schemas/openrpc.json index c4bcfdccd8..b4db94a5fc 100644 --- a/crates/rooch-open-rpc-spec/schemas/openrpc.json +++ b/crates/rooch-open-rpc-spec/schemas/openrpc.json @@ -861,6 +861,47 @@ } } }, + "AnnotatedMoveStructVectorView": { + "type": "object", + "required": [ + "abilities", + "field", + "type", + "value" + ], + "properties": { + "abilities": { + "description": "alilities of each element", + "type": "integer", + "format": "uint8", + "minimum": 0.0 + }, + "field": { + "description": "field of each element", + "type": "array", + "items": { + "$ref": "#/components/schemas/move_core_types::identifier::Identifier" + } + }, + "type": { + "description": "type of each element", + "allOf": [ + { + "$ref": "#/components/schemas/move_core_types::language_storage::StructTag" + } + ] + }, + "value": { + "type": "array", + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AnnotatedMoveValueView" + } + } + } + } + }, "AnnotatedMoveStructView": { "type": "object", "required": [ @@ -915,6 +956,9 @@ "$ref": "#/components/schemas/AnnotatedMoveValueView" } }, + { + "$ref": "#/components/schemas/AnnotatedMoveStructVectorView" + }, { "$ref": "#/components/schemas/alloc::vec::Vec" }, diff --git a/crates/rooch-rpc-api/src/jsonrpc_types/move_types.rs b/crates/rooch-rpc-api/src/jsonrpc_types/move_types.rs index e07ace7991..a5e47ab381 100644 --- a/crates/rooch-rpc-api/src/jsonrpc_types/move_types.rs +++ b/crates/rooch-rpc-api/src/jsonrpc_types/move_types.rs @@ -151,6 +151,61 @@ impl From for AnnotatedMoveStructView { } } +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Eq, PartialEq, PartialOrd, Ord)] +pub struct AnnotatedMoveStructVectorView { + /// alilities of each element + pub abilities: u8, + #[serde(rename = "type")] + /// type of each element + pub type_: StructTagView, + /// field of each element + pub field: Vec, + // values of the whole vector + pub value: Vec>, +} + +impl AnnotatedMoveStructVectorView { + fn try_from(origin: Vec) -> Result { + if origin.is_empty() { + Err(AnnotatedMoveValueView::Vector( + origin.into_iter().map(Into::into).collect(), + )) + } else { + let first = origin.first().unwrap(); + if let AnnotatedMoveValue::Struct(ele) = first { + let field = ele + .value + .iter() + .map(|x| IdentifierView::from(x.0.clone())) + .collect(); + let abilities = ele.abilities.into_u8(); + let type_ = StrView(ele.type_.clone()); + let value: Vec> = origin + .into_iter() + .map(|v| { + if let AnnotatedMoveValue::Struct(s) = v { + s.value.into_iter().map(|(_, v)| v.into()).collect() + } else { + unreachable!("AnnotatedMoveStructVectorView") + } + }) + .collect(); + + Ok(Self { + abilities, + type_, + field, + value, + }) + } else { + Err(AnnotatedMoveValueView::Vector( + origin.into_iter().map(Into::into).collect(), + )) + } + } + } +} + /// Some specific struct that we want to display in a special way for better readability #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Eq, PartialEq, PartialOrd, Ord)] #[serde(untagged)] @@ -161,7 +216,7 @@ pub enum SpecificStructView { } impl SpecificStructView { - pub fn try_from_annotated(move_struct: AnnotatedMoveStruct) -> Option { + pub fn try_from_annotated(move_struct: &AnnotatedMoveStruct) -> Option { if MoveString::struct_tag_match(&move_struct.type_) { MoveString::try_from(move_struct) .ok() @@ -192,6 +247,7 @@ pub enum AnnotatedMoveValueView { Bool(bool), Address(AccountAddressView), Vector(Vec), + StructVector(Box), Bytes(BytesView), Struct(AnnotatedMoveStructView), SpecificStruct(SpecificStructView), @@ -209,11 +265,14 @@ impl From for AnnotatedMoveValueView { AnnotatedMoveValue::Bool(b) => AnnotatedMoveValueView::Bool(b), AnnotatedMoveValue::Address(data) => AnnotatedMoveValueView::Address(StrView(data)), AnnotatedMoveValue::Vector(_type_tag, data) => { - AnnotatedMoveValueView::Vector(data.into_iter().map(Into::into).collect()) + match AnnotatedMoveStructVectorView::try_from(data) { + Ok(v) => AnnotatedMoveValueView::StructVector(Box::new(v)), + Err(v) => v, + } } AnnotatedMoveValue::Bytes(data) => AnnotatedMoveValueView::Bytes(StrView(data)), AnnotatedMoveValue::Struct(data) => { - match SpecificStructView::try_from_annotated(data.clone()) { + match SpecificStructView::try_from_annotated(&data) { Some(struct_view) => AnnotatedMoveValueView::SpecificStruct(struct_view), None => AnnotatedMoveValueView::Struct(data.into()), } diff --git a/crates/rooch-rpc-server/src/service/rpc_service.rs b/crates/rooch-rpc-server/src/service/rpc_service.rs index 8cfc8c33b0..69ac91fb54 100644 --- a/crates/rooch-rpc-server/src/service/rpc_service.rs +++ b/crates/rooch-rpc-server/src/service/rpc_service.rs @@ -404,7 +404,7 @@ impl RpcService { } else { BTreeMap::new() }; - let mut object_states = annotated_states + let mut object_states: Vec = annotated_states .into_iter() .zip(indexer_ids) .filter_map(|(state_opt, (object_id, indexer_state_id))| { @@ -423,7 +423,7 @@ impl RpcService { } } }) - .collect::>(); + .collect(); if !displays.is_empty() { object_states.iter_mut().for_each(|object_state| { object_state.display_fields = diff --git a/crates/testsuite/features/bitseed.feature b/crates/testsuite/features/bitseed.feature index 8ed1cdb417..a9e7af1271 100644 --- a/crates/testsuite/features/bitseed.feature +++ b/crates/testsuite/features/bitseed.feature @@ -31,7 +31,9 @@ Feature: Rooch CLI bitseed tests # Check mint generator validity Then cmd: "move view --function 0x4::ord::view_validity --args string:{{$.bitseed[-1].inscriptions[0].Id}} " Then assert: "{{$.move[-1].vm_status}} == Executed" - Then assert: "{{$.move[-1].return_values[0].decoded_value.value.vec[0].value.is_valid}} == true" + # For `.vec.value[0][1]`, the first index `0` means the first element of vec; + # the second index `1` means the second field of `0x4::ord::MetaprotocolValidity`, that is `is_valid`. + Then assert: "{{$.move[-1].return_values[0].decoded_value.value.vec.value[0][1]}} == true" # deploy Then cmd: "bitseed deploy --fee-rate 6000 --generator {{$.bitseed[-1].inscriptions[0].Id}} --tick bits --amount 210000000000 --deploy-args '{"height":{"type":"range","data":{"min":1,"max":1000}}}'" @@ -48,7 +50,9 @@ Feature: Rooch CLI bitseed tests # Check deploy validity Then cmd: "move view --function 0x4::ord::view_validity --args string:{{$.bitseed[-1].inscriptions[0].Id}} " Then assert: "{{$.move[-1].vm_status}} == Executed" - Then assert: "{{$.move[-1].return_values[0].decoded_value.value.vec[0].value.is_valid}} == true" + # For `.vec.value[0][1]`, the first index `0` means the first element of vec; + # the second index `1` means the second field of `0x4::ord::MetaprotocolValidity`, that is `is_valid`. + Then assert: "{{$.move[-1].return_values[0].decoded_value.value.vec.value[0][1]}} == true" # mint 1 Then cmd: "bitseed mint --fee-rate 6000 --deploy-inscription-id {{$.bitseed[-1].inscriptions[0].Id}} --user-input test" @@ -72,11 +76,15 @@ Feature: Rooch CLI bitseed tests # Check mint bits validity Then cmd: "move view --function 0x4::ord::view_validity --args string:{{$.bitseed[-1].inscriptions[0].Id}} " Then assert: "{{$.move[-1].vm_status}} == Executed" - Then assert: "{{$.move[-1].return_values[0].decoded_value.value.vec[0].value.is_valid}} == true" + # For `.vec.value[0][1]`, the first index `0` means the first element of vec; + # the second index `1` means the second field of `0x4::ord::MetaprotocolValidity`, that is `is_valid`. + Then assert: "{{$.move[-1].return_values[0].decoded_value.value.vec.value[0][1]}} == true" Then cmd: "move view --function 0x4::ord::view_validity --args string:{{$.bitseed[-2].inscriptions[0].Id}} " Then assert: "{{$.move[-1].vm_status}} == Executed" - Then assert: "{{$.move[-1].return_values[0].decoded_value.value.vec[0].value.is_valid}} == true" + # For `.vec.value[0][1]`, the first index `0` means the first element of vec; + # the second index `1` means the second field of `0x4::ord::MetaprotocolValidity`, that is `is_valid`. + Then assert: "{{$.move[-1].return_values[0].decoded_value.value.vec.value[0][1]}} == true" Then cmd: "bitseed merge --fee-rate 6000 --sft-inscription-ids {{$.bitseed[-1].inscriptions[0].Id}} --sft-inscription-ids {{$.bitseed[-2].inscriptions[0].Id}}" Then assert: "'{{$.bitseed[-1]}}' not_contains error" @@ -90,8 +98,10 @@ Feature: Rooch CLI bitseed tests Then cmd: "move view --function 0x4::ord::view_validity --args string:{{$.bitseed[-1].inscriptions[0].Id}} " Then assert: "{{$.move[-1].vm_status}} == Executed" - Then assert: "{{$.move[-1].return_values[0].decoded_value.value.vec[0].value.is_valid}} == true" - + # For `.vec.value[0][1]`, the first index `0` means the first element of vec; + # the second index `1` means the second field of `0x4::ord::MetaprotocolValidity`, that is `is_valid`. + Then assert: "{{$.move[-1].return_values[0].decoded_value.value.vec.value[0][1]}} == true" + # release servers Then stop the server Then stop the bitcoind server @@ -130,7 +140,9 @@ Feature: Rooch CLI bitseed tests # Check deploy validity Then cmd: "move view --function 0x4::ord::view_validity --args string:{{$.bitseed[-1].inscriptions[0].Id}} " Then assert: "{{$.move[-1].vm_status}} == Executed" - Then assert: "{{$.move[-1].return_values[0].decoded_value.value.vec[0].value.is_valid}} == true" + # For `.vec.value[0][1]`, the first index `0` means the first element of vec; + # the second index `1` means the second field of `0x4::ord::MetaprotocolValidity`, that is `is_valid`. + Then assert: "{{$.move[-1].return_values[0].decoded_value.value.vec.value[0][1]}} == true" # mint on rooch Then cmd: "move run --function 0xa::mint_get_factory::mint --args string:bitseed --args string:test --json" diff --git a/crates/testsuite/features/ord.feature b/crates/testsuite/features/ord.feature index de0f2d1e6d..9cfb75c5e8 100644 --- a/crates/testsuite/features/ord.feature +++ b/crates/testsuite/features/ord.feature @@ -45,7 +45,9 @@ Feature: Rooch Bitcoin ord tests # Check inscription burned Then cmd: "move view --function 0x4::ord::view_inscription_charm --args string:{{$.wallet[-3][0].inscription}} " Then assert: "{{$.move[-1].vm_status}} == Executed" - Then assert: "{{$.move[-1].return_values[0].decoded_value.value.vec[0].value.burned}} == true" + # For `.vec.value[0][1]`, the first index `0` means the first element of vec; + # the second index `12` means the 13th field of `0x4::ord::InscriptionCharm`, that is `burned`. + Then assert: "{{$.move[-1].return_values[0].decoded_value.value.vec.value[0][12]}} == true" # release servers Then stop the server diff --git a/moveos/moveos-types/src/move_std/ascii.rs b/moveos/moveos-types/src/move_std/ascii.rs index 9639a0974a..0c7a4b7246 100644 --- a/moveos/moveos-types/src/move_std/ascii.rs +++ b/moveos/moveos-types/src/move_std/ascii.rs @@ -114,6 +114,26 @@ impl TryFrom for MoveAsciiString { } } +impl TryFrom<&AnnotatedMoveStruct> for MoveAsciiString { + type Error = anyhow::Error; + + fn try_from(value: &AnnotatedMoveStruct) -> Result { + let annotated_move_struct = value; + let (field_name, field_value) = annotated_move_struct + .value + .first() + .ok_or_else(|| anyhow::anyhow!("Invalid MoveAsciiString"))?; + debug_assert!(field_name.as_str() == "bytes"); + let bytes = match field_value { + AnnotatedMoveValue::Bytes(bytes) => bytes, + _ => return Err(anyhow::anyhow!("Invalid MoveAsciiString")), + }; + Ok(MoveAsciiString { + bytes: bytes.clone(), + }) + } +} + impl TryFrom for Identifier { type Error = anyhow::Error; diff --git a/moveos/moveos-types/src/move_std/string.rs b/moveos/moveos-types/src/move_std/string.rs index 1639f52a67..b8e6539e6b 100644 --- a/moveos/moveos-types/src/move_std/string.rs +++ b/moveos/moveos-types/src/move_std/string.rs @@ -155,6 +155,26 @@ impl TryFrom for MoveString { } } +impl TryFrom<&AnnotatedMoveStruct> for MoveString { + type Error = anyhow::Error; + + fn try_from(value: &AnnotatedMoveStruct) -> Result { + let annotated_move_struct = value; + let (field_name, field_value) = annotated_move_struct + .value + .first() + .ok_or_else(|| anyhow::anyhow!("Invalid MoveString"))?; + debug_assert!(field_name.as_str() == "bytes"); + let bytes = match field_value { + AnnotatedMoveValue::Bytes(bytes) => bytes, + _ => return Err(anyhow::anyhow!("Invalid MoveString")), + }; + Ok(MoveString { + bytes: bytes.clone(), + }) + } +} + impl TryFrom for Identifier { type Error = anyhow::Error; diff --git a/moveos/moveos-types/src/moveos_std/object.rs b/moveos/moveos-types/src/moveos_std/object.rs index c0e82ee6b2..e135cabf55 100644 --- a/moveos/moveos-types/src/moveos_std/object.rs +++ b/moveos/moveos-types/src/moveos_std/object.rs @@ -295,6 +295,19 @@ impl TryFrom for ObjectID { } } +impl TryFrom<&AnnotatedMoveValue> for ObjectID { + type Error = anyhow::Error; + + fn try_from(value: &AnnotatedMoveValue) -> Result { + match value { + AnnotatedMoveValue::Struct(annotated_move_struct) => { + ObjectID::try_from(annotated_move_struct) + } + _ => Err(anyhow::anyhow!("Invalid ObjectID")), + } + } +} + impl TryFrom for ObjectID { type Error = anyhow::Error; @@ -303,6 +316,14 @@ impl TryFrom for ObjectID { } } +impl TryFrom<&AnnotatedMoveStruct> for ObjectID { + type Error = anyhow::Error; + + fn try_from(value: &AnnotatedMoveStruct) -> Result { + ObjectID::try_from_annotated_move_struct_ref(value) + } +} + impl From for ObjectID { fn from(address: AccountAddress) -> Self { ObjectID(vec![address]) diff --git a/sdk/typescript/rooch-sdk/src/client/client.ts b/sdk/typescript/rooch-sdk/src/client/client.ts index d59a9f3fbb..e66f1eae81 100644 --- a/sdk/typescript/rooch-sdk/src/client/client.ts +++ b/sdk/typescript/rooch-sdk/src/client/client.ts @@ -337,7 +337,7 @@ export class RoochClient { if (result.vm_status === 'Executed' && result.return_values) { const value = (result.return_values?.[0]?.decoded_value as AnnotatedMoveStructView).value - const address = (((value as any).vec[0] as AnnotatedMoveStructView).value as any).bytes + const address = (((value as any).vec as AnnotatedMoveStructView).value[0] as Array)[0] return new BitcoinAddress(address, input.network) } @@ -411,7 +411,14 @@ export class RoochClient { hasNextPage: false, } } - + const sss = ( + ( + (states?.[0]?.decoded_value as AnnotatedMoveStructView).value[ + 'value' + ] as AnnotatedMoveStructView + ).value['keys'] as AnnotatedMoveStructView + ).value['handle'] as AnnotatedMoveStructView + console.log(sss) // Maybe we should define the type? const tableId = ( ( @@ -439,8 +446,8 @@ export class RoochClient { const result = new Array() for (const scope of data) { - const value = scope.value - result.push(`${value.module_address}::${value.module_name}::${value.function_name}`) + const [pkg, mod, fn] = [scope[0], scope[1], scope[2]] + result.push(`${pkg}::${mod}::${fn}`) } return result @@ -459,7 +466,7 @@ export class RoochClient { appName: val.app_name, appUrl: val.app_url, authenticationKey: val.authentication_key, - scopes: parseScopes(val.scopes), + scopes: parseScopes(val.scopes.value), createTime: parseInt(val.create_time), lastActiveTime: parseInt(val.last_active_time), maxInactiveInterval: parseInt(val.max_inactive_interval), diff --git a/sdk/typescript/rooch-sdk/src/client/types/generated.ts b/sdk/typescript/rooch-sdk/src/client/types/generated.ts index 725716661d..d92dccb206 100644 --- a/sdk/typescript/rooch-sdk/src/client/types/generated.ts +++ b/sdk/typescript/rooch-sdk/src/client/types/generated.ts @@ -24,6 +24,15 @@ export interface AnnotatedFunctionReturnValueView { decoded_value: AnnotatedMoveValueView value: FunctionReturnValueView } +export interface AnnotatedMoveStructVectorView { + /** alilities of each element */ + abilities: number + /** field of each element */ + field: string[] + /** type of each element */ + type: string + value: AnnotatedMoveValueView[][] +} export interface AnnotatedMoveStructView { abilities: number type: string @@ -38,6 +47,7 @@ export type AnnotatedMoveValueView = | boolean | string | AnnotatedMoveValueView[] + | AnnotatedMoveStructVectorView | string | AnnotatedMoveStructView | SpecificStructView @@ -61,6 +71,35 @@ export interface BlockHeightHashView { block_hash: string block_height: string } +/** DA server basic status */ +export interface DAServerStatus { + /** The available backends names */ + avail_backends: string[] + /** + * The last available block number, may little behind the last block number. [0, + * last_avail_block_number] blocks were confirmed by DA backend. If meet error in background submitter job, + * it may be far behind the last block number. + */ + last_avail_block_number?: number | null + /** + * The last available block number updated time(Unix timestamp) If both of last_avail_block_number and + * last_avail_tx_order are not updated for a long time, it may indicate that the background submitter + * job is not working: 1. DA backends collapse 2. RoochStore is not consistent (cannot get tx from DB + * by tx order) + */ + last_avail_block_update_time?: string | null + /** The last available tx order */ + last_avail_tx_order?: string | null + /** The last block number */ + last_block_number?: number | null + /** + * The last block number updated time(Unix timestamp in seconds) Should be closed to request time if + * there were new blocks. None if no blocks were received after server start. + */ + last_block_update_time?: string | null + /** The last tx order in the last block */ + last_tx_order?: string | null +} export interface DisplayFieldsView { fields: { [key: string]: string @@ -545,6 +584,8 @@ export interface StateOptions { export interface Status { /** The status of the Bitcoin chain */ bitcoin_status: BitcoinStatus + /** The status of the DA service */ + da_server_status: DAServerStatus /** The status of the Rooch chain */ rooch_status: RoochStatus /** The status of the rpc service */ diff --git a/sdk/typescript/rooch-sdk/test-e2e/case/bitcoin.test.ts b/sdk/typescript/rooch-sdk/test-e2e/case/bitcoin.test.ts index b605db7546..9075bcab34 100644 --- a/sdk/typescript/rooch-sdk/test-e2e/case/bitcoin.test.ts +++ b/sdk/typescript/rooch-sdk/test-e2e/case/bitcoin.test.ts @@ -6,7 +6,6 @@ import { TestBox } from '../setup.js' describe('Bitcoin Assets API', () => { let testBox: TestBox - beforeAll(async () => { testBox = TestBox.setup() await testBox.loadBitcoinEnv()