Skip to content
This repository has been archived by the owner on Aug 21, 2024. It is now read-only.

Added getLedgerEntries and deprecation warning for getLedgerEntry #391

Closed
wants to merge 2 commits into from
Closed
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
318 changes: 318 additions & 0 deletions api/methods/getLedgerEntries.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,318 @@
---
sidebar_position: 4
---
For reading the current value of ledger entries directly.

Allows you to directly inspect the _current state_ of a contract, a contract's code, or any other ledger entry. This is a backup way to access your contract data which may not be available via events or `simulateTransaction`.

To fetch contract wasm byte-code, use the ContractCode ledger entry key.

## Parameters

- `keys`: `<xdr.LedgerKey[]>` - The keys of the ledger entries you wish to retrieve (serialized in a base64 string)

## Return

- `<object>`
- `entries`: `<object[]>` - Found ledger entries returned in the same order as given
- `xdr`: `<xdr.LedgerEntryData>` - The current value of this ledger entry (serialized in a base64 string)
- `lastModifiedLedgerSeq`: `<number>` - The ledger number of the last time this entry was updated (optional)
- `latestLedger`: `<number>` - The current latest ledger observed by the node when this response was generated.

## Examples

### Request

```json
{
"jsonrpc": "2.0",
"id": 8675309,
"method": "getLedgerEntries",
"params": {
"keys": [
"AAAABhv6ziOnWcVRdGMZjtFKSWnLSndMp9JPVLLXxQqAvKqJAAAABQAAAAdDT1VOVEVSAA=="
]
}
}
```

### Response

```json
{
"jsonrpc": "2.0",
"id": 8675309,
"result": {
"entries": [
{
"xdr": "AAAABhv6ziOnWcVRdGMZjtFKSWnLSndMp9JPVLLXxQqAvKqJAAAABQAAAAdDT1VOVEVSAAAAAAEAAAAD",
"lastModifiedLedgerSeq": "164986",
}
],
"latestLedger": "179436"
}
}
```

### Generating `keys` Parameters

The example above is querying a deployment of the [`increment` example contract] to find out what value is stored in the `COUNTER` ledger entry. This value can be derived using the following code snippets. You should be able to extrapolate from the provided examples how to get `keys` parameters for other types and values.

#### Python

:::note

If you are using the Python `stellar_sdk` to generate these keys, you will need to install the latest version of the `soroban` branch of the SDK. This can be done like so:

```bash
pip install git+https://github.com/StellarCN/py-stellar-base.git@soroban
```

:::

```python
import stellar_sdk.xdr as xdr, stellar_sdk.soroban_types as soroban_types

def get_ledger_key_symbol(contract_id, symbol_text):
ledger_key = xdr.ledger_key.LedgerKey(
type=xdr.ledger_entry_type.LedgerEntryType.CONTRACT_DATA,
contract_data=xdr.ledger_key_contract_data.LedgerKeyContractData(
contract_id=xdr.hash.Hash(bytes.fromhex(contract_id)),
key=soroban_types.Symbol(symbol_text)._to_xdr_sc_val()
)
)
return ledger_key.to_xdr()

print(get_ledger_key_symbol(
"1bface23a759c5517463198ed14a4969cb4a774ca7d24f54b2d7c50a80bcaa89",
"COUNTER"
))
```

#### JavaScript

```js
const SorobanClient = require('soroban-client')
const xdr = SorobanClient.xdr

const getLedgerKeySymbol = (contractId, symbolText) => {
const ledgerKey = xdr.LedgerKey.contractData(
new xdr.LedgerKeyContractData({
contractId: Buffer.from(contractId, 'hex'),
key: xdr.ScVal.scvSymbol(symbolText)
})
)
return ledgerKey.toXDR('base64')
}
console.log(getLedgerKeySymbol(
'1bface23a759c5517463198ed14a4969cb4a774ca7d24f54b2d7c50a80bcaa89',
'COUNTER'
))
```

### Requesting an Account

:::note

