diff --git a/rskj-core/src/main/java/co/rsk/RskContext.java b/rskj-core/src/main/java/co/rsk/RskContext.java index 8a80eec9deb..992f64395e6 100644 --- a/rskj-core/src/main/java/co/rsk/RskContext.java +++ b/rskj-core/src/main/java/co/rsk/RskContext.java @@ -1869,7 +1869,7 @@ private EthModuleWallet getEthModuleWallet() { if (wallet == null) { ethModuleWallet = new EthModuleWalletDisabled(); } else { - ethModuleWallet = new EthModuleWalletEnabled(wallet); + ethModuleWallet = new EthModuleWalletEnabled(wallet, getTransactionPool(), getReceivedTxSignatureCache()); } } diff --git a/rskj-core/src/main/java/co/rsk/rpc/Web3EthModule.java b/rskj-core/src/main/java/co/rsk/rpc/Web3EthModule.java index f4e7df6e256..71e50475bb7 100644 --- a/rskj-core/src/main/java/co/rsk/rpc/Web3EthModule.java +++ b/rskj-core/src/main/java/co/rsk/rpc/Web3EthModule.java @@ -121,6 +121,8 @@ default String eth_sendTransaction(CallArgumentsParam args) { TransactionResultDTO eth_getTransactionByBlockNumberAndIndex(BlockIdentifierParam bnOrId, HexIndexParam index) throws Exception; TransactionReceiptDTO eth_getTransactionReceipt(TxHashParam transactionHash) throws Exception; + TransactionResultDTO[] eth_pendingTransactions() throws Exception; + BlockResultDTO eth_getUncleByBlockHashAndIndex(BlockHashParam blockHash, HexIndexParam uncleIdx) throws Exception; diff --git a/rskj-core/src/main/java/co/rsk/rpc/Web3InformationRetriever.java b/rskj-core/src/main/java/co/rsk/rpc/Web3InformationRetriever.java index 328b8be17c1..40af9360099 100644 --- a/rskj-core/src/main/java/co/rsk/rpc/Web3InformationRetriever.java +++ b/rskj-core/src/main/java/co/rsk/rpc/Web3InformationRetriever.java @@ -47,7 +47,7 @@ public class Web3InformationRetriever { private static final String EARLIEST = "earliest"; private static final String LATEST = "latest"; - private static final String PENDING = "pending"; + public static final String PENDING = "pending"; private final TransactionPool transactionPool; private final Blockchain blockchain; diff --git a/rskj-core/src/main/java/co/rsk/rpc/modules/eth/EthModule.java b/rskj-core/src/main/java/co/rsk/rpc/modules/eth/EthModule.java index 4c6c02f1c2f..3602f3fa400 100644 --- a/rskj-core/src/main/java/co/rsk/rpc/modules/eth/EthModule.java +++ b/rskj-core/src/main/java/co/rsk/rpc/modules/eth/EthModule.java @@ -51,9 +51,7 @@ import javax.annotation.Nonnull; import java.io.IOException; -import java.util.Arrays; -import java.util.Map; -import java.util.Optional; +import java.util.*; import static java.util.Arrays.copyOfRange; import static org.ethereum.rpc.exception.RskJsonRpcRequestException.invalidParamError; @@ -189,6 +187,10 @@ public String estimateGas(CallArgumentsParam args, @Nonnull BlockIdentifierParam } } + public List ethPendingTransactions() { + return ethModuleWallet.ethPendingTransactions(); + } + protected String internalEstimateGas(ProgramResult reversibleExecutionResult) { long estimatedGas = reversibleExecutionResult.getMovedRemainingGasToChild() ? reversibleExecutionResult.getGasUsed() + reversibleExecutionResult.getDeductedRefund() : diff --git a/rskj-core/src/main/java/co/rsk/rpc/modules/eth/EthModuleWallet.java b/rskj-core/src/main/java/co/rsk/rpc/modules/eth/EthModuleWallet.java index aaf90d7ff35..fdc363ddafc 100644 --- a/rskj-core/src/main/java/co/rsk/rpc/modules/eth/EthModuleWallet.java +++ b/rskj-core/src/main/java/co/rsk/rpc/modules/eth/EthModuleWallet.java @@ -18,9 +18,15 @@ package co.rsk.rpc.modules.eth; +import org.ethereum.core.Transaction; + +import java.util.List; + public interface EthModuleWallet { String[] accounts(); String sign(String addr, String data); + + List ethPendingTransactions(); } \ No newline at end of file diff --git a/rskj-core/src/main/java/co/rsk/rpc/modules/eth/EthModuleWalletDisabled.java b/rskj-core/src/main/java/co/rsk/rpc/modules/eth/EthModuleWalletDisabled.java index 4cf03242cd2..6a70c38f03c 100644 --- a/rskj-core/src/main/java/co/rsk/rpc/modules/eth/EthModuleWalletDisabled.java +++ b/rskj-core/src/main/java/co/rsk/rpc/modules/eth/EthModuleWalletDisabled.java @@ -18,10 +18,13 @@ package co.rsk.rpc.modules.eth; +import org.ethereum.core.Transaction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Arrays; +import java.util.Collections; +import java.util.List; import static org.ethereum.rpc.exception.RskJsonRpcRequestException.invalidParamError; @@ -36,6 +39,13 @@ public String[] accounts() { return accounts; } + @Override + public List ethPendingTransactions() { + List transactions = Collections.emptyList(); + LOGGER.debug("eth_pendingTransactions(): {}", transactions); + return transactions; + } + @Override public String sign(String addr, String data) { LOGGER.debug("eth_sign({}, {}): {}", addr, data, null); diff --git a/rskj-core/src/main/java/co/rsk/rpc/modules/eth/EthModuleWalletEnabled.java b/rskj-core/src/main/java/co/rsk/rpc/modules/eth/EthModuleWalletEnabled.java index 7ac52b5e3a1..006896464df 100644 --- a/rskj-core/src/main/java/co/rsk/rpc/modules/eth/EthModuleWalletEnabled.java +++ b/rskj-core/src/main/java/co/rsk/rpc/modules/eth/EthModuleWalletEnabled.java @@ -22,10 +22,12 @@ import java.nio.charset.StandardCharsets; import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; import co.rsk.core.RskAddress; import org.bouncycastle.util.BigIntegers; -import org.ethereum.core.Account; +import org.ethereum.core.*; import org.ethereum.crypto.ECKey; import org.ethereum.crypto.HashUtil; import org.ethereum.crypto.signature.ECDSASignature; @@ -39,11 +41,14 @@ public class EthModuleWalletEnabled implements EthModuleWallet { private static final Logger LOGGER = LoggerFactory.getLogger("web3"); - private final Wallet wallet; + private final TransactionPool transactionPool; + private final SignatureCache signatureCache; - public EthModuleWalletEnabled(Wallet wallet) { + public EthModuleWalletEnabled(Wallet wallet, TransactionPool transactionPool, SignatureCache signatureCache) { this.wallet = wallet; + this.transactionPool = transactionPool; + this.signatureCache = signatureCache; } @Override @@ -90,4 +95,12 @@ private String sign(String data, ECKey ecKey) { new byte[]{signature.getV()} )); } + @Override + public List ethPendingTransactions() { + List pendingTxs = transactionPool.getPendingTransactions(); + List managedAccounts = Arrays.asList(accounts()); + return pendingTxs.stream() + .filter(tx -> managedAccounts.contains(tx.getSender(signatureCache).toJsonString())) + .collect(Collectors.toList()); + } } \ No newline at end of file diff --git a/rskj-core/src/main/java/org/ethereum/rpc/Web3Impl.java b/rskj-core/src/main/java/org/ethereum/rpc/Web3Impl.java index b0e56b7c691..c43896fbb59 100644 --- a/rskj-core/src/main/java/org/ethereum/rpc/Web3Impl.java +++ b/rskj-core/src/main/java/org/ethereum/rpc/Web3Impl.java @@ -852,6 +852,13 @@ public TransactionReceiptDTO eth_getTransactionReceipt(TxHashParam transactionHa return new TransactionReceiptDTO(block, txInfo, signatureCache); } + @Override + public TransactionResultDTO[] eth_pendingTransactions() { + return ethModule.ethPendingTransactions().stream() + .map(tx -> new TransactionResultDTO(null, null, tx, config.rpcZeroSignatureIfRemasc(), signatureCache)) + .toArray(TransactionResultDTO[]::new); + } + @Override public BlockResultDTO eth_getUncleByBlockHashAndIndex(BlockHashParam blockHash, HexIndexParam uncleIdx) { BlockResultDTO s = null; diff --git a/rskj-core/src/test/java/co/rsk/mine/TransactionModuleTest.java b/rskj-core/src/test/java/co/rsk/mine/TransactionModuleTest.java index 8e1aff4e871..1d2f6641773 100644 --- a/rskj-core/src/test/java/co/rsk/mine/TransactionModuleTest.java +++ b/rskj-core/src/test/java/co/rsk/mine/TransactionModuleTest.java @@ -643,7 +643,7 @@ private Web3Impl internalCreateEnvironment(Blockchain blockchain, EthModule ethModule = new EthModule( config.getNetworkConstants().getBridgeConstants(), config.getNetworkConstants().getChainId(), blockchain, transactionPool, reversibleTransactionExecutor1, new ExecutionBlockRetriever(blockchain, null, null), - repositoryLocator, new EthModuleWalletEnabled(wallet), transactionModule, + repositoryLocator, new EthModuleWalletEnabled(wallet, transactionPool, signatureCache), transactionModule, new BridgeSupportFactory( btcBlockStoreFactory, config.getNetworkConstants().getBridgeConstants(), config.getActivationConfig(), signatureCache), diff --git a/rskj-core/src/test/java/co/rsk/rpc/modules/eth/EthModuleTest.java b/rskj-core/src/test/java/co/rsk/rpc/modules/eth/EthModuleTest.java index e2e030935a0..b8b740adec1 100644 --- a/rskj-core/src/test/java/co/rsk/rpc/modules/eth/EthModuleTest.java +++ b/rskj-core/src/test/java/co/rsk/rpc/modules/eth/EthModuleTest.java @@ -19,10 +19,12 @@ import co.rsk.config.BridgeConstants; import co.rsk.config.TestSystemProperties; +import co.rsk.core.Coin; import co.rsk.core.ReversibleTransactionExecutor; import co.rsk.core.RskAddress; import co.rsk.core.Wallet; import co.rsk.core.bc.PendingState; +import co.rsk.crypto.Keccak256; import co.rsk.db.RepositoryLocator; import co.rsk.net.TransactionGateway; import co.rsk.peg.BridgeSupportFactory; @@ -33,6 +35,7 @@ import org.ethereum.config.Constants; import org.ethereum.core.*; import org.ethereum.crypto.ECKey; +import org.ethereum.crypto.signature.ECDSASignature; import org.ethereum.datasource.HashMapDB; import org.ethereum.rpc.CallArguments; import org.ethereum.rpc.exception.RskJsonRpcRequestException; @@ -49,6 +52,11 @@ import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; +import java.math.BigInteger; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.*; @@ -731,4 +739,160 @@ void whenExecuteSendTransactionWithInputAndDataParameters_callExecutorWithInput( .receiveTransaction(transactionCaptor.capture()); assertArrayEquals(Hex.decode(expectedDataValue), transactionCaptor.getValue().getData()); } + + @Test + void testEthPendingTransactionsWithNoTransactions() { + EthModuleWallet ethModuleWalletMock = mock(EthModuleWallet.class); + TransactionPool transactionPoolMock = mock(TransactionPool.class); + when(transactionPoolMock.getPendingTransactions()).thenReturn(Collections.emptyList()); + + Block block = mock(Block.class); + ExecutionBlockRetriever.Result blockResult = mock(ExecutionBlockRetriever.Result.class); + when(blockResult.getBlock()).thenReturn(block); + ExecutionBlockRetriever retriever = mock(ExecutionBlockRetriever.class); + Blockchain blockchain = mock(Blockchain.class); + + ReversibleTransactionExecutor reversibleTransactionExecutor = mock(ReversibleTransactionExecutor.class); + + EthModule ethModule = new EthModule( + null, + Constants.REGTEST_CHAIN_ID, + blockchain, + transactionPoolMock, + reversibleTransactionExecutor, + retriever, + mock(RepositoryLocator.class), + ethModuleWalletMock, + null, + new BridgeSupportFactory( + null, null, null, signatureCache), + config.getGasEstimationCap(), + config.getCallGasCap()); + + List result = ethModule.ethPendingTransactions(); + + assertTrue(result.isEmpty(), "Expected no transactions"); + } + + @Test + void pendingTransactionsWithMultipleManagedAccounts() { + Wallet wallet = mock(Wallet.class); + TransactionPool transactionPoolMock = mock(TransactionPool.class); + EthModuleWalletEnabled ethModuleWallet = new EthModuleWalletEnabled(wallet, transactionPoolMock, signatureCache); + ExecutionBlockRetriever retriever = mock(ExecutionBlockRetriever.class); + Blockchain blockchain = mock(Blockchain.class); + ReversibleTransactionExecutor reversibleTransactionExecutor = mock(ReversibleTransactionExecutor.class); + + EthModule ethModule = new EthModule( + null, + Constants.REGTEST_CHAIN_ID, + blockchain, + transactionPoolMock, + reversibleTransactionExecutor, + retriever, + mock(RepositoryLocator.class), + ethModuleWallet, + null, + new BridgeSupportFactory(null, null, null, signatureCache), + config.getGasEstimationCap(), + config.getCallGasCap()); + + Transaction mockTransaction1 = createMockTransaction("0x63a15ed8c3b83efc744f2e0a7824a00846c21860"); + Transaction mockTransaction2 = createMockTransaction( "0xa3a15ed8c3b83efc744f2e0a7824a00846c21860"); + Transaction mockTransaction3 = createMockTransaction( "0xb3a15ed8c3b83efc744f2e0a7824a00846c21860"); + List allTransactions = Arrays.asList(mockTransaction1, mockTransaction2, mockTransaction3); + + when(transactionPoolMock.getPendingTransactions()).thenReturn(allTransactions); + when(ethModuleWallet.accounts()).thenReturn(new String[]{"0x63a15ed8c3b83efc744f2e0a7824a00846c21860", "0xa3a15ed8c3b83efc744f2e0a7824a00846c21860"}); + + List result = ethModule.ethPendingTransactions(); + + assertEquals(2, result.size(), "Expected only transactions from managed accounts"); + } + + @Test + void pendingTransactionsWithNoManagedAccounts() { + Wallet wallet = mock(Wallet.class); + TransactionPool transactionPoolMock = mock(TransactionPool.class); + EthModuleWalletEnabled ethModuleWallet = new EthModuleWalletEnabled(wallet, transactionPoolMock, signatureCache); + ExecutionBlockRetriever retriever = mock(ExecutionBlockRetriever.class); + Blockchain blockchain = mock(Blockchain.class); + ReversibleTransactionExecutor reversibleTransactionExecutor = mock(ReversibleTransactionExecutor.class); + + EthModule ethModule = new EthModule( + null, + Constants.REGTEST_CHAIN_ID, + blockchain, + transactionPoolMock, + reversibleTransactionExecutor, + retriever, + mock(RepositoryLocator.class), + ethModuleWallet, + null, + new BridgeSupportFactory(null, null, null, signatureCache), + config.getGasEstimationCap(), + config.getCallGasCap()); + + Transaction mockTransaction1 = createMockTransaction("0x63a15ed8c3b83efc744f2e0a7824a00846c21860"); + Transaction mockTransaction2 = createMockTransaction("0x13a15ed8c3b83efc744f2e0a7824a00846c21860"); + List allTransactions = Arrays.asList(mockTransaction1, mockTransaction2); + + when(transactionPoolMock.getPendingTransactions()).thenReturn(allTransactions); + when(ethModuleWallet.accounts()).thenReturn(new String[]{}); + + List result = ethModule.ethPendingTransactions(); + + assertTrue(result.isEmpty(), "Expected no transactions as there are no managed accounts"); + } + + @Test + void pendingTransactionsWithWalletDisabled() { + TransactionPool transactionPoolMock = mock(TransactionPool.class); + EthModuleWalletDisabled ethModuleWallet = new EthModuleWalletDisabled(); + ExecutionBlockRetriever retriever = mock(ExecutionBlockRetriever.class); + Blockchain blockchain = mock(Blockchain.class); + ReversibleTransactionExecutor reversibleTransactionExecutor = mock(ReversibleTransactionExecutor.class); + + EthModule ethModule = new EthModule( + null, + Constants.REGTEST_CHAIN_ID, + blockchain, + transactionPoolMock, + reversibleTransactionExecutor, + retriever, + mock(RepositoryLocator.class), + ethModuleWallet, + null, + new BridgeSupportFactory(null, null, null, signatureCache), + config.getGasEstimationCap(), + config.getCallGasCap()); + + List result = ethModule.ethPendingTransactions(); + + assertTrue(result.isEmpty(), "Expected no transactions as wallet is disabled"); + } + + + private Transaction createMockTransaction(String fromAddress) { + Transaction transaction = mock(Transaction.class); + RskAddress address = new RskAddress(fromAddress); + System.out.println("mock address: " + address); + when(transaction.getSender(any(SignatureCache.class))).thenReturn(address); + + byte[] mockHashBytes = new byte[32]; + Arrays.fill(mockHashBytes, (byte) 1); + Keccak256 mockHash = new Keccak256(mockHashBytes); + when(transaction.getHash()).thenReturn(mockHash); + when(transaction.getReceiveAddress()).thenReturn(address); + when(transaction.getNonce()).thenReturn(BigInteger.ZERO.toByteArray()); + when(transaction.getGasLimit()).thenReturn(BigInteger.valueOf(21000).toByteArray()); + when(transaction.getGasPrice()).thenReturn(Coin.valueOf(50_000_000_000L)); + when(transaction.getValue()).thenReturn(Coin.ZERO); + when(transaction.getData()).thenReturn(new byte[0]); + ECDSASignature mockSignature = new ECDSASignature(BigInteger.ONE, BigInteger.ONE); + when(transaction.getSignature()).thenReturn(mockSignature); + when(transaction.getEncodedV()).thenReturn((byte) 1); + + return transaction; + } } diff --git a/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplLogsTest.java b/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplLogsTest.java index b9cbe67c95a..6afe3b19df5 100644 --- a/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplLogsTest.java +++ b/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplLogsTest.java @@ -1092,7 +1092,7 @@ private Web3Impl createWeb3() { EthModule ethModule = new EthModule( config.getNetworkConstants().getBridgeConstants(), config.getNetworkConstants().getChainId(), blockChain, transactionPool, null, new ExecutionBlockRetriever(blockChain, null, null), - null, new EthModuleWalletEnabled(wallet), null, + null, new EthModuleWalletEnabled(wallet, transactionPool, signatureCache), null, new BridgeSupportFactory( null, config.getNetworkConstants().getBridgeConstants(), config.getActivationConfig(), new BlockTxSignatureCache(new ReceivedTxSignatureCache())), config.getGasEstimationCap(), diff --git a/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplScoringTest.java b/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplScoringTest.java index c86d0a04e9f..a31a8fc3af0 100644 --- a/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplScoringTest.java +++ b/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplScoringTest.java @@ -393,7 +393,7 @@ private static Web3Impl createWeb3(PeerScoringManager peerScoringManager) { EthModule em = new EthModule( config.getNetworkConstants().getBridgeConstants(), config.getNetworkConstants().getChainId(), world.getBlockChain(), null, null, new ExecutionBlockRetriever(world.getBlockChain(), null, null), - null, new EthModuleWalletEnabled(wallet), null, + null, new EthModuleWalletEnabled(wallet, world.getTransactionPool(), world.getBlockTxSignatureCache()), null, new BridgeSupportFactory( null, config.getNetworkConstants().getBridgeConstants(), config.getActivationConfig(), new BlockTxSignatureCache(new ReceivedTxSignatureCache())), config.getGasEstimationCap(), diff --git a/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplTest.java b/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplTest.java index ecbf44231ec..93972addb83 100644 --- a/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplTest.java +++ b/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplTest.java @@ -26,6 +26,8 @@ import co.rsk.core.bc.TransactionPoolImpl; import co.rsk.crypto.Keccak256; import co.rsk.db.RepositoryLocator; +import co.rsk.logfilter.BlocksBloomStore; +import co.rsk.metrics.HashRateCalculator; import co.rsk.mine.MinerClient; import co.rsk.mine.MinerServer; import co.rsk.net.BlockProcessor; @@ -42,13 +44,17 @@ import co.rsk.rpc.modules.eth.EthModule; import co.rsk.rpc.modules.eth.EthModuleTransactionBase; import co.rsk.rpc.modules.eth.EthModuleWalletEnabled; +import co.rsk.rpc.modules.evm.EvmModule; +import co.rsk.rpc.modules.mnr.MnrModule; import co.rsk.rpc.modules.personal.PersonalModule; import co.rsk.rpc.modules.personal.PersonalModuleWalletDisabled; import co.rsk.rpc.modules.personal.PersonalModuleWalletEnabled; import co.rsk.rpc.modules.rsk.RskModule; import co.rsk.rpc.modules.rsk.RskModuleImpl; +import co.rsk.rpc.modules.trace.TraceModule; import co.rsk.rpc.modules.txpool.TxPoolModule; import co.rsk.rpc.modules.txpool.TxPoolModuleImpl; +import co.rsk.scoring.PeerScoringManager; import co.rsk.test.World; import co.rsk.test.builders.AccountBuilder; import co.rsk.test.builders.BlockBuilder; @@ -1102,6 +1108,109 @@ void getTransactionCountByNonCanonicalBlockHash() { final ChainParams chain = createChainWithATransaction(true); assertNonCanonicalBlockHash("0x1", chain.block, blockRef -> chain.web3.eth_getTransactionCount(new HexAddressParam(chain.accountAddress), new BlockRefParam(blockRef))); } + @Test + void pendingTransactionsNoTxShouldReturnEmptyList() { + World world = new World(); + Web3Impl web3 = createWeb3(world); + + TransactionResultDTO[] tr = web3.eth_pendingTransactions(); + + List trList = Arrays.asList(tr); + + assertNotNull(trList); + assertEquals(0, trList.size()); + } + + @Test + void pendingTransactionsReturnsFilteredList() { + EthModule ethModuleMock = mock(EthModule.class); + + Web3Impl web3 = createWeb3WithMocks(ethModuleMock); + + Transaction mockTransaction1 = mockTransactionFrom("0x63a15ed8c3b83efc744f2e0a7824a00846c21860"); + Transaction mockTransaction2 = mockTransactionFrom( "0xa3a15ed8c3b83efc744f2e0a7824a00846c21860"); + Transaction mockTransaction3 = mockTransactionFrom( "0xb3a15ed8c3b83efc744f2e0a7824a00846c21860"); + List allTransactions = Arrays.asList(mockTransaction1, mockTransaction2, mockTransaction3); + + when(ethModuleMock.ethPendingTransactions()).thenReturn(allTransactions); + + TransactionResultDTO[] result = web3.eth_pendingTransactions(); + + assertEquals(3, result.length, "Expected all transactions"); + } + + @Test + void pendingTransactionsReturnsCorrectTransaction() { + EthModule ethModuleMock = mock(EthModule.class); + + Web3Impl web3 = createWeb3WithMocks(ethModuleMock); + + Transaction mockTransaction1 = mockTransactionFrom("0x63a15ed8c3b83efc744f2e0a7824a00846c21860"); + List allTransactions = Arrays.asList(mockTransaction1); + + when(ethModuleMock.ethPendingTransactions()).thenReturn(allTransactions); + + TransactionResultDTO[] result = web3.eth_pendingTransactions(); + + assertEquals(1, result.length, "Expected only one transaction"); + assertEquals("0x63a15ed8c3b83efc744f2e0a7824a00846c21860", result[0].getFrom()); + } + + private Transaction mockTransactionFrom(String senderAddress) { + Transaction transaction = mock(Transaction.class); + RskAddress address = new RskAddress(senderAddress); + when(transaction.getSender(any(SignatureCache.class))).thenReturn(address); + + byte[] mockHashBytes = new byte[32]; + Arrays.fill(mockHashBytes, (byte) 1); + Keccak256 mockHash = new Keccak256(mockHashBytes); + when(transaction.getHash()).thenReturn(mockHash); + when(transaction.getReceiveAddress()).thenReturn(address); + when(transaction.getNonce()).thenReturn(BigInteger.ZERO.toByteArray()); + when(transaction.getGasLimit()).thenReturn(BigInteger.valueOf(21000).toByteArray()); + when(transaction.getGasPrice()).thenReturn(Coin.valueOf(50_000_000_000L)); + when(transaction.getValue()).thenReturn(Coin.ZERO); + when(transaction.getData()).thenReturn(new byte[0]); + ECDSASignature mockSignature = new ECDSASignature(BigInteger.ONE, BigInteger.ONE); + when(transaction.getSignature()).thenReturn(mockSignature); + when(transaction.getEncodedV()).thenReturn((byte) 1); + + return transaction; + } + + private Web3Impl createWeb3WithMocks(EthModule ethModule) { + Ethereum eth = mock(Ethereum.class); + Blockchain blockchain = mock(Blockchain.class); + BlockStore blockStore = mock(BlockStore.class); + ReceiptStore receiptStore = mock(ReceiptStore.class); + MinerClient minerClient = mock(MinerClient.class); + MinerServer minerServer = mock(MinerServer.class); + EvmModule evmModule = mock(EvmModule.class); + TxPoolModule txPoolModule = mock(TxPoolModule.class); + MnrModule mnrModule = mock(MnrModule.class); + DebugModule debugModule = mock(DebugModule.class); + TraceModule traceModule = mock(TraceModule.class); + RskModule rskModule = mock(RskModule.class); + ChannelManager channelManager = mock(ChannelManager.class); + PeerScoringManager peerScoringManager = mock(PeerScoringManager.class); + PeerServer peerServer = mock(PeerServer.class); + BlockProcessor nodeBlockProcessor = mock(BlockProcessor.class); + HashRateCalculator hashRateCalculator = mock(HashRateCalculator.class); + ConfigCapabilities configCapabilities = mock(ConfigCapabilities.class); + BuildInfo buildInfo = mock(BuildInfo.class); + BlocksBloomStore blocksBloomStore = mock(BlocksBloomStore.class); + SyncProcessor syncProcessor = mock(SyncProcessor.class); + SignatureCache signatureCache = mock(SignatureCache.class); + Web3InformationRetriever web3InformationRetriever = mock(Web3InformationRetriever.class); + PersonalModule personalModule = mock(PersonalModule.class); + + // Create Web3Impl with the mocked dependencies + return new Web3Impl(eth, blockchain, blockStore, receiptStore, config, minerClient, minerServer, + personalModule, ethModule, evmModule, txPoolModule, mnrModule, debugModule, + traceModule, rskModule, channelManager, peerScoringManager, peerServer, + nodeBlockProcessor, hashRateCalculator, configCapabilities, buildInfo, + blocksBloomStore, web3InformationRetriever, syncProcessor, signatureCache); + } @Test void getBlockByNumber() { @@ -2603,7 +2712,7 @@ private Web3Impl createWeb3(SimpleEthereum eth, PeerServer peerServer) { EthModule ethModule = new EthModule( config.getNetworkConstants().getBridgeConstants(), config.getNetworkConstants().getChainId(), blockchain, transactionPool, null, new ExecutionBlockRetriever(blockchain, null, null), - null, new EthModuleWalletEnabled(wallet), null, + null, new EthModuleWalletEnabled(wallet, transactionPool, signatureCache), null, new BridgeSupportFactory( null, config.getNetworkConstants().getBridgeConstants(), config.getActivationConfig(), signatureCache), config.getGasEstimationCap(), @@ -2720,7 +2829,7 @@ private Web3Impl createWeb3( TransactionGateway transactionGateway = new TransactionGateway(new SimpleChannelManager(), transactionPool); EthModule ethModule = new EthModule( config.getNetworkConstants().getBridgeConstants(), config.getNetworkConstants().getChainId(), blockchain, transactionPool, executor, - new ExecutionBlockRetriever(blockchain, null, null), repositoryLocator, new EthModuleWalletEnabled(wallet), + new ExecutionBlockRetriever(blockchain, null, null), repositoryLocator, new EthModuleWalletEnabled(wallet, transactionPool, signatureCache), new EthModuleTransactionBase(config.getNetworkConstants(), wallet, transactionPool, transactionGateway), new BridgeSupportFactory( null, config.getNetworkConstants().getBridgeConstants(), config.getActivationConfig(), signatureCache), @@ -2784,7 +2893,7 @@ private Web3Impl createWeb3CallNoReturn( EthModule ethModule = new EthModule( config.getNetworkConstants().getBridgeConstants(), config.getNetworkConstants().getChainId(), blockchain, transactionPool, executor, new ExecutionBlockRetriever(blockchain, null, null), repositoryLocator, - new EthModuleWalletEnabled(wallet), + new EthModuleWalletEnabled(wallet, transactionPool, signatureCache), new EthModuleTransactionBase(config.getNetworkConstants(), wallet, transactionPool, null), new BridgeSupportFactory( null, config.getNetworkConstants().getBridgeConstants(), config.getActivationConfig(), signatureCache),