[NC-1616] Added iBFT NewChainHeadHeader event and related blockchain observer (#59)

Signed-off-by: Adrian Sutton <adrian.sutton@consensys.net>
pull/2/head
Roberto Saltini 6 years ago committed by GitHub
parent d0416e95f8
commit 967a406252
  1. 31
      consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/IbftChainObserver.java
  2. 3
      consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/IbftEvents.java
  3. 56
      consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftevent/NewChainHead.java
  4. 78
      consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/IbftChainObserverTest.java
  5. 3
      pantheon/src/main/java/tech/pegasys/pantheon/controller/IbftPantheonController.java

@ -0,0 +1,31 @@
package tech.pegasys.pantheon.consensus.ibft;
import tech.pegasys.pantheon.consensus.ibft.ibftevent.NewChainHead;
import tech.pegasys.pantheon.ethereum.chain.BlockAddedEvent;
import tech.pegasys.pantheon.ethereum.chain.BlockAddedObserver;
import tech.pegasys.pantheon.ethereum.chain.Blockchain;
/**
* Blockchain observer that adds {@link NewChainHead} events to the event queue when a new block is
* added to the chain head
*/
public class IbftChainObserver implements BlockAddedObserver {
private final IbftEventQueue queue;
public IbftChainObserver(final IbftEventQueue queue) {
this.queue = queue;
}
@Override
public void onBlockAdded(final BlockAddedEvent event, final Blockchain blockchain) {
switch (event.getEventType()) {
case HEAD_ADVANCED:
queue.add(new NewChainHead(event.getBlock().getHeader()));
break;
default:
throw new IllegalStateException(
String.format("Unexpected BlockAddedEvent received: %s", event.getEventType()));
}
}
}

@ -9,6 +9,7 @@ public class IbftEvents {
} }
public enum Type { public enum Type {
ROUND_EXPIRY ROUND_EXPIRY,
NEW_CHAIN_HEAD_HEADER
} }
} }

@ -0,0 +1,56 @@
package tech.pegasys.pantheon.consensus.ibft.ibftevent;
import tech.pegasys.pantheon.consensus.ibft.IbftEvent;
import tech.pegasys.pantheon.consensus.ibft.IbftEvents.Type;
import tech.pegasys.pantheon.ethereum.core.BlockHeader;
import java.util.Objects;
import com.google.common.base.MoreObjects;
/** Event indicating that new chain head has been received */
public final class NewChainHead implements IbftEvent {
private final BlockHeader newChainHeadHeader;
/**
* Constructor for a NewChainHead event
*
* @param newChainHeadHeader The header of the current blockchain head
*/
public NewChainHead(final BlockHeader newChainHeadHeader) {
this.newChainHeadHeader = newChainHeadHeader;
}
@Override
public Type getType() {
return Type.NEW_CHAIN_HEAD_HEADER;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("New Chain Head Header", newChainHeadHeader)
.toString();
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final NewChainHead that = (NewChainHead) o;
return Objects.equals(newChainHeadHeader, that.newChainHeadHeader);
}
@Override
public int hashCode() {
return Objects.hash(newChainHeadHeader);
}
public BlockHeader getNewChainHeadHeader() {
return newChainHeadHeader;
}
}

