Skip to content

Commit

Permalink
soroban server improvements: getAccount, getContractData
Browse files Browse the repository at this point in the history
  • Loading branch information
christian-rogobete committed Jun 4, 2024
1 parent f26059e commit 403b9b5
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 32 deletions.
9 changes: 9 additions & 0 deletions stellarsdk/stellarsdk/sdk/Account.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ public class Account: TransactionAccount
self.sequenceNumber = sequenceNumber
}

public init(accountId: String, sequenceNumber: Int64) throws {
self.keyPair = try KeyPair(accountId: accountId)
self.sequenceNumber = sequenceNumber
}

/// Returns sequence number incremented by one, but does not increment internal counter.
public func incrementedSequenceNumber() -> Int64 {
return sequenceNumber + 1
Expand All @@ -37,4 +42,8 @@ public class Account: TransactionAccount
public func decrementSequenceNumber() {
sequenceNumber -= 1
}

public var accountId:String {
return keyPair.accountId
}
}
2 changes: 1 addition & 1 deletion stellarsdk/stellarsdk/sdk/MuxedAccount.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class MuxedAccount: Account
public private (set) var xdr: MuxedAccountXDR

/// Human readable Stellar ed25519 or med25519 account ID.
public var accountId: String {
public override var accountId: String {
get {
return xdr.accountId
}
Expand Down
69 changes: 69 additions & 0 deletions stellarsdk/stellarsdk/soroban/SorobanServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,16 @@ public enum GetContractCodeResponseEnum {
case failure(error: SorobanRpcRequestError)
}

public enum GetAccountResponseEnum {
case success(response: Account)
case failure(error: SorobanRpcRequestError)
}

public enum GetContractDataResponseEnum {
case success(response: LedgerEntry)
case failure(error: SorobanRpcRequestError)
}

/// A closure to be called with the response from a post challenge request.
public typealias GetHealthResponseClosure = (_ response:GetHealthResponseEnum) -> (Void)
public typealias GetNetworkResponseClosure = (_ response:GetNetworkResponseEnum) -> (Void)
Expand All @@ -71,6 +81,8 @@ public typealias GetTransactionResponseClosure = (_ response:GetTransactionRespo
public typealias GetEventsResponseClosure = (_ response:GetEventsResponseEnum) -> (Void)
public typealias GetNonceResponseClosure = (_ response:GetNonceResponseEnum) -> (Void)
public typealias GetContractCodeResponseClosure = (_ response:GetContractCodeResponseEnum) -> (Void)
public typealias GetAccountResponseClosure = (_ response:GetAccountResponseEnum) -> (Void)
public typealias GetContractDataResponseClosure = (_ response:GetContractDataResponseEnum) -> (Void)

/// An enum to diferentiate between succesful and failed responses
private enum RpcResult {
Expand Down Expand Up @@ -300,6 +312,63 @@ public class SorobanServer {
}
}

/// Fetches a minimal set of current info about a Stellar account. Needed to get the current sequence
/// number for the account, so you can build a successful transaction. Fails if the account was not found or accountiId is invalid
public func getAccount(accountId:String, completion:@escaping GetAccountResponseClosure) {
if let publicKey = try? PublicKey(accountId: accountId) {
let accountKey = LedgerKeyXDR.account(LedgerKeyAccountXDR(accountID: publicKey))
if let ledgerKeyBase64 = accountKey.xdrEncoded {
self.getLedgerEntries(base64EncodedKeys:[ledgerKeyBase64]) { (response) -> (Void) in
switch response {
case .success(let response):
let data = try? LedgerEntryDataXDR(fromBase64: response.entries[0].xdr)
if let accountData = data?.account {
let account = Account(keyPair: KeyPair(publicKey: accountData.accountID), sequenceNumber: accountData.sequenceNumber);
completion(.success(response: account))
}
else {
completion(.failure(error: .requestFailed(message: "could not find account")))
}
case .failure(let error):
completion(.failure(error: error))
}
}
} else {
completion(.failure(error: .requestFailed(message: "could not create ledger key")))
}
} else {
completion(.failure(error: .requestFailed(message: "invalid accountId")))
}

}

/// Reads the current value of contract data ledger entries directly.
public func getContractData(contractId: String, key: SCValXDR, durability: ContractDataDurability, completion:@escaping GetContractDataResponseClosure) {
if let contractAddress = try? SCAddressXDR.init(contractId: contractId) {
let contractDataKey = LedgerKeyContractDataXDR(contract: contractAddress, key: key, durability: durability)
let ledgerKey = LedgerKeyXDR.contractData(contractDataKey)
if let ledgerKeyBase64 = ledgerKey.xdrEncoded {
self.getLedgerEntries(base64EncodedKeys:[ledgerKeyBase64]) { (response) -> (Void) in
switch response {
case .success(let response):
if let result = response.entries.first {
completion(.success(response: result))
}
else {
completion(.failure(error: .requestFailed(message: "could not find contract data")))
}
case .failure(let error):
completion(.failure(error: error))
}
}
} else {
completion(.failure(error: .requestFailed(message: "could not create ledger key")))
}
} else {
completion(.failure(error: .requestFailed(message: "invalid contractId")))
}
}

/// Submit a trial contract invocation to get back return values, expected ledger footprint, and expected costs.
/// See: https://soroban.stellar.org/api/methods/simulateTransaction
public func simulateTransaction(simulateTxRequest: SimulateTransactionRequest, completion:@escaping SimulateTransactionResponseClosure) {
Expand Down
15 changes: 7 additions & 8 deletions stellarsdk/stellarsdkTests/soroban/SorobanAtomicSwapTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@ import XCTest
import stellarsdk

class SorobanAtomicSwapTest: XCTestCase {
// See https://soroban.stellar.org/docs/how-to-guides/atomic-swap
// See https://soroban.stellar.org/docs/learn/authorization
// See https://github.com/StellarCN/py-stellar-base/blob/soroban/examples/soroban_auth_atomic_swap.py
// See https://developers.stellar.org/docs/smart-contracts/example-contracts/atomic-swap
// See https://developers.stellar.org/docs/learn/smart-contract-internals/authorization

let sorobanServer = SorobanServer(endpoint: "https://soroban-testnet.stellar.org") // SorobanServer(endpoint: "https://rpc-futurenet.stellar.org")
let sdk = StellarSDK.testNet() // StellarSDK.futureNet()
Expand All @@ -26,7 +25,7 @@ class SorobanAtomicSwapTest: XCTestCase {
let tokenBId = "b6d208a3bf0b08ca12d4e1d2a39525caa9e866a0ba396ebe60307f9fbafd451f"
let swapFunctionName = "swap"
var invokeTransactionId:String?
var submitterAccount:AccountResponse?
var submitterAccount:Account?
var latestLedger:UInt32?

override func setUp() {
Expand Down Expand Up @@ -57,11 +56,11 @@ class SorobanAtomicSwapTest: XCTestCase {
let expectation = XCTestExpectation(description: "get account response received")

let accountId = submitterKeyPair.accountId
sdk.accounts.getAccountDetails(accountId: accountId) { (response) -> (Void) in
sorobanServer.getAccount(accountId: accountId) { (response) -> (Void) in
switch response {
case .success(let accResponse):
XCTAssertEqual(accountId, accResponse.accountId)
self.submitterAccount = accResponse
case .success(let account):
XCTAssertEqual(accountId, account.accountId)
self.submitterAccount = account
expectation.fulfill()
case .failure(_):
XCTFail()
Expand Down
20 changes: 10 additions & 10 deletions stellarsdk/stellarsdkTests/soroban/SorobanAuthTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ class SorobanAuthTest: XCTestCase {
var createTransactionId:String?
var contractId:String?
var invokeTransactionId:String?
var senderAccount:AccountResponse?
var invokerAccount:AccountResponse?
var senderAccount:Account?
var invokerAccount:Account?
var latestLedger:UInt32?

override func setUp() {
Expand Down Expand Up @@ -78,11 +78,11 @@ class SorobanAuthTest: XCTestCase {
let expectation = XCTestExpectation(description: "current account data received")

let accountId = senderKeyPair.accountId
sdk.accounts.getAccountDetails(accountId: accountId) { (response) -> (Void) in
sorobanServer.getAccount(accountId: accountId) { (response) -> (Void) in
switch response {
case .success(let accResponse):
XCTAssertEqual(accountId, accResponse.accountId)
self.senderAccount = accResponse
case .success(let account):
XCTAssertEqual(accountId, account.accountId)
self.senderAccount = account
expectation.fulfill()
case .failure(_):
XCTFail()
Expand Down Expand Up @@ -331,11 +331,11 @@ class SorobanAuthTest: XCTestCase {
let expectation = XCTestExpectation(description: "current account data received")

let accountId = invokerKeyPair.accountId
sdk.accounts.getAccountDetails(accountId: accountId) { (response) -> (Void) in
sorobanServer.getAccount(accountId: accountId) { (response) -> (Void) in
switch response {
case .success(let accResponse):
XCTAssertEqual(accountId, accResponse.accountId)
self.invokerAccount = accResponse
case .success(let account):
XCTAssertEqual(accountId, account.accountId)
self.invokerAccount = account
expectation.fulfill()
case .failure(_):
XCTFail()
Expand Down
10 changes: 5 additions & 5 deletions stellarsdk/stellarsdkTests/soroban/SorobanEventsTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class SorobanEventsTest: XCTestCase {
var createContractFootprint:Footprint? = nil
var invokeTransactionId:String? = nil
var invokeContractFootprint:Footprint? = nil
var submitterAccount:AccountResponse?
var submitterAccount:Account?
var transactionLedger:Int? = nil

override func setUp() {
Expand Down Expand Up @@ -64,11 +64,11 @@ class SorobanEventsTest: XCTestCase {
let expectation = XCTestExpectation(description: "current account data received")

let accountId = submitterKeyPair.accountId
sdk.accounts.getAccountDetails(accountId: accountId) { (response) -> (Void) in
sorobanServer.getAccount(accountId: accountId) { (response) -> (Void) in
switch response {
case .success(let accResponse):
XCTAssertEqual(accountId, accResponse.accountId)
self.submitterAccount = accResponse
case .success(let account):
XCTAssertEqual(accountId, account.accountId)
self.submitterAccount = account
expectation.fulfill()
case .failure(_):
XCTFail()
Expand Down
35 changes: 27 additions & 8 deletions stellarsdk/stellarsdkTests/soroban/SorobanTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class SorobanTest: XCTestCase {
var asset:Asset? = nil
var deployWithAssetTransactionId:String? = nil
var deployWithAssetFootprint:Footprint? = nil
var submitterAccount:AccountResponse?
var submitterAccount:Account?

override func setUp() {
super.setUp()
Expand Down Expand Up @@ -93,7 +93,7 @@ class SorobanTest: XCTestCase {
refreshSubmitterAccount()

// test restore contract code footprint
// see: https://soroban.stellar.org/docs/fundamentals-and-concepts/state-expiration
// see: https://developers.stellar.org/docs/learn/smart-contract-internals/state-archival
restoreContractCodeFootprint(fileName: "soroban_hello_world_contract")
checkTransactionStatusSuccess(transactionId: self.restoreTransactionId!)
getTransactionDetails(transactionHash: self.restoreTransactionId!, type:"restore_footprint")
Expand All @@ -108,7 +108,7 @@ class SorobanTest: XCTestCase {
getTransactionStatusError()

// test bump contract code footprint
// see: https://soroban.stellar.org/docs/fundamentals-and-concepts/state-expiration
// see: https://developers.stellar.org/docs/learn/smart-contract-internals/state-archival
refreshSubmitterAccount()
extendContractCodeFootprintTTL(wasmId: self.wasmId!, ledgersToExpire: 10000)
checkTransactionStatusSuccess(transactionId: self.bumpTransactionId!)
Expand All @@ -132,6 +132,7 @@ class SorobanTest: XCTestCase {
invokeContract()
getInvokeTransactionStatus()
getTransactionDetails(transactionHash: self.invokeTransactionId!, type:"HostFunctionTypeHostFunctionTypeInvokeContract")
getContractData()

// test SAC with source account
refreshSubmitterAccount()
Expand Down Expand Up @@ -179,17 +180,16 @@ class SorobanTest: XCTestCase {
let expectation = XCTestExpectation(description: "current account data received")

let accountId = submitterKeyPair.accountId
sdk.accounts.getAccountDetails(accountId: accountId) { (response) -> (Void) in
sorobanServer.getAccount(accountId: accountId) { (response) -> (Void) in
switch response {
case .success(let accResponse):
XCTAssertEqual(accountId, accResponse.accountId)
self.submitterAccount = accResponse
case .success(let account):
XCTAssertEqual(accountId, account.accountId)
self.submitterAccount = account
expectation.fulfill()
case .failure(_):
XCTFail()
}
}

wait(for: [expectation], timeout: 10.0)
}
}
Expand Down Expand Up @@ -783,6 +783,25 @@ class SorobanTest: XCTestCase {
}
}

func getContractData() {
XCTContext.runActivity(named: "getContractData") { activity in
let expectation = XCTestExpectation(description: "get ledger data")
// wait a couple of seconds before checking the status
self.sorobanServer.getContractData(contractId: self.contractId!, key: SCValXDR.ledgerKeyContractInstance,
durability: ContractDataDurability.persistent) { (response) -> (Void) in
switch response {
case .success(let response):
expectation.fulfill()
case .failure(let error):
self.printError(error: error)
XCTFail()
}
expectation.fulfill()
}
wait(for: [expectation], timeout: 10.0)
}
}

func deploySACWithSourceAccount() {
XCTContext.runActivity(named: "deploySACWithSourceAccount") { activity in
let expectation = XCTestExpectation(description: "contract successfully deployed")
Expand Down

0 comments on commit 403b9b5

Please sign in to comment.