This functionality is included in the js [soroban-client](https://www.npmjs.com/package/soroban-client) package as `Server.getAccount(address)`.

:::

Accounts are stored as ledger entries, so we can use this method to look up an account along with it's current sequence number.

```js
const SorobanClient = require('soroban-client')
const xdr = SorobanClient.xdr

const getLedgerKeyAccount = (address) => {
// We can use the `StrKey` library here to decode the address into the public
// key.
const publicKey = SorobanClient.StrKey.decodeEd25519PublicKey(address);

const ledgerKey = xdr.LedgerKey.account(
new xdr.LedgerKeyAccount({
accountId: xdr.PublicKey.publicKeyTypeEd25519(
publicKey
),
})
)
return ledgerKey.toXDR('base64')
}

console.log(getLedgerKeyAccount(
'GCU5YE6IVBOEZ5LUU5N2NB55VPB5YZNFERT65SSTVXTNMS7IEQWXKBM2'
))

# OUTPUT: AAAAAAAAAACp3BPIqFxM9XSnW6aHvavD3GWlJGfuylOt5tZL6CQtdQ==
```


We then take our output from this function, and use it as the `keys` parameter in our call to the `getLedgerEntries` method.

```json
{
"jsonrpc": "2.0",
"id": 8675309,
"method": "getLedgerEntries",
"params": {
"keys": [
"AAAAAAAAAACp3BPIqFxM9XSnW6aHvavD3GWlJGfuylOt5tZL6CQtdQ=="
]
}
}
```

And the response we get contains the `LedgerEntryData` with the current information about this account.

```json
{
"jsonrpc": "2.0",
"id": 8675309,
"result": {
"entries": [
{
"xdr": "AAAAAAAAAACp3BPIqFxM9XSnW6aHvavD3GWlJGfuylOt5tZL6CQtdQAAABdIdugAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAA",
"lastModifiedLedgerSeq": "164303",
},
],
"latestLedger": "246819"
}
}
```

We can then parse this result as an `xdr.LedgerEntryData` type.

```js
const parsed = xdr.LedgerEntryData.fromXDR(
'AAAAAAAAAACp3BPIqFxM9XSnW6aHvavD3GWlJGfuylOt5tZL6CQtdQAAABdIdugAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAA',
'base64'
)
console.log(parsed);
```

### Requesting a Contract's WASM Code

This can be a bit tricky to wrap your head around, but the conventions do make sense once you let it sink in.

In the previous examples, the `COUNTER` _key_ was used as a `LedgerKey` while the incremented _value_ was stored in a **`LedgerEntry`**. "Ledger Entry" is the relevant term to keep in mind during this discussion. That `LedgerEntry` was stored on the Stellar ledger, and was associated with a corresponding `LedgerKey`. `LedgerKey: LedgerEntry` works the same way you would think of almost any `key: value` storage system.

#### How Soroban Contract Deployment Works

When you deploy a contract, first the code is "installed" (i.e. it is uploaded onto the blockchain). This creates a `LedgerEntry` containing the WASM byte-code, which is uniquely identified by its hash (that is, the hash of the uploaded code itself). Then, when the contract is "deployed," we create a `LedgerEntry` with a reference to that code's hash. So fetching the contract code is a two-step process:

1. First, we look up the contract itself, to see which code hash it is referencing.
2. Then, we can look up the raw WASM byte-code using that hash.

#### Request the `LedgerKey` for the Contract Code

```python
def get_ledger_key_contract_code(contract_id):
ledger_key = xdr.ledger_key.LedgerKey(
type=xdr.ledger_entry_type.LedgerEntryType.CONTRACT_DATA,
contract_data=xdr.ledger_key_contract_data.LedgerKeyContractData(
contract_id=xdr.hash.Hash(bytes.fromhex(contract_id)),
key=xdr.sc_val.SCVal(
type=xdr.sc_val_type.SCValType.SCV_STATIC,
ic=xdr.sc_static.SCStatic.SCS_LEDGER_KEY_CONTRACT_CODE
)
)
)
return ledger_key.to_xdr()
print(get_ledger_key_contract_code(
"1bface23a759c5517463198ed14a4969cb4a774ca7d24f54b2d7c50a80bcaa89"
))
# OUTPUT: AAAABhv6ziOnWcVRdGMZjtFKSWnLSndMp9JPVLLXxQqAvKqJAAAAAwAAAAM=
```

We then take our output from this function, and use it as the `keys` parameter in our call to the `getLedgerEntries` method.

```json
{
"jsonrpc": "2.0",
"id": 8675309,
"method": "getLedgerEntries",
"params": {
"keys": [
"AAAABhv6ziOnWcVRdGMZjtFKSWnLSndMp9JPVLLXxQqAvKqJAAAAAwAAAAM="
]
}
}
```

And the response we get contains the `LedgerEntryData` that can be used to find the `hash` we must use to request the WASM byte-code. This hash is the `LedgerKey` that's been associated with the deployed contract code.

```json
{
"jsonrpc": "2.0",
"id": 8675309,
"result": {
"entries": [
{
"xdr": "AAAABhv6ziOnWcVRdGMZjtFKSWnLSndMp9JPVLLXxQqAvKqJAAAAAwAAAAMAAAAEAAAAAQAAAAcAAAAA1CpZRz0BSN27PmqtsmBhv+AAJJwHgmrvJNPrHRAl9l8=",
"lastModifiedLedgerSeq": "164303",
}
],
"latestLedger": "246819"
}
}
```

#### Request the `ContractCode` Using the Retrieved `LedgerKey`

Now take the `xdr` field from the previous response's `result` object, and create a `LedgerKey` from the hash contained inside.

```python
def get_ledger_key_wasm_id(contract_code_ledger_entry_data):
# First, we dig the wasm_id hash out of the xdr we received from RPC
contract_code_wasm_hash = xdr.ledger_entry_data.LedgerEntryData.from_xdr(
contract_code_ledger_entry_data
).contract_data.val.obj.contract_code.wasm_id.hash
# Now, we can create the `LedgerKey` as we've done in previous examples
ledger_key = xdr.ledger_key.LedgerKey(
type=xdr.ledger_entry_type.LedgerEntryType.CONTRACT_CODE,
contract_code=xdr.ledger_key_contract_code.LedgerKeyContractCode(
hash=xdr.hash.Hash(contract_code_wasm_hash)
)
)
return ledger_key.to_xdr()
print(get_ledger_key_wasm_id(
"AAAABhv6ziOnWcVRdGMZjtFKSWnLSndMp9JPVLLXxQqAvKqJAAAAAwAAAAMAAAAEAAAAAQAAAAcAAAAA1CpZRz0BSN27PmqtsmBhv+AAJJwHgmrvJNPrHRAl9l8="
))
# OUTPUT: AAAAB9QqWUc9AUjduz5qrbJgYb/gACScB4Jq7yTT6x0QJfZf
```

Now, finally we have a `LedgerKey` that correspond to the WASM byte-code that has been deployed under the `ContractId` we started out with so very long ago. This `LedgerKey` can be used in a final request to the Soroban-RPC endpoint.

```json
{
"jsonrpc": "2.0",
"id": 8675309,
"method": "getLedgerEntries",
"params": {
"keys": [
"AAAAB9QqWUc9AUjduz5qrbJgYb/gACScB4Jq7yTT6x0QJfZf"
]
}
}
```

And the response we get contains (even more) `LedgerEntryData` that we can decode and parse to get the actual, deployed, real-life contract byte-code. We'll leave that exercise up to you. You can check out what is contained using the ["View XDR" page of the Stellar Laboratory].

```json
{
"jsonrpc": "2.0",
"id": 8675309,
"result": {
"entries": [
{
"xdr": "AAAABwAAAADUKllHPQFI3bs+aq2yYGG/4AAknAeCau8k0+sdECX2XwAAAakAYXNtAQAAAAEXBWABfgF+YAJ+fgF+YAABfmABfwBgAAACEwMBbAEwAAABbAExAAABbAFfAAEDBgUCAwQEBAUDAQAQBhkDfwFBgIDAAAt/AEGAgMAAC38AQYCAwAALBzUFBm1lbW9yeQIACWluY3JlbWVudAADAV8ABwpfX2RhdGFfZW5kAwELX19oZWFwX2Jhc2UDAgrAAQWhAQMBfwF+AX8jgICAgABBEGsiACSAgICAAAJAAkBC2YP9sqDNAxCAgICAAEIVUg0AAkBC2YP9sqDNAxCBgICAACIBQg+DQgFSDQAgAUIEiKchAgwCCyAAQQhqEISAgIAAAAtBACECCwJAIAJBAWoiAg0AEIWAgIAAAAtC2YP9sqDNAyACrUIEhkIBhCIBEIKAgIAAGiAAQRBqJICAgIAAIAELCQAQhoCAgAAACwkAEIaAgIAAAAsEAAAACwIACwAeEWNvbnRyYWN0ZW52bWV0YXYwAAAAAAAAAAAAAAAbAC8OY29udHJhY3RzcGVjdjAAAAAAAAAACWluY3JlbWVudAAAAAAAAAAAAAABAAAAAQAAAA==",
"lastModifiedLedgerSeq": "164302",
}
],
"latestLedger": "246883"
}
}
```

[`increment` example contract]: /docs/getting-started/storing-data
["View XDR" page of the Stellar Laboratory]: <https://laboratory.stellar.org/#xdr-viewer?type=LedgerEntryData&network=futurenet>
2 changes: 1 addition & 1 deletion api/methods/getLedgerEntry.mdx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
sidebar_position: 4
---

:::note **This function is deprecated and will be removed in the next Soroban release. Please use getLedgerEntries instead.**
For reading the current value of ledger entries directly.

Allows you to directly inspect the _current state_ of a contract, a contract's code, or any other ledger entry. This is a backup way to access your contract data which may not be available via events or `simulateTransaction`.
Expand Down
1 change: 1 addition & 0 deletions sidebarsApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ module.exports = {
'methods/getHealth',
'methods/getLatestLedger',
'methods/getLedgerEntry',
'methods/getLedgerEntries',
'methods/getNetwork',
'methods/getTransaction',
'methods/sendTransaction',
Expand Down