@ -0,0 +1,78 @@
package tech.pegasys.pantheon.consensus.ibft;
import static java.util.Collections.emptyList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import tech.pegasys.pantheon.consensus.ibft.ibftevent.NewChainHead;
import tech.pegasys.pantheon.ethereum.chain.BlockAddedEvent;
import tech.pegasys.pantheon.ethereum.chain.Blockchain;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.Block;
import tech.pegasys.pantheon.ethereum.core.BlockBody;
import tech.pegasys.pantheon.ethereum.core.BlockHeader;
import tech.pegasys.pantheon.ethereum.core.BlockHeaderTestFixture;
import tech.pegasys.pantheon.ethereum.core.Hash;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
public class IbftChainObserverTest {
@Test
public void newChainHeadHeaderEventIsAddedToTheQueue() {
final Blockchain mockBlockchain = mock(Blockchain.class);
final IbftEventQueue mockQueue = mock(IbftEventQueue.class);
final BlockAddedEvent mockBlockAddedEvent = mock(BlockAddedEvent.class);
final IbftChainObserver ibftChainObserver = new IbftChainObserver(mockQueue);
final BlockHeader header =
new BlockHeaderTestFixture()
.number(1234)
.coinbase(Address.ECREC)
.parentHash(Hash.EMPTY_LIST_HASH)
.buildHeader();
final Block block = new Block(header, new BlockBody(emptyList(), emptyList()));
when(mockBlockAddedEvent.getEventType()).thenReturn(BlockAddedEvent.EventType.HEAD_ADVANCED);
when(mockBlockAddedEvent.getBlock()).thenReturn(block);
ibftChainObserver.onBlockAdded(mockBlockAddedEvent, mockBlockchain);
ArgumentCaptor<IbftEvent> ibftEventArgumentCaptor = ArgumentCaptor.forClass(IbftEvent.class);
verify(mockQueue).add(ibftEventArgumentCaptor.capture());
assertThat(ibftEventArgumentCaptor.getValue() instanceof NewChainHead).isTrue();
assertThat(((NewChainHead) ibftEventArgumentCaptor.getValue()).getNewChainHeadHeader())
.isEqualTo(header);
}
@Test(expected = IllegalStateException.class)
public void exceptionIsThrownWhenEventTypeIsFork() {
final Blockchain mockBlockchain = mock(Blockchain.class);
final IbftEventQueue mockQueue = mock(IbftEventQueue.class);
final BlockAddedEvent mockBlockAddedEvent = mock(BlockAddedEvent.class);
when(mockBlockAddedEvent.getEventType()).thenReturn(BlockAddedEvent.EventType.FORK);
final IbftChainObserver ibftChainObserver = new IbftChainObserver(mockQueue);
ibftChainObserver.onBlockAdded(mockBlockAddedEvent, mockBlockchain);
}
@Test(expected = IllegalStateException.class)
public void exceptionIsThrownWhenEventTypeIsChainReorg() {
final Blockchain mockBlockchain = mock(Blockchain.class);
final IbftEventQueue mockQueue = mock(IbftEventQueue.class);
final BlockAddedEvent mockBlockAddedEvent = mock(BlockAddedEvent.class);
when(mockBlockAddedEvent.getEventType()).thenReturn(BlockAddedEvent.EventType.CHAIN_REORG);
final IbftChainObserver ibftChainObserver = new IbftChainObserver(mockQueue);
ibftChainObserver.onBlockAdded(mockBlockAddedEvent, mockBlockchain);
}
}

@ -5,6 +5,7 @@ import static org.apache.logging.log4j.LogManager.getLogger;
import tech.pegasys.pantheon.consensus.common.EpochManager; import tech.pegasys.pantheon.consensus.common.EpochManager;
import tech.pegasys.pantheon.consensus.common.VoteProposer; import tech.pegasys.pantheon.consensus.common.VoteProposer;
import tech.pegasys.pantheon.consensus.common.VoteTally; import tech.pegasys.pantheon.consensus.common.VoteTally;
import tech.pegasys.pantheon.consensus.ibft.IbftChainObserver;
import tech.pegasys.pantheon.consensus.ibft.IbftContext; import tech.pegasys.pantheon.consensus.ibft.IbftContext;
import tech.pegasys.pantheon.consensus.ibft.IbftEventQueue; import tech.pegasys.pantheon.consensus.ibft.IbftEventQueue;
import tech.pegasys.pantheon.consensus.ibft.IbftProcessor; import tech.pegasys.pantheon.consensus.ibft.IbftProcessor;
@ -145,6 +146,8 @@ public class IbftPantheonController implements PantheonController<IbftContext, I
final IbftEventQueue ibftEventQueue = new IbftEventQueue(); final IbftEventQueue ibftEventQueue = new IbftEventQueue();
blockchain.observeBlockAdded(new IbftChainObserver(ibftEventQueue));
final IbftStateMachine ibftStateMachine = new IbftStateMachine(); final IbftStateMachine ibftStateMachine = new IbftStateMachine();
final IbftProcessor ibftProcessor = final IbftProcessor ibftProcessor =
new IbftProcessor( new IbftProcessor(

Loading…
Cancel
Save