Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DI POC #2108

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft

DI POC #2108

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 129 additions & 0 deletions rskj-core/src/main/java/co/rsk/Injector.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*
* This file is part of RskJ
* Copyright (C) 2022 RSK Labs Ltd.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package co.rsk;

import com.google.common.annotations.VisibleForTesting;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;

public class Injector {

private static Injector instance;

private final RskContext rskContext;

private final Map<Class<?>, Object> dependencies;

private Injector(RskContext rskContext) {
if (rskContext == null) {
throw new IllegalStateException("RskContext must not be null");
}
this.rskContext = rskContext;
this.dependencies = new HashMap<>();
}

/**
* Creates a singleton instance for the Injector.<br>
* Should be called whenever {@code RskContext} gets instantiated.
*
* @param rskContext {@code RskContext} to get dependencies from
*/
public static void init(RskContext rskContext) {
if (instance != null) {
throw new IllegalStateException("Already initialised Injector");
}

instance = new Injector(rskContext);
}

/**
* Closes the Injector instance, dependencies can no longer be retrieved after this method gets called.<br>
* Should be called when {@code RskContext} gets closed.
*/
public static void close() {
instance = null;
}

/**
* Gets an implementation of the requested {@code clazz} from {@code RskContext} defined singletons.
* Every retrieved dependency is stored on a cache for future requests.<br><br>
* init() should be called before calling this method
*
* @param clazz Interface or Class for which to get a singleton instance from {@code RskContext}
*
* @return An instance of the requested {@code clazz}
*
* @throws IllegalStateException if no singleton instance in {@code RskContext} matches requested {@code clazz} or
* any error occurs searching it
*/
public static <T> T getService(Class<T> clazz) {
checkInitialised();

T dep = (T) instance.dependencies.get(clazz);
if (dep != null) {
return dep;
}

for (Method method : instance.rskContext.getClass().getMethods()) {
if (Modifier.isPublic(method.getModifiers()) && method.getReturnType().isAssignableFrom(clazz)) {
try {
// can return null, but that's ok
dep = (T) method.invoke(instance.rskContext);
instance.dependencies.put(clazz, dep);
return dep;
} catch (InvocationTargetException | IllegalAccessException e) {
throw new IllegalStateException(getErrorMessage(clazz.toString(), "error"), e);
}
}
}

throw new IllegalStateException(getErrorMessage(clazz.toString(), "not found"));
}

/**
* This method is for testing purposes, and it should only be called from tests.<br>
* It forces a dependency to be stored in the injector cache, regardless it was already contained or not
* and regardless it exists on {@code RskContext}.<br>
* It will be the dependency returned by the injector after this call.
*
* @param clazz Interface or Class for which to store the given dependency {@code dependency}
* @param dependency Implementation of {@code clazz} to store into the injector's cache
*/
@VisibleForTesting
public static <T> void forceDependency(Class<T> clazz, T dependency) {
checkInitialised();
instance.dependencies.put(clazz, dependency);
}

private static void checkInitialised() {
if (instance == null) {
throw new IllegalStateException("Injector not yet initialized");
}
}

private static String getErrorMessage(String className, String extra) {
String additional = extra != null ? String.format(",%s", extra) : "";
return String.format("Could not get dependency %s%s", className, additional);
}

}
6 changes: 5 additions & 1 deletion rskj-core/src/main/java/co/rsk/RskContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,9 @@ public RskContext(String[] args, boolean ignoreUnmatchedArgs) {
NodeCliFlags.class,
ignoreUnmatchedArgs
).parse(args));

Injector.close(); // new RskContext = new injector
Injector.init(this);
}

private RskContext(CliArgs<NodeCliOptions, NodeCliFlags> cliArgs) {
Expand Down Expand Up @@ -394,7 +397,6 @@ public synchronized TransactionPool getTransactionPool() {
getReceivedTxSignatureCache(),
rskSystemProperties.txOutdatedThreshold(),
rskSystemProperties.txOutdatedTimeout(),
getTxQuotaChecker(),
getGasPriceTracker());
}

