From 75bc14692cddeb8442b587e34f43e21708766860 Mon Sep 17 00:00:00 2001 From: neodix Date: Tue, 12 Nov 2024 20:35:06 +0400 Subject: [PATCH] developed system stdout handling on linux; added to Cell getShortHash() method which returns "xxxx..xxxx"; --- .../main/java/org/ton/java/Blockchain.java | 44 ++++-- .../java/org/ton/java/BlockchainTest.java | 72 ++++++++-- .../src/main/java/org/ton/java/cell/Cell.java | 5 + emulator/pom.xml | 5 - .../ton/java/emulator/tvm/TvmEmulator.java | 81 ++++++----- .../org/ton/java/emulator/tx/TxEmulator.java | 99 +++++++------ .../java/org/ton/java/fift/FiftRunner.java | 8 +- .../smartcontract/SmartContractCompiler.java | 48 ++++--- tonlib/pom.xml | 5 - .../main/java/org/ton/java/tonlib/Tonlib.java | 66 ++++----- utils/pom.xml | 5 + .../main/java/org/ton/java/utils/Utils.java | 133 +++++++++++++++++- .../java/org/ton/java/utils/TestUtils.java | 10 ++ 13 files changed, 399 insertions(+), 182 deletions(-) diff --git a/blockchain/src/main/java/org/ton/java/Blockchain.java b/blockchain/src/main/java/org/ton/java/Blockchain.java index 7c76305e..3aa653e9 100644 --- a/blockchain/src/main/java/org/ton/java/Blockchain.java +++ b/blockchain/src/main/java/org/ton/java/Blockchain.java @@ -322,16 +322,44 @@ private void initializeTonlib() { } } + /** + * There is a huge difference between sendExternal(Cell body) and sendExternal(Message message). + * The first one sends a body to itself, the second one - sends the message to a destination specified in a message. + * Basically, in the first method we construct MsgUtils.createExternalMessage() with replaced destination address. + * + * @param body Cell + * @return SendExternalResult + */ + public SendExternalResult sendExternal(Cell body) { + Address address; + if (nonNull(contract)) { + address = contract.getAddress(); + } else { + address = stateInit.getAddress(); + } + Message message = MsgUtils.createExternalMessage(address, null, body); + return sendExternal(message); + } + public SendExternalResult sendExternal(Message message) { try { + + Address address; + if (nonNull(contract)) { + address = contract.getAddress(); + } else { + address = stateInit.getAddress(); + } + String bounceableAddress = + (network == Network.TESTNET) ? address.toBounceableTestnet() : address.toBounceable(); + if (network != Network.EMULATOR) { - String bounceableAddress = - (network == Network.TESTNET) - ? contract.getAddress().toBounceableTestnet() - : contract.getAddress().toBounceable(); + String initialBalance = Utils.formatNanoValue(tonlib.getAccountBalance(address)); + log.info("initialBalance {}", initialBalance); log.info( - "Sending external message to bounceable address {} on {}...", + "Sending external message (cell-hash: {}) to bounceable address {} on {}...", + message.toCell().getShortHash(), bounceableAddress, network); ExtMessageInfo tonlibResult = tonlib.sendRawMessage(message.toCell().toBase64()); @@ -350,9 +378,9 @@ public SendExternalResult sendExternal(Message message) { String initialBalance = Utils.formatNanoValue(customEmulatorShardAccount.getBalance()); log.info( - "Sending external message to bounceable address {} on {}...", - stateInit.getAddress().toBounceable(), - network); + "Sending external message (cell-hash: {}) to bounceable address {} on {}...", + message.toCell().getShortHash(), + bounceableAddress, network); EmulateTransactionResult emulateTransactionResult = txEmulator.emulateTransaction( customEmulatorShardAccount.toCell().toBase64(), message.toCell().toBase64()); diff --git a/blockchain/src/test/java/org/ton/java/BlockchainTest.java b/blockchain/src/test/java/org/ton/java/BlockchainTest.java index 9086eab5..3772fa7f 100644 --- a/blockchain/src/test/java/org/ton/java/BlockchainTest.java +++ b/blockchain/src/test/java/org/ton/java/BlockchainTest.java @@ -11,12 +11,15 @@ import org.ton.java.address.Address; import org.ton.java.cell.Cell; import org.ton.java.cell.CellBuilder; +import org.ton.java.emulator.tvm.TvmVerbosityLevel; +import org.ton.java.emulator.tx.TxVerbosityLevel; import org.ton.java.smartcontract.faucet.TestnetFaucet; import org.ton.java.smartcontract.types.WalletV3Config; import org.ton.java.smartcontract.utils.MsgUtils; import org.ton.java.smartcontract.wallet.v3.WalletV3R2; import org.ton.java.smartcontract.wallet.v5.WalletV5; import org.ton.java.tlb.types.Message; +import org.ton.java.tonlib.types.VerbosityLevel; import org.ton.java.utils.Utils; @Slf4j @@ -288,20 +291,23 @@ public void testSendMessageCustomContractOnTestnetTolk() { .storeUint(0, 32) .storeInt(Utils.getRandomInt(), 32) .endCell()) + .tvmEmulatorVerbosityLevel(TvmVerbosityLevel.WITH_ALL_STACK_VALUES) + .txEmulatorVerbosityLevel(TxVerbosityLevel.WITH_ALL_STACK_VALUES) +// .tonlibVerbosityLevel(VerbosityLevel.DEBUG) .build(); + assertThat(blockchain.deploy(30)).isTrue(); - GetterResult result = blockchain.runGetMethod("unique"); - System.out.printf("result %s\n", result); + + blockchain.runGetMethod("unique"); System.out.printf("current seqno %s\n", blockchain.runGetSeqNo()); Cell bodyCell = CellBuilder.beginCell() - .storeUint(0, 32) // seqno + .storeUint(1, 32) // seqno .endCell(); - Message extMsg = MsgUtils.createExternalMessage(dummyAddress, null, bodyCell); - - blockchain.sendExternal(extMsg); + blockchain.sendExternal(bodyCell); + //wait till delivered System.out.printf("current seqno %s\n", blockchain.runGetSeqNo()); } @@ -313,24 +319,24 @@ public void testSendMessageCustomContractOnEmulatorTolk() { .customContractAsResource("simple.tolk") .customContractDataCell( CellBuilder.beginCell() - .storeUint(0, 32) + .storeUint(1, 32) .storeInt(Utils.getRandomInt(), 32) .endCell()) // .tvmEmulatorVerbosityLevel(TvmVerbosityLevel.WITH_ALL_STACK_VALUES) // .txEmulatorVerbosityLevel(TxVerbosityLevel.WITH_ALL_STACK_VALUES) .build(); + assertThat(blockchain.deploy(30)).isTrue(); + blockchain.runGetMethod("unique"); System.out.printf("current seqno %s\n", blockchain.runGetSeqNo()); Cell bodyCell = CellBuilder.beginCell() - .storeUint(0, 32) // seqno + .storeUint(1, 32) // seqno .endCell(); - Message extMsg = MsgUtils.createExternalMessage(dummyAddress, null, bodyCell); - - blockchain.sendExternal(extMsg); + blockchain.sendExternal(bodyCell); System.out.printf("current seqno %s\n", blockchain.runGetSeqNo()); } @@ -358,8 +364,7 @@ public void testSendMessagesChainCustomContractOnEmulatorTolk() { .storeUint(0, 32) // seqno .endCell(); - Message extMsg = MsgUtils.createExternalMessage(dummyAddress, null, bodyCell); - blockchain.sendExternal(extMsg); + blockchain.sendExternal(bodyCell); currentSeqno = blockchain.runGetSeqNo(); System.out.printf("current seqno %s\n", currentSeqno); @@ -369,8 +374,45 @@ public void testSendMessagesChainCustomContractOnEmulatorTolk() { .storeUint(1, 32) // seqno .endCell(); - extMsg = MsgUtils.createExternalMessage(dummyAddress, null, bodyCell); - blockchain.sendExternal(extMsg); + blockchain.sendExternal(bodyCell); + currentSeqno = blockchain.runGetSeqNo(); + System.out.printf("current seqno %s\n", currentSeqno); + } + + + @Test + public void testSendMessagesChainCustomContractOnEmulatorFunc() { + Blockchain blockchain = + Blockchain.builder() + .network(Network.EMULATOR) + .customContractAsResource("simple.fc") + .customContractDataCell( + CellBuilder.beginCell() + .storeUint(0, 32) + .storeInt(Utils.getRandomInt(), 32) + .endCell()) + .build(); + assertThat(blockchain.deploy(30)).isTrue(); + blockchain.runGetMethod("unique"); + BigInteger currentSeqno = blockchain.runGetSeqNo(); + System.out.printf("current seqno %s\n", currentSeqno); + + Cell bodyCell = + CellBuilder.beginCell() + .storeUint(0, 32) // seqno + .endCell(); + + blockchain.sendExternal(bodyCell); + + currentSeqno = blockchain.runGetSeqNo(); + System.out.printf("current seqno %s\n", currentSeqno); + + bodyCell = + CellBuilder.beginCell() + .storeUint(1, 32) // seqno + .endCell(); + + blockchain.sendExternal(bodyCell); currentSeqno = blockchain.runGetSeqNo(); System.out.printf("current seqno %s\n", currentSeqno); } diff --git a/cell/src/main/java/org/ton/java/cell/Cell.java b/cell/src/main/java/org/ton/java/cell/Cell.java index ad70d596..0f3bf5ec 100644 --- a/cell/src/main/java/org/ton/java/cell/Cell.java +++ b/cell/src/main/java/org/ton/java/cell/Cell.java @@ -669,6 +669,11 @@ public byte[] getHash() { return getHash(levelMask.getLevel()); } + public String getShortHash() { + String hashHex = Utils.bytesToHex(getHash(levelMask.getLevel())); + return hashHex.substring(0, 4) + ".." + hashHex.substring(hashHex.length() - 5, hashHex.length() - 1); + } + public byte[] getHash(int lvl) { int hashIndex = levelMask.apply(lvl).getHashIndex(); if (type == CellType.PRUNED_BRANCH) { diff --git a/emulator/pom.xml b/emulator/pom.xml index 33837d56..ce2dddba 100644 --- a/emulator/pom.xml +++ b/emulator/pom.xml @@ -108,11 +108,6 @@ 0.7.1 compile - - net.java.dev.jna - jna-platform - 5.15.0 - \ No newline at end of file diff --git a/emulator/src/main/java/org/ton/java/emulator/tvm/TvmEmulator.java b/emulator/src/main/java/org/ton/java/emulator/tvm/TvmEmulator.java index 38380f8f..713a8367 100644 --- a/emulator/src/main/java/org/ton/java/emulator/tvm/TvmEmulator.java +++ b/emulator/src/main/java/org/ton/java/emulator/tvm/TvmEmulator.java @@ -6,8 +6,7 @@ import com.google.gson.GsonBuilder; import com.google.gson.ToNumberPolicy; import com.sun.jna.Native; -import com.sun.jna.platform.win32.Kernel32; -import com.sun.jna.platform.win32.WinNT; + import java.math.BigInteger; import java.util.Collections; import lombok.Builder; @@ -66,7 +65,7 @@ public TvmEmulator build() { super.verbosityLevel = TvmVerbosityLevel.TRUNCATED; } - redirectNativeOutput(); + Utils.disableNativeOutput(); if (isNull(super.codeBoc)) { throw new Error("codeBoc is not set"); @@ -82,6 +81,8 @@ public TvmEmulator build() { super.tvmEmulatorI.tvm_emulator_set_debug_enabled(super.tvmEmulator, true); } + Utils.enableNativeOutput(); + if (super.tvmEmulator == 0) { throw new Error("Can't create emulator instance"); } @@ -97,7 +98,9 @@ public TvmEmulator build() { } public void destroy() { + Utils.disableNativeOutput(); tvmEmulatorI.tvm_emulator_destroy(tvmEmulator); + Utils.enableNativeOutput(); } /** @@ -107,7 +110,10 @@ public void destroy() { * @return true in case of success, false in case of error */ public boolean setLibs(String libsBoc) { - return tvmEmulatorI.tvm_emulator_set_libraries(tvmEmulator, libsBoc); + Utils.disableNativeOutput(); + boolean result = tvmEmulatorI.tvm_emulator_set_libraries(tvmEmulator, libsBoc); + Utils.enableNativeOutput(); + return result; } /** @@ -136,8 +142,11 @@ public boolean setLibs(String libsBoc) { */ public boolean setC7( String address, long unixTime, long balance, String randSeedHex, String config) { - return tvmEmulatorI.tvm_emulator_set_c7( + Utils.disableNativeOutput(); + boolean result = tvmEmulatorI.tvm_emulator_set_c7( tvmEmulator, address, unixTime, balance, randSeedHex, config); + Utils.enableNativeOutput(); + return result; } /** @@ -147,7 +156,10 @@ public boolean setC7( * @return true in case of success, false in case of error */ public boolean setPrevBlockInfo(String infoBoc) { - return tvmEmulatorI.tvm_emulator_set_prev_blocks_info(tvmEmulator, infoBoc); + Utils.disableNativeOutput(); + boolean result = tvmEmulatorI.tvm_emulator_set_prev_blocks_info(tvmEmulator, infoBoc); + Utils.enableNativeOutput(); + return result; } /** @@ -157,7 +169,10 @@ public boolean setPrevBlockInfo(String infoBoc) { * @return true in case of success, false in case of error */ public boolean setGasLimit(long gasLimit) { - return tvmEmulatorI.tvm_emulator_set_gas_limit(tvmEmulator, gasLimit); + Utils.disableNativeOutput(); + boolean result = tvmEmulatorI.tvm_emulator_set_gas_limit(tvmEmulator, gasLimit); + Utils.enableNativeOutput(); + return result; } /** @@ -167,7 +182,10 @@ public boolean setGasLimit(long gasLimit) { * @return true in case of success, false in case of error */ public boolean setDebugEnabled(boolean debugEnabled) { - return tvmEmulatorI.tvm_emulator_set_debug_enabled(tvmEmulator, debugEnabled); + Utils.disableNativeOutput(); + boolean result = tvmEmulatorI.tvm_emulator_set_debug_enabled(tvmEmulator, debugEnabled); + Utils.enableNativeOutput(); + return result; } /** @@ -180,7 +198,9 @@ public boolean setDebugEnabled(boolean debugEnabled) { * serialized stack (VmStack)", "missing_library": null, "gas_used": 1212 } */ public GetMethodResult runGetMethod(int methodId, String stackBoc) { + Utils.disableNativeOutput(); String result = tvmEmulatorI.tvm_emulator_run_get_method(tvmEmulator, methodId, stackBoc); + Utils.enableNativeOutput(); Gson gson = new GsonBuilder().setObjectToNumberStrategy(ToNumberPolicy.BIG_DECIMAL).create(); return gson.fromJson(result, GetMethodResult.class); } @@ -194,6 +214,7 @@ public GetMethodResult runGetMethod(int methodId, String stackBoc) { * serialized stack (VmStack)", "missing_library": null, "gas_used": 1212 } */ public GetMethodResult runGetMethod(int methodId) { + Utils.disableNativeOutput(); String result = tvmEmulatorI.tvm_emulator_run_get_method( tvmEmulator, @@ -204,11 +225,13 @@ public GetMethodResult runGetMethod(int methodId) { .build() .toCell() .toBase64()); + Utils.enableNativeOutput(); Gson gson = new GsonBuilder().setObjectToNumberStrategy(ToNumberPolicy.BIG_DECIMAL).create(); return gson.fromJson(result, GetMethodResult.class); } public GetMethodResult runGetMethod(String methodName) { + Utils.disableNativeOutput(); String result = tvmEmulatorI.tvm_emulator_run_get_method( tvmEmulator, @@ -219,6 +242,7 @@ public GetMethodResult runGetMethod(String methodName) { .build() .toCell() .toBase64()); + Utils.enableNativeOutput(); Gson gson = new GsonBuilder().setObjectToNumberStrategy(ToNumberPolicy.BIG_DECIMAL).create(); return gson.fromJson(result, GetMethodResult.class); } @@ -233,9 +257,11 @@ public GetMethodResult runGetMethod(String methodName) { * serialized stack (VmStack)", "missing_library": null, "gas_used": 1212 } */ public GetMethodResult runGetMethod(String methodName, String stackBoc) { + Utils.disableNativeOutput(); String result = tvmEmulatorI.tvm_emulator_run_get_method( tvmEmulator, Utils.calculateMethodId(methodName), stackBoc); + Utils.enableNativeOutput(); Gson gson = new GsonBuilder().setObjectToNumberStrategy(ToNumberPolicy.BIG_DECIMAL).create(); return gson.fromJson(result, GetMethodResult.class); } @@ -291,7 +317,10 @@ public String runGetPublicKey() { * result$_ exit_code:(## 32) gas_used:(## 32) stack:^VmStack */ public String emulateRunMethod(int len, String paramsBoc, long gasLimit) { - return tvmEmulatorI.tvm_emulator_emulate_run_method(len, paramsBoc, gasLimit); + Utils.disableNativeOutput(); + String result = tvmEmulatorI.tvm_emulator_emulate_run_method(len, paramsBoc, gasLimit); + Utils.enableNativeOutput(); + return result; } /** @@ -305,7 +334,9 @@ public String emulateRunMethod(int len, String paramsBoc, long gasLimit) { * type (OutList n)" } */ public SendExternalMessageResult sendExternalMessage(String messageBodyBoc) { + Utils.disableNativeOutput(); String result = tvmEmulatorI.tvm_emulator_send_external_message(tvmEmulator, messageBodyBoc); + Utils.enableNativeOutput(); Gson gson = new GsonBuilder().setObjectToNumberStrategy(ToNumberPolicy.BIG_DECIMAL).create(); return gson.fromJson(result, SendExternalMessageResult.class); } @@ -322,39 +353,11 @@ public SendExternalMessageResult sendExternalMessage(String messageBodyBoc) { * type (OutList n)" } */ public SendInternalMessageResult sendInternalMessage(String messageBodyBoc, long amount) { + Utils.disableNativeOutput(); String result = tvmEmulatorI.tvm_emulator_send_internal_message(tvmEmulator, messageBodyBoc, amount); + Utils.enableNativeOutput(); Gson gson = new GsonBuilder().setObjectToNumberStrategy(ToNumberPolicy.BIG_DECIMAL).create(); return gson.fromJson(result, SendInternalMessageResult.class); } - - private static void redirectNativeOutput() { - - // Redirect native output on Windows - WinNT.HANDLE originalOut = Kernel32.INSTANCE.GetStdHandle(Kernel32.STD_OUTPUT_HANDLE); - WinNT.HANDLE originalErr = Kernel32.INSTANCE.GetStdHandle(Kernel32.STD_ERROR_HANDLE); - - // try (FileOutputStream nulStream = new FileOutputStream("NUL")) { - WinNT.HANDLE hNul = - Kernel32.INSTANCE.CreateFile( - "NUL", - Kernel32.GENERIC_WRITE, - Kernel32.FILE_SHARE_WRITE, - null, - Kernel32.OPEN_EXISTING, - 0, - null); - - // Redirect stdout and stderr to NUL - Kernel32.INSTANCE.SetStdHandle(Kernel32.STD_OUTPUT_HANDLE, hNul); - Kernel32.INSTANCE.SetStdHandle(Kernel32.STD_ERROR_HANDLE, hNul); - - // // Close the handle to NUL - // Kernel32.INSTANCE.CloseHandle(hNul); - // } finally { - // // Restore original stdout and stderr - // Kernel32.INSTANCE.SetStdHandle(Kernel32.STD_OUTPUT_HANDLE, originalOut); - // Kernel32.INSTANCE.SetStdHandle(Kernel32.STD_ERROR_HANDLE, originalErr); - // } - } } diff --git a/emulator/src/main/java/org/ton/java/emulator/tx/TxEmulator.java b/emulator/src/main/java/org/ton/java/emulator/tx/TxEmulator.java index 5e8997c1..24c94be7 100644 --- a/emulator/src/main/java/org/ton/java/emulator/tx/TxEmulator.java +++ b/emulator/src/main/java/org/ton/java/emulator/tx/TxEmulator.java @@ -6,10 +6,7 @@ import com.google.gson.GsonBuilder; import com.google.gson.ToNumberPolicy; import com.sun.jna.Native; -import com.sun.jna.platform.win32.Kernel32; -import com.sun.jna.platform.win32.WinNT; -import java.io.FileOutputStream; -import java.io.IOException; + import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.util.Objects; @@ -68,8 +65,6 @@ public TxEmulator build() { super.verbosityLevel = TxVerbosityLevel.TRUNCATED; } - redirectNativeOutput(); - if (isNull(super.configType)) { super.configType = TxEmulatorConfig.MAINNET; } @@ -101,6 +96,8 @@ public TxEmulator build() { } } + Utils.disableNativeOutput(); + super.txEmulator = super.txEmulatorI.transaction_emulator_create( configBoc, super.verbosityLevel.ordinal()); @@ -112,6 +109,8 @@ public TxEmulator build() { super.txEmulatorI.transaction_emulator_set_debug_enabled(super.txEmulator, true); } + Utils.enableNativeOutput(); + if (super.txEmulator == 0) { throw new Error("Can't create tx emulator instance"); } @@ -134,43 +133,7 @@ public TxEmulator build() { } } - private static void redirectNativeOutput() { - - if ((Utils.getOS() == Utils.OS.WINDOWS) || (Utils.getOS() == Utils.OS.WINDOWS_ARM)) { - // Redirect native output on Windows - WinNT.HANDLE originalOut = Kernel32.INSTANCE.GetStdHandle(Kernel32.STD_OUTPUT_HANDLE); - WinNT.HANDLE originalErr = Kernel32.INSTANCE.GetStdHandle(Kernel32.STD_ERROR_HANDLE); - - try (FileOutputStream nulStream = new FileOutputStream("NUL")) { - WinNT.HANDLE hNul = - Kernel32.INSTANCE.CreateFile( - "NUL", - Kernel32.GENERIC_WRITE, - Kernel32.FILE_SHARE_WRITE, - null, - Kernel32.OPEN_EXISTING, - 0, - null); - - // Redirect stdout and stderr to NUL - Kernel32.INSTANCE.SetStdHandle(Kernel32.STD_OUTPUT_HANDLE, hNul); - Kernel32.INSTANCE.SetStdHandle(Kernel32.STD_ERROR_HANDLE, hNul); - - // Close the handle to NUL - Kernel32.INSTANCE.CloseHandle(hNul); - } catch (IOException e) { - throw new RuntimeException(e); - } finally { - // Restore original stdout and stderr - Kernel32.INSTANCE.SetStdHandle(Kernel32.STD_OUTPUT_HANDLE, originalOut); - Kernel32.INSTANCE.SetStdHandle(Kernel32.STD_ERROR_HANDLE, originalErr); - } - } else if ((Utils.getOS() == Utils.OS.LINUX) || (Utils.getOS() == Utils.OS.LINUX_ARM)) { - // asdf - } else if ((Utils.getOS() == Utils.OS.MAC) || (Utils.getOS() == Utils.OS.MAC_ARM64)) { - // asdf - } - } + public void destroy() { txEmulatorI.transaction_emulator_destroy(txEmulator); @@ -189,9 +152,11 @@ public void destroy() { * actions boc (OutList n)", "elapsed_time": 0.02 } */ public EmulateTransactionResult emulateTransaction(String shardAccountBoc, String messageBoc) { + Utils.disableNativeOutput(); String result = txEmulatorI.transaction_emulator_emulate_transaction( txEmulator, shardAccountBoc, messageBoc); + Utils.enableNativeOutput(); Gson gson = new GsonBuilder().setObjectToNumberStrategy(ToNumberPolicy.BIG_DECIMAL).create(); return gson.fromJson(result, EmulateTransactionResult.class); } @@ -245,9 +210,11 @@ public EmulateTransactionResult emulateTransaction( String shardAccountBocBase64 = shardAccount.toCell().toBase64(); + Utils.disableNativeOutput(); String result = txEmulatorI.transaction_emulator_emulate_transaction( txEmulator, shardAccountBocBase64, messageBoc); + Utils.enableNativeOutput(); Gson gson = new GsonBuilder().setObjectToNumberStrategy(ToNumberPolicy.BIG_DECIMAL).create(); return gson.fromJson(result, EmulateTransactionResult.class); } @@ -259,7 +226,9 @@ public EmulateTransactionResult emulateTransaction( * debug) */ public void setVerbosityLevel(int verbosityLevel) { + Utils.disableNativeOutput(); txEmulatorI.emulator_set_verbosity_level(txEmulator, verbosityLevel); + Utils.enableNativeOutput(); } /** @@ -269,7 +238,10 @@ public void setVerbosityLevel(int verbosityLevel) { * @return true in case of success, false in case of error */ public boolean setDebugEnabled(boolean debugEnabled) { - return txEmulatorI.transaction_emulator_set_debug_enabled(txEmulator, debugEnabled); + Utils.disableNativeOutput(); + boolean result = txEmulatorI.transaction_emulator_set_debug_enabled(txEmulator, debugEnabled); + Utils.enableNativeOutput(); + return result; } /** @@ -279,7 +251,10 @@ public boolean setDebugEnabled(boolean debugEnabled) { * @return true in case of success, false in case of error */ public boolean setLibs(String libsBoc) { - return txEmulatorI.transaction_emulator_set_libs(txEmulator, libsBoc); + Utils.disableNativeOutput(); + boolean result = txEmulatorI.transaction_emulator_set_libs(txEmulator, libsBoc); + Utils.enableNativeOutput(); + return result; } /** @@ -289,7 +264,10 @@ public boolean setLibs(String libsBoc) { * @return true in case of success, false in case of error */ public boolean setPrevBlockInfo(String infoBoc) { - return txEmulatorI.transaction_emulator_set_prev_blocks_info(txEmulator, infoBoc); + Utils.disableNativeOutput(); + boolean result = txEmulatorI.transaction_emulator_set_prev_blocks_info(txEmulator, infoBoc); + Utils.enableNativeOutput(); + return result; } /** @@ -299,7 +277,10 @@ public boolean setPrevBlockInfo(String infoBoc) { * @return true in case of success, false in case of error */ public boolean setRandSeed(String randSeedHex) { - return txEmulatorI.transaction_emulator_set_rand_seed(txEmulator, randSeedHex); + Utils.disableNativeOutput(); + boolean result = txEmulatorI.transaction_emulator_set_rand_seed(txEmulator, randSeedHex); + Utils.enableNativeOutput(); + return result; } /** @@ -309,6 +290,7 @@ public boolean setRandSeed(String randSeedHex) { * @return true in case of success, false in case of error */ public boolean setUnixTime(long utime) { + Utils.disableNativeOutput(); return txEmulatorI.transaction_emulator_set_unixtime(txEmulator, utime); } @@ -319,7 +301,10 @@ public boolean setUnixTime(long utime) { * @return true in case of success, false in case of error */ public boolean setConfig(String configBoc) { - return txEmulatorI.transaction_emulator_set_config(txEmulator, configBoc); + Utils.disableNativeOutput(); + boolean result = txEmulatorI.transaction_emulator_set_config(txEmulator, configBoc); + Utils.enableNativeOutput(); + return result; } /** @@ -329,7 +314,10 @@ public boolean setConfig(String configBoc) { * @return Pointer to Config object or nullptr in case of error */ public long createConfig(String configBoc) { - return txEmulatorI.emulator_config_create(configBoc); + Utils.disableNativeOutput(); + long result = txEmulatorI.emulator_config_create(configBoc); + Utils.enableNativeOutput(); + return result; } /** @@ -338,7 +326,9 @@ public long createConfig(String configBoc) { * @param config Pointer to Config object */ public void destroyConfig(long config) { + Utils.disableNativeOutput(); txEmulatorI.emulator_config_destroy(config); + Utils.enableNativeOutput(); } /** @@ -354,6 +344,7 @@ public void destroyConfig(long config) { */ public EmulateTransactionResult emulateTickTockTransaction( String shardAccountBoc, boolean isTock) { + Utils.disableNativeOutput(); String result = txEmulatorI.transaction_emulator_emulate_tick_tock_transaction( txEmulator, shardAccountBoc, isTock); @@ -368,7 +359,10 @@ public EmulateTransactionResult emulateTickTockTransaction( * @return true in case of success, false in case of error */ public boolean setEmulatorLt(long lt) { - return txEmulatorI.transaction_emulator_set_lt(txEmulator, lt); + Utils.disableNativeOutput(); + boolean result = txEmulatorI.transaction_emulator_set_lt(txEmulator, lt); + Utils.enableNativeOutput(); + return result; } /** @@ -378,6 +372,9 @@ public boolean setEmulatorLt(long lt) { * @return true in case of success, false in case of error */ public boolean setIgnoreCheckSignature(boolean ignoreChksig) { - return txEmulatorI.transaction_emulator_set_ignore_chksig(txEmulator, ignoreChksig); + Utils.disableNativeOutput(); + boolean result = txEmulatorI.transaction_emulator_set_ignore_chksig(txEmulator, ignoreChksig); + Utils.enableNativeOutput(); + return result; } } diff --git a/fift/src/main/java/org/ton/java/fift/FiftRunner.java b/fift/src/main/java/org/ton/java/fift/FiftRunner.java index 396445b9..01a001bf 100644 --- a/fift/src/main/java/org/ton/java/fift/FiftRunner.java +++ b/fift/src/main/java/org/ton/java/fift/FiftRunner.java @@ -223,8 +223,12 @@ public Pair executeStdIn( String.join( " ", "powershell", "-c", "'" + stdin + "' | " + pathToBinary + " " + include); } else { // linux & macos - pb = null; // todo test - cmd = String.join(" ", "echo", "'" + stdin + "' | " + pathToBinary + " " + include); + pb = + new ProcessBuilder( + "/bin/bash", "-c", + "echo", "\"" + stdin + "\" | " + pathToBinary + " " + include ) + .redirectErrorStream(true); + cmd = String.join(" ", "/bin/bash", "-c", "\"echo", "'" , stdin , "'|", pathToBinary + " " + include + "\""); } if (printInfo) { diff --git a/smartcontract/src/main/java/org/ton/java/smartcontract/SmartContractCompiler.java b/smartcontract/src/main/java/org/ton/java/smartcontract/SmartContractCompiler.java index 14e3fee0..b0476bf0 100644 --- a/smartcontract/src/main/java/org/ton/java/smartcontract/SmartContractCompiler.java +++ b/smartcontract/src/main/java/org/ton/java/smartcontract/SmartContractCompiler.java @@ -8,6 +8,7 @@ import lombok.Builder; import lombok.Data; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import org.ton.java.cell.Cell; import org.ton.java.fift.FiftRunner; @@ -83,39 +84,52 @@ public String compile() { if (contractPath.contains(".func") || contractPath.contains(".fc")) { outputFiftAsmFile = funcRunner.run(new File(contractPath).getParent(), contractPath); // add missing includes, PROGRAM and to boc conversion - outputFiftAsmFile = - "\"\"\"\"TonUtil.fif\"\"\"\" include \"\"\"\"Asm.fif\"\"\"\" include PROGRAM{ " - + outputFiftAsmFile - + "}END>c 2 boc+>B dup Bx."; - outputFiftAsmFile = - outputFiftAsmFile - .replaceAll("(?:/\\*(?:[^*]|(?:\\*+[^*/]))*\\*+/)|(?://.*)", "") - .replaceAll("\n", " ") - .replaceAll("\r", " "); - } else { // told + + outputFiftAsmFile = + "\"TonUtil.fif\" include \"Asm.fif\" include PROGRAM{ " + + outputFiftAsmFile + + "}END>c 2 boc+>B dup Bx."; + outputFiftAsmFile = + outputFiftAsmFile + .replaceAll("(?:/\\*(?:[^*]|(?:\\*+[^*/]))*\\*+/)|(?://.*)", "") + .replaceAll("\n", " ") + .replaceAll("\r", " "); + + } else { // tolk outputFiftAsmFile = tolkRunner.run(new File(contractPath).getParent(), contractPath); - // add to boc conversion + outputFiftAsmFile = - "\"\"\"\"TonUtil.fif\"\"\"\" include \"\"\"\"Asm.fif\"\"\"\" include " - + outputFiftAsmFile - + " 2 boc+>B dup Bx."; + "\"TonUtil.fif\" include " + +outputFiftAsmFile + +" 2 boc+>B dup Bx."; + outputFiftAsmFile = outputFiftAsmFile - .replaceAll("\"Asm.fif\" include", "") .replaceAll("(?:/\\*(?:[^*]|(?:\\*+[^*/]))*\\*+/)|(?://.*)", "") .replaceAll("\n", " ") .replaceAll("\r", " "); } if (outputFiftAsmFile.contains("cannot generate code") - || outputFiftAsmFile.contains("error: undefined function")) { + || outputFiftAsmFile.contains("error: undefined function") || outputFiftAsmFile.contains("Failed to discover")) { throw new Error("Compile error: " + outputFiftAsmFile); } if (printFiftAsmOutput) { log.info(outputFiftAsmFile); } - return fiftRunner.runStdIn(new File(contractPath).getParent(), outputFiftAsmFile); + + String result; + try { + File fiftFile = new File(contractPath + ".fif"); + FileUtils.writeStringToFile(fiftFile, outputFiftAsmFile); + result = fiftRunner.run(new File(contractPath).getParent(), "-s", fiftFile.getAbsolutePath()); + FileUtils.deleteQuietly(fiftFile); + return result; + } + catch (Exception e) { + throw new Error("Cannot compile "+contractPath+ ", error " + e.getMessage()); + } } /** diff --git a/tonlib/pom.xml b/tonlib/pom.xml index 7b9ae876..2e49ed07 100644 --- a/tonlib/pom.xml +++ b/tonlib/pom.xml @@ -101,11 +101,6 @@ ch.qos.logback logback-classic - - net.java.dev.jna - jna-platform - 5.15.0 - org.assertj assertj-core 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 a72ce037..6c712f55 100644 --- a/tonlib/src/main/java/org/ton/java/tonlib/Tonlib.java +++ b/tonlib/src/main/java/org/ton/java/tonlib/Tonlib.java @@ -7,8 +7,7 @@ import com.google.gson.GsonBuilder; import com.google.gson.ToNumberPolicy; import com.sun.jna.Native; -import com.sun.jna.platform.win32.Kernel32; -import com.sun.jna.platform.win32.WinNT; + import java.io.InputStream; import java.math.BigInteger; import java.nio.file.Files; @@ -155,7 +154,6 @@ public Tonlib build() { super.runResultParser = new RunResultParser(); super.libraryResultParser = new LibraryResultParser(); - // String originalGlobalConfigStr; if (isNull(super.pathToGlobalConfig)) { if (isNull(super.globalConfigAsString)) { @@ -215,10 +213,12 @@ public Tonlib build() { super.tonlibJson = Native.load(super.pathToTonlibSharedLib, TonlibJsonI.class); - redirectNativeOutput(); + Utils.disableNativeOutput(); super.tonlib = super.tonlibJson.tonlib_client_json_create(); + Utils.enableNativeOutput(); + if (super.printInfo) { log.info( String.format( @@ -267,8 +267,11 @@ public Tonlib build() { VerbosityLevelQuery.builder() .new_verbosity_level(super.verbosityLevel.ordinal()) .build(); + + Utils.disableNativeOutput(); super.tonlibJson.tonlib_client_json_send(super.tonlib, gson.toJson(verbosityLevelQuery)); super.tonlibJson.tonlib_client_json_receive(super.tonlib, super.receiveTimeout); + Utils.enableNativeOutput(); initTonlibConfig(globalConfigCurrent); @@ -315,8 +318,10 @@ private void initTonlibConfig(TonGlobalConfig tonGlobalConfig) { .build()) .build(); + Utils.disableNativeOutput(); super.tonlibJson.tonlib_client_json_send(super.tonlib, gson.toJson(tonlibSetup)); super.tonlibJson.tonlib_client_json_receive(super.tonlib, super.receiveTimeout); + Utils.enableNativeOutput(); } } @@ -325,7 +330,9 @@ private void reinitTonlibConfig(TonGlobalConfig tonGlobalConfig) { // recreate tonlib instance // tonlibJson.tonlib_client_json_destroy(tonlib); destroy(); + tonlibJson = Native.load(pathToTonlibSharedLib, TonlibJsonI.class); + Utils.disableNativeOutput(); tonlib = tonlibJson.tonlib_client_json_create(); // set verbosity @@ -360,10 +367,14 @@ private void reinitTonlibConfig(TonGlobalConfig tonGlobalConfig) { tonlibJson.tonlib_client_json_send(tonlib, gson.toJson(tonlibSetup)); tonlibJson.tonlib_client_json_receive(tonlib, receiveTimeout); + + Utils.enableNativeOutput(); } public void destroy() { + Utils.disableNativeOutput(); tonlibJson.tonlib_client_json_destroy(tonlib); + Utils.enableNativeOutput(); } private String receive() { @@ -371,7 +382,7 @@ private String receive() { int retry = 0; while (isNull(result)) { if (retry > 0) { - log.info("retry " + retry); +// log.info("retry " + retry); } if (++retry > receiveRetryTimes) { throw new Error( @@ -379,7 +390,9 @@ private String receive() { + receiveRetryTimes + " times was not able retrieve result from lite-server."); } +// Utils.disableNativeOutput(); result = tonlibJson.tonlib_client_json_receive(tonlib, receiveTimeout); +// Utils.enableNativeOutput(); } return result; } @@ -387,9 +400,11 @@ private String receive() { private String syncAndRead(String query) { String response = null; try { + Utils.disableNativeOutput(); tonlibJson.tonlib_client_json_send(tonlib, query); TimeUnit.MILLISECONDS.sleep(200); response = receive(); + Utils.enableNativeOutput(); int retry = 0; outterloop: do { @@ -430,7 +445,9 @@ private String syncAndRead(String query) { reinitTonlibConfig(globalConfigCurrent); // repeat request + Utils.disableNativeOutput(); tonlibJson.tonlib_client_json_send(tonlib, query); + Utils.enableNativeOutput(); } } else if (response.contains("\"@type\":\"ok\"")) { String queryExtraId = StringUtils.substringBetween(query, "@extra\":\"", "\"}"); @@ -445,9 +462,10 @@ private String syncAndRead(String query) { if (response.contains(" : duplicate message\"")) { break outterloop; } + Utils.disableNativeOutput(); TimeUnit.MILLISECONDS.sleep(200); response = receive(); - + Utils.enableNativeOutput(); UpdateSyncState sync = gson.fromJson(response, UpdateSyncState.class); if (nonNull(sync) && nonNull(sync.getSync_state()) @@ -482,7 +500,9 @@ && nonNull(sync.getSync_state()) + " times was not able retrieve result from lite-server."); } + Utils.disableNativeOutput(); tonlibJson.tonlib_client_json_send(tonlib, query); + Utils.enableNativeOutput(); } } while (response.contains("error") || response.contains("syncStateInProgress")); @@ -1487,7 +1507,9 @@ public RawTransaction getRawTransaction(byte workchain, ShortTxId tx) { .try_decode_message(false) .build(); + Utils.disableNativeOutput(); String result = syncAndRead(gson.toJson(getRawTransactionsQuery)); + Utils.enableNativeOutput(); RawTransactions res = gson.fromJson(result, RawTransactions.class); List t = res.getTransactions(); if (t.size() >= 1) { @@ -1600,7 +1622,7 @@ public void waitForDeployment(Address address, int timeoutSeconds) { public void waitForBalanceChange(Address address, int timeoutSeconds) { log.info( - "Waiting for balance change up to {}s - {} - ({})", + "Waiting for balance change (up to {}s) - {} - ({})", timeoutSeconds, testnet ? address.toBounceableTestnet() : address.toBounceable(), address.toRaw()); @@ -1615,36 +1637,6 @@ public void waitForBalanceChange(Address address, int timeoutSeconds) { } while (initialBalance.equals(getAccountBalance(address))); } - private static void redirectNativeOutput() { - - // Redirect native output on Windows - WinNT.HANDLE originalOut = Kernel32.INSTANCE.GetStdHandle(Kernel32.STD_OUTPUT_HANDLE); - WinNT.HANDLE originalErr = Kernel32.INSTANCE.GetStdHandle(Kernel32.STD_ERROR_HANDLE); - - // try (FileOutputStream nulStream = new FileOutputStream("NUL")) { - WinNT.HANDLE hNul = - Kernel32.INSTANCE.CreateFile( - "NUL", - Kernel32.GENERIC_WRITE, - Kernel32.FILE_SHARE_WRITE, - null, - Kernel32.OPEN_EXISTING, - 0, - null); - - // Redirect stdout and stderr to NUL - Kernel32.INSTANCE.SetStdHandle(Kernel32.STD_OUTPUT_HANDLE, hNul); - Kernel32.INSTANCE.SetStdHandle(Kernel32.STD_ERROR_HANDLE, hNul); - - // // Close the handle to NUL - // Kernel32.INSTANCE.CloseHandle(hNul); - // } finally { - // // Restore original stdout and stderr - // Kernel32.INSTANCE.SetStdHandle(Kernel32.STD_OUTPUT_HANDLE, originalOut); - // Kernel32.INSTANCE.SetStdHandle(Kernel32.STD_ERROR_HANDLE, originalErr); - // } - } - public boolean isTestnet() { return testnet; } diff --git a/utils/pom.xml b/utils/pom.xml index ed03219a..930a3ba3 100644 --- a/utils/pom.xml +++ b/utils/pom.xml @@ -55,6 +55,11 @@ 0.4.7 compile + + net.java.dev.jna + jna-platform + 5.15.0 + junit diff --git a/utils/src/main/java/org/ton/java/utils/Utils.java b/utils/src/main/java/org/ton/java/utils/Utils.java index 99b53064..33ea9a24 100644 --- a/utils/src/main/java/org/ton/java/utils/Utils.java +++ b/utils/src/main/java/org/ton/java/utils/Utils.java @@ -1,9 +1,9 @@ package org.ton.java.utils; import com.iwebpp.crypto.TweetNaclFast; -import java.io.BufferedReader; -import java.io.InputStream; -import java.io.InputStreamReader; + +import java.io.*; +import java.lang.reflect.Field; import java.math.BigDecimal; import java.math.BigInteger; import java.math.RoundingMode; @@ -21,6 +21,11 @@ import java.util.logging.Logger; import java.util.regex.Pattern; import java.util.stream.Collectors; + +import com.sun.jna.Library; +import com.sun.jna.Native; +import com.sun.jna.platform.win32.Kernel32; +import com.sun.jna.platform.win32.WinNT; import org.apache.commons.codec.DecoderException; import org.apache.commons.codec.binary.Hex; import org.apache.commons.lang3.RandomStringUtils; @@ -1037,4 +1042,126 @@ public static int getRandomInt() { public static long getRandomLong() { return new Random().nextLong(); } + + public interface CStdLib extends Library { + int dup(int oldfd); // Duplicate a file descriptor + int dup2(int oldfd, int newfd); // Duplicate a file descriptor to a specified descriptor + int close(int fd); // Close a file descriptor + } + + // Redirect native output on Windows + static WinNT.HANDLE originalOut; + static WinNT.HANDLE originalErr; + static int originalStdoutFD; + static int originalStderrFD; + static CStdLib cStdLib; + + public static void disableNativeOutput() { +// System.out.println("disable"); + try { + if ((Utils.getOS() == Utils.OS.WINDOWS) || (Utils.getOS() == Utils.OS.WINDOWS_ARM)) { + // Redirect native output on Windows + originalOut = Kernel32.INSTANCE.GetStdHandle(Kernel32.STD_OUTPUT_HANDLE); + originalErr = Kernel32.INSTANCE.GetStdHandle(Kernel32.STD_ERROR_HANDLE); + + try { + FileOutputStream nulStream = new FileOutputStream("NUL"); + + WinNT.HANDLE hNul = + Kernel32.INSTANCE.CreateFile( + "NUL", + Kernel32.GENERIC_WRITE, + Kernel32.FILE_SHARE_WRITE, + null, + Kernel32.OPEN_EXISTING, + 0, + null); + + // Redirect stdout and stderr to NUL + Kernel32.INSTANCE.SetStdHandle(Kernel32.STD_OUTPUT_HANDLE, hNul); + Kernel32.INSTANCE.SetStdHandle(Kernel32.STD_ERROR_HANDLE, hNul); + + // Close the handle to NUL + Kernel32.INSTANCE.CloseHandle(hNul); + } catch (IOException e) { + throw new RuntimeException(e); + } + } else if ((Utils.getOS() == Utils.OS.LINUX) || (Utils.getOS() == Utils.OS.LINUX_ARM)) { + try { + // Load the native library + cStdLib = Native.load("c", CStdLib.class); + + // Save original stdout and stderr file descriptors + originalStdoutFD = cStdLib.dup(1); + originalStderrFD = cStdLib.dup(2); + + // Redirect stdout and stderr to /dev/null + try (FileOutputStream devNull = new FileOutputStream("/dev/null")) { + // Get the file descriptor for /dev/null + FileDescriptor fd = devNull.getFD(); + + // Get the file descriptor integer value by accessing the private field via reflection + // Retrieve the field that holds the actual fd (in a private field) + Field fdField = FileDescriptor.class.getDeclaredField("fd"); + fdField.setAccessible(true); + int devNullFD = (int) fdField.get(fd); + + cStdLib.dup2(devNullFD, 1); + cStdLib.dup2(devNullFD, 2); + + } catch (IOException | NoSuchFieldException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } catch (Exception e) { + System.out.println("error here " + e.getMessage()); + } + } else if ((Utils.getOS() == Utils.OS.MAC) || (Utils.getOS() == Utils.OS.MAC_ARM64)) { + // Load the native library + CStdLib cStdLib = Native.load("c", CStdLib.class); + + // Redirect stdout and stderr to /dev/null + try (FileOutputStream devNull = new FileOutputStream("/dev/null")) { + // Get the file descriptor for /dev/null + FileDescriptor fd = devNull.getFD(); + + // Get the file descriptor integer value by accessing the private field via reflection + // Retrieve the field that holds the actual fd (in a private field) + Field fdField = FileDescriptor.class.getDeclaredField("fd"); + fdField.setAccessible(true); + int devNullFD = (int) fdField.get(fd); + + // Duplicate and redirect stdout and stderr + int stdoutFD = 1; // File descriptor for stdout + int stderrFD = 2; // File descriptor for stderr + cStdLib.dup2(devNullFD, stdoutFD); + cStdLib.dup2(devNullFD, stderrFD); + + } catch (IOException | NoSuchFieldException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } + } + catch (Exception e) { + System.err.println("cannot disable native stdout"); + } + } + + public static void enableNativeOutput() { + try { + if ((Utils.getOS() == Utils.OS.WINDOWS) || (Utils.getOS() == Utils.OS.WINDOWS_ARM)) { + Kernel32.INSTANCE.SetStdHandle(Kernel32.STD_OUTPUT_HANDLE, originalOut); + Kernel32.INSTANCE.SetStdHandle(Kernel32.STD_ERROR_HANDLE, originalErr); + } else if ((Utils.getOS() == Utils.OS.LINUX) || (Utils.getOS() == Utils.OS.LINUX_ARM)) { + cStdLib.dup2(originalStdoutFD, 1); + cStdLib.dup2(originalStderrFD, 2); + } else if ((Utils.getOS() == Utils.OS.MAC) || (Utils.getOS() == Utils.OS.MAC_ARM64)) { + cStdLib.dup2(originalStdoutFD, 1); + cStdLib.dup2(originalStderrFD, 2); + } + } + catch (Exception e) { + System.err.println("cannot enable native stdout"); + } +// System.out.println("enable"); + } } diff --git a/utils/src/test/java/org/ton/java/utils/TestUtils.java b/utils/src/test/java/org/ton/java/utils/TestUtils.java index 7d933a1b..3def24bd 100644 --- a/utils/src/test/java/org/ton/java/utils/TestUtils.java +++ b/utils/src/test/java/org/ton/java/utils/TestUtils.java @@ -2,6 +2,7 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.DecoderException; +import org.apache.commons.lang3.SystemUtils; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -432,6 +433,15 @@ public void testStringIpToInt() { ip = "135.181.177.59"; // mainnet [2] - 135.181.177.59 log.info("ip {}", ip2int(ip)); assertThat(ip2int(ip)).isEqualTo(-2018135749); + } + @Test + public void testDisableEnableSystemOutput() { + long l = System.currentTimeMillis(); + for (int i = 0; i < 10000; i++) { + Utils.disableNativeOutput(); + Utils.enableNativeOutput(); + } + log.info("10k switches took {}ms", System.currentTimeMillis() - l); } }