Skip to content

Commit

Permalink
Merge branch 'tvm-emulator' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
neodiX committed Jun 20, 2024
2 parents f0cc364 + 496888c commit bb1dd81
Show file tree
Hide file tree
Showing 12 changed files with 279 additions and 61 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,15 @@ You can use each submodule individually. Click the module below to get more deta
* Improve code coverage and add more integration tests
* BinTree serialization / deserialization

## Support ton4j development
If you want to speed up ton4j development and thus change its priority in my backlog, you are welcome to donate some toncoins:

```UQBguBMWc_wUA8pJjC-A9JbTJFzb7lbFbbkiFYajA33-U9YU```

## Star History

[![Star History Chart](https://api.star-history.com/svg?repos=neodiX42/ton4j&type=Date)](https://star-history.com/#neodiX42/ton4j&Date)

<!-- Badges -->

[maven-central-svg]: https://img.shields.io/maven-central/v/io.github.neodix42/smartcontract
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.concurrent.ExecutionException;

import static java.util.Objects.isNull;
import static java.util.Objects.nonNull;
Expand Down Expand Up @@ -303,7 +302,7 @@ public void testTvmEmulatorSendExternalMessage() {
}

@Test
public void testTvmEmulatorSendExternalMessageCustom() throws IOException, ExecutionException, InterruptedException {
public void testTvmEmulatorSendExternalMessageCustom() throws IOException {

SmartContractCompiler smcFunc = SmartContractCompiler.builder()
.contractPath("G:/smartcontracts/new-wallet-v4r2.fc")
Expand Down Expand Up @@ -419,7 +418,7 @@ public void testTvmEmulatorSendExternalMessageCustom() throws IOException, Execu
}

@Test
public void testTvmEmulatorSendInternalMessageCustomContract() throws IOException, ExecutionException, InterruptedException {
public void testTvmEmulatorSendInternalMessageCustomContract() throws IOException {
SmartContractCompiler smcFunc = SmartContractCompiler.builder()
.contractPath("G:/smartcontracts/new-wallet-v4r2.fc")
.build();
Expand Down
26 changes: 26 additions & 0 deletions liteclient/src/main/java/org/ton/java/liteclient/LiteClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,32 @@ public String executeRunMethod(String address, String methodId, String params) t
return execute(command).getRight().get();
}

/**
* @param address base64 or raw
* @param blockId in format (-1,8000000000000000,20301335):root-hash:file-hash
* @param methodId method name, e.g. seqno
* @param params space separated params
* @return lite-client output
*/
public String executeRunMethod(String address, String blockId, String methodId, String params) throws Exception {
final String command = String.format("runmethod %s %s %s %s", address, blockId, methodId, params);
log.info(command);
return execute(command).getRight().get();
}

/**
* @param address base64 or raw
* @param blockId ResultLastBlock
* @param methodId method name, e.g. seqno
* @param params space separated params
* @return lite-client output
*/
public String executeRunMethod(String address, ResultLastBlock blockId, String methodId, String params) throws Exception {
final String command = String.format("runmethod %s %s %s %s", address, blockId.getFullBlockSeqno(), methodId, params);
log.info(command);
return execute(command).getRight().get();
}

public String executeSendfile(String absolutePathFile) throws Exception {
final String command = "sendfile " + absolutePathFile;
Pair<Process, Future<String>> result = execute(command);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.ton.java.liteclient.api.ResultLastBlock;
import org.ton.java.liteclient.api.ResultListBlockTransactions;
Expand Down Expand Up @@ -50,10 +49,35 @@ public void testLastExecuted() {
assertThat(liteClient.executeLast()).isNotNull().contains("last masterchain block is").contains("server time is");
}

@Ignore

@Test
public void testRunMethod() throws Exception {
final String result = liteClient.executeRunMethod("EQDCJVrezD71y-KPcTIG-YeKNj4naeiR7odpQgVA1uDsZqPC", "seqno", "");
log.info(result);
assertThat(result).contains("arguments").contains("result");
}

@Test
public void testRunmethod() throws Exception {
final String result = liteClient.executeRunMethod("EQBdFkus6WRkJ1PP6z24Fw5C6E1YKet_nSJ6K1H7HHuOdwMC", "seqno", "");
public void testRunMethodWithBlockId() throws Exception {
final String result = liteClient.executeRunMethod("EQDCJVrezD71y-KPcTIG-YeKNj4naeiR7odpQgVA1uDsZqPC",
"(-1,8000000000000000,20301499):070D07EB64D36CCA2D8D20AA644489637059C150E2CD466247C25B4997FB8CD9:D7D7271D466D52D0A98771F9E8DCAA06E43FCE01C977AACD9DE9DAD9A9F9A424",
"seqno", "");
log.info(result);
assertThat(result).contains("arguments").contains("result");
}

@Test
public void testRunMethodWithResultBlockId() throws Exception {
ResultLastBlock blockId = ResultLastBlock.builder()
.wc(-1L)
.shard("8000000000000000")
.seqno(BigInteger.valueOf(20301499))
.rootHash("070D07EB64D36CCA2D8D20AA644489637059C150E2CD466247C25B4997FB8CD9")
.fileHash("D7D7271D466D52D0A98771F9E8DCAA06E43FCE01C977AACD9DE9DAD9A9F9A424")
.build();
final String result = liteClient.executeRunMethod("EQDCJVrezD71y-KPcTIG-YeKNj4naeiR7odpQgVA1uDsZqPC",
blockId,
"seqno", "");
log.info(result);
assertThat(result).contains("arguments").contains("result");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package org.ton.java.smartcontract.token.ft;

import com.iwebpp.crypto.TweetNaclFast;
import lombok.Builder;
import lombok.Getter;
import org.ton.java.address.Address;
Expand All @@ -19,13 +18,10 @@

import java.math.BigInteger;

import static java.util.Objects.isNull;

@Builder
@Getter
public class JettonWallet implements Contract {

TweetNaclFast.Signature.KeyPair keyPair;
Address address;

public static class JettonWalletBuilder {
Expand All @@ -38,9 +34,7 @@ public static JettonWalletBuilder builder() {
private static class CustomJettonWalletBuilder extends JettonWalletBuilder {
@Override
public JettonWallet build() {
if (isNull(super.keyPair)) {
super.keyPair = Utils.generateSignatureKeyPair();
}

return super.build();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,30 +139,29 @@ public void testWalletV3R2() throws InterruptedException {
if (nonNull(tx.getIn_msg()) && (!tx.getIn_msg().getSource().getAccount_address().equals(""))) {
log.info("{}, {} <<<<< {} : {}, comment: {} ", Utils.toUTC(tx.getUtime()),
tx.getIn_msg().getSource().getAccount_address(), tx.getIn_msg().getDestination().getAccount_address(),
Utils.formatNanoValue(tx.getIn_msg().getValue()), CellSlice.beginParse(Cell.fromHex(Utils.base64ToHexString(tx.getIn_msg().getMsg_data().getText()))).loadSnakeString());
Utils.formatNanoValue(tx.getIn_msg().getValue()), tx.getIn_msg().getComment());
}
if (nonNull(tx.getOut_msgs())) {
for (RawMessage msg : tx.getOut_msgs()) {
log.info("{}, {} >>>>> {} : {}, comment: {}", Utils.toUTC(tx.getUtime()),
msg.getSource().getAccount_address(), msg.getDestination().getAccount_address(),
Utils.formatNanoValue(msg.getValue()), CellSlice.beginParse(Cell.fromHex(Utils.base64ToHexString(msg.getMsg_data().getText()))).loadSnakeString());
Utils.formatNanoValue(msg.getValue()), msg.getComment());
}
}
}
// CellSlice.beginParse(Cell.fromHex(Utils.base64ToHexString()).loadSnakeString()
log.info("txs of wallet2");
txs = tonlib.getRawTransactions(bounceableAddress2, null, null);
for (RawTransaction tx : txs.getTransactions()) {
if (nonNull(tx.getIn_msg()) && (!tx.getIn_msg().getSource().getAccount_address().equals(""))) {
log.info("{}, {} <<<<< {} : {}, comment: {} ", Utils.toUTC(tx.getUtime()),
tx.getIn_msg().getSource().getAccount_address(), tx.getIn_msg().getDestination().getAccount_address(),
Utils.formatNanoValue(tx.getIn_msg().getValue()), CellSlice.beginParse(Cell.fromHex(Utils.base64ToHexString(tx.getIn_msg().getMsg_data().getText()))).loadSnakeString());
Utils.formatNanoValue(tx.getIn_msg().getValue()), tx.getIn_msg().getComment());
}
if (nonNull(tx.getOut_msgs())) {
for (RawMessage msg : tx.getOut_msgs()) {
log.info("{}, {} >>>>> {} : {}, comment: {}", Utils.toUTC(tx.getUtime()),
msg.getSource().getAccount_address(), msg.getDestination().getAccount_address(),
Utils.formatNanoValue(msg.getValue()), CellSlice.beginParse(Cell.fromHex(Utils.base64ToHexString(msg.getMsg_data().getText()))).loadSnakeString());
Utils.formatNanoValue(msg.getValue()), msg.getComment());
}
}
}
Expand Down
97 changes: 67 additions & 30 deletions tonlib/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

Java Tonlib library uses JNA to access methods in native Tonlib shared library.

Since this is Java Tonlib wrapper around the native binary, you have to specify path to the library, see the example below.

You can get the latest tonlib library by:

* downloading it from the official TON Github release page [here](https://github.com/ton-blockchain/ton/releases).
* by installing precompiled binaries, see instructions [here](https://github.com/ton-blockchain/packages).

## Maven [![Maven Central][maven-central-svg]][maven-central]

```xml
Expand All @@ -27,14 +34,13 @@ Java Tonlib library uses JNA to access methods in native Tonlib shared library.
## Constructor, getLast, lookupBlock, getBlockHeader, getShards

```java
// constructor
Tonlib(String pathToTonlibSharedLib, // path to native Tonlib shared library
int verbosityLevel, // values from 0 to 4, where 0 - output is suppresed, 4 - debug level output; default - true
boolean testnet, // switch between testnet and mainnet; default - true (testnet)
boolean keystoreInMemory, // switch for keystore location; default - false (keystore in local disk)
String keystorePath) // path to a keystore; default - current directory (.)

Tonlib tonlib=Tonlib.builder().build(); // if path of Tonlib shared library is not specified Tonlib, the module tries to find one in local dir or java.library.path.
// builder
Tonlib tonlib = Tonlib.builder()
.pathToTonlibSharedLib("/mnt/tonlibjson.so")
.pathToGlobalConfig("/mnt/testnet-global.config.json")
.verbosityLevel(VerbosityLevel.FATAL)
.testnet(true)
.build();

// lookupBlock
BlockIdExt fullblock = tonlib.lookupBlock(304479,-1,-9223372036854775808L,0,0);
Expand Down Expand Up @@ -62,19 +68,24 @@ Shards(type=blocks.shards,shards=[BlockIdExt(type=ton.blockIdExt,workchain=0,sha
## Get all block transactions

```java
Tonlib tonlib=Tonlib.builder().build();
Tonlib tonlib = Tonlib.builder()
.pathToTonlibSharedLib("/mnt/tonlibjson.so")
.pathToGlobalConfig("/mnt/testnet-global.config.json")
.verbosityLevel(VerbosityLevel.FATAL)
.testnet(true)
.build();

//lookupBlock
BlockIdExt fullblock = tonlib.lookupBlock(444699, -1, -9223372036854775808L, 0, 0);
log.info(fullblock.toString());

Map<String, RawTransactions> txs=tonlib.getAllBlockTransactions(fullblock,100,null);
for(Map.Entry<String, RawTransactions> entry:txs.entrySet()){
for(RawTransaction tx:((RawTransactions)entry.getValue()).getTransactions()){
if((tx.getIn_msg()!=null) && (!tx.getIn_msg().getSource().getAccount_address().equals(""))){
Map<String, RawTransactions> txs = tonlib.getAllBlockTransactions(fullblock,100,null);
for(Map.Entry<String, RawTransactions> entry: txs.entrySet()){
for(RawTransaction tx: ((RawTransactions)entry.getValue()).getTransactions()){
if((nonNull(tx.getIn_msg()) && (!tx.getIn_msg().getSource().getAccount_address().equals(""))){
log.info("{} <<<<< {} : {} ", tx.getIn_msg().getSource().getAccount_address(), tx.getIn_msg().getDestination().getAccount_address(),tx.getIn_msg().getValueToncoins(9));
}
if(tx.getOut_msgs()!=null){
if(nonNull(tx.getOut_msgs()){
for(RawMessage msg:tx.getOut_msgs()){
log.info("{} >>>>> {} : {} ",msg.getSource().getAccount_address(),msg.getDestination().getAccount_address(),msg.getValue());
}
Expand Down Expand Up @@ -103,18 +114,24 @@ Ef8zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM0vF<<<<<Ef99gz58SExDexFVJteOSZcPDxF
## Get transactions by address

```java
Tonlib tonlib = Tonlib.builder().build();
Tonlib tonlib = Tonlib.builder()
.pathToTonlibSharedLib("/mnt/tonlibjson.so")
.pathToGlobalConfig("/mnt/testnet-global.config.json")
.verbosityLevel(VerbosityLevel.FATAL)
.testnet(true)
.build();

Address address = Address.of(MY_TESTNET_VALIDATOR_ADDR);
log.info("address: " + address.toString(true));
RawTransactions rawTransactions = tonlib.getRawTransactions(address.toString(false),null,null);
log.info("total txs: {}", rawTransactions.getTransactions().size());

for(RawTransaction tx:rawTransactions.getTransactions()){
if((tx.getIn_msg()!=null)&&(!tx.getIn_msg().getSource().getAccount_address().equals(""))){
if((nonNull(tx.getIn_msg()) && (!tx.getIn_msg().getSource().getAccount_address().equals(""))){
log.info("{}, {} <<<<< {} : {} ",Utils.toUTC(tx.getUtime()),tx.getIn_msg().getSource().getAccount_address(),tx.getIn_msg().getDestination().getAccount_address(),tx.getIn_msg().getValueToncoins(9));
}
if(tx.getOut_msgs()!=null){
for(RawMessage msg:tx.getOut_msgs()){
if(nonNull(tx.getOut_msgs())){
for(RawMessage msg: tx.getOut_msgs()){
log.info("{}, {} >>>>> {} : {} ",Utils.toUTC(tx.getUtime()),msg.getSource().getAccount_address(),msg.getDestination().getAccount_address(),msg.getValue());
}

Expand All @@ -137,18 +154,23 @@ total txs:10
## Get accountHelper state, balance

```java
Tonlib tonlib = Tonlib.builder().build();
Tonlib tonlib = Tonlib.builder()
.pathToTonlibSharedLib("/mnt/tonlibjson.so")
.pathToGlobalConfig("/mnt/testnet-global.config.json")
.verbosityLevel(VerbosityLevel.FATAL)
.testnet(true)
.build();

Address addr = Address.of("kQBeuMOZrZyCrtvZ1dMaMKmlxpulQFCOYCLI8EYcqMvI6v6E");
log.info("address: "+addr.toString(true));
log.info("address: " + addr.toString(true));

AccountAddressOnly accountAddressOnly=AccountAddressOnly.builder()
.account_address(addr.toString(true))
.build();
FullAccountState account = tonlib.getAccountState(accountAddressOnly);

log.info(account.toString());
log.info("balance: {}",account.getBalance());
log.info("balance: {}", account.getBalance());

// result
address:kQBeuMOZrZyCrtvZ1dMaMKmlxpulQFCOYCLI8EYcqMvI6v6E
Expand All @@ -159,13 +181,18 @@ balance:4995640997
## Encrypt/decrypt with mnemonic

```java
Tonlib tonlib = Tonlib.builder().build();
Tonlib tonlib = Tonlib.builder()
.pathToTonlibSharedLib("/mnt/tonlibjson.so")
.pathToGlobalConfig("/mnt/testnet-global.config.json")
.verbosityLevel(VerbosityLevel.FATAL)
.testnet(true)
.build();

String base64mnemonic = Utils.stringToBase64("centring moist twopenny bursary could carbarn abide flirt ground shoelace songster isomeric pis strake jittery penguin gab guileful lierne salivary songbird shore verbal measures");

String dataToEncrypt = Utils.stringToBase64("ABC");
Data encrypted = tonlib.encrypt(dataToEncrypt,base64mnemonic);
log.info("encrypted {}",encrypted.getBytes());
log.info("encrypted {}", encrypted.getBytes());

Data decrypted = tonlib.decrypt(encrypted.getBytes(),base64mnemonic);
String dataDecrypted = Utils.base64ToString(decrypted.getBytes());
Expand All @@ -177,14 +204,19 @@ assertThat("ABC").isEqualTo(dataDecrypted);
Get seqno

```java
Tonlib tonlib = Tonlib.builder().build();
Tonlib tonlib = Tonlib.builder()
.pathToTonlibSharedLib("/mnt/tonlibjson.so")
.pathToGlobalConfig("/mnt/testnet-global.config.json")
.verbosityLevel(VerbosityLevel.FATAL)
.testnet(true)
.build();
Address address = Address.of("kQB7wRCSr02IwL1nOxkEipop3goYb4oN6ZehZMS2jImwyS1t");
RunResult result = tonlib.runMethod(address,"seqno");

log.info("gas_used {}, exit_code {} ",result.getGas_used(),result.getExit_code());

TvmStackEntryNumber seqno=(TvmStackEntryNumber)result.getStack().get(0);
log.info("seqno: {}",eqno.getNumber());
log.info("seqno: {}", eqno.getNumber());

// result
gas_used 505,exit_code 0
Expand All @@ -195,7 +227,7 @@ Retrieve past_election_ids

```java
Address address=Address.of("-1:3333333333333333333333333333333333333333333333333333333333333333");
RunResult result = tonlib.runMethod(address,"past_election_ids");
RunResult result = tonlib.runMethod(address, "past_election_ids");
TvmStackEntryList listResult = (TvmStackEntryList)result.getStack().get(0);

for(Object o:listResult.getList().getElements()){
Expand Down Expand Up @@ -223,19 +255,24 @@ Use as array of strings:
* [slice, 0x80C11AAFA0014 ] // BoC in hex

```java
Tonlib tonlib = Tonlib.builder().build();
Tonlib tonlib = Tonlib.builder()
.pathToTonlibSharedLib("/mnt/tonlibjson.so")
.pathToGlobalConfig("/mnt/testnet-global.config.json")
.verbosityLevel(VerbosityLevel.FATAL)
.testnet(true)
.build();

Address address = Address.of("-1:3333333333333333333333333333333333333333333333333333333333333333");
RunResult result = tonlib.runMethod(address,"compute_returned_stake",null);
log.info("result: {}",result);
log.info("result: {}", result);
assertThat(result.getExit_code()).isEqualTo(2); // error since compute_returned_stake requires an argument

Deque<String> stack=new ArrayDeque<>();
address = Address.of(TESTNET_VALIDATOR_ADDR);
stack.offer("[num, "+address.toDecimal()+"]");
stack.offer("[num, " + address.toDecimal()+"]");

result = tonlib.runMethod(address,"compute_returned_stake",stack);
log.info("result: {} ",result);
log.info("result: {} ", result);
```

More examples in [TestTonlibJson](../tonlib/src/test/java/org/ton/java/tonlib/TestTonlibJson.java) and
Expand Down
Loading

0 comments on commit bb1dd81

Please sign in to comment.