@ -21,12 +21,18 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verifyNoMoreInteractions ;
import static org.mockito.Mockito.when ;
import org.hyperledger.besu.consensus.common.bft.BftBlockHeaderFunctions ;
import org.hyperledger.besu.consensus.common.bft.BftExtraDataCodec ;
import org.hyperledger.besu.consensus.common.bft.ConsensusRoundHelpers ;
import org.hyperledger.besu.consensus.common.bft.ConsensusRoundIdentifier ;
import org.hyperledger.besu.consensus.common.bft.ProposedBlockHelpers ;
import org.hyperledger.besu.consensus.qbft.QbftContext ;
import org.hyperledger.besu.consensus.qbft.QbftExtraDataCodec ;
import org.hyperledger.besu.consensus.qbft.messagewrappers.Proposal ;
import org.hyperledger.besu.consensus.qbft.payload.MessageFactory ;
import org.hyperledger.besu.consensus.qbft.pki.PkiQbftBlockHeaderFunctions ;
import org.hyperledger.besu.consensus.qbft.pki.PkiQbftExtraData ;
import org.hyperledger.besu.consensus.qbft.pki.PkiQbftExtraDataCodec ;
import org.hyperledger.besu.crypto.NodeKey ;
import org.hyperledger.besu.crypto.NodeKeyUtils ;
import org.hyperledger.besu.ethereum.BlockValidator ;
@ -35,12 +41,19 @@ import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain ;
import org.hyperledger.besu.ethereum.core.Address ;
import org.hyperledger.besu.ethereum.core.Block ;
import org.hyperledger.besu.ethereum.core.BlockDataGenerator ;
import org.hyperledger.besu.ethereum.core.BlockDataGenerator.BlockOptions ;
import org.hyperledger.besu.ethereum.core.Hash ;
import org.hyperledger.besu.ethereum.core.Util ;
import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode ;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive ;
import org.hyperledger.besu.pki.cms.CmsValidator ;
import java.util.Collections ;
import java.util.List ;
import java.util.Optional ;
import org.apache.tuweni.bytes.Bytes ;
import org.junit.Before ;
import org.junit.Test ;
import org.junit.runner.RunWith ;
@ -53,6 +66,7 @@ public class ProposalPayloadValidatorTest {
@Mock private BlockValidator blockValidator ;
@Mock private MutableBlockchain blockChain ;
@Mock private WorldStateArchive worldStateArchive ;
@Mock private CmsValidator cmsValidator ;
private ProtocolContext protocolContext ;
private static final int CHAIN_HEIGHT = 3 ;
@ -64,7 +78,7 @@ public class ProposalPayloadValidatorTest {
private final MessageFactory messageFactory = new MessageFactory ( nodeKey ) ;
final ConsensusRoundIdentifier roundIdentifier =
ConsensusRoundHelpers . createFrom ( targetRound , 1 , 0 ) ;
final QbftExtraDataCodec bftExtraDataEncoder = new QbftExtraDataCodec ( ) ;
final QbftExtraDataCodec bftExtraDataCodec = new QbftExtraDataCodec ( ) ;
@Before
public void setup ( ) {
@ -72,16 +86,16 @@ public class ProposalPayloadValidatorTest {
new ProtocolContext (
blockChain ,
worldStateArchive ,
setupContextWithBftExtraDataEncoder ( emptyList ( ) , bftExtraDataEncoder ) ) ;
setupContextWithBftExtraDataEncoder ( QbftContext . class , emptyList ( ) , bftExtraDataCodec ) ) ;
}
@Test
public void validationPassesWhenProposerAndRoundMatchAndBlockIsValid ( ) {
final ProposalPayloadValidator payloadValidator =
new ProposalPayloadValidator (
expectedProposer , roundIdentifier , blockValidator , protocolContext ) ;
expectedProposer , roundIdentifier , blockValidator , protocolContext , bftExtraDataCodec ) ;
final Block block =
ProposedBlockHelpers . createProposalBlock ( emptyList ( ) , roundIdentifier , bftExtraDataEncoder ) ;
ProposedBlockHelpers . createProposalBlock ( emptyList ( ) , roundIdentifier , bftExtraDataCodec ) ;
final Proposal proposal =
messageFactory . createProposal ( roundIdentifier , block , emptyList ( ) , emptyList ( ) ) ;
@ -99,13 +113,13 @@ public class ProposalPayloadValidatorTest {
public void validationPassesWhenBlockRoundDoesNotMatchProposalRound ( ) {
final ProposalPayloadValidator payloadValidator =
new ProposalPayloadValidator (
expectedProposer , roundIdentifier , blockValidator , protocolContext ) ;
expectedProposer , roundIdentifier , blockValidator , protocolContext , bftExtraDataCodec ) ;
final Block block =
ProposedBlockHelpers . createProposalBlock (
emptyList ( ) ,
ConsensusRoundHelpers . createFrom ( roundIdentifier , 0 , + 1 ) ,
bftExtraDataEncoder ) ;
bftExtraDataCodec ) ;
final Proposal proposal =
messageFactory . createProposal ( roundIdentifier , block , emptyList ( ) , emptyList ( ) ) ;
@ -126,9 +140,9 @@ public class ProposalPayloadValidatorTest {
final ProposalPayloadValidator payloadValidator =
new ProposalPayloadValidator (
expectedProposer , roundIdentifier , blockValidator , protocolContext ) ;
expectedProposer , roundIdentifier , blockValidator , protocolContext , bftExtraDataCodec ) ;
final Block block =
ProposedBlockHelpers . createProposalBlock ( emptyList ( ) , roundIdentifier , bftExtraDataEncoder ) ;
ProposedBlockHelpers . createProposalBlock ( emptyList ( ) , roundIdentifier , bftExtraDataCodec ) ;
final Proposal proposal =
messageFactory . createProposal ( roundIdentifier , block , emptyList ( ) , emptyList ( ) ) ;
@ -146,7 +160,11 @@ public class ProposalPayloadValidatorTest {
public void validationFailsWhenExpectedProposerDoesNotMatchPayloadsAuthor ( ) {
final ProposalPayloadValidator payloadValidator =
new ProposalPayloadValidator (
Address . fromHexString ( "0x1" ) , roundIdentifier , blockValidator , protocolContext ) ;
Address . fromHexString ( "0x1" ) ,
roundIdentifier ,
blockValidator ,
protocolContext ,
bftExtraDataCodec ) ;
final Block block = ProposedBlockHelpers . createProposalBlock ( emptyList ( ) , roundIdentifier ) ;
final Proposal proposal =
messageFactory . createProposal ( roundIdentifier , block , emptyList ( ) , emptyList ( ) ) ;
@ -159,7 +177,7 @@ public class ProposalPayloadValidatorTest {
public void validationFailsWhenMessageMismatchesExpectedRound ( ) {
final ProposalPayloadValidator payloadValidator =
new ProposalPayloadValidator (
expectedProposer , roundIdentifier , blockValidator , protocolContext ) ;
expectedProposer , roundIdentifier , blockValidator , protocolContext , bftExtraDataCodec ) ;
final Block block = ProposedBlockHelpers . createProposalBlock ( emptyList ( ) , roundIdentifier ) ;
final Proposal proposal =
@ -177,7 +195,7 @@ public class ProposalPayloadValidatorTest {
public void validationFailsWhenMessageMismatchesExpectedHeight ( ) {
final ProposalPayloadValidator payloadValidator =
new ProposalPayloadValidator (
expectedProposer , roundIdentifier , blockValidator , protocolContext ) ;
expectedProposer , roundIdentifier , blockValidator , protocolContext , bftExtraDataCodec ) ;
final Block block = ProposedBlockHelpers . createProposalBlock ( emptyList ( ) , roundIdentifier ) ;
final Proposal proposal =
@ -195,12 +213,12 @@ public class ProposalPayloadValidatorTest {
public void validationFailsForBlockWithIncorrectHeight ( ) {
final ProposalPayloadValidator payloadValidator =
new ProposalPayloadValidator (
expectedProposer , roundIdentifier , blockValidator , protocolContext ) ;
expectedProposer , roundIdentifier , blockValidator , protocolContext , bftExtraDataCodec ) ;
final Block block =
ProposedBlockHelpers . createProposalBlock (
emptyList ( ) ,
ConsensusRoundHelpers . createFrom ( roundIdentifier , + 1 , 0 ) ,
bftExtraDataEncoder ) ;
bftExtraDataCodec ) ;
final Proposal proposal =
messageFactory . createProposal ( roundIdentifier , block , emptyList ( ) , emptyList ( ) ) ;
@ -213,4 +231,102 @@ public class ProposalPayloadValidatorTest {
assertThat ( payloadValidator . validate ( proposal . getSignedPayload ( ) ) ) . isFalse ( ) ;
}
@Test
public void validationForCmsFailsWhenCmsFailsValidation ( ) {
final PkiQbftExtraDataCodec pkiQbftExtraDataCodec = new PkiQbftExtraDataCodec ( ) ;
final QbftContext qbftContext =
setupContextWithBftExtraDataEncoder ( QbftContext . class , emptyList ( ) , pkiQbftExtraDataCodec ) ;
final Bytes cms = Bytes . fromHexStringLenient ( "0x1" ) ;
final ProtocolContext protocolContext =
new ProtocolContext ( blockChain , worldStateArchive , qbftContext ) ;
final ProposalPayloadValidator payloadValidator =
new ProposalPayloadValidator (
expectedProposer ,
roundIdentifier ,
blockValidator ,
protocolContext ,
pkiQbftExtraDataCodec ,
Optional . of ( cmsValidator ) ) ;
final Block block =
createPkiProposalBlock ( emptyList ( ) , roundIdentifier , pkiQbftExtraDataCodec , cms ) ;
final Proposal proposal =
messageFactory . createProposal ( roundIdentifier , block , emptyList ( ) , emptyList ( ) ) ;
final Hash hashWithoutCms =
PkiQbftBlockHeaderFunctions . forCmsSignature ( pkiQbftExtraDataCodec ) . hash ( block . getHeader ( ) ) ;
when ( blockValidator . validateAndProcessBlock (
eq ( protocolContext ) ,
eq ( block ) ,
eq ( HeaderValidationMode . LIGHT ) ,
eq ( HeaderValidationMode . FULL ) ) )
. thenReturn ( Optional . of ( new BlockProcessingOutputs ( null , null ) ) ) ;
when ( cmsValidator . validate ( eq ( cms ) , eq ( hashWithoutCms ) ) ) . thenReturn ( false ) ;
assertThat ( payloadValidator . validate ( proposal . getSignedPayload ( ) ) ) . isFalse ( ) ;
}
@Test
public void validationForCmsPassesWhenCmsIsValid ( ) {
final PkiQbftExtraDataCodec pkiQbftExtraDataCodec = new PkiQbftExtraDataCodec ( ) ;
final QbftContext qbftContext =
setupContextWithBftExtraDataEncoder ( QbftContext . class , emptyList ( ) , pkiQbftExtraDataCodec ) ;
final Bytes cms = Bytes . fromHexStringLenient ( "0x1" ) ;
final ProtocolContext protocolContext =
new ProtocolContext ( blockChain , worldStateArchive , qbftContext ) ;
final ProposalPayloadValidator payloadValidator =
new ProposalPayloadValidator (
expectedProposer ,
roundIdentifier ,
blockValidator ,
protocolContext ,
pkiQbftExtraDataCodec ,
Optional . of ( cmsValidator ) ) ;
final Block block =
createPkiProposalBlock ( emptyList ( ) , roundIdentifier , pkiQbftExtraDataCodec , cms ) ;
final Proposal proposal =
messageFactory . createProposal ( roundIdentifier , block , emptyList ( ) , emptyList ( ) ) ;
final Hash hashWithoutCms =
PkiQbftBlockHeaderFunctions . forCmsSignature ( pkiQbftExtraDataCodec ) . hash ( block . getHeader ( ) ) ;
when ( blockValidator . validateAndProcessBlock (
eq ( protocolContext ) ,
eq ( block ) ,
eq ( HeaderValidationMode . LIGHT ) ,
eq ( HeaderValidationMode . FULL ) ) )
. thenReturn ( Optional . of ( new BlockProcessingOutputs ( null , null ) ) ) ;
when ( cmsValidator . validate ( eq ( cms ) , eq ( hashWithoutCms ) ) ) . thenReturn ( true ) ;
assertThat ( payloadValidator . validate ( proposal . getSignedPayload ( ) ) ) . isTrue ( ) ;
}
public static Block createPkiProposalBlock (
final List < Address > validators ,
final ConsensusRoundIdentifier roundId ,
final BftExtraDataCodec bftExtraDataCodec ,
final Bytes cms ) {
final Bytes extraData =
bftExtraDataCodec . encode (
new PkiQbftExtraData (
Bytes . wrap ( new byte [ 32 ] ) ,
Collections . emptyList ( ) ,
Optional . empty ( ) ,
roundId . getRoundNumber ( ) ,
validators ,
cms ) ) ;
final BlockOptions blockOptions =
BlockOptions . create ( )
. setExtraData ( extraData )
. setBlockNumber ( roundId . getSequenceNumber ( ) )
. setBlockHeaderFunctions ( BftBlockHeaderFunctions . forCommittedSeal ( bftExtraDataCodec ) )
. hasOmmers ( false )
. hasTransactions ( false ) ;
if ( validators . size ( ) > 0 ) {
blockOptions . setCoinbase ( validators . get ( 0 ) ) ;
}
return new BlockDataGenerator ( ) . block ( blockOptions ) ;
}
}