diff --git a/tonlib/src/main/java/org/ton/java/tonlib/Tonlib.java b/tonlib/src/main/java/org/ton/java/tonlib/Tonlib.java index 84f0506c..e2b74fdc 100644 --- a/tonlib/src/main/java/org/ton/java/tonlib/Tonlib.java +++ b/tonlib/src/main/java/org/ton/java/tonlib/Tonlib.java @@ -666,6 +666,40 @@ public RawAccountState getRawAccountState(Address address) { } } + public RawAccountState getRawAccountState(Address address, BlockIdExt blockId) { + synchronized (gson) { + + if (StringUtils.isEmpty(blockId.getRoot_hash())) { // retrieve hashes + blockId = lookupBlock(blockId.getSeqno(), blockId.getWorkchain(), blockId.getShard(),0 ); + if (StringUtils.isEmpty(blockId.getRoot_hash())) { + throw new Error("Cannot lookup block for hashes by seqno. Probably block not in db. Try to specify block's root and file hashes manually in base64 format."); + } + System.out.println("got hashes "+blockId); + } + + AccountAddressOnly accountAddressOnly = AccountAddressOnly.builder() + .account_address(address.toString(false)) + .build(); + + GetRawAccountStateQueryOnly getAccountStateQuery = GetRawAccountStateQueryOnly.builder() + .account_address(accountAddressOnly) + .build(); + + RawGetAccountStateOnlyWithBlockQuery rawGetAccountStateOnlyWithBlockQuery = RawGetAccountStateOnlyWithBlockQuery.builder() + .id(blockId) + .function(getAccountStateQuery) + .build(); + + String result = syncAndRead(gson.toJson(rawGetAccountStateOnlyWithBlockQuery)); + + if ((isNull(result)) || (result.contains("@type") && result.contains("error"))) { + throw new Error ("Cannot getRawAccountState, error" + result); + } + + return gson.fromJson(result, RawAccountState.class); + } + } + /** * Returns status of an address * @@ -734,6 +768,43 @@ public FullAccountState getAccountState(Address address) { } } + public FullAccountState getAccountState(Address address, BlockIdExt blockId) { + synchronized (gson) { + + synchronized (gson) { + + if (StringUtils.isEmpty(blockId.getRoot_hash())) { // retrieve hashes + blockId = lookupBlock(blockId.getSeqno(), blockId.getWorkchain(), blockId.getShard(),0 ); + if (StringUtils.isEmpty(blockId.getRoot_hash())) { + throw new Error("Cannot lookup block for hashes by seqno. Probably block not in db. Try to specify block's root and file hashes manually in base64 format."); + } + System.out.println("got hashes "+blockId); + } + + AccountAddressOnly accountAddressOnly = AccountAddressOnly.builder() + .account_address(address.toString(false)) + .build(); + + GetAccountStateQueryOnly getAccountStateQuery = GetAccountStateQueryOnly.builder() + .account_address(accountAddressOnly) + .build(); + + GetAccountStateOnlyWithBlockQuery getAccountStateOnlyWithBlockQuery = GetAccountStateOnlyWithBlockQuery.builder() + .id(blockId) + .function(getAccountStateQuery) + .build(); + + String result = syncAndRead(gson.toJson(getAccountStateOnlyWithBlockQuery)); + + if ((isNull(result)) || (result.contains("@type") && result.contains("error"))) { + throw new Error ("Cannot getAccountState, error" + result); + } + + return gson.fromJson(result, FullAccountState.class); + } + } + } + /** * Returns account status by address. * @@ -753,6 +824,25 @@ public String getAccountStatus(Address address) { } } + /** + * Returns account status by address and blockId. + * + * @param address Address + * @return String - uninitialized, frozen or active. + */ + public String getAccountStatus(Address address, BlockIdExt blockId) { + RawAccountState state = getRawAccountState(address, blockId); + if (nonNull(state) && StringUtils.isEmpty(state.getCode())) { + if (StringUtils.isEmpty(state.getFrozen_hash())) { + return "uninitialized"; + } else { + return "frozen"; + } + } else { + return "active"; + } + } + public Cell getConfigAll(int mode) { synchronized (gson) { @@ -762,7 +852,6 @@ public Cell getConfigAll(int mode) { String result = syncAndRead(gson.toJson(configParamQuery)); ConfigInfo ci = gson.fromJson(result, ConfigInfo.class); - System.out.println(Utils.bytesToHex(Utils.base64ToBytes(ci.getConfig().getBytes()))); return CellBuilder.beginCell().fromBoc(Utils.base64ToBytes(ci.getConfig().getBytes())).endCell(); } } @@ -775,13 +864,11 @@ public Cell getConfigParam(BlockIdExt id, long param) { .build(); String result = syncAndRead(gson.toJson(configParamQuery)); - System.out.println(result); ConfigInfo ci = gson.fromJson(result, ConfigInfo.class); return CellBuilder.beginCell().fromBoc(Utils.base64ToBytes(ci.getConfig().getBytes())).endCell(); } } - public long loadContract(AccountAddressOnly address) { synchronized (gson) { LoadContractQuery loadContractQuery = LoadContractQuery.builder() @@ -793,6 +880,10 @@ public long loadContract(AccountAddressOnly address) { return gson.fromJson(result, LoadContract.class).getId(); } } + + /** + * loads contract by seqno within master chain and shard -9223372036854775808 + */ public long loadContract(AccountAddressOnly address, long seqno) { synchronized (gson) { BlockIdExt fullBlock; @@ -807,12 +898,40 @@ public long loadContract(AccountAddressOnly address, long seqno) { .account_address(address) .build(); - WithBlockQuery withBlockQuery = WithBlockQuery.builder() + LoadContractWithBlockQuery loadContractWithBlockQuery = LoadContractWithBlockQuery.builder() .id(fullBlock) .function(loadContractQuery) .build(); - String result = syncAndRead(gson.toJson(withBlockQuery)); + String result = syncAndRead(gson.toJson(loadContractWithBlockQuery)); + + return gson.fromJson(result, LoadContract.class).getId(); + } + } + + /** + * load contract by blockId + * @param address contract's address + * @param blockId BlockIdExt + * @return contract's id + */ + public long loadContract(AccountAddressOnly address, BlockIdExt blockId) { + synchronized (gson) { + + if (StringUtils.isEmpty(blockId.getRoot_hash())) { + blockId = lookupBlock(blockId.getSeqno(),blockId.getWorkchain(),blockId.getShard(),0 ); + } + + LoadContractQuery loadContractQuery = LoadContractQuery.builder() + .account_address(address) + .build(); + + LoadContractWithBlockQuery loadContractWithBlockQuery = LoadContractWithBlockQuery.builder() + .id(blockId) + .function(loadContractQuery) + .build(); + + String result = syncAndRead(gson.toJson(loadContractWithBlockQuery)); return gson.fromJson(result, LoadContract.class).getId(); } @@ -828,6 +947,36 @@ public RunResult runMethod(Address contractAddress, String methodName) { } } + /** + * executes runMethod at specified blockId + */ + public RunResult runMethod(Address contractAddress, String methodName, BlockIdExt blockId) { + long contractId = loadContract(AccountAddressOnly.builder() + .account_address(contractAddress.toString(false)).build(), + blockId); + if (contractId == -1) { + System.err.println("cannot load contract " + AccountAddressOnly.builder().account_address(contractAddress.toString(false))); + return null; + } else { + return runMethod(contractId, methodName, null); + } + } + + /** + * executes runMethod by seqno within master chain and shard -9223372036854775808 + */ + public RunResult runMethod(Address contractAddress, String methodName, long seqno) { + long contractId = loadContract(AccountAddressOnly.builder() + .account_address(contractAddress.toString(false)).build(), + seqno); + if (contractId == -1) { + System.err.println("cannot load contract " + AccountAddressOnly.builder().account_address(contractAddress.toString(false))); + return null; + } else { + return runMethod(contractId, methodName, null); + } + } + public RunResult runMethod(Address contractAddress, String methodName, Deque stackData) { synchronized (gson) { long contractId = loadContract(AccountAddressOnly.builder().account_address(contractAddress.toString(false)).build()); diff --git a/tonlib/src/main/java/org/ton/java/tonlib/queries/GetAccountStateOnlyWithBlockQuery.java b/tonlib/src/main/java/org/ton/java/tonlib/queries/GetAccountStateOnlyWithBlockQuery.java new file mode 100644 index 00000000..e6d4d187 --- /dev/null +++ b/tonlib/src/main/java/org/ton/java/tonlib/queries/GetAccountStateOnlyWithBlockQuery.java @@ -0,0 +1,19 @@ +package org.ton.java.tonlib.queries; + +import com.google.gson.annotations.SerializedName; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import org.ton.java.tonlib.types.BlockIdExt; + +@Builder +@Setter +@Getter +@ToString +public class GetAccountStateOnlyWithBlockQuery extends ExtraQuery { + @SerializedName(value = "@type") + final String type = "withBlock"; + BlockIdExt id; + GetAccountStateQueryOnly function; +} \ No newline at end of file diff --git a/tonlib/src/main/java/org/ton/java/tonlib/queries/GetAccountStateWithBlockQuery.java b/tonlib/src/main/java/org/ton/java/tonlib/queries/GetAccountStateWithBlockQuery.java new file mode 100644 index 00000000..8f17d056 --- /dev/null +++ b/tonlib/src/main/java/org/ton/java/tonlib/queries/GetAccountStateWithBlockQuery.java @@ -0,0 +1,19 @@ +package org.ton.java.tonlib.queries; + +import com.google.gson.annotations.SerializedName; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import org.ton.java.tonlib.types.BlockIdExt; + +@Builder +@Setter +@Getter +@ToString +public class GetAccountStateWithBlockQuery extends ExtraQuery { + @SerializedName(value = "@type") + final String type = "withBlock"; + BlockIdExt id; + GetAccountStateQuery function; +} \ No newline at end of file diff --git a/tonlib/src/main/java/org/ton/java/tonlib/queries/WithBlockQuery.java b/tonlib/src/main/java/org/ton/java/tonlib/queries/LoadContractWithBlockQuery.java similarity index 86% rename from tonlib/src/main/java/org/ton/java/tonlib/queries/WithBlockQuery.java rename to tonlib/src/main/java/org/ton/java/tonlib/queries/LoadContractWithBlockQuery.java index b875b5bb..88457707 100644 --- a/tonlib/src/main/java/org/ton/java/tonlib/queries/WithBlockQuery.java +++ b/tonlib/src/main/java/org/ton/java/tonlib/queries/LoadContractWithBlockQuery.java @@ -11,7 +11,7 @@ @Setter @Getter @ToString -public class WithBlockQuery extends ExtraQuery { +public class LoadContractWithBlockQuery extends ExtraQuery { @SerializedName(value = "@type") final String type = "withBlock"; BlockIdExt id; diff --git a/tonlib/src/main/java/org/ton/java/tonlib/queries/RawGetAccountStateOnlyWithBlockQuery.java b/tonlib/src/main/java/org/ton/java/tonlib/queries/RawGetAccountStateOnlyWithBlockQuery.java new file mode 100644 index 00000000..a9f2ee61 --- /dev/null +++ b/tonlib/src/main/java/org/ton/java/tonlib/queries/RawGetAccountStateOnlyWithBlockQuery.java @@ -0,0 +1,19 @@ +package org.ton.java.tonlib.queries; + +import com.google.gson.annotations.SerializedName; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import org.ton.java.tonlib.types.BlockIdExt; + +@Builder +@Setter +@Getter +@ToString +public class RawGetAccountStateOnlyWithBlockQuery extends ExtraQuery { + @SerializedName(value = "@type") + final String type = "withBlock"; + BlockIdExt id; + GetRawAccountStateQueryOnly function; +} \ No newline at end of file diff --git a/tonlib/src/main/java/org/ton/java/tonlib/queries/RawGetAccountStateWithBlockQuery.java b/tonlib/src/main/java/org/ton/java/tonlib/queries/RawGetAccountStateWithBlockQuery.java new file mode 100644 index 00000000..6eba1e55 --- /dev/null +++ b/tonlib/src/main/java/org/ton/java/tonlib/queries/RawGetAccountStateWithBlockQuery.java @@ -0,0 +1,19 @@ +package org.ton.java.tonlib.queries; + +import com.google.gson.annotations.SerializedName; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import org.ton.java.tonlib.types.BlockIdExt; + +@Builder +@Setter +@Getter +@ToString +public class RawGetAccountStateWithBlockQuery extends ExtraQuery { + @SerializedName(value = "@type") + final String type = "withBlock"; + BlockIdExt id; + GetRawAccountStateQuery function; +} \ No newline at end of file diff --git a/tonlib/src/test/java/org/ton/java/tonlib/TestTonlibJson.java b/tonlib/src/test/java/org/ton/java/tonlib/TestTonlibJson.java index 605548ad..acb6ab3a 100644 --- a/tonlib/src/test/java/org/ton/java/tonlib/TestTonlibJson.java +++ b/tonlib/src/test/java/org/ton/java/tonlib/TestTonlibJson.java @@ -46,7 +46,7 @@ public class TestTonlibJson { @BeforeClass public static void setUpBeforeClass() throws Exception { - tonlib = Tonlib.builder().build(); + tonlib = Tonlib.builder().ignoreCache(false).build(); } @Test @@ -524,7 +524,7 @@ public void testTonlibLoadContractSeqno() { @Test public void testTonlibRunMethodComputeReturnedStake() { Address elector = Address.of(ELECTOR_ADDRESSS); - RunResult result = tonlib.runMethod(elector, "compute_returned_stake", null); + RunResult result = tonlib.runMethod(elector, "compute_returned_stake", new ArrayDeque<>()); log.info("result: {}", result); assertThat(result.getExit_code()).isEqualTo(2); // error since compute_returned_stake requires an argument @@ -600,12 +600,28 @@ public void testTonlibTryLocateTxByOutcomingMessage() { @Test public void testTonlibStateAndStatus() { - int i = 0; + FullAccountState accountState1 = tonlib.getAccountState(Address.of("EQCtPHFrtkIw3UC2rNfSgVWYT1MiMLDUtgMy2M7j1P_eNMDq")); - log.info("{} with balance {} and code [{}]: {}", "EQCtPHFrtkIw3UC2rNfSgVWYT1MiMLDUtgMy2M7j1P_eNMDq", i, accountState1.getBalance(), accountState1); + log.info("FullAccountState {}", accountState1); RawAccountState accountState2 = tonlib.getRawAccountState(Address.of("EQCtPHFrtkIw3UC2rNfSgVWYT1MiMLDUtgMy2M7j1P_eNMDq")); - log.info("{} with balance {} and code [{}]: {}", "EQCtPHFrtkIw3UC2rNfSgVWYT1MiMLDUtgMy2M7j1P_eNMDq", i, accountState2.getBalance(), accountState2); + log.info("RawAccountState {}", accountState2); + + BlockIdExt blockId = BlockIdExt.builder() + .workchain(-1) + .shard(-9223372036854775808L) + .seqno(27894542) + .root_hash("akLZC86Ve0IPDW2HGhSCKKz7RkJk1yAgl34qMpo1RlE=") + .file_hash("KjJvOENNPc39inKO2cOce0s/fUX9Nv8/qdS1VFj2yw0=") + .build(); + + log.info("input blockId {}", blockId); + + FullAccountState accountState1AtBlock = tonlib.getAccountState(Address.of("EQCtPHFrtkIw3UC2rNfSgVWYT1MiMLDUtgMy2M7j1P_eNMDq"), blockId); + log.info("accountState1AtBlock {}", accountState1AtBlock); + + RawAccountState accountState2AtBlock = tonlib.getRawAccountState(Address.of("EQCtPHFrtkIw3UC2rNfSgVWYT1MiMLDUtgMy2M7j1P_eNMDq"), blockId); + log.info("RawAccountStateAtBlock {}", accountState2AtBlock); String accountState1Status = tonlib.getRawAccountStatus(Address.of("EQCtPHFrtkIw3UC2rNfSgVWYT1MiMLDUtgMy2M7j1P_eNMDq")); log.info("==========================================");