PAN-2445: Onchain account permissioning (#1507)

Signed-off-by: Adrian Sutton <adrian.sutton@consensys.net>
pull/2/head
Lucas Saldanha 6 years ago committed by GitHub
parent c7d74690f5
commit 576c12dd46
  1. 1
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetTransactionValidator.java
  2. 7
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MutableProtocolSchedule.java
  3. 4
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/ProtocolSchedule.java
  4. 5
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/ProtocolSpec.java
  5. 3
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/TransactionValidator.java
  6. 8
      ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetTransactionValidatorTest.java
  7. 18
      ethereum/permissioning/src/main/java/tech/pegasys/pantheon/ethereum/permissioning/AccountLocalConfigPermissioningController.java
  8. 19
      ethereum/permissioning/src/main/java/tech/pegasys/pantheon/ethereum/permissioning/TransactionSmartContractPermissioningController.java
  9. 52
      ethereum/permissioning/src/main/java/tech/pegasys/pantheon/ethereum/permissioning/account/AccountPermissioningController.java
  10. 4
      ethereum/permissioning/src/test/java/tech/pegasys/pantheon/ethereum/permissioning/TransactionSmartContractPermissioningControllerTest.java
  11. 5
      ethereum/permissioning/src/test/java/tech/pegasys/pantheon/ethereum/permissioning/account/AccountPermissioningControllerTest.java
  12. 32
      pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java
  13. 136
      pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java
  14. 14
      pantheon/src/test/java/tech/pegasys/pantheon/cli/CommandTestAbstract.java
  15. 93
      pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java
  16. 2
      pantheon/src/test/resources/everything_config.toml

@ -166,6 +166,7 @@ public class MainnetTransactionValidator implements TransactionValidator {
return transactionFilter.map(c -> c.permitted(transaction, isStateChange)).orElse(true); return transactionFilter.map(c -> c.permitted(transaction, isStateChange)).orElse(true);
} }
@Override
public void setTransactionFilter(final TransactionFilter transactionFilter) { public void setTransactionFilter(final TransactionFilter transactionFilter) {
this.transactionFilter = Optional.of(transactionFilter); this.transactionFilter = Optional.of(transactionFilter);
} }

@ -14,6 +14,8 @@ package tech.pegasys.pantheon.ethereum.mainnet;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import tech.pegasys.pantheon.ethereum.core.TransactionFilter;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.Comparator; import java.util.Comparator;
import java.util.NavigableSet; import java.util.NavigableSet;
@ -69,4 +71,9 @@ public class MutableProtocolSchedule<C> implements ProtocolSchedule<C> {
.map(spec -> spec.getSpec().getName() + ": " + spec.getBlock()) .map(spec -> spec.getSpec().getName() + ": " + spec.getBlock())
.collect(Collectors.joining(", ", "[", "]")); .collect(Collectors.joining(", ", "[", "]"));
} }
@Override
public void setTransactionFilter(final TransactionFilter transactionFilter) {
protocolSpecs.forEach(spec -> spec.getSpec().setTransactionFilter(transactionFilter));
}
} }

@ -12,6 +12,8 @@
*/ */
package tech.pegasys.pantheon.ethereum.mainnet; package tech.pegasys.pantheon.ethereum.mainnet;
import tech.pegasys.pantheon.ethereum.core.TransactionFilter;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.Optional; import java.util.Optional;
@ -20,4 +22,6 @@ public interface ProtocolSchedule<C> {
ProtocolSpec<C> getByBlockNumber(long number); ProtocolSpec<C> getByBlockNumber(long number);
Optional<BigInteger> getChainId(); Optional<BigInteger> getChainId();
void setTransactionFilter(TransactionFilter transactionFilter);
} }

@ -15,6 +15,7 @@ package tech.pegasys.pantheon.ethereum.mainnet;
import tech.pegasys.pantheon.ethereum.BlockValidator; import tech.pegasys.pantheon.ethereum.BlockValidator;
import tech.pegasys.pantheon.ethereum.core.BlockHeaderFunctions; import tech.pegasys.pantheon.ethereum.core.BlockHeaderFunctions;
import tech.pegasys.pantheon.ethereum.core.BlockImporter; import tech.pegasys.pantheon.ethereum.core.BlockImporter;
import tech.pegasys.pantheon.ethereum.core.TransactionFilter;
import tech.pegasys.pantheon.ethereum.core.Wei; import tech.pegasys.pantheon.ethereum.core.Wei;
import tech.pegasys.pantheon.ethereum.mainnet.MainnetBlockProcessor.TransactionReceiptFactory; import tech.pegasys.pantheon.ethereum.mainnet.MainnetBlockProcessor.TransactionReceiptFactory;
import tech.pegasys.pantheon.ethereum.vm.EVM; import tech.pegasys.pantheon.ethereum.vm.EVM;
@ -243,4 +244,8 @@ public class ProtocolSpec<C> {
public PrecompileContractRegistry getPrecompileContractRegistry() { public PrecompileContractRegistry getPrecompileContractRegistry() {
return precompileContractRegistry; return precompileContractRegistry;
} }
public void setTransactionFilter(final TransactionFilter transactionFilter) {
transactionValidator.setTransactionFilter(transactionFilter);
}
} }

