Skip to content
This repository has been archived by the owner on Sep 26, 2019. It is now read-only.

PAN-2445: Onchain account permissioning #1507

Merged
merged 17 commits into from
Jun 7, 2019
Merged
Show file tree
Hide file tree
Changes from 11 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
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ private boolean isSenderAllowed(final Transaction transaction, final boolean isS
return transactionFilter.map(c -> c.permitted(transaction, isStateChange)).orElse(true);
}

@Override
public void setTransactionFilter(final TransactionFilter transactionFilter) {
this.transactionFilter = Optional.of(transactionFilter);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

import static com.google.common.base.Preconditions.checkArgument;

import tech.pegasys.pantheon.ethereum.core.TransactionFilter;

import java.math.BigInteger;
import java.util.Comparator;
import java.util.NavigableSet;
Expand Down Expand Up @@ -69,4 +71,10 @@ public String listMilestones() {
.map(spec -> spec.getSpec().getName() + ": " + spec.getBlock())
.collect(Collectors.joining(", ", "[", "]"));
}

@Override
public void addTransactionFilterToTransactionValidators(
lucassaldanha marked this conversation as resolved.
Show resolved Hide resolved
final TransactionFilter transactionFilter) {
protocolSpecs.forEach(spec -> spec.getSpec().setTransactionFilter(transactionFilter));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
*/
package tech.pegasys.pantheon.ethereum.mainnet;

import tech.pegasys.pantheon.ethereum.core.TransactionFilter;

import java.math.BigInteger;
import java.util.Optional;

Expand All @@ -20,4 +22,6 @@ public interface ProtocolSchedule<C> {
ProtocolSpec<C> getByBlockNumber(long number);

Optional<BigInteger> getChainId();

void addTransactionFilterToTransactionValidators(TransactionFilter transactionFilter);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import tech.pegasys.pantheon.ethereum.BlockValidator;
import tech.pegasys.pantheon.ethereum.core.BlockHeaderFunctions;
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.mainnet.MainnetBlockProcessor.TransactionReceiptFactory;
import tech.pegasys.pantheon.ethereum.vm.EVM;
Expand Down Expand Up @@ -243,4 +244,8 @@ public MiningBeneficiaryCalculator getMiningBeneficiaryCalculator() {
public PrecompileContractRegistry getPrecompileContractRegistry() {
return precompileContractRegistry;
}

public void setTransactionFilter(final TransactionFilter transactionFilter) {
transactionValidator.setTransactionFilter(transactionFilter);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import tech.pegasys.pantheon.ethereum.core.Account;
import tech.pegasys.pantheon.ethereum.core.Transaction;
import tech.pegasys.pantheon.ethereum.core.TransactionFilter;

/** Validates transaction based on some criteria. */
public interface TransactionValidator {
Expand Down Expand Up @@ -54,6 +55,8 @@ default ValidationResult<TransactionInvalidReason> validateForSender(
ValidationResult<TransactionInvalidReason> validateForSender(
Transaction transaction, Account sender, TransactionValidationParams validationParams);

void setTransactionFilter(TransactionFilter transactionFilter);

enum TransactionInvalidReason {
WRONG_CHAIN_ID,
REPLAY_PROTECTED_SIGNATURES_NOT_SUPPORTED,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,6 @@ public void transactionWithNullSenderCanBeValidIfGasPriceAndValueIsZero() {
public void shouldRejectTransactionIfAccountIsNotPermitted() {
final MainnetTransactionValidator validator =
new MainnetTransactionValidator(gasCalculator, false, Optional.empty());

validator.setTransactionFilter(transactionFilter(false));

assertThat(validator.validateForSender(basicTransaction, accountWithNonce(0), true))
Expand All @@ -169,7 +168,6 @@ public void shouldRejectTransactionIfAccountIsNotPermitted() {
public void shouldAcceptValidTransactionIfAccountIsPermitted() {
final MainnetTransactionValidator validator =
new MainnetTransactionValidator(gasCalculator, false, Optional.empty());

validator.setTransactionFilter(transactionFilter(true));

assertThat(validator.validateForSender(basicTransaction, accountWithNonce(0), true))
Expand All @@ -178,13 +176,13 @@ public void shouldAcceptValidTransactionIfAccountIsPermitted() {

@Test
public void shouldPropagateCorrectStateChangeParamToTransactionFilter() {
final MainnetTransactionValidator validator =
new MainnetTransactionValidator(gasCalculator, false, Optional.empty());

final ArgumentCaptor<Boolean> stateChangeParamCaptor = ArgumentCaptor.forClass(Boolean.class);
final TransactionFilter transactionFilter = mock(TransactionFilter.class);
when(transactionFilter.permitted(any(Transaction.class), stateChangeParamCaptor.capture()))
.thenReturn(true);

final MainnetTransactionValidator validator =
new MainnetTransactionValidator(gasCalculator, false, Optional.empty());
validator.setTransactionFilter(transactionFilter);

final TransactionValidationParams validationParams =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
package tech.pegasys.pantheon.ethereum.permissioning;

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.permissioning.account.TransactionPermissioningProvider;
import tech.pegasys.pantheon.metrics.Counter;
Expand Down Expand Up @@ -232,17 +233,32 @@ private List<String> normalizeAccounts(final List<String> accounts) {

@Override
public boolean isPermitted(final Transaction transaction) {
this.checkCounter.inc();
final Hash transactionHash = transaction.hash();
final Address sender = transaction.getSender();

LOG.trace("Account permissioning - Local Config: Checking transaction {}", transactionHash);

this.checkCounter.inc();
if (sender == null) {
this.checkCounterUnpermitted.inc();
LOG.trace(
"Account permissioning - Local Config: Rejected transaction {} without sender",
transactionHash);
return false;
} else {
if (contains(sender.toString())) {
this.checkCounterPermitted.inc();
LOG.trace(
"Account permissioning - Local Config: Permitted transaction {} from {}",
transactionHash,
sender);
return true;
} else {
this.checkCounterUnpermitted.inc();
LOG.trace(
"Account permissioning - Local Config: Rejected transaction {} from {}",
transactionHash,
sender);
return false;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,18 @@

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
* transactionAllowed(address,address,uint256,uint256,uint256,bytes)
*/
public class TransactionSmartContractPermissioningController
implements TransactionPermissioningProvider {

private static final Logger LOG = LogManager.getLogger();

private final Address contractAddress;
private final TransactionSimulator transactionSimulator;

Expand Down Expand Up @@ -101,6 +107,11 @@ public TransactionSmartContractPermissioningController(
*/
@Override
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();
final BytesValue payload = createPayload(transaction);
final CallParameter callParams =
Expand Down Expand Up @@ -131,9 +142,17 @@ public boolean isPermitted(final Transaction transaction) {

if (result.map(r -> checkTransactionResult(r.getOutput())).orElse(false)) {
this.checkCounterPermitted.inc();
LOG.trace(
"Account permissioning - Smart Contract: Permitted transaction {} from {}",
transactionHash,
sender);
return true;
} else {
this.checkCounterUnpermitted.inc();
LOG.trace(
"Account permissioning - Smart Contract: Rejected transaction {} from {}",
transactionHash,
sender);
return false;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,69 @@
*/
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.permissioning.AccountLocalConfigPermissioningController;
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 {

private final AccountLocalConfigPermissioningController accountLocalConfigPermissioningController;
private final TransactionSmartContractPermissioningController
private static final Logger LOG = LogManager.getLogger();

private final Optional<AccountLocalConfigPermissioningController>
accountLocalConfigPermissioningController;
private final Optional<TransactionSmartContractPermissioningController>
transactionSmartContractPermissioningController;

public AccountPermissioningController(
final AccountLocalConfigPermissioningController accountLocalConfigPermissioningController,
final TransactionSmartContractPermissioningController
final Optional<AccountLocalConfigPermissioningController>
accountLocalConfigPermissioningController,
final Optional<TransactionSmartContractPermissioningController>
transactionSmartContractPermissioningController) {
this.accountLocalConfigPermissioningController = accountLocalConfigPermissioningController;
this.transactionSmartContractPermissioningController =
transactionSmartContractPermissioningController;
}

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) {
return accountLocalConfigPermissioningController.isPermitted(transaction)
&& transactionSmartContractPermissioningController.isPermitted(transaction);
permitted =
accountLocalConfigPermissioningController
.map(c -> c.isPermitted(transaction))
.orElse(true)
&& transactionSmartContractPermissioningController
.map(c -> c.isPermitted(transaction))
.orElse(true);
} 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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import static tech.pegasys.pantheon.ethereum.core.InMemoryStorageProvider.createInMemoryWorldStateArchive;

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.MutableBlockchain;
import tech.pegasys.pantheon.ethereum.core.Address;
Expand All @@ -37,6 +38,7 @@
import tech.pegasys.pantheon.util.bytes.BytesValue;

import java.io.IOException;
import java.math.BigInteger;

import com.google.common.io.Resources;
import org.junit.Test;
Expand Down Expand Up @@ -97,6 +99,8 @@ private Transaction transactionForAccount(final Address address) {
.gasPrice(Wei.ZERO)
.gasLimit(0)
.payload(BytesValue.EMPTY)
.nonce(1)
.signature(Signature.create(BigInteger.ONE, BigInteger.TEN, (byte) 1))
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import tech.pegasys.pantheon.ethereum.permissioning.AccountLocalConfigPermissioningController;
import tech.pegasys.pantheon.ethereum.permissioning.TransactionSmartContractPermissioningController;

import java.util.Optional;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
Expand All @@ -40,7 +42,8 @@ public class AccountPermissioningControllerTest {
@Before
public void before() {
permissioningController =
new AccountPermissioningController(localConfigController, smartContractController);
new AccountPermissioningController(
Optional.of(localConfigController), Optional.of(smartContractController));
}

@Test
Expand Down
Loading