From b617af20ba657c15f04e4ac1b7f06ac8c0f2ba11 Mon Sep 17 00:00:00 2001 From: bladehan1 Date: Thu, 11 Jun 2026 11:47:04 +0800 Subject: [PATCH] Record internal tx for precompile value calls --- .../org/tron/core/vm/program/Program.java | 10 +++++ .../common/runtime/vm/OperationsTest.java | 44 +++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/actuator/src/main/java/org/tron/core/vm/program/Program.java b/actuator/src/main/java/org/tron/core/vm/program/Program.java index 3ed968e1af..b18a417aeb 100644 --- a/actuator/src/main/java/org/tron/core/vm/program/Program.java +++ b/actuator/src/main/java/org/tron/core/vm/program/Program.java @@ -1740,6 +1740,16 @@ public void callToPrecompiledAddress(MessageCall msg, this.refundEnergy(msg.getEnergy().longValue() - requiredEnergy, CALL_PRE_COMPILED); this.stackPushOne(); returnDataBuffer = out.getRight(); + if (endowment > 0) { + increaseNonce(); + HashMap tokenInfo = null; + if (isTokenTransfer) { + tokenInfo = new HashMap<>(); + tokenInfo.put(new String(stripLeadingZeroes(tokenId)), endowment); + } + addInternalTx(null, senderAddress, contextAddress, !isTokenTransfer ? endowment : 0, + data, "call", nonce, tokenInfo); + } deposit.commit(); } else { // spend all energy on failure, push zero and revert state changes diff --git a/framework/src/test/java/org/tron/common/runtime/vm/OperationsTest.java b/framework/src/test/java/org/tron/common/runtime/vm/OperationsTest.java index 651248bd9e..313bfa3912 100644 --- a/framework/src/test/java/org/tron/common/runtime/vm/OperationsTest.java +++ b/framework/src/test/java/org/tron/common/runtime/vm/OperationsTest.java @@ -783,11 +783,55 @@ Op.CALL, new DataWord(10000), DataWord.ZERO(), DataWord.ZERO(), DataWord.ZERO(), false); program.callToPrecompiledAddress(messageCall, new PrecompiledContracts.ECRecover()); + Assert.assertTrue(program.getResult().getInternalTransactions().isEmpty()); DecodeUtil.addressPreFixByte = prePrefixByte; VMConfig.initAllowTvmSelfdestructRestriction(0); } + @Test + public void testCallPrecompiledAddressWithValueAddsInternalTransaction() + throws ContractValidateException { + invoke = new ProgramInvokeMockImpl(); + Protocol.Transaction trx = Protocol.Transaction.getDefaultInstance(); + InternalTransaction interTrx = + new InternalTransaction(trx, InternalTransaction.TrxType.TRX_UNKNOWN_TYPE); + + byte prePrefixByte = DecodeUtil.addressPreFixByte; + DecodeUtil.addressPreFixByte = Constant.ADD_PRE_FIX_BYTE_MAINNET; + try { + program = new Program(new byte[0], new byte[0], invoke, interTrx); + DataWord precompiledAddressWord = new DataWord(4); + byte[] precompiledAddress = precompiledAddressWord.toTronAddress(); + byte[] senderAddress = invoke.getContractAddress().toTronAddress(); + long value = 123L; + + invoke.getDeposit().createAccount(precompiledAddress, Protocol.AccountType.Normal); + invoke.getDeposit().addBalance(senderAddress, 1000L); + + MessageCall messageCall = new MessageCall( + Op.CALL, new DataWord(10000), + precompiledAddressWord, new DataWord(value), + DataWord.ZERO(), DataWord.ZERO(), + DataWord.ZERO(), DataWord.ZERO(), + DataWord.ZERO(), false); + program.callToPrecompiledAddress(messageCall, new PrecompiledContracts.Identity()); + + Assert.assertEquals(1000L - value, invoke.getDeposit().getBalance(senderAddress)); + Assert.assertEquals(value, invoke.getDeposit().getBalance(precompiledAddress)); + Assert.assertEquals(1, program.getResult().getInternalTransactions().size()); + + InternalTransaction internalTx = program.getResult().getInternalTransactions().get(0); + Assert.assertFalse(internalTx.isRejected()); + Assert.assertEquals("call", internalTx.getNote()); + Assert.assertEquals(value, internalTx.getValue()); + Assert.assertArrayEquals(senderAddress, internalTx.getSender()); + Assert.assertArrayEquals(precompiledAddress, internalTx.getTransferToAddress()); + } finally { + DecodeUtil.addressPreFixByte = prePrefixByte; + } + } + // TIP-854 outer-frame containment: a CALL to validateMultiSign or // batchValidateSign with malformed calldata must (a) push 0 onto the outer // stack, (b) leave the outer frame free of any propagated exception, and