Expand Down Expand Up @@ -1200,6 +1202,8 @@ public synchronized void close() {

closed = true;

Injector.close();

// as RskContext creates PeerExplorer and manages its lifecycle, dispose it here
if (peerExplorer != null) {
peerExplorer.dispose();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*/
package co.rsk.core.bc;

import co.rsk.Injector;
import co.rsk.config.RskSystemProperties;
import co.rsk.core.Coin;
import co.rsk.core.TransactionExecutorFactory;
Expand Down Expand Up @@ -76,12 +77,12 @@ public class TransactionPoolImpl implements TransactionPool {

private final TxPendingValidator validator;

private final TxQuotaChecker quotaChecker;
private final TxQuotaChecker quotaChecker = Injector.getService(TxQuotaChecker.class);

private final GasPriceTracker gasPriceTracker;

@java.lang.SuppressWarnings("squid:S107")
public TransactionPoolImpl(RskSystemProperties config, RepositoryLocator repositoryLocator, BlockStore blockStore, BlockFactory blockFactory, EthereumListener listener, TransactionExecutorFactory transactionExecutorFactory, SignatureCache signatureCache, int outdatedThreshold, int outdatedTimeout, TxQuotaChecker txQuotaChecker, GasPriceTracker gasPriceTracker) {
public TransactionPoolImpl(RskSystemProperties config, RepositoryLocator repositoryLocator, BlockStore blockStore, BlockFactory blockFactory, EthereumListener listener, TransactionExecutorFactory transactionExecutorFactory, SignatureCache signatureCache, int outdatedThreshold, int outdatedTimeout, GasPriceTracker gasPriceTracker) {
this.config = config;
this.blockStore = blockStore;
this.repositoryLocator = repositoryLocator;
Expand All @@ -91,7 +92,6 @@ public TransactionPoolImpl(RskSystemProperties config, RepositoryLocator reposit
this.signatureCache = signatureCache;
this.outdatedThreshold = outdatedThreshold;
this.outdatedTimeout = outdatedTimeout;
this.quotaChecker = txQuotaChecker;
this.gasPriceTracker = gasPriceTracker;

pendingTransactions = new TransactionSet(this.signatureCache);
Expand Down
7 changes: 7 additions & 0 deletions rskj-core/src/test/java/co/rsk/cli/tools/CliToolsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,10 @@
import org.ethereum.db.ReceiptStore;
import org.ethereum.db.ReceiptStoreImpl;
import org.ethereum.util.ByteUtil;
import org.ethereum.util.TestInjectorUtil;
import org.hamcrest.MatcherAssert;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.mockito.ArgumentCaptor;
Expand Down Expand Up @@ -86,6 +88,11 @@
*/
class CliToolsTest {

@BeforeEach
public void setUp() {
TestInjectorUtil.initEmpty();
}

@TempDir
private Path tempDir;

Expand Down
7 changes: 7 additions & 0 deletions rskj-core/src/test/java/co/rsk/core/CallContractTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@
import co.rsk.test.World;
import co.rsk.test.builders.AccountBuilder;
import org.ethereum.core.*;
import org.ethereum.util.TestInjectorUtil;
import org.ethereum.vm.PrecompiledContracts;
import org.ethereum.vm.program.ProgramResult;
import org.ethereum.vm.program.invoke.ProgramInvokeFactoryImpl;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.math.BigInteger;
Expand All @@ -37,6 +39,11 @@
*/
class CallContractTest {

@BeforeEach
public void setUp() {
TestInjectorUtil.initEmpty();
}

private static final TestSystemProperties config = new TestSystemProperties();
private static final BlockFactory blockFactory = new BlockFactory(config.getActivationConfig());

Expand Down
7 changes: 7 additions & 0 deletions rskj-core/src/test/java/co/rsk/core/TransactionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@
import org.ethereum.util.ByteUtil;
import org.ethereum.util.RLP;
import org.ethereum.util.RLPList;
import org.ethereum.util.TestInjectorUtil;
import org.ethereum.vm.PrecompiledContracts;
import org.ethereum.vm.program.ProgramResult;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.math.BigInteger;
Expand All @@ -50,6 +52,11 @@ class TransactionTest {
private final byte chainId = config.getNetworkConstants().getChainId();
private final BlockFactory blockFactory = new BlockFactory(config.getActivationConfig());

@BeforeEach
public void setUp() {
TestInjectorUtil.initEmpty();
}

@Test /* achieve public key of the sender */
void test2() throws Exception {
if (chainId != 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

package co.rsk.core.bc;

import co.rsk.Injector;
import co.rsk.blockchain.utils.BlockGenerator;
import co.rsk.config.RskSystemProperties;
import co.rsk.core.Coin;
Expand All @@ -26,7 +27,6 @@
import co.rsk.net.handler.quota.TxQuotaChecker;
import co.rsk.remasc.RemascTransaction;
import co.rsk.test.builders.BlockBuilder;
import org.ethereum.TestUtils;
import org.ethereum.core.*;
import org.ethereum.core.genesis.GenesisLoader;
import org.ethereum.listener.GasPriceTracker;
Expand Down Expand Up @@ -87,6 +87,10 @@ protected RepositoryLocator buildRepositoryLocator() {
RskSystemProperties rskSystemProperties = spy(rskTestContext.getRskSystemProperties());
when(rskSystemProperties.isAccountTxRateLimitEnabled()).thenReturn(true);

quotaChecker = mock(TxQuotaChecker.class);
when(quotaChecker.acceptTx(any(), any(), any())).thenReturn(true);
Injector.forceDependency(TxQuotaChecker.class, quotaChecker);

transactionPool = new TransactionPoolImpl(
rskSystemProperties,
repositoryLocator,
Expand All @@ -97,13 +101,8 @@ protected RepositoryLocator buildRepositoryLocator() {
signatureCache,
10,
100,
Mockito.mock(TxQuotaChecker.class),
Mockito.mock(GasPriceTracker.class));

quotaChecker = mock(TxQuotaChecker.class);
when(quotaChecker.acceptTx(any(), any(), any())).thenReturn(true);
TestUtils.setInternalState(transactionPool, "quotaChecker", quotaChecker);

// don't call start to avoid creating threads
transactionPool.processBest(blockChain.getBestBlock());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,21 @@
import org.ethereum.core.Blockchain;
import org.ethereum.datasource.HashMapDB;
import org.ethereum.datasource.KeyValueDataSource;
import org.ethereum.util.TestInjectorUtil;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

/**
* Created by ajlopez on 29/09/2020.
*/
class BlocksBloomProcessorTest {

@BeforeEach
public void setUp() {
TestInjectorUtil.initEmpty();
}

@Test
void noBlocksBloomInProcessAtTheBeginning() {
KeyValueDataSource dataSource = new HashMapDB();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,22 @@
import org.ethereum.datasource.HashMapDB;
import org.ethereum.datasource.KeyValueDataSource;
import org.ethereum.listener.CompositeEthereumListener;
import org.ethereum.util.TestInjectorUtil;
import org.ethereum.vm.DataWord;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

/**
* Created by ajlopez on 29/09/2020.
*/
class BlocksBloomServiceTest {

@BeforeEach
public void setUp() {
TestInjectorUtil.initEmpty();
}

@Test
void processFirstRange() {
World world = new World();
Expand Down
1 change: 0 additions & 1 deletion rskj-core/src/test/java/co/rsk/mine/MinerServerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,6 @@ protected RepositoryLocator buildRepositoryLocator() {
signatureCache,
10,
100,
mock(TxQuotaChecker.class),
mock(GasPriceTracker.class));

transactionPool.processBest(standardBlockchain.getBestBlock());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ void sendTransactionMustNotBeMined() {
BlockStore blockStore = world.getBlockStore();

TransactionPool transactionPool = new TransactionPoolImpl(config, repositoryLocator, blockStore, blockFactory, null, buildTransactionExecutorFactory(blockStore, null, world.getBlockTxSignatureCache()),
world.getReceivedTxSignatureCache(), 10, 100, Mockito.mock(TxQuotaChecker.class), Mockito.mock(GasPriceTracker.class));
world.getReceivedTxSignatureCache(), 10, 100, Mockito.mock(GasPriceTracker.class));
TransactionGateway transactionGateway = new TransactionGateway(new SimpleChannelManager(), transactionPool);

Web3Impl web3 = createEnvironment(blockchain, null, trieStore, transactionPool, blockStore, false, world.getBlockTxSignatureCache(), transactionGateway);
Expand All @@ -127,7 +127,7 @@ void sendTransactionMustBeMined() {
BlockStore blockStore = world.getBlockStore();

TransactionPool transactionPool = new TransactionPoolImpl(config, repositoryLocator, blockStore, blockFactory, null, buildTransactionExecutorFactory(blockStore, null, world.getBlockTxSignatureCache()),
world.getReceivedTxSignatureCache(), 10, 100, Mockito.mock(TxQuotaChecker.class), Mockito.mock(GasPriceTracker.class));
world.getReceivedTxSignatureCache(), 10, 100, Mockito.mock(GasPriceTracker.class));
TransactionGateway transactionGateway = new TransactionGateway(new SimpleChannelManager(), transactionPool);


Expand Down Expand Up @@ -192,7 +192,7 @@ void sendRawTransactionWithAutoMining() throws Exception {
BlockStore blockStore = world.getBlockStore();

TransactionPool transactionPool = new TransactionPoolImpl(config, repositoryLocator, blockStore, blockFactory, null, buildTransactionExecutorFactory(blockStore, receiptStore, world.getBlockTxSignatureCache()),
world.getReceivedTxSignatureCache(), 10, 100, Mockito.mock(TxQuotaChecker.class), Mockito.mock(GasPriceTracker.class));
world.getReceivedTxSignatureCache(), 10, 100, Mockito.mock(GasPriceTracker.class));
TransactionGateway transactionGateway = new TransactionGateway(new SimpleChannelManager(), transactionPool);

Web3Impl web3 = createEnvironment(blockchain, receiptStore, trieStore, transactionPool, blockStore, true, world.getBlockTxSignatureCache(), transactionGateway);
Expand Down Expand Up @@ -221,7 +221,7 @@ void sendRawTransactionWithoutAutoMining() {
BlockStore blockStore = world.getBlockStore();

TransactionPool transactionPool = new TransactionPoolImpl(config, repositoryLocator, blockStore, blockFactory, null, buildTransactionExecutorFactory(blockStore, receiptStore, world.getBlockTxSignatureCache()),
world.getReceivedTxSignatureCache(), 10, 100, Mockito.mock(TxQuotaChecker.class), Mockito.mock(GasPriceTracker.class));
world.getReceivedTxSignatureCache(), 10, 100, Mockito.mock(GasPriceTracker.class));
TransactionGateway transactionGateway = new TransactionGateway(new SimpleChannelManager(), transactionPool);

Web3Impl web3 = createEnvironment(blockchain, receiptStore, trieStore, transactionPool, blockStore, false, world.getBlockTxSignatureCache(), transactionGateway);
Expand Down Expand Up @@ -258,7 +258,6 @@ void testGasEstimation() {
receivedTxSignatureCache,
10,
100,
Mockito.mock(TxQuotaChecker.class),
Mockito.mock(GasPriceTracker.class)
);
TransactionGateway transactionGateway = new TransactionGateway(new SimpleChannelManager(), transactionPool);
Expand Down
Loading