@ -14,6 +14,7 @@ package tech.pegasys.pantheon.ethereum.mainnet;
import tech.pegasys.pantheon.ethereum.core.Account; import tech.pegasys.pantheon.ethereum.core.Account;
import tech.pegasys.pantheon.ethereum.core.Transaction; import tech.pegasys.pantheon.ethereum.core.Transaction;
import tech.pegasys.pantheon.ethereum.core.TransactionFilter;
/** Validates transaction based on some criteria. */ /** Validates transaction based on some criteria. */
public interface TransactionValidator { public interface TransactionValidator {
@ -54,6 +55,8 @@ public interface TransactionValidator {
ValidationResult<TransactionInvalidReason> validateForSender( ValidationResult<TransactionInvalidReason> validateForSender(
Transaction transaction, Account sender, TransactionValidationParams validationParams); Transaction transaction, Account sender, TransactionValidationParams validationParams);
void setTransactionFilter(TransactionFilter transactionFilter);
enum TransactionInvalidReason { enum TransactionInvalidReason {
WRONG_CHAIN_ID, WRONG_CHAIN_ID,
REPLAY_PROTECTED_SIGNATURES_NOT_SUPPORTED, REPLAY_PROTECTED_SIGNATURES_NOT_SUPPORTED,

@ -158,7 +158,6 @@ public class MainnetTransactionValidatorTest {
public void shouldRejectTransactionIfAccountIsNotPermitted() { public void shouldRejectTransactionIfAccountIsNotPermitted() {
final MainnetTransactionValidator validator = final MainnetTransactionValidator validator =
new MainnetTransactionValidator(gasCalculator, false, Optional.empty()); new MainnetTransactionValidator(gasCalculator, false, Optional.empty());
validator.setTransactionFilter(transactionFilter(false)); validator.setTransactionFilter(transactionFilter(false));
assertThat(validator.validateForSender(basicTransaction, accountWithNonce(0), true)) assertThat(validator.validateForSender(basicTransaction, accountWithNonce(0), true))
@ -169,7 +168,6 @@ public class MainnetTransactionValidatorTest {
public void shouldAcceptValidTransactionIfAccountIsPermitted() { public void shouldAcceptValidTransactionIfAccountIsPermitted() {
final MainnetTransactionValidator validator = final MainnetTransactionValidator validator =
new MainnetTransactionValidator(gasCalculator, false, Optional.empty()); new MainnetTransactionValidator(gasCalculator, false, Optional.empty());
validator.setTransactionFilter(transactionFilter(true)); validator.setTransactionFilter(transactionFilter(true));
assertThat(validator.validateForSender(basicTransaction, accountWithNonce(0), true)) assertThat(validator.validateForSender(basicTransaction, accountWithNonce(0), true))
@ -178,13 +176,13 @@ public class MainnetTransactionValidatorTest {
@Test @Test
public void shouldPropagateCorrectStateChangeParamToTransactionFilter() { public void shouldPropagateCorrectStateChangeParamToTransactionFilter() {
final MainnetTransactionValidator validator =
new MainnetTransactionValidator(gasCalculator, false, Optional.empty());
final ArgumentCaptor<Boolean> stateChangeParamCaptor = ArgumentCaptor.forClass(Boolean.class); final ArgumentCaptor<Boolean> stateChangeParamCaptor = ArgumentCaptor.forClass(Boolean.class);
final TransactionFilter transactionFilter = mock(TransactionFilter.class); final TransactionFilter transactionFilter = mock(TransactionFilter.class);
when(transactionFilter.permitted(any(Transaction.class), stateChangeParamCaptor.capture())) when(transactionFilter.permitted(any(Transaction.class), stateChangeParamCaptor.capture()))
.thenReturn(true); .thenReturn(true);
final MainnetTransactionValidator validator =
new MainnetTransactionValidator(gasCalculator, false, Optional.empty());
validator.setTransactionFilter(transactionFilter); validator.setTransactionFilter(transactionFilter);
final TransactionValidationParams validationParams = final TransactionValidationParams validationParams =

@ -13,6 +13,7 @@
package tech.pegasys.pantheon.ethereum.permissioning; package tech.pegasys.pantheon.ethereum.permissioning;
import tech.pegasys.pantheon.ethereum.core.Address; import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.Hash;
import tech.pegasys.pantheon.ethereum.core.Transaction; import tech.pegasys.pantheon.ethereum.core.Transaction;
import tech.pegasys.pantheon.ethereum.permissioning.account.TransactionPermissioningProvider; import tech.pegasys.pantheon.ethereum.permissioning.account.TransactionPermissioningProvider;
import tech.pegasys.pantheon.metrics.Counter; import tech.pegasys.pantheon.metrics.Counter;
@ -232,17 +233,32 @@ public class AccountLocalConfigPermissioningController implements TransactionPer
@Override @Override
public boolean isPermitted(final Transaction transaction) { public boolean isPermitted(final Transaction transaction) {
this.checkCounter.inc(); final Hash transactionHash = transaction.hash();
final Address sender = transaction.getSender(); final Address sender = transaction.getSender();
LOG.trace("Account permissioning - Local Config: Checking transaction {}", transactionHash);
this.checkCounter.inc();
if (sender == null) { if (sender == null) {
this.checkCounterUnpermitted.inc(); this.checkCounterUnpermitted.inc();
LOG.trace(
"Account permissioning - Local Config: Rejected transaction {} without sender",
transactionHash);
return false; return false;
} else { } else {
if (contains(sender.toString())) { if (contains(sender.toString())) {
this.checkCounterPermitted.inc(); this.checkCounterPermitted.inc();
LOG.trace(
"Account permissioning - Local Config: Permitted transaction {} from {}",
transactionHash,
sender);
return true; return true;
} else { } else {
this.checkCounterUnpermitted.inc(); this.checkCounterUnpermitted.inc();
LOG.trace(
"Account permissioning - Local Config: Rejected transaction {} from {}",
transactionHash,
sender);
return false; return false;
} }
} }

@ -30,12 +30,18 @@ import tech.pegasys.pantheon.util.bytes.BytesValues;
import java.util.Optional; import java.util.Optional;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/** /**
* Controller that can read from a smart contract that exposes the permissioning call * Controller that can read from a smart contract that exposes the permissioning call
* transactionAllowed(address,address,uint256,uint256,uint256,bytes) * transactionAllowed(address,address,uint256,uint256,uint256,bytes)
*/ */
public class TransactionSmartContractPermissioningController public class TransactionSmartContractPermissioningController
implements TransactionPermissioningProvider { implements TransactionPermissioningProvider {
private static final Logger LOG = LogManager.getLogger();
private final Address contractAddress; private final Address contractAddress;
private final TransactionSimulator transactionSimulator; private final TransactionSimulator transactionSimulator;
@ -101,6 +107,11 @@ public class TransactionSmartContractPermissioningController
*/ */
@Override @Override
public boolean isPermitted(final Transaction transaction) { public boolean isPermitted(final Transaction transaction) {
final tech.pegasys.pantheon.ethereum.core.Hash transactionHash = transaction.hash();
final Address sender = transaction.getSender();
LOG.trace("Account permissioning - Smart Contract : Checking transaction {}", transactionHash);
this.checkCounter.inc(); this.checkCounter.inc();
final BytesValue payload = createPayload(transaction); final BytesValue payload = createPayload(transaction);
final CallParameter callParams = final CallParameter callParams =
@ -131,9 +142,17 @@ public class TransactionSmartContractPermissioningController
if (result.map(r -> checkTransactionResult(r.getOutput())).orElse(false)) { if (result.map(r -> checkTransactionResult(r.getOutput())).orElse(false)) {
this.checkCounterPermitted.inc(); this.checkCounterPermitted.inc();
LOG.trace(
"Account permissioning - Smart Contract: Permitted transaction {} from {}",
transactionHash,
sender);
return true; return true;
} else { } else {
this.checkCounterUnpermitted.inc(); this.checkCounterUnpermitted.inc();
LOG.trace(
"Account permissioning - Smart Contract: Rejected transaction {} from {}",
transactionHash,
sender);
return false; return false;
} }
} }

@ -12,19 +12,30 @@
*/ */
package tech.pegasys.pantheon.ethereum.permissioning.account; package tech.pegasys.pantheon.ethereum.permissioning.account;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.Hash;
import tech.pegasys.pantheon.ethereum.core.Transaction; import tech.pegasys.pantheon.ethereum.core.Transaction;
import tech.pegasys.pantheon.ethereum.permissioning.AccountLocalConfigPermissioningController; import tech.pegasys.pantheon.ethereum.permissioning.AccountLocalConfigPermissioningController;
import tech.pegasys.pantheon.ethereum.permissioning.TransactionSmartContractPermissioningController; import tech.pegasys.pantheon.ethereum.permissioning.TransactionSmartContractPermissioningController;
import java.util.Optional;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class AccountPermissioningController { public class AccountPermissioningController {
private final AccountLocalConfigPermissioningController accountLocalConfigPermissioningController; private static final Logger LOG = LogManager.getLogger();
private final TransactionSmartContractPermissioningController
private final Optional<AccountLocalConfigPermissioningController>
accountLocalConfigPermissioningController;
private final Optional<TransactionSmartContractPermissioningController>
transactionSmartContractPermissioningController; transactionSmartContractPermissioningController;
public AccountPermissioningController( public AccountPermissioningController(
final AccountLocalConfigPermissioningController accountLocalConfigPermissioningController, final Optional<AccountLocalConfigPermissioningController>
final TransactionSmartContractPermissioningController accountLocalConfigPermissioningController,
final Optional<TransactionSmartContractPermissioningController>
transactionSmartContractPermissioningController) { transactionSmartContractPermissioningController) {
this.accountLocalConfigPermissioningController = accountLocalConfigPermissioningController; this.accountLocalConfigPermissioningController = accountLocalConfigPermissioningController;
this.transactionSmartContractPermissioningController = this.transactionSmartContractPermissioningController =
@ -32,11 +43,38 @@ public class AccountPermissioningController {
} }
public boolean isPermitted(final Transaction transaction, final boolean includeOnChainCheck) { public boolean isPermitted(final Transaction transaction, final boolean includeOnChainCheck) {
final Hash transactionHash = transaction.hash();
final Address sender = transaction.getSender();
LOG.trace("Account permissioning: Checking transaction {}", transactionHash);
boolean permitted;
if (includeOnChainCheck) { if (includeOnChainCheck) {
return accountLocalConfigPermissioningController.isPermitted(transaction) permitted =
&& transactionSmartContractPermissioningController.isPermitted(transaction); accountLocalConfigPermissioningController
.map(c -> c.isPermitted(transaction))
.orElse(true)
&& transactionSmartContractPermissioningController
.map(c -> c.isPermitted(transaction))
.orElse(true);
} else { } else {
return accountLocalConfigPermissioningController.isPermitted(transaction); permitted =
accountLocalConfigPermissioningController
.map(c -> c.isPermitted(transaction))
.orElse(true);
} }
if (permitted) {
LOG.trace("Account permissioning: Permitted transaction {} from {}", transactionHash, sender);
} else {
LOG.trace("Account permissioning: Rejected transaction {} from {}", transactionHash, sender);
}
return permitted;
}
public Optional<AccountLocalConfigPermissioningController>
getAccountLocalConfigPermissioningController() {
return accountLocalConfigPermissioningController;
} }
} }

@ -22,6 +22,7 @@ import static tech.pegasys.pantheon.ethereum.core.InMemoryStorageProvider.create
import static tech.pegasys.pantheon.ethereum.core.InMemoryStorageProvider.createInMemoryWorldStateArchive; import static tech.pegasys.pantheon.ethereum.core.InMemoryStorageProvider.createInMemoryWorldStateArchive;
import tech.pegasys.pantheon.config.GenesisConfigFile; import tech.pegasys.pantheon.config.GenesisConfigFile;
import tech.pegasys.pantheon.crypto.SECP256K1.Signature;
import tech.pegasys.pantheon.ethereum.chain.GenesisState; import tech.pegasys.pantheon.ethereum.chain.GenesisState;
import tech.pegasys.pantheon.ethereum.chain.MutableBlockchain; import tech.pegasys.pantheon.ethereum.chain.MutableBlockchain;
import tech.pegasys.pantheon.ethereum.core.Address; import tech.pegasys.pantheon.ethereum.core.Address;
@ -37,6 +38,7 @@ import tech.pegasys.pantheon.metrics.MetricsSystem;
import tech.pegasys.pantheon.util.bytes.BytesValue; import tech.pegasys.pantheon.util.bytes.BytesValue;
import java.io.IOException; import java.io.IOException;
import java.math.BigInteger;
import com.google.common.io.Resources; import com.google.common.io.Resources;
import org.junit.Test; import org.junit.Test;
@ -97,6 +99,8 @@ public class TransactionSmartContractPermissioningControllerTest {
.gasPrice(Wei.ZERO) .gasPrice(Wei.ZERO)
.gasLimit(0) .gasLimit(0)
.payload(BytesValue.EMPTY) .payload(BytesValue.EMPTY)
.nonce(1)
.signature(Signature.create(BigInteger.ONE, BigInteger.TEN, (byte) 1))
.build(); .build();
} }

@ -23,6 +23,8 @@ import tech.pegasys.pantheon.ethereum.core.Transaction;
import tech.pegasys.pantheon.ethereum.permissioning.AccountLocalConfigPermissioningController; import tech.pegasys.pantheon.ethereum.permissioning.AccountLocalConfigPermissioningController;
import tech.pegasys.pantheon.ethereum.permissioning.TransactionSmartContractPermissioningController; import tech.pegasys.pantheon.ethereum.permissioning.TransactionSmartContractPermissioningController;
import java.util.Optional;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -40,7 +42,8 @@ public class AccountPermissioningControllerTest {
@Before @Before
public void before() { public void before() {
permissioningController = permissioningController =
new AccountPermissioningController(localConfigController, smartContractController); new AccountPermissioningController(
Optional.of(localConfigController), Optional.of(smartContractController));
} }
@Test @Test

@ -62,10 +62,10 @@ import tech.pegasys.pantheon.ethereum.p2p.permissions.PeerPermissionsBlacklist;
import tech.pegasys.pantheon.ethereum.p2p.wire.Capability; import tech.pegasys.pantheon.ethereum.p2p.wire.Capability;
import tech.pegasys.pantheon.ethereum.p2p.wire.SubProtocol; import tech.pegasys.pantheon.ethereum.p2p.wire.SubProtocol;
import tech.pegasys.pantheon.ethereum.permissioning.AccountLocalConfigPermissioningController; import tech.pegasys.pantheon.ethereum.permissioning.AccountLocalConfigPermissioningController;
import tech.pegasys.pantheon.ethereum.permissioning.LocalPermissioningConfiguration;
import tech.pegasys.pantheon.ethereum.permissioning.NodeLocalConfigPermissioningController; import tech.pegasys.pantheon.ethereum.permissioning.NodeLocalConfigPermissioningController;
import tech.pegasys.pantheon.ethereum.permissioning.NodePermissioningControllerFactory; import tech.pegasys.pantheon.ethereum.permissioning.NodePermissioningControllerFactory;
import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration; import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration;
import tech.pegasys.pantheon.ethereum.permissioning.account.AccountPermissioningController;
import tech.pegasys.pantheon.ethereum.permissioning.node.NodePermissioningController; import tech.pegasys.pantheon.ethereum.permissioning.node.NodePermissioningController;
import tech.pegasys.pantheon.ethereum.transaction.TransactionSimulator; import tech.pegasys.pantheon.ethereum.transaction.TransactionSimulator;
import tech.pegasys.pantheon.ethereum.worldstate.WorldStateArchive; import tech.pegasys.pantheon.ethereum.worldstate.WorldStateArchive;
@ -110,6 +110,7 @@ public class RunnerBuilder {
private MetricsSystem metricsSystem; private MetricsSystem metricsSystem;
private Optional<PermissioningConfiguration> permissioningConfiguration = Optional.empty(); private Optional<PermissioningConfiguration> permissioningConfiguration = Optional.empty();
private Collection<EnodeURL> staticNodes = Collections.emptyList(); private Collection<EnodeURL> staticNodes = Collections.emptyList();
private AccountPermissioningController accountPermissioningController;
public RunnerBuilder vertx(final Vertx vertx) { public RunnerBuilder vertx(final Vertx vertx) {
this.vertx = vertx; this.vertx = vertx;
@ -197,6 +198,12 @@ public class RunnerBuilder {
return this; return this;
} }
public RunnerBuilder accountPermissioningController(
final AccountPermissioningController accountPermissioningController) {
this.accountPermissioningController = accountPermissioningController;
return this;
}
public Runner build() { public Runner build() {
Preconditions.checkNotNull(pantheonController); Preconditions.checkNotNull(pantheonController);
@ -245,9 +252,6 @@ public class RunnerBuilder {
final List<EnodeURL> bootnodes = discoveryConfiguration.getBootnodes(); final List<EnodeURL> bootnodes = discoveryConfiguration.getBootnodes();
final Optional<LocalPermissioningConfiguration> localPermissioningConfiguration =
permissioningConfiguration.flatMap(PermissioningConfiguration::getLocalConfig);
final Synchronizer synchronizer = pantheonController.getSynchronizer(); final Synchronizer synchronizer = pantheonController.getSynchronizer();
final TransactionSimulator transactionSimulator = final TransactionSimulator transactionSimulator =
@ -289,16 +293,6 @@ public class RunnerBuilder {
final TransactionPool transactionPool = pantheonController.getTransactionPool(); final TransactionPool transactionPool = pantheonController.getTransactionPool();
final MiningCoordinator miningCoordinator = pantheonController.getMiningCoordinator(); final MiningCoordinator miningCoordinator = pantheonController.getMiningCoordinator();
final Optional<AccountLocalConfigPermissioningController> accountWhitelistController =
localPermissioningConfiguration
.filter(LocalPermissioningConfiguration::isAccountWhitelistEnabled)
.map(
configuration -> {
final AccountLocalConfigPermissioningController whitelistController =
new AccountLocalConfigPermissioningController(configuration, metricsSystem);
transactionPool.setAccountFilter(whitelistController::contains);
return whitelistController;
});
final PrivacyParameters privacyParameters = pantheonController.getPrivacyParameters(); final PrivacyParameters privacyParameters = pantheonController.getPrivacyParameters();
final FilterManager filterManager = createFilterManager(vertx, context, transactionPool); final FilterManager filterManager = createFilterManager(vertx, context, transactionPool);
@ -312,6 +306,12 @@ public class RunnerBuilder {
final Optional<NodeLocalConfigPermissioningController> nodeLocalConfigPermissioningController = final Optional<NodeLocalConfigPermissioningController> nodeLocalConfigPermissioningController =
nodePermissioningController.flatMap(NodePermissioningController::localConfigController); nodePermissioningController.flatMap(NodePermissioningController::localConfigController);
final Optional<AccountLocalConfigPermissioningController>
accountLocalConfigPermissioningController =
accountPermissioningController != null
? accountPermissioningController.getAccountLocalConfigPermissioningController()
: Optional.empty();
Optional<JsonRpcHttpService> jsonRpcHttpService = Optional.empty(); Optional<JsonRpcHttpService> jsonRpcHttpService = Optional.empty();
if (jsonRpcConfiguration.isEnabled()) { if (jsonRpcConfiguration.isEnabled()) {
final Map<String, JsonRpcMethod> jsonRpcMethods = final Map<String, JsonRpcMethod> jsonRpcMethods =
@ -327,7 +327,7 @@ public class RunnerBuilder {
supportedCapabilities, supportedCapabilities,
jsonRpcConfiguration.getRpcApis(), jsonRpcConfiguration.getRpcApis(),
filterManager, filterManager,
accountWhitelistController, accountLocalConfigPermissioningController,
nodeLocalConfigPermissioningController, nodeLocalConfigPermissioningController,
privacyParameters, privacyParameters,
jsonRpcConfiguration, jsonRpcConfiguration,
@ -378,7 +378,7 @@ public class RunnerBuilder {
supportedCapabilities, supportedCapabilities,
webSocketConfiguration.getRpcApis(), webSocketConfiguration.getRpcApis(),
filterManager, filterManager,
accountWhitelistController, accountLocalConfigPermissioningController,
nodeLocalConfigPermissioningController, nodeLocalConfigPermissioningController,
privacyParameters, privacyParameters,
jsonRpcConfiguration, jsonRpcConfiguration,

@ -40,6 +40,7 @@ import tech.pegasys.pantheon.cli.rlp.RLPSubCommand;
import tech.pegasys.pantheon.config.GenesisConfigFile; import tech.pegasys.pantheon.config.GenesisConfigFile;
import tech.pegasys.pantheon.controller.KeyPairUtil; import tech.pegasys.pantheon.controller.KeyPairUtil;
import tech.pegasys.pantheon.controller.PantheonController; import tech.pegasys.pantheon.controller.PantheonController;
import tech.pegasys.pantheon.ethereum.ProtocolContext;
import tech.pegasys.pantheon.ethereum.core.Address; import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.MiningParameters; import tech.pegasys.pantheon.ethereum.core.MiningParameters;
import tech.pegasys.pantheon.ethereum.core.PrivacyParameters; import tech.pegasys.pantheon.ethereum.core.PrivacyParameters;
@ -54,12 +55,17 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.JsonRpcConfiguration;
import tech.pegasys.pantheon.ethereum.jsonrpc.RpcApi; import tech.pegasys.pantheon.ethereum.jsonrpc.RpcApi;
import tech.pegasys.pantheon.ethereum.jsonrpc.RpcApis; import tech.pegasys.pantheon.ethereum.jsonrpc.RpcApis;
import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration; import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule;
import tech.pegasys.pantheon.ethereum.p2p.config.DiscoveryConfiguration; import tech.pegasys.pantheon.ethereum.p2p.config.DiscoveryConfiguration;
import tech.pegasys.pantheon.ethereum.p2p.peers.StaticNodesParser; import tech.pegasys.pantheon.ethereum.p2p.peers.StaticNodesParser;
import tech.pegasys.pantheon.ethereum.permissioning.AccountLocalConfigPermissioningController;
import tech.pegasys.pantheon.ethereum.permissioning.LocalPermissioningConfiguration; import tech.pegasys.pantheon.ethereum.permissioning.LocalPermissioningConfiguration;
import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration; import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration;
import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfigurationBuilder; import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfigurationBuilder;
import tech.pegasys.pantheon.ethereum.permissioning.SmartContractPermissioningConfiguration; import tech.pegasys.pantheon.ethereum.permissioning.SmartContractPermissioningConfiguration;
import tech.pegasys.pantheon.ethereum.permissioning.TransactionSmartContractPermissioningController;
import tech.pegasys.pantheon.ethereum.permissioning.account.AccountPermissioningController;
import tech.pegasys.pantheon.ethereum.transaction.TransactionSimulator;
import tech.pegasys.pantheon.metrics.MetricCategory; import tech.pegasys.pantheon.metrics.MetricCategory;
import tech.pegasys.pantheon.metrics.MetricsSystem; import tech.pegasys.pantheon.metrics.MetricsSystem;
import tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration; import tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration;
@ -514,6 +520,18 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
description = "Enable node level permissions via smart contract (default: ${DEFAULT-VALUE})") description = "Enable node level permissions via smart contract (default: ${DEFAULT-VALUE})")
private final Boolean permissionsNodesContractEnabled = false; private final Boolean permissionsNodesContractEnabled = false;
@Option(
names = {"--permissions-accounts-contract-address"},
description = "Address of the account permissioning smart contract",
arity = "1")
private final Address permissionsAccountsContractAddress = null;
@Option(
names = {"--permissions-accounts-contract-enabled"},
description =
"Enable account level permissions via smart contract (default: ${DEFAULT-VALUE})")
private final Boolean permissionsAccountsContractEnabled = false;
@Option( @Option(
names = {"--privacy-enabled"}, names = {"--privacy-enabled"},
description = "Enable private transactions (default: ${DEFAULT-VALUE})") description = "Enable private transactions (default: ${DEFAULT-VALUE})")
@ -733,6 +751,14 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
final PantheonController<?> pantheonController = buildController(); final PantheonController<?> pantheonController = buildController();
final MetricsConfiguration metricsConfiguration = metricsConfiguration(); final MetricsConfiguration metricsConfiguration = metricsConfiguration();
final AccountPermissioningController accountPermissioningController =
buildAccountPermissioningController(permissioningConfiguration, pantheonController);
if (permissionsAccountsEnabled || permissionsAccountsContractEnabled) {
pantheonController
.getProtocolSchedule()
.setTransactionFilter(accountPermissioningController::isPermitted);
}
pantheonPluginContext.addService( pantheonPluginContext.addService(
PantheonEvents.class, PantheonEvents.class,
new PantheonEventsImpl((pantheonController.getProtocolManager().getBlockBroadcaster()))); new PantheonEventsImpl((pantheonController.getProtocolManager().getBlockBroadcaster())));
@ -751,12 +777,64 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
webSocketConfiguration, webSocketConfiguration,
metricsConfiguration, metricsConfiguration,
permissioningConfiguration, permissioningConfiguration,
staticNodes); staticNodes,
accountPermissioningController);
} catch (final Exception e) { } catch (final Exception e) {
throw new ParameterException(this.commandLine, e.getMessage(), e); throw new ParameterException(this.commandLine, e.getMessage(), e);
} }
} }
private AccountPermissioningController buildAccountPermissioningController(
final Optional<PermissioningConfiguration> permissioningConfiguration,
final PantheonController<?> pantheonController) {
Optional<AccountLocalConfigPermissioningController> accountLocalConfigPermissioningController =
Optional.empty();
Optional<TransactionSmartContractPermissioningController>
transactionSmartContractPermissioningController = Optional.empty();
if (permissioningConfiguration.isPresent()) {
final PermissioningConfiguration config = permissioningConfiguration.get();
if (config.getLocalConfig().isPresent()) {
final LocalPermissioningConfiguration localPermissioningConfiguration =
config.getLocalConfig().get();
if (localPermissioningConfiguration.isAccountWhitelistEnabled()) {
accountLocalConfigPermissioningController =
Optional.of(
new AccountLocalConfigPermissioningController(
localPermissioningConfiguration, metricsSystem.get()));
}
}
if (config.getSmartContractConfig().isPresent()) {
final SmartContractPermissioningConfiguration smartContractPermissioningConfiguration =
config.getSmartContractConfig().get();
if (smartContractPermissioningConfiguration.isSmartContractAccountWhitelistEnabled()) {
final Address accountSmartContractAddress =
smartContractPermissioningConfiguration.getAccountSmartContractAddress();
final ProtocolContext<?> protocolContext = pantheonController.getProtocolContext();
final ProtocolSchedule<?> protocolSchedule = pantheonController.getProtocolSchedule();
final TransactionSimulator transactionSimulator =
new TransactionSimulator(
protocolContext.getBlockchain(),
protocolContext.getWorldStateArchive(),
protocolSchedule);
transactionSmartContractPermissioningController =
Optional.of(
new TransactionSmartContractPermissioningController(
accountSmartContractAddress, transactionSimulator, metricsSystem.get()));
}
}
}
return new AccountPermissioningController(
accountLocalConfigPermissioningController, transactionSmartContractPermissioningController);
}
private NetworkName getNetwork() { private NetworkName getNetwork() {
// noinspection ConstantConditions network is not always null but injected by // noinspection ConstantConditions network is not always null but injected by
// PicoCLI if used // PicoCLI if used
@ -924,10 +1002,6 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
} }
private Optional<PermissioningConfiguration> permissioningConfiguration() throws Exception { private Optional<PermissioningConfiguration> permissioningConfiguration() throws Exception {
final Optional<LocalPermissioningConfiguration> localPermissioningConfigurationOptional;
final Optional<SmartContractPermissioningConfiguration>
smartContractPermissioningConfigurationOptional;
if (!(localPermissionsEnabled() || contractPermissionsEnabled())) { if (!(localPermissionsEnabled() || contractPermissionsEnabled())) {
if (rpcHttpApis.contains(RpcApis.PERM) || rpcWsApis.contains(RpcApis.PERM)) { if (rpcHttpApis.contains(RpcApis.PERM) || rpcWsApis.contains(RpcApis.PERM)) {
logger.warn( logger.warn(
@ -936,6 +1010,7 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
return Optional.empty(); return Optional.empty();
} }
final Optional<LocalPermissioningConfiguration> localPermissioningConfigurationOptional;
if (localPermissionsEnabled()) { if (localPermissionsEnabled()) {
final Optional<String> nodePermissioningConfigFile = final Optional<String> nodePermissioningConfigFile =
Optional.ofNullable(nodePermissionsConfigFile()); Optional.ofNullable(nodePermissionsConfigFile());
@ -965,30 +1040,46 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
localPermissioningConfigurationOptional = Optional.empty(); localPermissioningConfigurationOptional = Optional.empty();
} }
if (contractPermissionsEnabled()) { final SmartContractPermissioningConfiguration smartContractPermissioningConfiguration =
SmartContractPermissioningConfiguration.createDefault();
if (permissionsNodesContractEnabled) {
if (permissionsNodesContractAddress == null) { if (permissionsNodesContractAddress == null) {
throw new ParameterException( throw new ParameterException(
this.commandLine, this.commandLine,
"No contract address specified. Cannot enable contract based permissions."); "No node permissioning contract address specified. Cannot enable smart contract based node permissioning.");
} } else {
final SmartContractPermissioningConfiguration smartContractPermissioningConfiguration = smartContractPermissioningConfiguration.setSmartContractNodeWhitelistEnabled(
PermissioningConfigurationBuilder.smartContractPermissioningConfiguration( permissionsNodesContractEnabled);
permissionsNodesContractAddress, permissionsNodesContractEnabled); smartContractPermissioningConfiguration.setNodeSmartContractAddress(
smartContractPermissioningConfigurationOptional =
Optional.of(smartContractPermissioningConfiguration);
} else {
if (permissionsNodesContractAddress != null) {
logger.warn(
"Smart contract address set {} but no contract permissions enabled",
permissionsNodesContractAddress); permissionsNodesContractAddress);
} }
smartContractPermissioningConfigurationOptional = Optional.empty(); } else if (permissionsNodesContractAddress != null) {
logger.warn(
"Node permissioning smart contract address set {} but smart contract node permissioning is disabled.",
permissionsNodesContractAddress);
}
if (permissionsAccountsContractEnabled) {
if (permissionsAccountsContractAddress == null) {
throw new ParameterException(
this.commandLine,
"No account permissioning contract address specified. Cannot enable smart contract based account permissioning.");
} else {
smartContractPermissioningConfiguration.setSmartContractAccountWhitelistEnabled(
permissionsAccountsContractEnabled);
smartContractPermissioningConfiguration.setAccountSmartContractAddress(
permissionsAccountsContractAddress);
}
} else if (permissionsAccountsContractAddress != null) {
logger.warn(
"Account permissioning smart contract address set {} but smart contract account permissioning is disabled.",
permissionsAccountsContractAddress);
} }
final PermissioningConfiguration permissioningConfiguration = final PermissioningConfiguration permissioningConfiguration =
new PermissioningConfiguration( new PermissioningConfiguration(
localPermissioningConfigurationOptional, localPermissioningConfigurationOptional,
smartContractPermissioningConfigurationOptional); Optional.of(smartContractPermissioningConfiguration));
return Optional.of(permissioningConfiguration); return Optional.of(permissioningConfiguration);
} }
@ -998,8 +1089,7 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
} }
private boolean contractPermissionsEnabled() { private boolean contractPermissionsEnabled() {
// TODO add permissionsAccountsContractEnabled return permissionsNodesContractEnabled || permissionsAccountsContractEnabled;
return permissionsNodesContractEnabled;
} }
private PrivacyParameters privacyParameters() throws IOException { private PrivacyParameters privacyParameters() throws IOException {
@ -1054,7 +1144,8 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
final WebSocketConfiguration webSocketConfiguration, final WebSocketConfiguration webSocketConfiguration,
final MetricsConfiguration metricsConfiguration, final MetricsConfiguration metricsConfiguration,
final Optional<PermissioningConfiguration> permissioningConfiguration, final Optional<PermissioningConfiguration> permissioningConfiguration,
final Collection<EnodeURL> staticNodes) { final Collection<EnodeURL> staticNodes,
final AccountPermissioningController accountPermissioningController) {
checkNotNull(runnerBuilder); checkNotNull(runnerBuilder);
@ -1079,6 +1170,7 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
.metricsSystem(metricsSystem) .metricsSystem(metricsSystem)
.metricsConfiguration(metricsConfiguration) .metricsConfiguration(metricsConfiguration)
.staticNodes(staticNodes) .staticNodes(staticNodes)
.accountPermissioningController(accountPermissioningController)
.build(); .build();
addShutdownHook(runner); addShutdownHook(runner);

@ -17,6 +17,7 @@ import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import tech.pegasys.pantheon.Runner; import tech.pegasys.pantheon.Runner;
@ -25,6 +26,7 @@ import tech.pegasys.pantheon.cli.PublicKeySubCommand.KeyLoader;
import tech.pegasys.pantheon.controller.PantheonController; import tech.pegasys.pantheon.controller.PantheonController;
import tech.pegasys.pantheon.controller.PantheonControllerBuilder; import tech.pegasys.pantheon.controller.PantheonControllerBuilder;
import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair; import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair;
import tech.pegasys.pantheon.ethereum.ProtocolContext;
import tech.pegasys.pantheon.ethereum.eth.EthereumWireProtocolConfiguration; import tech.pegasys.pantheon.ethereum.eth.EthereumWireProtocolConfiguration;
import tech.pegasys.pantheon.ethereum.eth.manager.EthProtocolManager; import tech.pegasys.pantheon.ethereum.eth.manager.EthProtocolManager;
import tech.pegasys.pantheon.ethereum.eth.sync.BlockBroadcaster; import tech.pegasys.pantheon.ethereum.eth.sync.BlockBroadcaster;
@ -32,7 +34,9 @@ import tech.pegasys.pantheon.ethereum.eth.sync.SynchronizerConfiguration;
import tech.pegasys.pantheon.ethereum.graphql.GraphQLConfiguration; import tech.pegasys.pantheon.ethereum.graphql.GraphQLConfiguration;
import tech.pegasys.pantheon.ethereum.jsonrpc.JsonRpcConfiguration; import tech.pegasys.pantheon.ethereum.jsonrpc.JsonRpcConfiguration;
import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration; import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule;
import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration; import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration;
import tech.pegasys.pantheon.ethereum.permissioning.account.AccountPermissioningController;
import tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration; import tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration;
import tech.pegasys.pantheon.services.PantheonPluginContextImpl; import tech.pegasys.pantheon.services.PantheonPluginContextImpl;
import tech.pegasys.pantheon.services.kvstore.RocksDbConfiguration; import tech.pegasys.pantheon.services.kvstore.RocksDbConfiguration;
@ -80,6 +84,8 @@ public abstract class CommandTestAbstract {
@Mock PantheonControllerBuilder<Void> mockControllerBuilder; @Mock PantheonControllerBuilder<Void> mockControllerBuilder;
@Mock EthProtocolManager mockEthProtocolManager; @Mock EthProtocolManager mockEthProtocolManager;
@Mock ProtocolSchedule<Object> mockProtocolSchedule;
@Mock ProtocolContext<Object> mockProtocolContext;
@Mock BlockBroadcaster mockBlockBroadcaster; @Mock BlockBroadcaster mockBlockBroadcaster;
@Mock SynchronizerConfiguration.Builder mockSyncConfBuilder; @Mock SynchronizerConfiguration.Builder mockSyncConfBuilder;
@Mock EthereumWireProtocolConfiguration.Builder mockEthereumWireProtocolConfigurationBuilder; @Mock EthereumWireProtocolConfiguration.Builder mockEthereumWireProtocolConfigurationBuilder;
@ -105,6 +111,9 @@ public abstract class CommandTestAbstract {
@Captor ArgumentCaptor<PermissioningConfiguration> permissioningConfigurationArgumentCaptor; @Captor ArgumentCaptor<PermissioningConfiguration> permissioningConfigurationArgumentCaptor;
@Captor
ArgumentCaptor<AccountPermissioningController> accountPermissioningControllerArgumentCaptor;
@Rule public final TemporaryFolder temp = new TemporaryFolder(); @Rule public final TemporaryFolder temp = new TemporaryFolder();
@Before @Before
@ -134,7 +143,9 @@ public abstract class CommandTestAbstract {
// doReturn used because of generic PantheonController // doReturn used because of generic PantheonController
doReturn(mockController).when(mockControllerBuilder).build(); doReturn(mockController).when(mockControllerBuilder).build();
when(mockController.getProtocolManager()).thenReturn(mockEthProtocolManager); lenient().when(mockController.getProtocolManager()).thenReturn(mockEthProtocolManager);
lenient().when(mockController.getProtocolSchedule()).thenReturn(mockProtocolSchedule);
lenient().when(mockController.getProtocolContext()).thenReturn(mockProtocolContext);
when(mockEthProtocolManager.getBlockBroadcaster()).thenReturn(mockBlockBroadcaster); when(mockEthProtocolManager.getBlockBroadcaster()).thenReturn(mockBlockBroadcaster);
@ -165,6 +176,7 @@ public abstract class CommandTestAbstract {
when(mockRunnerBuilder.metricsSystem(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.metricsSystem(any())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.metricsConfiguration(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.metricsConfiguration(any())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.staticNodes(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.staticNodes(any())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.accountPermissioningController(any())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.build()).thenReturn(mockRunner); when(mockRunnerBuilder.build()).thenReturn(mockRunner);
} }

@ -17,6 +17,7 @@ import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue; import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNotNull; import static org.mockito.ArgumentMatchers.isNotNull;
import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.atLeast;
@ -48,6 +49,7 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration;
import tech.pegasys.pantheon.ethereum.permissioning.LocalPermissioningConfiguration; import tech.pegasys.pantheon.ethereum.permissioning.LocalPermissioningConfiguration;
import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration; import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration;
import tech.pegasys.pantheon.ethereum.permissioning.SmartContractPermissioningConfiguration; import tech.pegasys.pantheon.ethereum.permissioning.SmartContractPermissioningConfiguration;
import tech.pegasys.pantheon.ethereum.permissioning.account.AccountPermissioningController;
import tech.pegasys.pantheon.metrics.MetricCategory; import tech.pegasys.pantheon.metrics.MetricCategory;
import tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration; import tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration;
import tech.pegasys.pantheon.util.bytes.BytesValue; import tech.pegasys.pantheon.util.bytes.BytesValue;
@ -339,7 +341,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
} }
@Test @Test
public void permissionsSmartContractWithoutOptionMustError() { public void nodePermissionsSmartContractWithoutOptionMustError() {
parseCommand("--permissions-nodes-contract-address"); parseCommand("--permissions-nodes-contract-address");
verifyZeroInteractions(mockRunnerBuilder); verifyZeroInteractions(mockRunnerBuilder);
@ -350,17 +352,18 @@ public class PantheonCommandTest extends CommandTestAbstract {
} }
@Test @Test
public void permissionsEnabledWithoutContractAddressMustError() { public void nodePermissionsEnabledWithoutContractAddressMustError() {
parseCommand("--permissions-nodes-contract-enabled"); parseCommand("--permissions-nodes-contract-enabled");
verifyZeroInteractions(mockRunnerBuilder); verifyZeroInteractions(mockRunnerBuilder);
assertThat(commandErrorOutput.toString()).contains("No contract address specified"); assertThat(commandErrorOutput.toString())
.contains("No node permissioning contract address specified");
assertThat(commandOutput.toString()).isEmpty(); assertThat(commandOutput.toString()).isEmpty();
} }
@Test @Test
public void permissionsEnabledWithInvalidContractAddressMustError() { public void nodePermissionsEnabledWithInvalidContractAddressMustError() {
parseCommand( parseCommand(
"--permissions-nodes-contract-enabled", "--permissions-nodes-contract-enabled",
"--permissions-nodes-contract-address", "--permissions-nodes-contract-address",
@ -373,7 +376,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
} }
@Test @Test
public void permissionsEnabledWithTooShortContractAddressMustError() { public void nodePermissionsEnabledWithTooShortContractAddressMustError() {
parseCommand( parseCommand(
"--permissions-nodes-contract-enabled", "--permissions-nodes-contract-address", "0x1234"); "--permissions-nodes-contract-enabled", "--permissions-nodes-contract-address", "0x1234");
@ -384,7 +387,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
} }
@Test @Test
public void permissionsSmartContractMustUseOption() { public void nodePermissionsSmartContractMustUseOption() {
String smartContractAddress = "0x0000000000000000000000000000000000001234"; String smartContractAddress = "0x0000000000000000000000000000000000001234";
@ -410,6 +413,75 @@ public class PantheonCommandTest extends CommandTestAbstract {
assertThat(commandOutput.toString()).isEmpty(); assertThat(commandOutput.toString()).isEmpty();
} }
@Test
public void accountPermissionsSmartContractWithoutOptionMustError() {
parseCommand("--permissions-accounts-contract-address");
verifyZeroInteractions(mockRunnerBuilder);
assertThat(commandErrorOutput.toString())
.startsWith(
"Missing required parameter for option '--permissions-accounts-contract-address'");
assertThat(commandOutput.toString()).isEmpty();
}
@Test
public void accountPermissionsEnabledWithoutContractAddressMustError() {
parseCommand("--permissions-accounts-contract-enabled");
verifyZeroInteractions(mockRunnerBuilder);
assertThat(commandErrorOutput.toString())
.contains("No account permissioning contract address specified");
assertThat(commandOutput.toString()).isEmpty();
}
@Test
public void accountPermissionsEnabledWithInvalidContractAddressMustError() {
parseCommand(
"--permissions-accounts-contract-enabled",
"--permissions-accounts-contract-address",
"invalid-smart-contract-address");
verifyZeroInteractions(mockRunnerBuilder);
assertThat(commandErrorOutput.toString()).contains("Invalid value");
assertThat(commandOutput.toString()).isEmpty();
}
@Test
public void accountPermissionsEnabledWithTooShortContractAddressMustError() {
parseCommand(
"--permissions-accounts-contract-enabled",
"--permissions-accounts-contract-address",
"0x1234");
verifyZeroInteractions(mockRunnerBuilder);
assertThat(commandErrorOutput.toString()).contains("Invalid value");
assertThat(commandOutput.toString()).isEmpty();
}
@Test
public void accountPermissionsSmartContractMustUseOption() {
String smartContractAddress = "0x0000000000000000000000000000000000001234";
parseCommand(
"--permissions-accounts-contract-enabled",
"--permissions-accounts-contract-address",
smartContractAddress);
final SmartContractPermissioningConfiguration smartContractPermissioningConfiguration =
new SmartContractPermissioningConfiguration();
smartContractPermissioningConfiguration.setAccountSmartContractAddress(
Address.fromHexString(smartContractAddress));
smartContractPermissioningConfiguration.setSmartContractAccountWhitelistEnabled(true);
verify(mockController.getProtocolSchedule()).setTransactionFilter(any());
assertThat(commandErrorOutput.toString()).isEmpty();
assertThat(commandOutput.toString()).isEmpty();
}
@Test @Test
public void nodePermissioningTomlPathWithoutOptionMustDisplayUsage() { public void nodePermissioningTomlPathWithoutOptionMustDisplayUsage() {
parseCommand("--permissions-nodes-config-file"); parseCommand("--permissions-nodes-config-file");
@ -549,12 +621,11 @@ public class PantheonCommandTest extends CommandTestAbstract {
Collections.singletonList("0x0000000000000000000000000000000000000009")); Collections.singletonList("0x0000000000000000000000000000000000000009"));
verify(mockRunnerBuilder) verify(mockRunnerBuilder)
.permissioningConfiguration(permissioningConfigurationArgumentCaptor.capture()); .accountPermissioningController(accountPermissioningControllerArgumentCaptor.capture());
verify(mockRunnerBuilder).build();
PermissioningConfiguration config = permissioningConfigurationArgumentCaptor.getValue(); AccountPermissioningController controller =
assertThat(config.getLocalConfig().get()) accountPermissioningControllerArgumentCaptor.getValue();
.isEqualToComparingFieldByField(localPermissioningConfiguration); assertThat(controller.getAccountLocalConfigPermissioningController()).isPresent();
assertThat(commandErrorOutput.toString()).isEmpty(); assertThat(commandErrorOutput.toString()).isEmpty();
assertThat(commandOutput.toString()).isEmpty(); assertThat(commandOutput.toString()).isEmpty();

@ -84,6 +84,8 @@ permissions-accounts-config-file-enabled=false
permissions-accounts-config-file="./permissions_config.toml" permissions-accounts-config-file="./permissions_config.toml"
permissions-nodes-contract-enabled=false permissions-nodes-contract-enabled=false
permissions-nodes-contract-address="0x0000000000000000000000000000000000001234" permissions-nodes-contract-address="0x0000000000000000000000000000000000001234"
permissions-accounts-contract-enabled=false
permissions-accounts-contract-address="0x0000000000000000000000000000000000006789"
# Privacy # Privacy
privacy-url="http://127.0.0.1:8888" privacy-url="http://127.0.0.1:8888"

Loading…
Cancel
Save