Clique acceptance tests (#290)

Jason Frame 6 years ago committed by GitHub
parent 24b8d730e4
commit ab744a916c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      acceptance-tests/build.gradle
  2. 49
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/clique/CliqueDiscardRpcAcceptanceTest.java
  3. 68
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/clique/CliqueGetSignersRpcTest.java
  4. 75
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/clique/CliqueMiningAcceptanceTest.java
  5. 45
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/clique/CliqueProposalRpcAcceptanceTest.java
  6. 115
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/clique/CliqueProposeRpcAcceptanceTest.java
  7. 9
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/AcceptanceTestBase.java
  8. 7
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/WaitUtils.java
  9. 6
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/blockchain/Blockchain.java
  10. 36
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/blockchain/ExpectBeneficiary.java
  11. 76
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/blockchain/ExpectBlockNotCreated.java
  12. 37
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/blockchain/ExpectBlockNumber.java
  13. 123
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/clique/CliqueConditions.java
  14. 43
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/clique/ExpectNonceVote.java
  15. 38
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/clique/ExpectProposals.java
  16. 39
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/clique/ExpectValidators.java
  17. 42
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/clique/ExpectValidatorsAtBlock.java
  18. 43
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/clique/ExpectValidatorsAtBlockHash.java
  19. 21
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/Cluster.java
  20. 21
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/GenesisConfigProvider.java
  21. 4
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/Node.java
  22. 8
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/NodeConfiguration.java
  23. 68
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/PantheonNode.java
  24. 109
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/PantheonNodeFactory.java
  25. 25
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/ProcessPantheonNodeRunner.java
  26. 3
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/RunnableNode.java
  27. 7
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/ThreadPantheonNodeRunner.java
  28. 2
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/DeploySmartContractTransaction.java
  29. 81
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/PantheonWeb3j.java
  30. 4
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/Transaction.java
  31. 4
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/account/TransferTransaction.java
  32. 5
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/account/TransferTransactionSet.java
  33. 41
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/clique/CliqueDiscard.java
  34. 43
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/clique/CliqueGetSigners.java
  35. 44
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/clique/CliqueGetSignersAtHash.java
  36. 38
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/clique/CliqueProposals.java
  37. 43
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/clique/CliquePropose.java
  38. 48
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/clique/CliqueTransactions.java
  39. 4
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/eth/EthAccountsTransaction.java
  40. 5
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/eth/EthBlockNumberTransaction.java
  41. 4
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/eth/EthGetBalanceTransaction.java
  42. 48
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/eth/EthGetBlockTransaction.java
  43. 4
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/eth/EthGetTransactionReceiptTransaction.java
  44. 4
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/eth/EthGetWorkTransaction.java
  45. 11
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/eth/EthTransactions.java
  46. 4
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/net/NetPeerCountTransaction.java
  47. 4
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/net/NetVersionTransaction.java
  48. 4
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/web3/Web3Sha3Transaction.java
  49. 20
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/waitcondition/WaitCondition.java
  50. 48
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/waitcondition/WaitConditions.java
  51. 42
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/waitcondition/WaitUntilSignersChanged.java
  52. 42
      acceptance-tests/src/test/resources/clique/clique.json
  53. 15
      consensus/clique/src/main/java/tech/pegasys/pantheon/consensus/clique/CliqueExtraData.java
  54. 22
      consensus/clique/src/test/java/tech/pegasys/pantheon/consensus/clique/CliqueExtraDataTest.java
  55. 31
      pantheon/src/main/java/tech/pegasys/pantheon/cli/EthNetworkConfig.java
  56. 13
      pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java
  57. 6
      pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonControllerBuilder.java
  58. 47
      pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java
  59. 2
      pantheon/src/test/resources/complete_config.toml

@ -22,6 +22,8 @@ dependencies {
testImplementation project(':ethereum:blockcreation')
testImplementation project(':ethereum:jsonrpc')
testImplementation project(':pantheon')
testImplementation project(':config')
testImplementation project(':consensus:clique')
testImplementation project(':util')
testImplementation project(path: ':ethereum:core', configuration: 'testSupportArtifacts')

@ -0,0 +1,49 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.tests.acceptance.clique;
import tech.pegasys.pantheon.tests.acceptance.dsl.AcceptanceTestBase;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.PantheonNode;
import java.io.IOException;
import org.junit.Test;
public class CliqueDiscardRpcAcceptanceTest extends AcceptanceTestBase {
@Test
public void shouldDiscardVotes() throws IOException {
final String[] initialValidators = {"miner1", "miner2"};
final PantheonNode minerNode1 =
pantheon.createCliqueNodeWithValidators("miner1", initialValidators);
final PantheonNode minerNode2 =
pantheon.createCliqueNodeWithValidators("miner2", initialValidators);
final PantheonNode minerNode3 =
pantheon.createCliqueNodeWithValidators("miner3", initialValidators);
cluster.start(minerNode1, minerNode2, minerNode3);
minerNode1.execute(cliqueTransactions.createRemoveProposal(minerNode2));
minerNode2.execute(cliqueTransactions.createRemoveProposal(minerNode2));
minerNode1.execute(cliqueTransactions.createAddProposal(minerNode3));
minerNode2.execute(cliqueTransactions.createAddProposal(minerNode3));
minerNode1.execute(cliqueTransactions.createDiscardProposal(minerNode2));
minerNode1.execute(cliqueTransactions.createDiscardProposal(minerNode3));
minerNode1.waitUntil(wait.chainHeadHasProgressed(minerNode1, 2));
cluster.verify(clique.validatorsEqual(minerNode1, minerNode2));
minerNode1.verify(clique.noProposals());
minerNode2.verify(
clique.proposalsEqual().removeProposal(minerNode2).addProposal(minerNode3).build());
}
}

@ -0,0 +1,68 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.tests.acceptance.clique;
import static tech.pegasys.pantheon.tests.acceptance.dsl.transaction.clique.CliqueTransactions.LATEST;
import tech.pegasys.pantheon.tests.acceptance.dsl.AcceptanceTestBase;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.PantheonNode;
import org.junit.Before;
import org.junit.Test;
public class CliqueGetSignersRpcTest extends AcceptanceTestBase {
private PantheonNode minerNode1;
private PantheonNode minerNode2;
private PantheonNode minerNode3;
private PantheonNode[] allNodes;
private PantheonNode[] initialNodes;
@Before
public void setUp() throws Exception {
final String[] validators = {"miner1", "miner2"};
minerNode1 = pantheon.createCliqueNodeWithValidators("miner1", validators);
minerNode2 = pantheon.createCliqueNodeWithValidators("miner2", validators);
minerNode3 = pantheon.createCliqueNodeWithValidators("miner3", validators);
initialNodes = new PantheonNode[] {minerNode1, minerNode2};
allNodes = new PantheonNode[] {minerNode1, minerNode2, minerNode3};
cluster.start(allNodes);
}
@Test
public void shouldBeAbleToGetValidatorsForBlockNumber() {
cluster.verify(clique.validatorsAtBlockEqual("0x0", initialNodes));
cluster.waitUntil(wait.chainHeadIsAt(1));
minerNode1.execute(cliqueTransactions.createAddProposal(minerNode3));
minerNode2.execute(cliqueTransactions.createAddProposal(minerNode3));
cluster.waitUntil(wait.chainHeadHasProgressed(minerNode1, 1));
cluster.verify(clique.validatorsAtBlockEqual("0x2", initialNodes));
minerNode1.waitUntil(wait.chainHeadHasProgressed(minerNode1, 1));
cluster.verify(clique.validatorsAtBlockEqual("0x3", allNodes));
cluster.verify(clique.validatorsAtBlockEqual(LATEST, allNodes));
}
@Test
public void shouldBeAbleToGetValidatorsForBlockHash() {
cluster.verify(clique.validatorsAtBlockHashFromBlockNumberEqual(minerNode1, 0, initialNodes));
minerNode1.execute(cliqueTransactions.createAddProposal(minerNode3));
minerNode2.execute(cliqueTransactions.createAddProposal(minerNode3));
minerNode1.waitUntil(wait.chainHeadHasProgressed(minerNode1, 1));
cluster.verify(clique.validatorsAtBlockHashFromBlockNumberEqual(minerNode1, 1, initialNodes));
minerNode1.waitUntil(wait.chainHeadHasProgressed(minerNode1, 1));
cluster.verify(clique.validatorsAtBlockHashFromBlockNumberEqual(minerNode1, 3, allNodes));
}
}

@ -0,0 +1,75 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.tests.acceptance.clique;
import tech.pegasys.pantheon.tests.acceptance.dsl.AcceptanceTestBase;
import tech.pegasys.pantheon.tests.acceptance.dsl.account.Account;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.PantheonNode;
import java.io.IOException;
import org.junit.Test;
public class CliqueMiningAcceptanceTest extends AcceptanceTestBase {
@Test
public void shouldMineTransactionsOnSingleNode() throws IOException {
final PantheonNode minerNode = pantheon.createCliqueNode("miner1");
cluster.start(minerNode);
final Account sender = accounts.createAccount("account1");
final Account receiver = accounts.createAccount("account2");
minerNode.execute(transactions.createTransfer(sender, 50));
cluster.verify(sender.balanceEquals(50));
minerNode.execute(transactions.createIncrementalTransfers(sender, receiver, 1));
cluster.verify(receiver.balanceEquals(1));
minerNode.execute(transactions.createIncrementalTransfers(sender, receiver, 2));
cluster.verify(receiver.balanceEquals(3));
}
@Test
public void shouldMineTransactionsOnMultipleNodes() throws IOException {
final PantheonNode minerNode1 = pantheon.createCliqueNode("miner1");
final PantheonNode minerNode2 = pantheon.createCliqueNode("miner2");
final PantheonNode minerNode3 = pantheon.createCliqueNode("miner3");
cluster.start(minerNode1, minerNode2, minerNode3);
final Account sender = accounts.createAccount("account1");
final Account receiver = accounts.createAccount("account2");
minerNode1.execute(transactions.createTransfer(sender, 50));
cluster.verify(sender.balanceEquals(50));
minerNode2.execute(transactions.createIncrementalTransfers(sender, receiver, 1));
cluster.verify(receiver.balanceEquals(1));
minerNode3.execute(transactions.createIncrementalTransfers(sender, receiver, 2));
cluster.verify(receiver.balanceEquals(3));
}
@Test
public void shouldStallMiningWhenInsufficientValidators() throws IOException {
final PantheonNode minerNode1 = pantheon.createCliqueNode("miner1");
final PantheonNode minerNode2 = pantheon.createCliqueNode("miner2");
final PantheonNode minerNode3 = pantheon.createCliqueNode("miner3");
cluster.start(minerNode1, minerNode2, minerNode3);
cluster.stopNode(minerNode2);
cluster.stopNode(minerNode3);
minerNode1.verify(net.awaitPeerCount(0));
minerNode1.verify(clique.noNewBlockCreated(minerNode1));
}
}

@ -0,0 +1,45 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.tests.acceptance.clique;
import tech.pegasys.pantheon.tests.acceptance.dsl.AcceptanceTestBase;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.PantheonNode;
import java.io.IOException;
import org.junit.Test;
public class CliqueProposalRpcAcceptanceTest extends AcceptanceTestBase {
@Test
public void shouldReturnProposals() throws IOException {
final String[] initialValidators = {"miner1", "miner2"};
final PantheonNode minerNode1 =
pantheon.createCliqueNodeWithValidators("miner1", initialValidators);
final PantheonNode minerNode2 =
pantheon.createCliqueNodeWithValidators("miner2", initialValidators);
final PantheonNode minerNode3 =
pantheon.createCliqueNodeWithValidators("miner3", initialValidators);
cluster.start(minerNode1, minerNode2, minerNode3);
cluster.verify(clique.noProposals());
minerNode1.execute(cliqueTransactions.createAddProposal(minerNode3));
minerNode1.execute(cliqueTransactions.createRemoveProposal(minerNode2));
minerNode2.execute(cliqueTransactions.createRemoveProposal(minerNode3));
minerNode1.verify(
clique.proposalsEqual().addProposal(minerNode3).removeProposal(minerNode2).build());
minerNode2.verify(clique.proposalsEqual().removeProposal(minerNode3).build());
minerNode3.verify(clique.noProposals());
}
}

@ -0,0 +1,115 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.tests.acceptance.clique;
import tech.pegasys.pantheon.tests.acceptance.dsl.AcceptanceTestBase;
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.clique.ExpectNonceVote.CLIQUE_NONCE_VOTE;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.PantheonNode;
import tech.pegasys.pantheon.tests.acceptance.dsl.waitcondition.WaitCondition;
import java.io.IOException;
import org.junit.Test;
public class CliqueProposeRpcAcceptanceTest extends AcceptanceTestBase {
@Test
public void shouldAddValidators() throws IOException {
final String[] initialValidators = {"miner1", "miner2"};
final PantheonNode minerNode1 =
pantheon.createCliqueNodeWithValidators("miner1", initialValidators);
final PantheonNode minerNode2 =
pantheon.createCliqueNodeWithValidators("miner2", initialValidators);
final PantheonNode minerNode3 =
pantheon.createCliqueNodeWithValidators("miner3", initialValidators);
cluster.start(minerNode1, minerNode2, minerNode3);
cluster.verify(clique.validatorsEqual(minerNode1, minerNode2));
final WaitCondition cliqueValidatorsChanged = wait.cliqueValidatorsChanged(minerNode1);
minerNode1.execute(cliqueTransactions.createAddProposal(minerNode3));
minerNode2.execute(cliqueTransactions.createAddProposal(minerNode3));
cluster.waitUntil(cliqueValidatorsChanged);
cluster.verify(clique.validatorsEqual(minerNode1, minerNode2, minerNode3));
}
@Test
public void shouldRemoveValidators() throws IOException {
final String[] initialValidators = {"miner1", "miner2", "miner3"};
final PantheonNode minerNode1 =
pantheon.createCliqueNodeWithValidators("miner1", initialValidators);
final PantheonNode minerNode2 =
pantheon.createCliqueNodeWithValidators("miner2", initialValidators);
final PantheonNode minerNode3 =
pantheon.createCliqueNodeWithValidators("miner3", initialValidators);
cluster.start(minerNode1, minerNode2, minerNode3);
cluster.verify(clique.validatorsEqual(minerNode1, minerNode2, minerNode3));
final WaitCondition cliqueValidatorsChanged = wait.cliqueValidatorsChanged(minerNode1);
minerNode1.execute(cliqueTransactions.createRemoveProposal(minerNode3));
minerNode2.execute(cliqueTransactions.createRemoveProposal(minerNode3));
cluster.waitUntil(cliqueValidatorsChanged);
cluster.verify(clique.validatorsEqual(minerNode1, minerNode2));
}
@Test
public void shouldNotAddValidatorWhenInsufficientVotes() throws IOException {
final String[] initialValidators = {"miner1", "miner2"};
final PantheonNode minerNode1 =
pantheon.createCliqueNodeWithValidators("miner1", initialValidators);
final PantheonNode minerNode2 =
pantheon.createCliqueNodeWithValidators("miner2", initialValidators);
final PantheonNode minerNode3 =
pantheon.createCliqueNodeWithValidators("miner3", initialValidators);
cluster.start(minerNode1, minerNode2, minerNode3);
cluster.verify(clique.validatorsEqual(minerNode1, minerNode2));
minerNode1.execute(cliqueTransactions.createAddProposal(minerNode3));
minerNode1.waitUntil(wait.chainHeadHasProgressed(minerNode1, 1));
cluster.verify(clique.validatorsEqual(minerNode1, minerNode2));
}
@Test
public void shouldNotRemoveValidatorWhenInsufficientVotes() throws IOException {
final PantheonNode minerNode1 = pantheon.createCliqueNode("miner1");
final PantheonNode minerNode2 = pantheon.createCliqueNode("miner2");
final PantheonNode minerNode3 = pantheon.createCliqueNode("miner3");
cluster.start(minerNode1, minerNode2, minerNode3);
cluster.verify(clique.validatorsEqual(minerNode1, minerNode2, minerNode3));
minerNode1.execute(cliqueTransactions.createRemoveProposal(minerNode3));
minerNode1.waitUntil(wait.chainHeadHasProgressed(minerNode1, 1));
cluster.verify(clique.validatorsEqual(minerNode1, minerNode2, minerNode3));
}
@Test
public void shouldIncludeVoteInBlockHeader() throws IOException {
final String[] initialValidators = {"miner1", "miner2"};
final PantheonNode minerNode1 =
pantheon.createCliqueNodeWithValidators("miner1", initialValidators);
final PantheonNode minerNode2 =
pantheon.createCliqueNodeWithValidators("miner2", initialValidators);
final PantheonNode minerNode3 =
pantheon.createCliqueNodeWithValidators("miner3", initialValidators);
cluster.start(minerNode1, minerNode2, minerNode3);
minerNode1.execute(cliqueTransactions.createAddProposal(minerNode3));
minerNode1.waitUntil(wait.chainHeadHasProgressed(minerNode1, 1));
minerNode1.verify(blockchain.beneficiaryEquals(minerNode3));
minerNode1.verify(clique.nonceVoteEquals(CLIQUE_NONCE_VOTE.AUTH));
minerNode1.execute(cliqueTransactions.createRemoveProposal(minerNode2));
minerNode1.waitUntil(wait.chainHeadHasProgressed(minerNode1, 1));
minerNode1.verify(blockchain.beneficiaryEquals(minerNode2));
minerNode1.verify(clique.nonceVoteEquals(CLIQUE_NONCE_VOTE.DROP));
}
}

@ -14,6 +14,7 @@ package tech.pegasys.pantheon.tests.acceptance.dsl;
import tech.pegasys.pantheon.tests.acceptance.dsl.account.Accounts;
import tech.pegasys.pantheon.tests.acceptance.dsl.blockchain.Blockchain;
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.clique.CliqueConditions;
import tech.pegasys.pantheon.tests.acceptance.dsl.contract.ContractVerifier;
import tech.pegasys.pantheon.tests.acceptance.dsl.jsonrpc.Eth;
import tech.pegasys.pantheon.tests.acceptance.dsl.jsonrpc.Net;
@ -21,9 +22,11 @@ import tech.pegasys.pantheon.tests.acceptance.dsl.jsonrpc.Web3;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.Cluster;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.PantheonNodeFactory;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.Transactions;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.clique.CliqueTransactions;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.eth.EthTransactions;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.net.NetTransactions;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.web3.Web3Transactions;
import tech.pegasys.pantheon.tests.acceptance.dsl.waitcondition.WaitConditions;
import org.junit.After;
@ -32,24 +35,30 @@ public class AcceptanceTestBase {
protected final Accounts accounts;
protected final Blockchain blockchain;
protected final Cluster cluster;
protected final CliqueConditions clique;
protected final CliqueTransactions cliqueTransactions;
protected final Transactions transactions;
protected final Web3 web3;
protected final Eth eth;
protected final Net net;
protected final PantheonNodeFactory pantheon;
protected final ContractVerifier contractVerifier;
protected final WaitConditions wait;
protected AcceptanceTestBase() {
final EthTransactions ethTransactions = new EthTransactions();
accounts = new Accounts(ethTransactions);
blockchain = new Blockchain(ethTransactions);
eth = new Eth(ethTransactions);
cliqueTransactions = new CliqueTransactions();
clique = new CliqueConditions(ethTransactions, cliqueTransactions);
net = new Net(new NetTransactions());
cluster = new Cluster(net);
transactions = new Transactions(accounts);
web3 = new Web3(new Web3Transactions());
pantheon = new PantheonNodeFactory();
contractVerifier = new ContractVerifier(accounts.getPrimaryBenefactor());
wait = new WaitConditions(ethTransactions, cliqueTransactions);
}
@After

@ -21,4 +21,11 @@ public class WaitUtils {
public static void waitFor(final ThrowingRunnable condition) {
Awaitility.await().ignoreExceptions().atMost(30, TimeUnit.SECONDS).untilAsserted(condition);
}
public static void waitFor(final int timeout, final ThrowingRunnable condition) {
Awaitility.await()
.ignoreExceptions()
.atMost(timeout, TimeUnit.SECONDS)
.untilAsserted(condition);
}
}

@ -13,8 +13,10 @@
package tech.pegasys.pantheon.tests.acceptance.dsl.blockchain;
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.Condition;
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.blockchain.ExpectBeneficiary;
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.blockchain.ExpectMinimumBlockNumber;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.Node;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.PantheonNode;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.eth.EthTransactions;
public class Blockchain {
@ -28,4 +30,8 @@ public class Blockchain {
public Condition blockNumberMustBeLatest(final Node node) {
return new ExpectMinimumBlockNumber(eth, node.execute(eth.blockNumber()));
}
public Condition beneficiaryEquals(final PantheonNode node) {
return new ExpectBeneficiary(eth, node);
}
}

@ -0,0 +1,36 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.tests.acceptance.dsl.condition.blockchain;
import static org.assertj.core.api.Java6Assertions.assertThat;
import static tech.pegasys.pantheon.tests.acceptance.dsl.WaitUtils.waitFor;
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.Condition;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.Node;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.PantheonNode;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.eth.EthTransactions;
public class ExpectBeneficiary implements Condition {
private final EthTransactions eth;
private final String beneficiary;
public ExpectBeneficiary(final EthTransactions eth, final PantheonNode node) {
this.eth = eth;
this.beneficiary = node.getAddress().toString();
}
@Override
public void verify(final Node node) {
waitFor(() -> assertThat(node.execute(eth.block()).getMiner()).isEqualTo(beneficiary));
}
}

@ -0,0 +1,76 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.tests.acceptance.dsl.condition.blockchain;
import static junit.framework.TestCase.fail;
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.Condition;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.Node;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.eth.EthTransactions;
import java.math.BigInteger;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.awaitility.Awaitility;
public class ExpectBlockNotCreated implements Condition {
private final EthTransactions eth;
private final int initialWait;
private final int blockPeriodWait;
public ExpectBlockNotCreated(
final EthTransactions eth, final int initialWait, final int blockPeriodWait) {
this.eth = eth;
this.initialWait = initialWait;
this.blockPeriodWait = blockPeriodWait;
}
@Override
public void verify(final Node node) {
final AtomicInteger tries = new AtomicInteger();
final int triesRequired = blockPeriodWait / 1000;
final BigInteger initialBlock = initialBlock(node);
Awaitility.await()
.pollInterval(1, TimeUnit.SECONDS)
.atMost(30, TimeUnit.SECONDS)
.until(() -> isNewBlock(node, tries, triesRequired, initialBlock));
}
private Boolean isNewBlock(
final Node node,
final AtomicInteger tries,
final int triesRequired,
final BigInteger lastBlock) {
final BigInteger currentBlock = node.execute(eth.blockNumber());
final int currentTries = tries.getAndIncrement();
if (!currentBlock.equals(lastBlock)) {
final String failMsg =
String.format(
"New block created. Expected block=%s got block=%s", lastBlock, currentBlock);
fail(failMsg);
}
// Only consider that the block is really the same after a period of time or number tries as a
// block could be currently be in the process of being mined
return currentTries > triesRequired;
}
private BigInteger initialBlock(final Node node) {
try {
Thread.sleep(initialWait);
return node.execute(eth.blockNumber());
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
}
}

@ -0,0 +1,37 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.tests.acceptance.dsl.condition.blockchain;
import static org.assertj.core.api.Java6Assertions.assertThat;
import static tech.pegasys.pantheon.tests.acceptance.dsl.WaitUtils.waitFor;
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.Condition;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.Node;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.eth.EthTransactions;
import java.math.BigInteger;
public class ExpectBlockNumber implements Condition {
private final EthTransactions eth;
private final BigInteger blockNumber;
public ExpectBlockNumber(final EthTransactions eth, final BigInteger blockNumber) {
this.eth = eth;
this.blockNumber = blockNumber;
}
@Override
public void verify(final Node node) {
waitFor(() -> assertThat(node.execute(eth.blockNumber())).isEqualTo(blockNumber));
}
}

@ -0,0 +1,123 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.tests.acceptance.dsl.condition.clique;
import static java.util.Collections.emptyList;
import static tech.pegasys.pantheon.ethereum.core.Hash.fromHexString;
import tech.pegasys.pantheon.config.CliqueConfigOptions;
import tech.pegasys.pantheon.config.GenesisConfigFile;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.Condition;
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.blockchain.ExpectBlockNotCreated;
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.clique.ExpectNonceVote.CLIQUE_NONCE_VOTE;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.Node;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.PantheonNode;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.clique.CliqueTransactions;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.eth.EthTransactions;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import com.google.common.collect.ImmutableMap;
import org.web3j.protocol.core.DefaultBlockParameter;
public class CliqueConditions {
private final EthTransactions eth;
private final CliqueTransactions clique;
public CliqueConditions(final EthTransactions eth, final CliqueTransactions clique) {
this.eth = eth;
this.clique = clique;
}
public ExpectValidators validatorsEqual(final PantheonNode... validators) {
return new ExpectValidators(clique, validatorAddresses(validators));
}
public Condition validatorsAtBlockEqual(
final String blockNumber, final PantheonNode... validators) {
return new ExpectValidatorsAtBlock(clique, blockNumber, validatorAddresses(validators));
}
public Condition validatorsAtBlockHashFromBlockNumberEqual(
final Node node, final long blockNumber, final PantheonNode... validators) {
final DefaultBlockParameter blockParameter =
DefaultBlockParameter.valueOf(BigInteger.valueOf(blockNumber));
final String blockHash = node.execute(eth.block(blockParameter)).getHash();
return new ExpectValidatorsAtBlockHash(
clique, fromHexString(blockHash), validatorAddresses(validators));
}
public ProposalsConfig proposalsEqual() {
return new ProposalsConfig(clique);
}
public Condition noProposals() {
return new ExpectProposals(clique, ImmutableMap.of());
}
public Condition nonceVoteEquals(final CLIQUE_NONCE_VOTE clique_nonce_vote) {
return new ExpectNonceVote(eth, clique_nonce_vote);
}
public Condition noNewBlockCreated(final PantheonNode node) {
final int blockPeriodSeconds = cliqueBlockPeriod(node);
final int blockPeriodWait = blockPeriodSeconds * 1000;
return new ExpectBlockNotCreated(eth, blockPeriodWait, blockPeriodWait);
}
private int cliqueBlockPeriod(final PantheonNode node) {
final String config = node.genesisConfigProvider().createGenesisConfig(emptyList()).get();
final GenesisConfigFile genesisConfigFile = GenesisConfigFile.fromConfig(config);
final CliqueConfigOptions cliqueConfigOptions =
genesisConfigFile.getConfigOptions().getCliqueConfigOptions();
return cliqueConfigOptions.getBlockPeriodSeconds();
}
private Address[] validatorAddresses(final PantheonNode[] validators) {
return Arrays.stream(validators).map(PantheonNode::getAddress).sorted().toArray(Address[]::new);
}
public static class ProposalsConfig {
private final Map<PantheonNode, Boolean> proposals = new HashMap<>();
private final CliqueTransactions clique;
public ProposalsConfig(final CliqueTransactions clique) {
this.clique = clique;
}
public ProposalsConfig addProposal(final PantheonNode node) {
proposals.put(node, true);
return this;
}
public ProposalsConfig removeProposal(final PantheonNode node) {
proposals.put(node, false);
return this;
}
public Condition build() {
final Map<Address, Boolean> proposalsAsAddress =
this.proposals
.entrySet()
.stream()
.collect(Collectors.toMap(p -> p.getKey().getAddress(), Entry::getValue));
return new ExpectProposals(clique, proposalsAsAddress);
}
}
}

@ -0,0 +1,43 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.tests.acceptance.dsl.condition.clique;
import static org.assertj.core.api.Java6Assertions.assertThat;
import static tech.pegasys.pantheon.tests.acceptance.dsl.WaitUtils.waitFor;
import static tech.pegasys.pantheon.tests.acceptance.dsl.condition.clique.ExpectNonceVote.CLIQUE_NONCE_VOTE.AUTH;
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.Condition;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.Node;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.eth.EthTransactions;
public class ExpectNonceVote implements Condition {
private static final String NONCE_AUTH = "0xffffffffffffffff";
private static final String NONCE_DROP = "0x0000000000000000";
private final EthTransactions eth;
private final String expectedNonce;
public enum CLIQUE_NONCE_VOTE {
AUTH,
DROP
}
public ExpectNonceVote(final EthTransactions eth, final CLIQUE_NONCE_VOTE vote) {
this.eth = eth;
this.expectedNonce = vote == AUTH ? NONCE_AUTH : NONCE_DROP;
}
@Override
public void verify(final Node node) {
waitFor(() -> assertThat(node.execute(eth.block()).getNonceRaw()).isEqualTo(expectedNonce));
}
}

@ -0,0 +1,38 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.tests.acceptance.dsl.condition.clique;
import static org.assertj.core.api.Assertions.assertThat;
import static tech.pegasys.pantheon.tests.acceptance.dsl.WaitUtils.waitFor;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.Condition;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.Node;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.clique.CliqueTransactions;
import java.util.Map;
public class ExpectProposals implements Condition {
private final CliqueTransactions clique;
private final Map<Address, Boolean> proposers;
public ExpectProposals(final CliqueTransactions clique, final Map<Address, Boolean> proposers) {
this.clique = clique;
this.proposers = proposers;
}
@Override
public void verify(final Node node) {
waitFor(() -> assertThat(node.execute(clique.createProposals())).isEqualTo(proposers));
}
}

@ -0,0 +1,39 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.tests.acceptance.dsl.condition.clique;
import static org.assertj.core.api.Assertions.assertThat;
import static tech.pegasys.pantheon.tests.acceptance.dsl.WaitUtils.waitFor;
import static tech.pegasys.pantheon.tests.acceptance.dsl.transaction.clique.CliqueTransactions.LATEST;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.Condition;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.Node;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.clique.CliqueTransactions;
public class ExpectValidators implements Condition {
private final CliqueTransactions clique;
private final Address[] validators;
public ExpectValidators(final CliqueTransactions clique, final Address... validators) {
this.clique = clique;
this.validators = validators;
}
@Override
public void verify(final Node node) {
waitFor(
() ->
assertThat(node.execute(clique.createGetSigners(LATEST))).containsExactly(validators));
}
}

@ -0,0 +1,42 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.tests.acceptance.dsl.condition.clique;
import static org.assertj.core.api.Assertions.assertThat;
import static tech.pegasys.pantheon.tests.acceptance.dsl.WaitUtils.waitFor;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.Condition;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.Node;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.clique.CliqueTransactions;
public class ExpectValidatorsAtBlock implements Condition {
private final CliqueTransactions clique;
private final String blockParameter;
private final Address[] validators;
public ExpectValidatorsAtBlock(
final CliqueTransactions clique, final String blockNumber, final Address... validators) {
this.clique = clique;
this.blockParameter = blockNumber;
this.validators = validators;
}
@Override
public void verify(final Node node) {
waitFor(
() ->
assertThat(node.execute(clique.createGetSigners(blockParameter)))
.containsExactly(validators));
}
}

@ -0,0 +1,43 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.tests.acceptance.dsl.condition.clique;
import static org.assertj.core.api.Assertions.assertThat;
import static tech.pegasys.pantheon.tests.acceptance.dsl.WaitUtils.waitFor;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.Hash;
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.Condition;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.Node;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.clique.CliqueTransactions;
public class ExpectValidatorsAtBlockHash implements Condition {
private final CliqueTransactions clique;
private final Hash blockHash;
private final Address[] validators;
public ExpectValidatorsAtBlockHash(
final CliqueTransactions clique, final Hash blockHash, final Address... validators) {
this.clique = clique;
this.blockHash = blockHash;
this.validators = validators;
}
@Override
public void verify(final Node node) {
waitFor(
() ->
assertThat(node.execute(clique.createGetSignersAtHash(blockHash)))
.containsExactly(validators));
}
}

@ -14,17 +14,21 @@ package tech.pegasys.pantheon.tests.acceptance.dsl.node;
import static org.assertj.core.api.Assertions.assertThat;
import tech.pegasys.pantheon.cli.EthNetworkConfig;
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.Condition;
import tech.pegasys.pantheon.tests.acceptance.dsl.jsonrpc.Net;
import tech.pegasys.pantheon.tests.acceptance.dsl.waitcondition.WaitCondition;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
public class Cluster implements AutoCloseable {
public static final int NETWORK_ID = 10;
private final Map<String, RunnableNode> nodes = new HashMap<>();
private final PantheonNodeRunner pantheonNodeRunner = PantheonNodeRunner.instance();
@ -49,6 +53,7 @@ public class Cluster implements AutoCloseable {
this.nodes.clear();
final List<String> bootNodes = new ArrayList<>();
for (final RunnableNode node : nodes) {
this.nodes.put(node.getName(), node);
bootNodes.add(node.getConfiguration().enodeUrl());
@ -56,6 +61,12 @@ public class Cluster implements AutoCloseable {
for (final RunnableNode node : nodes) {
node.getConfiguration().bootnodes(bootNodes);
Optional<EthNetworkConfig> ethNetworkConfig =
node.getConfiguration()
.genesisConfigProvider()
.createGenesisConfig(nodes)
.map(config -> new EthNetworkConfig(config, NETWORK_ID, bootNodes));
node.getConfiguration().ethNetworkConfig(ethNetworkConfig);
node.start(pantheonNodeRunner);
}
@ -71,6 +82,10 @@ public class Cluster implements AutoCloseable {
pantheonNodeRunner.shutdown();
}
public void stopNode(final PantheonNode node) {
pantheonNodeRunner.stopNode(node);
}
@Override
public void close() {
for (final RunnableNode node : nodes.values()) {
@ -84,4 +99,10 @@ public class Cluster implements AutoCloseable {
expected.verify(node);
}
}
public void waitUntil(final WaitCondition condition) {
for (final Node node : nodes.values()) {
node.waitUntil(condition);
}
}
}

@ -0,0 +1,21 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.tests.acceptance.dsl.node;
import java.util.List;
import java.util.Optional;
@FunctionalInterface
public interface GenesisConfigProvider {
Optional<String> createGenesisConfig(final List<RunnableNode> validators);
}

@ -14,10 +14,12 @@ package tech.pegasys.pantheon.tests.acceptance.dsl.node;
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.Condition;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.Transaction;
import tech.pegasys.pantheon.tests.acceptance.dsl.waitcondition.WaitCondition;
public interface Node {
<T> T execute(Transaction<T> transaction);
void verify(final Condition expected);
void waitUntil(final WaitCondition condition);
}

@ -12,6 +12,8 @@
*/
package tech.pegasys.pantheon.tests.acceptance.dsl.node;
import tech.pegasys.pantheon.cli.EthNetworkConfig;
import java.util.List;
import java.util.Optional;
@ -28,4 +30,10 @@ public interface NodeConfiguration {
String hostName();
boolean jsonRpcEnabled();
GenesisConfigProvider genesisConfigProvider();
Optional<EthNetworkConfig> ethNetworkConfig();
void ethNetworkConfig(Optional<EthNetworkConfig> ethNetworkConfig);
}

@ -14,13 +14,18 @@ package tech.pegasys.pantheon.tests.acceptance.dsl.node;
import static org.apache.logging.log4j.LogManager.getLogger;
import tech.pegasys.pantheon.cli.EthNetworkConfig;
import tech.pegasys.pantheon.controller.KeyPairUtil;
import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.MiningParameters;
import tech.pegasys.pantheon.ethereum.core.Util;
import tech.pegasys.pantheon.ethereum.jsonrpc.JsonRpcConfiguration;
import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration;
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.Condition;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.PantheonWeb3j;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.Transaction;
import tech.pegasys.pantheon.tests.acceptance.dsl.waitcondition.WaitCondition;
import java.io.File;
import java.io.FileInputStream;
@ -43,7 +48,6 @@ import org.apache.logging.log4j.Logger;
import org.awaitility.Awaitility;
import org.awaitility.core.ConditionTimeoutException;
import org.java_websocket.exceptions.WebsocketNotConnectedException;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.http.HttpService;
import org.web3j.protocol.websocket.WebSocketClient;
import org.web3j.protocol.websocket.WebSocketListener;
@ -64,15 +68,20 @@ public class PantheonNode implements Node, NodeConfiguration, RunnableNode, Auto
private final MiningParameters miningParameters;
private final JsonRpcConfiguration jsonRpcConfiguration;
private final WebSocketConfiguration webSocketConfiguration;
private final GenesisConfigProvider genesisConfigProvider;
private final boolean devMode;
private List<String> bootnodes = new ArrayList<>();
private Web3j web3j;
private PantheonWeb3j pantheonWeb3j;
private Optional<EthNetworkConfig> ethNetworkConfig = Optional.empty();
public PantheonNode(
final String name,
final MiningParameters miningParameters,
final JsonRpcConfiguration jsonRpcConfiguration,
final WebSocketConfiguration webSocketConfiguration,
final boolean devMode,
final GenesisConfigProvider genesisConfigProvider,
final int p2pPort)
throws IOException {
this.homeDirectory = Files.createTempDirectory("acctest");
@ -82,6 +91,8 @@ public class PantheonNode implements Node, NodeConfiguration, RunnableNode, Auto
this.miningParameters = miningParameters;
this.jsonRpcConfiguration = jsonRpcConfiguration;
this.webSocketConfiguration = webSocketConfiguration;
this.genesisConfigProvider = genesisConfigProvider;
this.devMode = devMode;
LOG.info("Created PantheonNode {}", this.toString());
}
@ -138,19 +149,19 @@ public class PantheonNode implements Node, NodeConfiguration, RunnableNode, Auto
return LOCALHOST;
}
private Web3j web3j() {
private PantheonWeb3j pantheonWeb3j() {
if (web3j == null) {
if (pantheonWeb3j == null) {
if (!jsonRpcBaseUrl().isPresent()) {
return Web3j.build(
return new PantheonWeb3j(
new HttpService("http://" + LOCALHOST + ":8545"), 2000, Async.defaultExecutorService());
}
return Web3j.build(
return new PantheonWeb3j(
new HttpService(jsonRpcBaseUrl().get()), 2000, Async.defaultExecutorService());
}
return web3j;
return pantheonWeb3j;
}
/** All future JSON-RPC calls are made via a web sockets connection. */
@ -167,11 +178,11 @@ public class PantheonNode implements Node, NodeConfiguration, RunnableNode, Auto
throw new RuntimeException("Error connection to WebSocket endpoint", e);
}
if (web3j != null) {
web3j.shutdown();
if (pantheonWeb3j != null) {
pantheonWeb3j.shutdown();
}
web3j = Web3j.build(webSocketService, 2000, Async.defaultExecutorService());
pantheonWeb3j = new PantheonWeb3j(webSocketService, 2000, Async.defaultExecutorService());
}
private void checkIfWebSocketEndpointIsAvailable(final String url) {
@ -235,6 +246,11 @@ public class PantheonNode implements Node, NodeConfiguration, RunnableNode, Auto
}
}
@Override
public Address getAddress() {
return Util.publicKeyToAddress(keyPair.getPublicKey());
}
Path homeDirectory() {
return homeDirectory;
}
@ -293,6 +309,10 @@ public class PantheonNode implements Node, NodeConfiguration, RunnableNode, Auto
return miningParameters;
}
public boolean isDevMode() {
return devMode;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
@ -305,9 +325,9 @@ public class PantheonNode implements Node, NodeConfiguration, RunnableNode, Auto
@Override
public void stop() {
if (web3j != null) {
web3j.shutdown();
web3j = null;
if (pantheonWeb3j != null) {
pantheonWeb3j.shutdown();
pantheonWeb3j = null;
}
}
@ -321,13 +341,33 @@ public class PantheonNode implements Node, NodeConfiguration, RunnableNode, Auto
}
}
@Override
public GenesisConfigProvider genesisConfigProvider() {
return genesisConfigProvider;
}
@Override
public Optional<EthNetworkConfig> ethNetworkConfig() {
return ethNetworkConfig;
}
@Override
public void ethNetworkConfig(final Optional<EthNetworkConfig> ethNetworkConfig) {
this.ethNetworkConfig = ethNetworkConfig;
}
@Override
public <T> T execute(final Transaction<T> transaction) {
return transaction.execute(web3j());
return transaction.execute(pantheonWeb3j());
}
@Override
public void verify(final Condition expected) {
expected.verify(this);
}
@Override
public void waitUntil(final WaitCondition expected) {
expected.waitUntil(this);
}
}

@ -12,6 +12,12 @@
*/
package tech.pegasys.pantheon.tests.acceptance.dsl.node;
import static java.util.Arrays.asList;
import static java.util.stream.Collectors.toList;
import static tech.pegasys.pantheon.consensus.clique.jsonrpc.CliqueRpcApis.CLIQUE;
import tech.pegasys.pantheon.consensus.clique.CliqueExtraData;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.MiningParameters;
import tech.pegasys.pantheon.ethereum.core.MiningParametersTestBuilder;
import tech.pegasys.pantheon.ethereum.jsonrpc.JsonRpcConfiguration;
@ -20,7 +26,15 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration;
import java.io.IOException;
import java.net.ServerSocket;
import java.util.Arrays;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import com.google.common.io.Resources;
public class PantheonNodeFactory {
@ -32,6 +46,8 @@ public class PantheonNodeFactory {
config.getMiningParameters(),
config.getJsonRpcConfiguration(),
config.getWebSocketConfiguration(),
config.isDevMode(),
config.getGenesisConfigProvider(),
serverSocket.getLocalPort());
serverSocket.close();
@ -62,15 +78,74 @@ public class PantheonNodeFactory {
public PantheonNode createArchiveNodeWithRpcApis(
final String name, final RpcApi... enabledRpcApis) throws IOException {
final JsonRpcConfiguration jsonRpcConfig = createJsonRpcConfig();
jsonRpcConfig.setRpcApis(Arrays.asList(enabledRpcApis));
jsonRpcConfig.setRpcApis(asList(enabledRpcApis));
final WebSocketConfiguration webSocketConfig = createWebSocketConfig();
webSocketConfig.setRpcApis(Arrays.asList(enabledRpcApis));
webSocketConfig.setRpcApis(asList(enabledRpcApis));
return create(
new PantheonFactoryConfiguration(
name, createMiningParameters(false), jsonRpcConfig, webSocketConfig));
}
public PantheonNode createCliqueNode(final String name) throws IOException {
return create(
new PantheonFactoryConfiguration(
name,
createMiningParameters(true),
jsonRpcConfigWithClique(),
createWebSocketConfig(),
false,
this::createCliqueGenesisConfig));
}
public PantheonNode createCliqueNodeWithValidators(final String name, final String... validators)
throws IOException {
return create(
new PantheonFactoryConfiguration(
name,
createMiningParameters(true),
jsonRpcConfigWithClique(),
createWebSocketConfig(),
false,
nodes -> createCliqueGenesisConfigForValidators(asList(validators), nodes)));
}
private Optional<String> createCliqueGenesisConfig(final Collection<RunnableNode> validators) {
String genesisTemplate = cliqueGenesisTemplateConfig();
String cliqueExtraData = encodeCliqueExtraData(validators);
String genesis = genesisTemplate.replaceAll("%cliqueExtraData%", cliqueExtraData);
return Optional.of(genesis);
}
private Optional<String> createCliqueGenesisConfigForValidators(
final Collection<String> validators, final Collection<RunnableNode> pantheonNodes) {
List<RunnableNode> collect =
pantheonNodes.stream().filter(n -> validators.contains(n.getName())).collect(toList());
return createCliqueGenesisConfig(collect);
}
private String cliqueGenesisTemplateConfig() {
try {
URI uri = Resources.getResource("clique/clique.json").toURI();
return Resources.toString(uri.toURL(), Charset.defaultCharset());
} catch (URISyntaxException | IOException e) {
throw new IllegalStateException("Unable to get test clique genesis config");
}
}
private String encodeCliqueExtraData(final Collection<RunnableNode> nodes) {
final List<Address> addresses = nodes.stream().map(RunnableNode::getAddress).collect(toList());
return CliqueExtraData.createGenesisExtraDataString(addresses);
}
private JsonRpcConfiguration jsonRpcConfigWithClique() {
final JsonRpcConfiguration jsonRpcConfig = createJsonRpcConfig();
final List<RpcApi> rpcApis = new ArrayList<>(jsonRpcConfig.getRpcApis());
rpcApis.add(CLIQUE);
jsonRpcConfig.setRpcApis(rpcApis);
return jsonRpcConfig;
}
private MiningParameters createMiningParameters(final boolean miner) {
return new MiningParametersTestBuilder().enabled(miner).build();
}
@ -95,16 +170,36 @@ public class PantheonNodeFactory {
private final MiningParameters miningParameters;
private final JsonRpcConfiguration jsonRpcConfiguration;
private final WebSocketConfiguration webSocketConfiguration;
private final boolean devMode;
private final GenesisConfigProvider genesisConfigProvider;
public PantheonFactoryConfiguration(
final String name,
final MiningParameters miningParameters,
final JsonRpcConfiguration jsonRpcConfiguration,
final WebSocketConfiguration webSocketConfiguration) {
this(
name,
miningParameters,
jsonRpcConfiguration,
webSocketConfiguration,
true,
ignore -> Optional.empty());
}
public PantheonFactoryConfiguration(
final String name,
final MiningParameters miningParameters,
final JsonRpcConfiguration jsonRpcConfiguration,
final WebSocketConfiguration webSocketConfiguration,
final boolean devMode,
final GenesisConfigProvider genesisConfigProvider) {
this.name = name;
this.miningParameters = miningParameters;
this.jsonRpcConfiguration = jsonRpcConfiguration;
this.webSocketConfiguration = webSocketConfiguration;
this.devMode = devMode;
this.genesisConfigProvider = genesisConfigProvider;
}
public String getName() {
@ -122,5 +217,13 @@ public class PantheonNodeFactory {
public WebSocketConfiguration getWebSocketConfiguration() {
return webSocketConfiguration;
}
public boolean isDevMode() {
return devMode;
}
public GenesisConfigProvider getGenesisConfigProvider() {
return genesisConfigProvider;
}
}
}

@ -12,11 +12,15 @@
*/
package tech.pegasys.pantheon.tests.acceptance.dsl.node;
import static java.nio.charset.StandardCharsets.UTF_8;
import tech.pegasys.pantheon.cli.EthNetworkConfig;
import tech.pegasys.pantheon.ethereum.jsonrpc.RpcApi;
import tech.pegasys.pantheon.ethereum.jsonrpc.RpcApis;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
@ -49,7 +53,9 @@ public class ProcessPantheonNodeRunner implements PantheonNodeRunner {
params.add("--datadir");
params.add(dataDir.toAbsolutePath().toString());
if (node.isDevMode()) {
params.add("--dev-mode");
}
params.add("--p2p-listen");
params.add(node.p2pListenAddress());
@ -79,6 +85,15 @@ public class ProcessPantheonNodeRunner implements PantheonNodeRunner {
params.add(apiList(node.webSocketConfiguration().getRpcApis()));
}
if (node.ethNetworkConfig().isPresent()) {
EthNetworkConfig ethNetworkConfig = node.ethNetworkConfig().get();
Path genesisFile = createGenesisFile(node, ethNetworkConfig);
params.add("--genesis");
params.add(genesisFile.toString());
params.add("--network-id");
params.add(Integer.toString(ethNetworkConfig.getNetworkId()));
}
final ProcessBuilder processBuilder =
new ProcessBuilder(params)
.directory(new File(System.getProperty("user.dir")).getParentFile())
@ -94,6 +109,16 @@ public class ProcessPantheonNodeRunner implements PantheonNodeRunner {
waitForPortsFile(dataDir);
}
private Path createGenesisFile(final PantheonNode node, final EthNetworkConfig ethNetworkConfig) {
try {
Path genesisFile = Files.createTempFile(node.homeDirectory(), "gensis", "");
Files.write(genesisFile, ethNetworkConfig.getGenesisConfig().getBytes(UTF_8));
return genesisFile;
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
private String apiList(final Collection<RpcApi> rpcApis) {
return String.join(",", rpcApis.stream().map(RpcApis::getValue).collect(Collectors.toList()));
}

@ -12,6 +12,7 @@
*/
package tech.pegasys.pantheon.tests.acceptance.dsl.node;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.Condition;
public interface RunnableNode extends Node {
@ -27,4 +28,6 @@ public interface RunnableNode extends Node {
void awaitPeerDiscovery(final Condition condition);
String getName();
Address getAddress();
}

@ -51,8 +51,9 @@ public class ThreadPantheonNodeRunner implements PantheonNodeRunner {
final PantheonControllerBuilder builder = new PantheonControllerBuilder();
final EthNetworkConfig ethNetworkConfig =
new EthNetworkConfig.Builder(mainnet()).setNetworkId(NETWORK_ID).build();
final PantheonController<?> pantheonController;
node.ethNetworkConfig()
.orElse(new EthNetworkConfig.Builder(mainnet()).setNetworkId(NETWORK_ID).build());
PantheonController<?> pantheonController;
try {
pantheonController =
builder.build(
@ -61,7 +62,7 @@ public class ThreadPantheonNodeRunner implements PantheonNodeRunner {
ethNetworkConfig,
false,
node.getMiningParameters(),
true,
node.isDevMode(),
KeyPairUtil.getDefaultKeyFile(node.homeDirectory()));
} catch (final IOException e) {
throw new RuntimeException("Error building PantheonController", e);

@ -37,7 +37,7 @@ public class DeploySmartContractTransaction<T extends Contract> implements Trans
}
@Override
public T execute(final Web3j node) {
public T execute(final PantheonWeb3j node) {
try {
final Method method =
clazz.getMethod(

@ -0,0 +1,81 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.tests.acceptance.dsl.transaction;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.Hash;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import org.web3j.protocol.Web3jService;
import org.web3j.protocol.core.JsonRpc2_0Web3j;
import org.web3j.protocol.core.Request;
import org.web3j.protocol.core.Response;
public class PantheonWeb3j extends JsonRpc2_0Web3j {
public PantheonWeb3j(final Web3jService web3jService) {
super(web3jService);
}
public PantheonWeb3j(
final Web3jService web3jService,
final long pollingInterval,
final ScheduledExecutorService scheduledExecutorService) {
super(web3jService, pollingInterval, scheduledExecutorService);
}
public Request<?, ProposeResponse> cliquePropose(final String address, final Boolean auth) {
return new Request<>(
"clique_propose",
Arrays.asList(address, auth.toString()),
web3jService,
ProposeResponse.class);
}
public Request<?, DiscardResponse> cliqueDiscard(final String address) {
return new Request<>(
"clique_discard", singletonList(address), web3jService, DiscardResponse.class);
}
public Request<?, ProposalsResponse> cliqueProposals() {
return new Request<>("clique_proposals", emptyList(), web3jService, ProposalsResponse.class);
}
public Request<?, SignersBlockResponse> cliqueGetSigners(final String blockNumber) {
return new Request<>(
"clique_getSigners", singletonList(blockNumber), web3jService, SignersBlockResponse.class);
}
public Request<?, SignersBlockResponse> cliqueGetSignersAtHash(final Hash hash) {
return new Request<>(
"clique_getSignersAtHash",
singletonList(hash.toString()),
web3jService,
SignersBlockResponse.class);
}
public static class ProposeResponse extends Response<Boolean> {}
public static class DiscardResponse extends Response<Boolean> {}
public static class SignersBlockResponse extends Response<List<Address>> {}
public static class ProposalsResponse extends Response<Map<Address, Boolean>> {}
}

@ -12,9 +12,7 @@
*/
package tech.pegasys.pantheon.tests.acceptance.dsl.transaction;
import org.web3j.protocol.Web3j;
public interface Transaction<T> {
T execute(final Web3j node);
T execute(final PantheonWeb3j node);
}

@ -16,6 +16,7 @@ import static org.web3j.utils.Numeric.toHexString;
import tech.pegasys.pantheon.ethereum.core.Hash;
import tech.pegasys.pantheon.tests.acceptance.dsl.account.Account;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.PantheonWeb3j;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.Transaction;
import java.io.IOException;
@ -23,7 +24,6 @@ import java.math.BigInteger;
import org.web3j.crypto.RawTransaction;
import org.web3j.crypto.TransactionEncoder;
import org.web3j.protocol.Web3j;
import org.web3j.utils.Convert;
import org.web3j.utils.Convert.Unit;
@ -46,7 +46,7 @@ public class TransferTransaction implements Transaction<Hash> {
}
@Override
public Hash execute(final Web3j node) {
public Hash execute(final PantheonWeb3j node) {
final RawTransaction transaction =
RawTransaction.createEtherTransaction(
sender.getNextNonce(),

@ -13,13 +13,12 @@
package tech.pegasys.pantheon.tests.acceptance.dsl.transaction.account;
import tech.pegasys.pantheon.ethereum.core.Hash;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.PantheonWeb3j;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.Transaction;
import java.util.ArrayList;
import java.util.List;
import org.web3j.protocol.Web3j;
public class TransferTransactionSet implements Transaction<List<Hash>> {
private final List<TransferTransaction> transactions;
@ -29,7 +28,7 @@ public class TransferTransactionSet implements Transaction<List<Hash>> {
}
@Override
public List<Hash> execute(final Web3j node) {
public List<Hash> execute(final PantheonWeb3j node) {
final List<Hash> hashes = new ArrayList<>();
for (final TransferTransaction transaction : transactions) {

@ -0,0 +1,41 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.tests.acceptance.dsl.transaction.clique;
import static org.assertj.core.api.Assertions.assertThat;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.PantheonWeb3j;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.PantheonWeb3j.DiscardResponse;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.Transaction;
import java.io.IOException;
public class CliqueDiscard implements Transaction<Boolean> {
private final String address;
public CliqueDiscard(final String address) {
this.address = address;
}
@Override
public Boolean execute(final PantheonWeb3j node) {
try {
final DiscardResponse result = node.cliqueDiscard(address).send();
assertThat(result).isNotNull();
assertThat(result.hasError()).isFalse();
return result.getResult();
} catch (final IOException e) {
throw new RuntimeException(e);
}
}
}

@ -0,0 +1,43 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.tests.acceptance.dsl.transaction.clique;
import static org.assertj.core.api.Assertions.assertThat;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.PantheonWeb3j;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.PantheonWeb3j.SignersBlockResponse;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.Transaction;
import java.io.IOException;
import java.util.List;
public class CliqueGetSigners implements Transaction<List<Address>> {
private final String blockNumber;
public CliqueGetSigners(final String blockNumber) {
this.blockNumber = blockNumber;
}
@Override
public List<Address> execute(final PantheonWeb3j node) {
try {
final SignersBlockResponse result = node.cliqueGetSigners(blockNumber).send();
assertThat(result).isNotNull();
assertThat(result.hasError()).isFalse();
return result.getResult();
} catch (final IOException e) {
throw new RuntimeException(e);
}
}
}

@ -0,0 +1,44 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.tests.acceptance.dsl.transaction.clique;
import static org.assertj.core.api.Assertions.assertThat;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.Hash;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.PantheonWeb3j;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.PantheonWeb3j.SignersBlockResponse;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.Transaction;
import java.io.IOException;
import java.util.List;
public class CliqueGetSignersAtHash implements Transaction<List<Address>> {
private final Hash hash;
public CliqueGetSignersAtHash(final Hash hash) {
this.hash = hash;
}
@Override
public List<Address> execute(final PantheonWeb3j node) {
try {
final SignersBlockResponse result = node.cliqueGetSignersAtHash(hash).send();
assertThat(result).isNotNull();
assertThat(result.hasError()).isFalse();
return result.getResult();
} catch (final IOException e) {
throw new RuntimeException(e);
}
}
}

@ -0,0 +1,38 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.tests.acceptance.dsl.transaction.clique;
import static org.assertj.core.api.Assertions.assertThat;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.PantheonWeb3j;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.PantheonWeb3j.ProposalsResponse;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.Transaction;
import java.io.IOException;
import java.util.Map;
public class CliqueProposals implements Transaction<Map<Address, Boolean>> {
@Override
public Map<Address, Boolean> execute(final PantheonWeb3j node) {
try {
final ProposalsResponse result = node.cliqueProposals().send();
assertThat(result).isNotNull();
assertThat(result.hasError()).isFalse();
return result.getResult();
} catch (final IOException e) {
throw new RuntimeException(e);
}
}
}

@ -0,0 +1,43 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.tests.acceptance.dsl.transaction.clique;
import static org.assertj.core.api.Assertions.assertThat;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.PantheonWeb3j;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.PantheonWeb3j.ProposeResponse;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.Transaction;
import java.io.IOException;
public class CliquePropose implements Transaction<Boolean> {
private final String address;
private final boolean auth;
public CliquePropose(final String address, final boolean auth) {
this.address = address;
this.auth = auth;
}
@Override
public Boolean execute(final PantheonWeb3j node) {
try {
final ProposeResponse result = node.cliquePropose(address, auth).send();
assertThat(result).isNotNull();
assertThat(result.hasError()).isFalse();
return result.getResult();
} catch (final IOException e) {
throw new RuntimeException(e);
}
}
}

@ -0,0 +1,48 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.tests.acceptance.dsl.transaction.clique;
import tech.pegasys.pantheon.ethereum.core.Hash;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.PantheonNode;
public class CliqueTransactions {
public static final String LATEST = "latest";
public CliquePropose createRemoveProposal(final PantheonNode node) {
return propose(node.getAddress().toString(), false);
}
public CliquePropose createAddProposal(final PantheonNode node) {
return propose(node.getAddress().toString(), true);
}
private CliquePropose propose(final String address, final boolean auth) {
return new CliquePropose(address, auth);
}
public CliqueProposals createProposals() {
return new CliqueProposals();
}
public CliqueGetSigners createGetSigners(final String blockNumber) {
return new CliqueGetSigners(blockNumber);
}
public CliqueGetSignersAtHash createGetSignersAtHash(final Hash blockHash) {
return new CliqueGetSignersAtHash(blockHash);
}
public CliqueDiscard createDiscardProposal(final PantheonNode node) {
return new CliqueDiscard(node.getAddress().toString());
}
}

@ -14,12 +14,12 @@ package tech.pegasys.pantheon.tests.acceptance.dsl.transaction.eth;
import static org.assertj.core.api.Assertions.assertThat;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.PantheonWeb3j;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.Transaction;
import java.io.IOException;
import java.util.List;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.methods.response.EthAccounts;
public class EthAccountsTransaction implements Transaction<List<String>> {
@ -27,7 +27,7 @@ public class EthAccountsTransaction implements Transaction<List<String>> {
EthAccountsTransaction() {}
@Override
public List<String> execute(final Web3j node) {
public List<String> execute(final PantheonWeb3j node) {
try {
final EthAccounts result = node.ethAccounts().send();
assertThat(result).isNotNull();

@ -14,19 +14,18 @@ package tech.pegasys.pantheon.tests.acceptance.dsl.transaction.eth;
import static org.assertj.core.api.Assertions.assertThat;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.PantheonWeb3j;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.Transaction;
import java.io.IOException;
import java.math.BigInteger;
import org.web3j.protocol.Web3j;
public class EthBlockNumberTransaction implements Transaction<BigInteger> {
EthBlockNumberTransaction() {}
@Override
public BigInteger execute(final Web3j node) {
public BigInteger execute(final PantheonWeb3j node) {
try {
final org.web3j.protocol.core.methods.response.EthBlockNumber result =
node.ethBlockNumber().send();

@ -16,12 +16,12 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.web3j.protocol.core.DefaultBlockParameterName.LATEST;
import tech.pegasys.pantheon.tests.acceptance.dsl.account.Account;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.PantheonWeb3j;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.Transaction;
import java.io.IOException;
import java.math.BigInteger;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.methods.response.EthGetBalance;
public class EthGetBalanceTransaction implements Transaction<BigInteger> {
@ -33,7 +33,7 @@ public class EthGetBalanceTransaction implements Transaction<BigInteger> {
}
@Override
public BigInteger execute(final Web3j node) {
public BigInteger execute(final PantheonWeb3j node) {
try {
final EthGetBalance result = node.ethGetBalance(account.getAddress(), LATEST).send();
assertThat(result).isNotNull();

@ -0,0 +1,48 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.tests.acceptance.dsl.transaction.eth;
import static org.assertj.core.api.Assertions.assertThat;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.PantheonWeb3j;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.Transaction;
import java.io.IOException;
import org.web3j.protocol.core.DefaultBlockParameter;
import org.web3j.protocol.core.methods.response.EthBlock;
import org.web3j.protocol.core.methods.response.EthBlock.Block;
public class EthGetBlockTransaction implements Transaction<Block> {
private final DefaultBlockParameter blockParameter;
private final boolean fullTransactionObjects;
EthGetBlockTransaction(
final DefaultBlockParameter blockParameter, final boolean fullTransactionObjects) {
this.blockParameter = blockParameter;
this.fullTransactionObjects = fullTransactionObjects;
}
@Override
public Block execute(final PantheonWeb3j node) {
try {
final EthBlock result =
node.ethGetBlockByNumber(blockParameter, fullTransactionObjects).send();
assertThat(result).isNotNull();
assertThat(result.hasError()).isFalse();
return result.getBlock();
} catch (final IOException e) {
throw new RuntimeException(e);
}
}
}

@ -14,12 +14,12 @@ package tech.pegasys.pantheon.tests.acceptance.dsl.transaction.eth;
import static org.assertj.core.api.Assertions.assertThat;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.PantheonWeb3j;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.Transaction;
import java.io.IOException;
import java.util.Optional;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.methods.response.EthGetTransactionReceipt;
import org.web3j.protocol.core.methods.response.TransactionReceipt;
@ -33,7 +33,7 @@ public class EthGetTransactionReceiptTransaction
}
@Override
public Optional<TransactionReceipt> execute(final Web3j node) {
public Optional<TransactionReceipt> execute(final PantheonWeb3j node) {
try {
final EthGetTransactionReceipt result = node.ethGetTransactionReceipt(input).send();
assertThat(result.hasError()).isFalse();

@ -14,11 +14,11 @@ package tech.pegasys.pantheon.tests.acceptance.dsl.transaction.eth;
import static org.assertj.core.api.Assertions.assertThat;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.PantheonWeb3j;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.Transaction;
import java.io.IOException;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.methods.response.EthGetWork;
public class EthGetWorkTransaction implements Transaction<String[]> {
@ -26,7 +26,7 @@ public class EthGetWorkTransaction implements Transaction<String[]> {
EthGetWorkTransaction() {}
@Override
public String[] execute(final Web3j node) {
public String[] execute(final PantheonWeb3j node) {
try {
final EthGetWork result = node.ethGetWork().send();
assertThat(result).isNotNull();

@ -14,6 +14,9 @@ package tech.pegasys.pantheon.tests.acceptance.dsl.transaction.eth;
import tech.pegasys.pantheon.tests.acceptance.dsl.account.Account;
import org.web3j.protocol.core.DefaultBlockParameter;
import org.web3j.protocol.core.DefaultBlockParameterName;
public class EthTransactions {
public EthGetWorkTransaction getWork() {
@ -24,6 +27,14 @@ public class EthTransactions {
return new EthBlockNumberTransaction();
}
public EthGetBlockTransaction block() {
return block(DefaultBlockParameterName.LATEST);
}
public EthGetBlockTransaction block(final DefaultBlockParameter blockParameter) {
return new EthGetBlockTransaction(blockParameter, false);
}
public EthGetBalanceTransaction getBalance(final Account account) {
return new EthGetBalanceTransaction(account);
}

@ -14,12 +14,12 @@ package tech.pegasys.pantheon.tests.acceptance.dsl.transaction.net;
import static org.assertj.core.api.Assertions.assertThat;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.PantheonWeb3j;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.Transaction;
import java.io.IOException;
import java.math.BigInteger;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.methods.response.NetPeerCount;
public class NetPeerCountTransaction implements Transaction<BigInteger> {
@ -27,7 +27,7 @@ public class NetPeerCountTransaction implements Transaction<BigInteger> {
NetPeerCountTransaction() {}
@Override
public BigInteger execute(final Web3j node) {
public BigInteger execute(final PantheonWeb3j node) {
try {
final NetPeerCount result = node.netPeerCount().send();
assertThat(result).isNotNull();

@ -14,11 +14,11 @@ package tech.pegasys.pantheon.tests.acceptance.dsl.transaction.net;
import static org.assertj.core.api.Assertions.assertThat;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.PantheonWeb3j;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.Transaction;
import java.io.IOException;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.methods.response.NetVersion;
public class NetVersionTransaction implements Transaction<String> {
@ -26,7 +26,7 @@ public class NetVersionTransaction implements Transaction<String> {
NetVersionTransaction() {}
@Override
public String execute(final Web3j node) {
public String execute(final PantheonWeb3j node) {
try {
final NetVersion result = node.netVersion().send();
assertThat(result).isNotNull();

@ -14,11 +14,11 @@ package tech.pegasys.pantheon.tests.acceptance.dsl.transaction.web3;
import static org.assertj.core.api.Assertions.assertThat;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.PantheonWeb3j;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.Transaction;
import java.io.IOException;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.methods.response.Web3Sha3;
public class Web3Sha3Transaction implements Transaction<String> {
@ -30,7 +30,7 @@ public class Web3Sha3Transaction implements Transaction<String> {
}
@Override
public String execute(final Web3j node) {
public String execute(final PantheonWeb3j node) {
try {
final Web3Sha3 result = node.web3Sha3(input).send();
assertThat(result).isNotNull();

@ -0,0 +1,20 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.tests.acceptance.dsl.waitcondition;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.Node;
@FunctionalInterface
public interface WaitCondition {
void waitUntil(Node node);
}

@ -0,0 +1,48 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.tests.acceptance.dsl.waitcondition;
import static tech.pegasys.pantheon.tests.acceptance.dsl.transaction.clique.CliqueTransactions.LATEST;
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.blockchain.ExpectBlockNumber;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.Node;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.PantheonNode;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.clique.CliqueTransactions;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.eth.EthTransactions;
import java.math.BigInteger;
public class WaitConditions {
private final EthTransactions eth;
private final CliqueTransactions clique;
public WaitConditions(final EthTransactions eth, final CliqueTransactions clique) {
this.eth = eth;
this.clique = clique;
}
public WaitCondition chainHeadHasProgressed(
final PantheonNode node, final int blocksAheadOfLatest) {
final BigInteger futureBlock =
node.execute(eth.blockNumber()).add(BigInteger.valueOf(blocksAheadOfLatest));
return new ExpectBlockNumber(eth, futureBlock)::verify;
}
public WaitCondition cliqueValidatorsChanged(final Node node) {
return new WaitUntilSignersChanged(node.execute(clique.createGetSigners(LATEST)), clique);
}
public WaitCondition chainHeadIsAt(final long blockNumber) {
return new ExpectBlockNumber(eth, BigInteger.valueOf(blockNumber))::verify;
}
}

@ -0,0 +1,42 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.tests.acceptance.dsl.waitcondition;
import static org.assertj.core.api.Assertions.assertThat;
import static tech.pegasys.pantheon.tests.acceptance.dsl.WaitUtils.waitFor;
import static tech.pegasys.pantheon.tests.acceptance.dsl.transaction.clique.CliqueTransactions.LATEST;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.Node;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.clique.CliqueTransactions;
import java.util.List;
public class WaitUntilSignersChanged implements WaitCondition {
private final CliqueTransactions clique;
private final List<Address> initialSigners;
public WaitUntilSignersChanged(
final List<Address> initialSigners, final CliqueTransactions clique) {
this.initialSigners = initialSigners;
this.clique = clique;
}
@Override
public void waitUntil(final Node node) {
waitFor(
60,
() ->
assertThat(node.execute(clique.createGetSigners(LATEST))).isNotEqualTo(initialSigners));
}
}

@ -0,0 +1,42 @@
{
"config": {
"chainId": 4,
"homesteadBlock": 1,
"eip150Block": 2,
"eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"eip155Block": 0,
"eip158Block": 0,
"byzantiumBlock": 0,
"clique": {
"period": 5,
"epoch": 30000
}
},
"nonce": "0x0",
"timestamp": "0x58ee40ba",
"extraData": "%cliqueExtraData%",
"gasLimit": "0x47b760",
"difficulty": "0x1",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"coinbase": "0x0000000000000000000000000000000000000000",
"alloc": {
"fe3b557e8fb62b89f4916b721be55ceb828dbd73": {
"privateKey": "8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63",
"comment": "private key and this comment are ignored. In a real chain, the private key should NOT be stored",
"balance": "0xad78ebc5ac6200000"
},
"627306090abaB3A6e1400e9345bC60c78a8BEf57": {
"privateKey": "c87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3",
"comment": "private key and this comment are ignored. In a real chain, the private key should NOT be stored",
"balance": "90000000000000000000000"
},
"f17f52151EbEF6C7334FAD080c5704D77216b732": {
"privateKey": "ae6ae8e5ccbfb04590405997ee2d52d2b330726137b875053c36d94e974d162f",
"comment": "private key and this comment are ignored. In a real chain, the private key should NOT be stored",
"balance": "90000000000000000000000"
}
},
"number": "0x0",
"gasUsed": "0x0",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
}

@ -15,11 +15,8 @@ package tech.pegasys.pantheon.consensus.clique;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import tech.pegasys.pantheon.crypto.SECP256K1.PrivateKey;
import tech.pegasys.pantheon.crypto.SECP256K1.PublicKey;
import tech.pegasys.pantheon.crypto.SECP256K1.Signature;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.Util;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import tech.pegasys.pantheon.util.bytes.BytesValues;
@ -110,20 +107,10 @@ public class CliqueExtraData {
return validators;
}
public static String createGenesisExtraDataString(final List<PrivateKey> privKeys) {
final List<Address> validators = convertPrivKeysToAddresses(privKeys);
public static String createGenesisExtraDataString(final List<Address> validators) {
final CliqueExtraData cliqueExtraData =
new CliqueExtraData(BytesValue.wrap(new byte[32]), null, validators);
final BytesValue output = cliqueExtraData.encode();
return output.toString();
}
private static List<Address> convertPrivKeysToAddresses(final List<PrivateKey> privKeys) {
final List<Address> validators = Lists.newArrayList();
for (final PrivateKey privKey : privKeys) {
final PublicKey pubKey = PublicKey.create(privKey);
validators.add(Util.publicKeyToAddress(pubKey));
}
return validators;
}
}

@ -16,7 +16,6 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair;
import tech.pegasys.pantheon.crypto.SECP256K1.PrivateKey;
import tech.pegasys.pantheon.crypto.SECP256K1.Signature;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.AddressHelpers;
@ -95,26 +94,23 @@ public class CliqueExtraDataTest {
}
@Test
public void privKeysToExtraDataString() {
public void addressToExtraDataString() {
final List<KeyPair> nodeKeys = Lists.newArrayList();
for (int i = 0; i < 4; i++) {
nodeKeys.add(KeyPair.generate());
}
final List<PrivateKey> privKeys =
nodeKeys.stream().map(k -> k.getPrivateKey()).collect(Collectors.toList());
final String hexOutput = CliqueExtraData.createGenesisExtraDataString(privKeys);
final CliqueExtraData extraData = CliqueExtraData.decode(BytesValue.fromHexString(hexOutput));
final List<Address> expectedAddresses =
final List<Address> addresses =
nodeKeys
.stream()
.map(k -> Util.publicKeyToAddress(k.getPublicKey()))
.map(KeyPair::getPublicKey)
.map(Util::publicKeyToAddress)
.collect(Collectors.toList());
assertThat(extraData.getValidators())
.containsExactly(expectedAddresses.toArray(new Address[expectedAddresses.size()]));
final String hexOutput = CliqueExtraData.createGenesisExtraDataString(addresses);
final CliqueExtraData extraData = CliqueExtraData.decode(BytesValue.fromHexString(hexOutput));
assertThat(extraData.getValidators()).containsExactly(addresses.toArray(new Address[0]));
}
}

@ -12,10 +12,12 @@
*/
package tech.pegasys.pantheon.cli;
import static java.nio.charset.StandardCharsets.UTF_8;
import static tech.pegasys.pantheon.ethereum.p2p.config.DiscoveryConfiguration.MAINNET_BOOTSTRAP_NODES;
import static tech.pegasys.pantheon.ethereum.p2p.config.DiscoveryConfiguration.RINKEBY_BOOTSTRAP_NODES;
import static tech.pegasys.pantheon.ethereum.p2p.config.DiscoveryConfiguration.ROPSTEN_BOOTSTRAP_NODES;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collection;
@ -31,12 +33,12 @@ public class EthNetworkConfig {
private static final String MAINNET_GENESIS = "mainnet.json";
private static final String RINKEBY_GENESIS = "rinkeby.json";
private static final String ROPSTEN_GENESIS = "ropsten.json";
private final URI genesisConfig;
private final String genesisConfig;
private final int networkId;
private final Collection<?> bootNodes;
public EthNetworkConfig(
final URI genesisConfig, final int networkId, final Collection<?> bootNodes) {
final String genesisConfig, final int networkId, final Collection<?> bootNodes) {
Preconditions.checkNotNull(genesisConfig);
Preconditions.checkNotNull(bootNodes);
this.genesisConfig = genesisConfig;
@ -44,7 +46,7 @@ public class EthNetworkConfig {
this.bootNodes = bootNodes;
}
public URI getGenesisConfig() {
public String getGenesisConfig() {
return genesisConfig;
}
@ -88,31 +90,32 @@ public class EthNetworkConfig {
}
public static EthNetworkConfig mainnet() {
final URI genesisConfig = jsonConfigURI(MAINNET_GENESIS);
return new EthNetworkConfig(genesisConfig, MAINNET_NETWORK_ID, MAINNET_BOOTSTRAP_NODES);
return new EthNetworkConfig(
jsonConfig(MAINNET_GENESIS), MAINNET_NETWORK_ID, MAINNET_BOOTSTRAP_NODES);
}
public static EthNetworkConfig rinkeby() {
final URI genesisConfig = jsonConfigURI(RINKEBY_GENESIS);
return new EthNetworkConfig(genesisConfig, RINKEBY_NETWORK_ID, RINKEBY_BOOTSTRAP_NODES);
return new EthNetworkConfig(
jsonConfig(RINKEBY_GENESIS), RINKEBY_NETWORK_ID, RINKEBY_BOOTSTRAP_NODES);
}
public static EthNetworkConfig ropsten() {
final URI genesisConfig = jsonConfigURI(ROPSTEN_GENESIS);
return new EthNetworkConfig(genesisConfig, ROPSTEN_NETWORK_ID, ROPSTEN_BOOTSTRAP_NODES);
return new EthNetworkConfig(
jsonConfig(ROPSTEN_GENESIS), ROPSTEN_NETWORK_ID, ROPSTEN_BOOTSTRAP_NODES);
}
private static URI jsonConfigURI(final String resourceName) {
private static String jsonConfig(final String resourceName) {
try {
return Resources.getResource(resourceName).toURI();
} catch (final URISyntaxException e) {
URI uri = Resources.getResource(resourceName).toURI();
return Resources.toString(uri.toURL(), UTF_8);
} catch (final URISyntaxException | IOException e) {
throw new IllegalStateException(e);
}
}
public static class Builder {
private URI genesisConfig;
private String genesisConfig;
private int networkId;
private Collection<?> bootNodes;
@ -122,7 +125,7 @@ public class EthNetworkConfig {
this.bootNodes = ethNetworkConfig.bootNodes;
}
public Builder setGenesisConfig(final URI genesisConfig) {
public Builder setGenesisConfig(final String genesisConfig) {
this.genesisConfig = genesisConfig;
return this;
}

@ -13,6 +13,7 @@
package tech.pegasys.pantheon.cli;
import static com.google.common.base.Preconditions.checkNotNull;
import static java.nio.charset.StandardCharsets.UTF_8;
import tech.pegasys.pantheon.Runner;
import tech.pegasys.pantheon.RunnerBuilder;
@ -51,6 +52,7 @@ import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Stream;
import com.google.common.io.Resources;
import com.google.common.net.HostAndPort;
import io.vertx.core.Vertx;
import org.apache.logging.log4j.Level;
@ -618,7 +620,7 @@ public class PantheonCommand implements Runnable {
private EthNetworkConfig updateNetworkConfig(final EthNetworkConfig ethNetworkConfig) {
final EthNetworkConfig.Builder builder = new EthNetworkConfig.Builder(ethNetworkConfig);
if (genesisFile != null) {
builder.setGenesisConfig(genesisFile.toPath().toUri());
builder.setGenesisConfig(genesisConfig());
}
if (networkId != null) {
builder.setNetworkId(networkId);
@ -628,4 +630,13 @@ public class PantheonCommand implements Runnable {
}
return builder.build();
}
private String genesisConfig() {
try {
return Resources.toString(genesisFile.toURI().toURL(), UTF_8);
} catch (IOException e) {
throw new ParameterException(
new CommandLine(this), String.format("Unable to load genesis file %s.", genesisFile), e);
}
}
}

@ -12,7 +12,6 @@
*/
package tech.pegasys.pantheon.cli;
import static java.nio.charset.StandardCharsets.UTF_8;
import static tech.pegasys.pantheon.controller.KeyPairUtil.loadKeyPair;
import static tech.pegasys.pantheon.controller.PantheonController.DATABASE_PATH;
@ -30,8 +29,6 @@ import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import com.google.common.io.Resources;
public class PantheonControllerBuilder {
public PantheonController<?> build(
@ -59,8 +56,7 @@ public class PantheonControllerBuilder {
miningParameters,
nodeKeys);
} else {
final String genesisConfig =
Resources.toString(ethNetworkConfig.getGenesisConfig().toURL(), UTF_8);
final String genesisConfig = ethNetworkConfig.getGenesisConfig();
final GenesisConfigFile genesisConfigFile = GenesisConfigFile.fromConfig(genesisConfig);
return PantheonController.fromConfig(
genesisConfigFile,

@ -42,6 +42,7 @@ import tech.pegasys.pantheon.util.bytes.BytesValue;
import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@ -61,10 +62,10 @@ import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatchers;
public class PantheonCommandTest extends CommandTestAbstract {
@Rule public final TemporaryFolder temp = new TemporaryFolder();
private static final JsonRpcConfiguration defaultJsonRpcConfiguration;
private static final WebSocketConfiguration defaultWebSocketConfiguration;
private static final String GENESIS_CONFIG_TESTDATA = "genesis_config";
static {
final JsonRpcConfiguration rpcConf = JsonRpcConfiguration.createDefault();
@ -160,13 +161,12 @@ public class PantheonCommandTest extends CommandTestAbstract {
assertThat(miningArg.getValue().getMinTransactionGasPrice()).isEqualTo(Wei.of(1000));
assertThat(miningArg.getValue().getExtraData()).isEqualTo(BytesValue.EMPTY);
assertThat(networkArg.getValue().getNetworkId()).isEqualTo(1);
assertThat(networkArg.getValue().getGenesisConfig().toString()).endsWith("mainnet.json");
assertThat(networkArg.getValue().getBootNodes()).isEqualTo(MAINNET_BOOTSTRAP_NODES);
}
// Testing each option
@Test
public void CallingWithConfigOptionButNoConfigFileShouldDisplayHelp() {
public void callingWithConfigOptionButNoConfigFileShouldDisplayHelp() {
parseCommand("--config");
@ -176,7 +176,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
}
@Test
public void CallingWithConfigOptionButNonExistingFileShouldDisplayHelp() throws IOException {
public void callingWithConfigOptionButNonExistingFileShouldDisplayHelp() throws IOException {
final File tempConfigFile = temp.newFile("an-invalid-file-name-without-extension");
parseCommand("--config", tempConfigFile.getPath());
@ -186,7 +186,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
}
@Test
public void CallingWithConfigOptionButTomlFileNotFoundShouldDisplayHelp() {
public void callingWithConfigOptionButTomlFileNotFoundShouldDisplayHelp() {
parseCommand("--config", "./an-invalid-file-name-sdsd87sjhqoi34io23.toml");
@ -196,7 +196,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
}
@Test
public void CallingWithConfigOptionButInvalidContentTomlFileShouldDisplayHelp() throws Exception {
public void callingWithConfigOptionButInvalidContentTomlFileShouldDisplayHelp() throws Exception {
// We write a config file to prevent an invalid file in resource folder to raise errors in
// code checks (CI + IDE)
@ -217,7 +217,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
}
@Test
public void CallingWithConfigOptionButInvalidValueTomlFileShouldDisplayHelp() throws Exception {
public void callingWithConfigOptionButInvalidValueTomlFileShouldDisplayHelp() throws Exception {
// We write a config file to prevent an invalid file in resource folder to raise errors in
// code checks (CI + IDE)
@ -238,8 +238,13 @@ public class PantheonCommandTest extends CommandTestAbstract {
}
@Test
public void OverrideDefaultValuesIfKeyIsPresentInConfigFile() throws IOException {
final String configFile = Resources.getResource("complete_config.toml").getFile();
public void overrideDefaultValuesIfKeyIsPresentInConfigFile() throws IOException {
final URL configFile = Resources.getResource("complete_config.toml");
final Path genesisFile = createFakeGenesisFile();
final String updatedConfig =
Resources.toString(configFile, UTF_8).replaceAll("~/genesis.json", genesisFile.toString());
final Path toml = Files.createTempFile("toml", "");
Files.write(toml, updatedConfig.getBytes(UTF_8));
final JsonRpcConfiguration jsonRpcConfiguration = JsonRpcConfiguration.createDefault();
jsonRpcConfiguration.setEnabled(false);
@ -258,7 +263,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
webSocketConfiguration.addRpcApi(CliqueRpcApis.CLIQUE);
webSocketConfiguration.addRpcApi(IbftRpcApis.IBFT);
parseCommand("--config", configFile);
parseCommand("--config", toml.toString());
verify(mockRunnerBuilder)
.build(
@ -280,7 +285,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
final EthNetworkConfig networkConfig =
new Builder(EthNetworkConfig.mainnet())
.setGenesisConfig(new File("~/genesys.json").toPath().toUri())
.setGenesisConfig(GENESIS_CONFIG_TESTDATA)
.setBootNodes(nodes)
.build();
verify(mockControllerBuilder)
@ -303,7 +308,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
}
@Test
public void NoOverrideDefaultValuesIfKeyIsNotPresentInConfigFile() throws IOException {
public void noOverrideDefaultValuesIfKeyIsNotPresentInConfigFile() throws IOException {
final String configFile = Resources.getResource("partial_config.toml").getFile();
parseCommand("--config", configFile);
@ -386,16 +391,16 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test
public void genesisPathOptionMustBeUsed() throws Exception {
final Path path = Paths.get(".");
final Path genesisFile = createFakeGenesisFile();
final ArgumentCaptor<EthNetworkConfig> networkArg =
ArgumentCaptor.forClass(EthNetworkConfig.class);
parseCommand("--genesis", path.toString());
parseCommand("--genesis", genesisFile.toString());
verify(mockControllerBuilder)
.build(any(), any(), networkArg.capture(), anyBoolean(), any(), anyBoolean(), any());
assertThat(networkArg.getValue().getGenesisConfig()).isEqualTo(path.toUri());
assertThat(networkArg.getValue().getGenesisConfig()).isEqualTo("genesis_config");
assertThat(commandOutput.toString()).isEmpty();
assertThat(commandErrorOutput.toString()).isEmpty();
@ -996,7 +1001,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test
public void rinkebyValuesCanBeOverridden() throws Exception {
final String[] nodes = {"enode://001@123:4567", "enode://002@123:4567", "enode://003@123:4567"};
final Path path = Paths.get(".");
final Path genesisFile = createFakeGenesisFile();
parseCommand(
"--rinkeby",
"--network-id",
@ -1004,7 +1009,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
"--bootnodes",
String.join(",", nodes),
"--genesis",
path.toString());
genesisFile.toString());
final ArgumentCaptor<EthNetworkConfig> networkArg =
ArgumentCaptor.forClass(EthNetworkConfig.class);
@ -1012,8 +1017,14 @@ public class PantheonCommandTest extends CommandTestAbstract {
.build(any(), any(), networkArg.capture(), anyBoolean(), any(), anyBoolean(), any());
assertThat(commandOutput.toString()).isEmpty();
assertThat(commandErrorOutput.toString()).isEmpty();
assertThat(networkArg.getValue().getGenesisConfig()).isEqualTo(path.toUri());
assertThat(networkArg.getValue().getGenesisConfig()).isEqualTo("genesis_config");
assertThat(networkArg.getValue().getBootNodes()).isEqualTo(Arrays.asList(nodes));
assertThat(networkArg.getValue().getNetworkId()).isEqualTo(1);
}
private Path createFakeGenesisFile() throws IOException {
final Path genesisFile = Files.createTempFile("genesisFile", "");
Files.write(genesisFile, "genesis_config".getBytes(UTF_8));
return genesisFile;
}
}

@ -13,7 +13,7 @@ rpc-listen="5.6.7.8:5678" # IP:port
ws-listen="9.10.11.12:9101" # IP:port
# chain
genesis="~/genesys.json" # Path
genesis="~/genesis.json" # Path
sync-mode="fast"# should be FAST or FULL (or fast or full)
ottoman=false # true means using ottoman testnet if genesys file uses iBFT

Loading…
Cancel
Save