From 634625b5780d61c13888f2b1d610b15d344c868f Mon Sep 17 00:00:00 2001 From: neodiX Date: Fri, 8 Nov 2024 18:59:42 +0400 Subject: [PATCH] add windows stdout handling from shared lib; adjust info msgs when executing from Blockchain --- .../main/java/org/ton/java/Blockchain.java | 16 +++- .../java/org/ton/java/BlockchainTest.java | 48 ++++++++++- blockchain/src/test/resources/simple.fc | 8 +- blockchain/src/test/resources/simple.tolk | 8 +- .../org/ton/java/tlb/types/MessageFees.java | 2 +- .../org/ton/java/tlb/types/Transaction.java | 2 +- .../ton/java/emulator/tvm/TvmEmulator.java | 85 +++++++++++++++++-- .../org/ton/java/emulator/tx/TxEmulator.java | 15 ++-- 8 files changed, 159 insertions(+), 25 deletions(-) diff --git a/blockchain/src/main/java/org/ton/java/Blockchain.java b/blockchain/src/main/java/org/ton/java/Blockchain.java index 92854229..a154230d 100644 --- a/blockchain/src/main/java/org/ton/java/Blockchain.java +++ b/blockchain/src/main/java/org/ton/java/Blockchain.java @@ -213,7 +213,7 @@ private void printBlockchainInfo() { if (super.network == Network.EMULATOR) { System.out.printf( - "Java Blockchain configuration:\n" + "Blockchain configuration:\n" + "Target network: %s\n" + "Emulator location: %s, configType: %s, txVerbosity: %s, tvmVerbosity: %s\n" + "Emulator ShardAccount: balance %s, address: %s, lastPaid: %s, lastTransLt: %s\n" @@ -241,7 +241,7 @@ private void printBlockchainInfo() { : super.customContractPath); } else { System.out.printf( - "\nBlockchain configuration:\n" + "Blockchain configuration:\n" + "Target network: %s\n" + "Emulator not used\n" + "Tonlib location: %s\n" @@ -380,7 +380,16 @@ public boolean deploy(int waitForDeploymentSeconds) { public GetterResult runGetMethod(String methodName) { System.out.printf("running GetMethod %s on %s\n", methodName, network); if (network == Network.EMULATOR) { - return GetterResult.builder().emulatorResult(tvmEmulator.runGetMethod(methodName)).build(); + GetterResult result = + GetterResult.builder().emulatorResult(tvmEmulator.runGetMethod(methodName)).build(); + if (result.getEmulatorResult().getVm_exit_code() != 0) { + throw new Error( + "Cannot execute run method (" + + methodName + + "), Error:\n" + + result.getEmulatorResult().getVm_log()); + } + return result; } else { Address address; if (nonNull(contract)) { @@ -396,6 +405,7 @@ public BigInteger runGetSeqNo() { System.out.printf("running %s on %s\n", "seqno", network); if (network == Network.EMULATOR) { return tvmEmulator.runGetSeqNo(); + } else { Address address; if (nonNull(contract)) { diff --git a/blockchain/src/test/java/org/ton/java/BlockchainTest.java b/blockchain/src/test/java/org/ton/java/BlockchainTest.java index 2516455e..dc91aada 100644 --- a/blockchain/src/test/java/org/ton/java/BlockchainTest.java +++ b/blockchain/src/test/java/org/ton/java/BlockchainTest.java @@ -8,9 +8,11 @@ import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.ton.java.address.Address; +import org.ton.java.cell.Cell; import org.ton.java.cell.CellBuilder; 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; @@ -19,6 +21,8 @@ @Slf4j @RunWith(JUnit4.class) public class BlockchainTest { + Address dummyAddress = Address.of("EQAyjRKDnEpTBNfRHqYdnzGEQjdY4KG3gxgqiG3DpDY46u8G"); + @Test public void testDeployV3R2ContractOnEmulator() { TweetNaclFast.Signature.KeyPair keyPair = Utils.generateSignatureKeyPair(); @@ -247,6 +251,8 @@ public void testSendMessageV3R2ContractOnEmulator() { Blockchain blockchain = Blockchain.builder().network(Network.EMULATOR).contract(wallet).build(); assertThat(blockchain.deploy(30)).isTrue(); + blockchain.runGetMethod("seqno"); + WalletV3Config configA = WalletV3Config.builder() .walletId(42) @@ -259,7 +265,17 @@ public void testSendMessageV3R2ContractOnEmulator() { Message msg = wallet.prepareExternalMsg(configA); - blockchain.sendExternal(msg); + SendExternalResult result = blockchain.sendExternal(msg); + log.info("result {}", result); + } + + @Test(expected = Error.class) + public void testSendMessageV3R2ContractOnEmulatorErrorNoMethod() { + TweetNaclFast.Signature.KeyPair keyPair = Utils.generateSignatureKeyPair(); + WalletV3R2 wallet = WalletV3R2.builder().keyPair(keyPair).walletId(42).build(); + Blockchain blockchain = Blockchain.builder().network(Network.EMULATOR).contract(wallet).build(); + assertThat(blockchain.deploy(30)).isTrue(); + blockchain.runGetMethod("unique"); } @Test @@ -279,4 +295,34 @@ public void testSendMessageCustomContractOnTestnetTolk() { System.out.printf("result %s\n", result); System.out.printf("returned seqno %s\n", blockchain.runGetSeqNo()); } + + @Test + public void testSendMessageCustomContractOnEmulatorTolk() { + Blockchain blockchain = + Blockchain.builder() + .network(Network.EMULATOR) + .customContractAsResource("simple.tolk") + .customContractDataCell( + CellBuilder.beginCell() + .storeUint(0, 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("returned seqno %s\n", blockchain.runGetSeqNo()); + + Cell bodyCell = + CellBuilder.beginCell() + .storeUint(0, 32) // seqno + .endCell(); + + Message extMsg = MsgUtils.createExternalMessage(dummyAddress, null, bodyCell); + + SendExternalResult result = blockchain.sendExternal(extMsg); + // log.info("result {}", result); + + } } diff --git a/blockchain/src/test/resources/simple.fc b/blockchain/src/test/resources/simple.fc index 03e204f0..9827563e 100644 --- a/blockchain/src/test/resources/simple.fc +++ b/blockchain/src/test/resources/simple.fc @@ -3,7 +3,7 @@ #include "stdlib.fc"; () recv_internal(int my_ton_balance, int msg_value, cell in_msg_full, slice in_msg_body) impure inline { - ~dump(40); + ~dump(140); ~dump(my_ton_balance); slice cs = in_msg_full.begin_parse(); @@ -16,7 +16,7 @@ } () recv_external(slice in_msg_body) impure inline { - ~dump(41); + ~dump(141); var msg_seqno = in_msg_body~load_uint(32); var ds = get_data().begin_parse(); @@ -31,11 +31,11 @@ } int seqno() method_id { - ~dump(42); + ~dump(142); return get_data().begin_parse().preload_uint(32); } int unique() method_id { - ~dump(42); + ~dump(143); return get_data().begin_parse().skip_bits(32).preload_uint(32); } \ No newline at end of file diff --git a/blockchain/src/test/resources/simple.tolk b/blockchain/src/test/resources/simple.tolk index fb2a098e..5d513ad3 100644 --- a/blockchain/src/test/resources/simple.tolk +++ b/blockchain/src/test/resources/simple.tolk @@ -4,7 +4,7 @@ import "@stdlib/gas-payments" @inline fun onInternalMessage(myTonBalance: int, msgValue: int, inMsgFull: cell, inMsgBody: slice) { - debugPrint(40); + debugPrint(140); debugPrint(myTonBalance); var cs: slice = inMsgFull.beginParse(); @@ -18,7 +18,7 @@ fun onInternalMessage(myTonBalance: int, msgValue: int, inMsgFull: cell, inMsgBo @inline fun onExternalMessage(inMsgBody: slice) { - debugPrint(41); + debugPrint(141); var msgSeqno = inMsgBody.loadUint(32); var ds = getContractData().beginParse(); @@ -33,11 +33,11 @@ fun onExternalMessage(inMsgBody: slice) { } get seqno(): int { - debugPrint(42); + debugPrint(142); return getContractData().beginParse().preloadUint(32); } get unique(): int { - debugPrint(42); + debugPrint(143); return getContractData().beginParse().skipBits(32).preloadUint(32); } diff --git a/cell/src/main/java/org/ton/java/tlb/types/MessageFees.java b/cell/src/main/java/org/ton/java/tlb/types/MessageFees.java index 6753428b..76b970de 100644 --- a/cell/src/main/java/org/ton/java/tlb/types/MessageFees.java +++ b/cell/src/main/java/org/ton/java/tlb/types/MessageFees.java @@ -52,7 +52,7 @@ public void printMessageFees() { public static void printMessageFeesHeader() { String header = "| in/out | type | op | value | fwdFee | ihrFee | importFee | timestamp | lt | src | dst |"; - System.out.println("Messages"); + System.out.println("\nMessages"); System.out.println( "---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"); System.out.println(header); diff --git a/cell/src/main/java/org/ton/java/tlb/types/Transaction.java b/cell/src/main/java/org/ton/java/tlb/types/Transaction.java index c2838fad..8ad8f8dc 100644 --- a/cell/src/main/java/org/ton/java/tlb/types/Transaction.java +++ b/cell/src/main/java/org/ton/java/tlb/types/Transaction.java @@ -570,7 +570,7 @@ public void printAllMessages(boolean withHeader) { } public static void printTxHeader() { - System.out.println("Transactions"); + System.out.println("\nTransactions"); System.out.println( "------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"); System.out.println( 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 76e5c7d6..38380f8f 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,6 +6,8 @@ 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; @@ -61,8 +63,11 @@ public TvmEmulator build() { super.tvmEmulatorI = Native.load(super.pathToEmulatorSharedLib, TvmEmulatorI.class); if (isNull(super.verbosityLevel)) { - super.verbosityLevel = TvmVerbosityLevel.WITH_ALL_STACK_VALUES; + super.verbosityLevel = TvmVerbosityLevel.TRUNCATED; } + + redirectNativeOutput(); + if (isNull(super.codeBoc)) { throw new Error("codeBoc is not set"); } @@ -73,6 +78,10 @@ public TvmEmulator build() { super.tvmEmulatorI.tvm_emulator_create( super.codeBoc, super.dataBoc, super.verbosityLevel.ordinal()); + if (super.verbosityLevel == TvmVerbosityLevel.WITH_ALL_STACK_VALUES) { + super.tvmEmulatorI.tvm_emulator_set_debug_enabled(super.tvmEmulator, true); + } + if (super.tvmEmulator == 0) { throw new Error("Can't create emulator instance"); } @@ -102,13 +111,19 @@ public boolean setLibs(String libsBoc) { } /** - * Prepares the c7 tuple (virtual machine context) for a compute phase of a transaction. * - *

C7 tlb-scheme FYI: * - *

smc_info#076ef1ea actions:uint16 msgs_sent:uint16 unixtime:uint32 block_lt:uint64 - * trans_lt:uint64 rand_seed:bits256 balance_remaining:CurrencyCollection myself:MsgAddressInt - * global_config:(Maybe Cell) = SmartContractInfo; + *

+   * Prepares the c7 tuple (virtual machine context) for a compute phase of a transaction.
+   * C7 tlb-scheme FYI:
+   * smc_info#076ef1ea
+   *   actions:uint16 msgs_sent:uint16
+   *   unixtime:uint32 block_lt:uint64
+   *   trans_lt:uint64 rand_seed:bits256
+   *   balance_remaining:CurrencyCollection
+   *   myself:MsgAddressInt
+   *   global_config:(Maybe Cell) = SmartContractInfo;
+   * 
* *

Set c7 parameters * @@ -208,8 +223,28 @@ public GetMethodResult runGetMethod(String methodName) { return gson.fromJson(result, GetMethodResult.class); } + /** + * Run get method + * + * @param methodName String method id + * @param stackBoc Base64 encoded BoC serialized stack (VmStack) + * @return Json object with error: { "success": false, "error": "Error description" } Or success: + * { "success": true "vm_log": "...", "vm_exit_code": 0, "stack": "Base64 encoded BoC + * serialized stack (VmStack)", "missing_library": null, "gas_used": 1212 } + */ + public GetMethodResult runGetMethod(String methodName, String stackBoc) { + String result = + tvmEmulatorI.tvm_emulator_run_get_method( + tvmEmulator, Utils.calculateMethodId(methodName), stackBoc); + Gson gson = new GsonBuilder().setObjectToNumberStrategy(ToNumberPolicy.BIG_DECIMAL).create(); + return gson.fromJson(result, GetMethodResult.class); + } + public BigInteger runGetSeqNo() { GetMethodResult methodResult = runGetMethod(Utils.calculateMethodId("seqno")); + if (methodResult.getVm_exit_code() != 0) { + throw new Error("Cannot execute run method (seqno), Error:\n" + methodResult.getVm_log()); + } VmStack stack = methodResult.getStack(); VmStackList vmStackList = stack.getStack(); return VmStackValueTinyInt.deserialize( @@ -219,6 +254,10 @@ public BigInteger runGetSeqNo() { public BigInteger runGetSubWalletId() { GetMethodResult methodResult = runGetMethod(Utils.calculateMethodId("get_subwallet_id")); + if (methodResult.getVm_exit_code() != 0) { + throw new Error( + "Cannot execute run method (get_subwallet_id), Error:\n" + methodResult.getVm_log()); + } VmStack stack = methodResult.getStack(); VmStackList vmStackList = stack.getStack(); return VmStackValueTinyInt.deserialize( @@ -228,6 +267,10 @@ public BigInteger runGetSubWalletId() { public String runGetPublicKey() { GetMethodResult methodResult = runGetMethod(Utils.calculateMethodId("get_public_key")); + if (methodResult.getVm_exit_code() != 0) { + throw new Error( + "Cannot execute run method (get_public_key), Error:\n" + methodResult.getVm_log()); + } VmStack stack = methodResult.getStack(); int depth = stack.getDepth(); VmStackList vmStackList = stack.getStack(); @@ -284,4 +327,34 @@ public SendInternalMessageResult sendInternalMessage(String messageBodyBoc, long 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 f30d925b..68cb39bc 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 @@ -62,14 +62,14 @@ public TxEmulator build() { super.txEmulatorI = Native.load(super.pathToEmulatorSharedLib, TxEmulatorI.class); - redirectNativeOutput(); - if (isNull(super.verbosityLevel)) { - super.verbosityLevel = TxVerbosityLevel.WITH_ALL_STACK_VALUES; + super.verbosityLevel = TxVerbosityLevel.TRUNCATED; } + + redirectNativeOutput(); + if (isNull(super.configType)) { super.configType = TxEmulatorConfig.MAINNET; - // log.info("Using default TxEmulator Config - MAINNET"); } String configBoc = ""; @@ -103,7 +103,12 @@ public TxEmulator build() { super.txEmulatorI.transaction_emulator_create( configBoc, super.verbosityLevel.ordinal()); - super.txEmulatorI.emulator_set_verbosity_level(super.txEmulator, 0); + super.txEmulatorI.emulator_set_verbosity_level( + super.txEmulator, super.verbosityLevel.ordinal()); + + if (super.verbosityLevel == TxVerbosityLevel.WITH_ALL_STACK_VALUES) { + super.txEmulatorI.transaction_emulator_set_debug_enabled(super.txEmulator, true); + } if (super.txEmulator == 0) { throw new Error("Can't create tx emulator instance");