mirror of https://github.com/hyperledger/besu
Allow IBFT validators to be forked at a given block (#173)
This change allows a user to specify a list of address which are to become validators in an IBFT2 network at a specific block number. This has required extending the VoteTallyCache, and also added a new "CustomForks" section to the genesis file. At the moment only validators are able to be changed, however the framework now exists for future modifications to be defined that affect the behaviour of the system "outside" of traditional Ethereum milestones. Signed-off-by: Trent Mohay <trent.mohay@consensys.net>pull/182/head
parent
bc5a37ec19
commit
dd46332530
@ -0,0 +1,65 @@ |
||||
/* |
||||
* Copyright 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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.config; |
||||
|
||||
import static java.util.Collections.emptyList; |
||||
|
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
import java.util.Optional; |
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator; |
||||
import com.fasterxml.jackson.databind.node.ArrayNode; |
||||
import com.fasterxml.jackson.databind.node.ObjectNode; |
||||
import com.google.common.collect.Lists; |
||||
|
||||
public class CustomForksConfigOptions { |
||||
|
||||
public static final String IBFT2_FORKS = "ibft2"; |
||||
|
||||
public static final CustomForksConfigOptions DEFAULT = |
||||
new CustomForksConfigOptions(JsonUtil.createEmptyObjectNode()); |
||||
|
||||
private final ObjectNode customForkConfigRoot; |
||||
|
||||
@JsonCreator |
||||
public CustomForksConfigOptions(final ObjectNode customForkConfigRoot) { |
||||
this.customForkConfigRoot = customForkConfigRoot; |
||||
} |
||||
|
||||
public List<IbftFork> getIbftForks() { |
||||
final Optional<ArrayNode> ibftForksNode = |
||||
JsonUtil.getArrayNode(customForkConfigRoot, IBFT2_FORKS); |
||||
|
||||
if (ibftForksNode.isEmpty()) { |
||||
return emptyList(); |
||||
} |
||||
|
||||
final List<IbftFork> ibftForks = Lists.newArrayList(); |
||||
|
||||
ibftForksNode |
||||
.get() |
||||
.elements() |
||||
.forEachRemaining( |
||||
node -> { |
||||
if (!node.isObject()) { |
||||
throw new IllegalArgumentException("Ibft2 fork is illegally formatted."); |
||||
} |
||||
ibftForks.add(new IbftFork((ObjectNode) node)); |
||||
}); |
||||
|
||||
return Collections.unmodifiableList(ibftForks); |
||||
} |
||||
} |
@ -0,0 +1,73 @@ |
||||
/* |
||||
* Copyright 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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.config; |
||||
|
||||
import java.util.List; |
||||
import java.util.Optional; |
||||
import java.util.OptionalInt; |
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator; |
||||
import com.fasterxml.jackson.databind.node.ArrayNode; |
||||
import com.fasterxml.jackson.databind.node.ObjectNode; |
||||
import com.google.common.collect.Lists; |
||||
|
||||
public class IbftFork { |
||||
|
||||
private static final String FORK_BLOCK_KEY = "block"; |
||||
private static final String VALIDATORS_KEY = "validators"; |
||||
private static final String BLOCK_PERIOD_SECONDS_KEY = "blockperiodseconds"; |
||||
|
||||
private final ObjectNode forkConfigRoot; |
||||
|
||||
@JsonCreator |
||||
public IbftFork(final ObjectNode forkConfigRoot) { |
||||
this.forkConfigRoot = forkConfigRoot; |
||||
} |
||||
|
||||
public long getForkBlock() { |
||||
return JsonUtil.getLong(forkConfigRoot, FORK_BLOCK_KEY) |
||||
.orElseThrow( |
||||
() -> |
||||
new IllegalArgumentException( |
||||
"Fork block not specified for IBFT2 fork in custom forks")); |
||||
} |
||||
|
||||
public OptionalInt getBlockPeriodSeconds() { |
||||
return JsonUtil.getInt(forkConfigRoot, BLOCK_PERIOD_SECONDS_KEY); |
||||
} |
||||
|
||||
public Optional<List<String>> getValidators() throws IllegalArgumentException { |
||||
final Optional<ArrayNode> validatorNode = JsonUtil.getArrayNode(forkConfigRoot, VALIDATORS_KEY); |
||||
|
||||
if (validatorNode.isEmpty()) { |
||||
return Optional.empty(); |
||||
} |
||||
|
||||
List<String> validators = Lists.newArrayList(); |
||||
validatorNode |
||||
.get() |
||||
.elements() |
||||
.forEachRemaining( |
||||
value -> { |
||||
if (!value.isTextual()) { |
||||
throw new IllegalArgumentException( |
||||
"Ibft Validator fork does not contain a string " + value.toString()); |
||||
} |
||||
|
||||
validators.add(value.asText()); |
||||
}); |
||||
return Optional.of(validators); |
||||
} |
||||
} |
@ -0,0 +1,120 @@ |
||||
/* |
||||
* Copyright 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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.config; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
import java.io.IOException; |
||||
import java.nio.charset.StandardCharsets; |
||||
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode; |
||||
import com.fasterxml.jackson.databind.node.ObjectNode; |
||||
import com.google.common.io.Resources; |
||||
import org.junit.Test; |
||||
|
||||
public class JsonGenesisConfigOptionsTest { |
||||
|
||||
private ObjectNode loadCompleteDataSet() { |
||||
try { |
||||
final String configText = |
||||
Resources.toString( |
||||
Resources.getResource("valid_config_with_custom_forks.json"), StandardCharsets.UTF_8); |
||||
return JsonUtil.objectNodeFromString(configText); |
||||
} catch (final IOException e) { |
||||
throw new RuntimeException("Failed to load resource", e); |
||||
} |
||||
} |
||||
|
||||
private ObjectNode loadConfigWithNoCustomForks() { |
||||
final ObjectNode configNode = loadCompleteDataSet(); |
||||
configNode.remove("customforks"); |
||||
return configNode; |
||||
} |
||||
|
||||
private ObjectNode loadConfigWithNoIbft2Forks() { |
||||
final ObjectNode configNode = loadCompleteDataSet(); |
||||
final ObjectNode customForksNode = JsonUtil.getObjectNode(configNode, "customforks").get(); |
||||
customForksNode.remove("ibft2"); |
||||
|
||||
return configNode; |
||||
} |
||||
|
||||
private ObjectNode loadConfigWithAnIbft2ForkWithMissingValidators() { |
||||
final ObjectNode configNode = loadCompleteDataSet(); |
||||
final ObjectNode customForksNode = JsonUtil.getObjectNode(configNode, "customforks").get(); |
||||
final ArrayNode ibftNode = JsonUtil.getArrayNode(customForksNode, "ibft2").get(); |
||||
((ObjectNode) ibftNode.get(0)).remove("validators"); |
||||
|
||||
return configNode; |
||||
} |
||||
|
||||
@Test |
||||
public void customForksDecodesCorrectlyFromFile() { |
||||
final ObjectNode configNode = loadCompleteDataSet(); |
||||
|
||||
final JsonGenesisConfigOptions configOptions = |
||||
JsonGenesisConfigOptions.fromJsonObject(configNode); |
||||
|
||||
assertThat(configOptions.getCustomForks()).isNotNull(); |
||||
assertThat(configOptions.getCustomForks().getIbftForks().size()).isEqualTo(2); |
||||
assertThat(configOptions.getCustomForks().getIbftForks().get(0).getForkBlock()).isEqualTo(20); |
||||
assertThat(configOptions.getCustomForks().getIbftForks().get(0).getValidators()).isNotEmpty(); |
||||
assertThat(configOptions.getCustomForks().getIbftForks().get(0).getValidators().get()) |
||||
.containsExactly( |
||||
"0x12345678901234567890123456789012345678900x1234567890123456789012345678901234567890", |
||||
"0x98765432109876543210987654321098765432100x9876543210987654321098765432109876543210"); |
||||
|
||||
assertThat(configOptions.getCustomForks().getIbftForks().get(1).getForkBlock()).isEqualTo(25); |
||||
assertThat(configOptions.getCustomForks().getIbftForks().get(1).getValidators()).isNotEmpty(); |
||||
assertThat(configOptions.getCustomForks().getIbftForks().get(1).getValidators().get()) |
||||
.containsExactly( |
||||
"0x12345678901234567890123456789012345678900x1234567890123456789012345678901234567890"); |
||||
} |
||||
|
||||
@Test |
||||
public void configWithMissingCustomForksIsValid() { |
||||
final ObjectNode configNode = loadConfigWithNoCustomForks(); |
||||
|
||||
final JsonGenesisConfigOptions configOptions = |
||||
JsonGenesisConfigOptions.fromJsonObject(configNode); |
||||
|
||||
assertThat(configOptions.getCustomForks()).isNotNull(); |
||||
assertThat(configOptions.getCustomForks().getIbftForks().size()).isZero(); |
||||
} |
||||
|
||||
@Test |
||||
public void configWithNoIbft2ForksIsValid() { |
||||
final ObjectNode configNode = loadConfigWithNoIbft2Forks(); |
||||
|
||||
final JsonGenesisConfigOptions configOptions = |
||||
JsonGenesisConfigOptions.fromJsonObject(configNode); |
||||
|
||||
assertThat(configOptions.getCustomForks()).isNotNull(); |
||||
assertThat(configOptions.getCustomForks().getIbftForks().size()).isZero(); |
||||
} |
||||
|
||||
@Test |
||||
public void configWithAnIbftWithNoValidatorsListedIsValid() { |
||||
final ObjectNode configNode = loadConfigWithAnIbft2ForkWithMissingValidators(); |
||||
|
||||
final JsonGenesisConfigOptions configOptions = |
||||
JsonGenesisConfigOptions.fromJsonObject(configNode); |
||||
|
||||
assertThat(configOptions.getCustomForks().getIbftForks().get(0).getValidators().isPresent()) |
||||
.isFalse(); |
||||
assertThat(configOptions.getCustomForks().getIbftForks().get(1).getValidators().get().size()) |
||||
.isEqualTo(1); |
||||
} |
||||
} |
@ -0,0 +1,31 @@ |
||||
{ |
||||
"chainId": 4, |
||||
"homesteadBlock": 1, |
||||
"eip150Block": 2, |
||||
"eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000", |
||||
"eip155Block": 3, |
||||
"eip158Block": 3, |
||||
"byzantiumBlock": 1035301, |
||||
"ibft2": { |
||||
"blockperiodseconds": 2, |
||||
"epochlength": 30000, |
||||
"requesttimeoutseconds": 10 |
||||
}, |
||||
"customforks": { |
||||
"ibft2": [ |
||||
{ |
||||
"block": 20, |
||||
"validators": [ |
||||
"0x12345678901234567890123456789012345678900x1234567890123456789012345678901234567890", |
||||
"0x98765432109876543210987654321098765432100x9876543210987654321098765432109876543210" |
||||
] |
||||
}, |
||||
{ |
||||
"block": 25, |
||||
"validators": [ |
||||
"0x12345678901234567890123456789012345678900x1234567890123456789012345678901234567890" |
||||
] |
||||
} |
||||
] |
||||
} |
||||
} |
@ -0,0 +1,46 @@ |
||||
/* |
||||
* Copyright 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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.consensus.common; |
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull; |
||||
|
||||
import org.hyperledger.besu.ethereum.chain.Blockchain; |
||||
import org.hyperledger.besu.ethereum.core.BlockHeader; |
||||
|
||||
public class ForkingVoteTallyCache extends VoteTallyCache { |
||||
|
||||
private final IbftValidatorOverrides validatorOverrides; |
||||
|
||||
public ForkingVoteTallyCache( |
||||
final Blockchain blockchain, |
||||
final VoteTallyUpdater voteTallyUpdater, |
||||
final EpochManager epochManager, |
||||
final BlockInterface blockInterface, |
||||
final IbftValidatorOverrides validatorOverrides) { |
||||
super(blockchain, voteTallyUpdater, epochManager, blockInterface); |
||||
checkNotNull(validatorOverrides); |
||||
this.validatorOverrides = validatorOverrides; |
||||
} |
||||
|
||||
@Override |
||||
protected VoteTally getValidatorsAfter(final BlockHeader header) { |
||||
final long nextBlockNumber = header.getNumber() + 1L; |
||||
|
||||
return validatorOverrides |
||||
.getForBlock(nextBlockNumber) |
||||
.map(VoteTally::new) |
||||
.orElse(super.getValidatorsAfter(header)); |
||||
} |
||||
} |
@ -0,0 +1,35 @@ |
||||
/* |
||||
* Copyright 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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.consensus.common; |
||||
|
||||
import org.hyperledger.besu.ethereum.core.Address; |
||||
|
||||
import java.util.Collection; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.Optional; |
||||
|
||||
public class IbftValidatorOverrides { |
||||
|
||||
private final Map<Long, List<Address>> overriddenValidators; |
||||
|
||||
public IbftValidatorOverrides(final Map<Long, List<Address>> overriddenValidators) { |
||||
this.overriddenValidators = overriddenValidators; |
||||
} |
||||
|
||||
public Optional<Collection<Address>> getForBlock(final long blockNumber) { |
||||
return Optional.ofNullable(overriddenValidators.get(blockNumber)); |
||||
} |
||||
} |
@ -0,0 +1,109 @@ |
||||
/* |
||||
* Copyright 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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.consensus.common; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.mockito.Mockito.mock; |
||||
|
||||
import org.hyperledger.besu.ethereum.core.Address; |
||||
|
||||
import java.util.HashMap; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
import com.google.common.collect.Lists; |
||||
import org.junit.Test; |
||||
|
||||
public class ForkingVoteTallyCacheTest extends VoteTallyCacheTestBase { |
||||
|
||||
@Test |
||||
public void validatorFromForkAreReturnedRatherThanPriorBlock() { |
||||
final List<Address> forkedValidators = |
||||
Lists.newArrayList(Address.fromHexString("5"), Address.fromHexString("6")); |
||||
final Map<Long, List<Address>> forkingValidatorMap = new HashMap<>(); |
||||
forkingValidatorMap.put(3L, forkedValidators); |
||||
|
||||
final VoteTallyUpdater tallyUpdater = mock(VoteTallyUpdater.class); |
||||
final ForkingVoteTallyCache cache = |
||||
new ForkingVoteTallyCache( |
||||
blockChain, |
||||
tallyUpdater, |
||||
new EpochManager(30_000), |
||||
blockInterface, |
||||
new IbftValidatorOverrides(forkingValidatorMap)); |
||||
|
||||
final VoteTally result = cache.getVoteTallyAfterBlock(block_2.getHeader()); |
||||
|
||||
assertThat(result.getValidators()).containsExactlyElementsOf(forkedValidators); |
||||
} |
||||
|
||||
@Test |
||||
public void emptyForkingValidatorMapResultsInValidatorsBeingReadFromPreviousHeader() { |
||||
final VoteTallyUpdater tallyUpdater = mock(VoteTallyUpdater.class); |
||||
final ForkingVoteTallyCache cache = |
||||
new ForkingVoteTallyCache( |
||||
blockChain, |
||||
tallyUpdater, |
||||
new EpochManager(30_000), |
||||
blockInterface, |
||||
new IbftValidatorOverrides(new HashMap<>())); |
||||
|
||||
final VoteTally result = cache.getVoteTallyAfterBlock(block_2.getHeader()); |
||||
|
||||
assertThat(result.getValidators()).containsExactlyElementsOf(validators); |
||||
} |
||||
|
||||
@Test |
||||
public void validatorsInForkUsedIfForkDirectlyFollowsEpoch() { |
||||
final List<Address> forkedValidators = |
||||
Lists.newArrayList(Address.fromHexString("5"), Address.fromHexString("6")); |
||||
final Map<Long, List<Address>> forkingValidatorMap = new HashMap<>(); |
||||
forkingValidatorMap.put(3L, forkedValidators); |
||||
|
||||
final VoteTallyUpdater tallyUpdater = mock(VoteTallyUpdater.class); |
||||
final ForkingVoteTallyCache cache = |
||||
new ForkingVoteTallyCache( |
||||
blockChain, |
||||
tallyUpdater, |
||||
new EpochManager(2L), |
||||
blockInterface, |
||||
new IbftValidatorOverrides(forkingValidatorMap)); |
||||
|
||||
final VoteTally result = cache.getVoteTallyAfterBlock(block_2.getHeader()); |
||||
|
||||
assertThat(result.getValidators()).containsExactlyElementsOf(forkedValidators); |
||||
} |
||||
|
||||
@Test |
||||
public void atHeadApiOperatesIdenticallyToUnderlyingApi() { |
||||
final List<Address> forkedValidators = |
||||
Lists.newArrayList(Address.fromHexString("5"), Address.fromHexString("6")); |
||||
final Map<Long, List<Address>> forkingValidatorMap = new HashMap<>(); |
||||
forkingValidatorMap.put(3L, forkedValidators); |
||||
|
||||
final VoteTallyUpdater tallyUpdater = mock(VoteTallyUpdater.class); |
||||
final ForkingVoteTallyCache cache = |
||||
new ForkingVoteTallyCache( |
||||
blockChain, |
||||
tallyUpdater, |
||||
new EpochManager(30_000L), |
||||
blockInterface, |
||||
new IbftValidatorOverrides(forkingValidatorMap)); |
||||
|
||||
final VoteTally result = cache.getVoteTallyAtHead(); |
||||
|
||||
assertThat(result.getValidators()).containsExactlyElementsOf(forkedValidators); |
||||
} |
||||
} |
@ -0,0 +1,74 @@ |
||||
/* |
||||
* Copyright 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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.consensus.common; |
||||
|
||||
import static org.hyperledger.besu.ethereum.core.InMemoryStorageProvider.createInMemoryBlockchain; |
||||
import static org.mockito.ArgumentMatchers.any; |
||||
import static org.mockito.Mockito.mock; |
||||
import static org.mockito.Mockito.when; |
||||
|
||||
import org.hyperledger.besu.ethereum.chain.MutableBlockchain; |
||||
import org.hyperledger.besu.ethereum.core.Address; |
||||
import org.hyperledger.besu.ethereum.core.AddressHelpers; |
||||
import org.hyperledger.besu.ethereum.core.Block; |
||||
import org.hyperledger.besu.ethereum.core.BlockBody; |
||||
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture; |
||||
import org.hyperledger.besu.ethereum.core.Hash; |
||||
import org.hyperledger.besu.util.bytes.BytesValue; |
||||
|
||||
import java.util.List; |
||||
|
||||
import org.assertj.core.util.Lists; |
||||
import org.junit.Before; |
||||
|
||||
public class VoteTallyCacheTestBase { |
||||
|
||||
protected final BlockHeaderTestFixture headerBuilder = new BlockHeaderTestFixture(); |
||||
|
||||
protected Block createEmptyBlock(final long blockNumber, final Hash parentHash) { |
||||
headerBuilder.number(blockNumber).parentHash(parentHash).coinbase(AddressHelpers.ofValue(0)); |
||||
return new Block( |
||||
headerBuilder.buildHeader(), new BlockBody(Lists.emptyList(), Lists.emptyList())); |
||||
} |
||||
|
||||
protected MutableBlockchain blockChain; |
||||
protected Block genesisBlock; |
||||
protected Block block_1; |
||||
protected Block block_2; |
||||
|
||||
protected final List<Address> validators = Lists.newArrayList(); |
||||
|
||||
protected final BlockInterface blockInterface = mock(BlockInterface.class); |
||||
|
||||
@Before |
||||
public void constructThreeBlockChain() { |
||||
for (int i = 0; i < 3; i++) { |
||||
validators.add(AddressHelpers.ofValue(i)); |
||||
} |
||||
headerBuilder.extraData(BytesValue.wrap(new byte[32])); |
||||
|
||||
genesisBlock = createEmptyBlock(0, Hash.ZERO); |
||||
|
||||
blockChain = createInMemoryBlockchain(genesisBlock); |
||||
|
||||
block_1 = createEmptyBlock(1, genesisBlock.getHeader().getHash()); |
||||
block_2 = createEmptyBlock(2, block_1.getHeader().getHash()); |
||||
|
||||
blockChain.appendBlock(block_1, Lists.emptyList()); |
||||
blockChain.appendBlock(block_2, Lists.emptyList()); |
||||
|
||||
when(blockInterface.validatorsInBlock(any())).thenReturn(validators); |
||||
} |
||||
} |
Loading…
Reference in new issue