Commit 3853c8fc authored by David Vorick's avatar David Vorick

remove orphan handling from state

parent 279d9696
package consensus
import (
"fmt"
"math/big"
)
......@@ -85,35 +84,5 @@ func (s *State) addBlockToTree(b Block) (err error) {
}
}
// Reconnect all orphans that now have a parent.
childMap := s.missingParents[b.ID()]
for _, child := range childMap {
// This is a recursive call, which means someone could make it take
// a long time by giving us a bunch of false orphan blocks with the
// same parent. We have to check them all anyway though, it just
// allows them to cluster the processing. Utimately I think
// bandwidth will be the bigger issue, we'll reject bad orphans
// before we verify signatures.
err = s.validHeader(child)
if err != nil {
// TODO: There's nothing we can really do about the error that gets
// returned, because it doesn't reflect a problem with the current
// block. It means that someone managed to give us orphans with
// errors.
fmt.Println(err)
continue
}
err = s.addBlockToTree(child)
if err != nil {
// TODO: There's nothing we can really do about the error that gets
// returned, because it doesn't reflect a problem with the current
// block. It means that someone managed to give us orphans with
// errors.
fmt.Println(err)
}
}
delete(s.missingParents, b.ID())
return
}
......@@ -29,43 +29,12 @@ var (
BlockKnownErr = errors.New("block exists in block map.")
EarlyTimestampErr = errors.New("block timestamp is too early, block is illegal.")
FutureBlockErr = errors.New("timestamp too far in future, will try again later.")
KnownOrphanErr = errors.New("block is a known orphan")
OrphanErr = errors.New("block has no known parent")
LargeBlockErr = errors.New("block is too large to be accepted")
MinerPayoutErr = errors.New("miner payout sum does not equal block subsidy")
MissedTargetErr = errors.New("block does not meet target")
UnknownOrphanErr = errors.New("block is an unknown orphan")
)
// handleOrphanBlock adds a block to the list of orphans, returning an error
// indicating whether the orphan existed previously or not. handleOrphanBlock
// always returns an error.
func (s *State) handleOrphanBlock(b Block) error {
// Sanity check - block must be an orphan!
if DEBUG {
_, exists := s.blockMap[b.ParentID]
if exists {
panic("Incorrect use of handleOrphanBlock")
}
}
// Check if the missing parent is unknown
missingParent, exists := s.missingParents[b.ParentID]
if !exists {
// Add an entry for the parent and add the orphan block to the entry.
s.missingParents[b.ParentID] = make(map[BlockID]Block)
s.missingParents[b.ParentID][b.ID()] = b
return UnknownOrphanErr
}
// Check if the orphan is already known, and add the orphan if not.
_, exists = missingParent[b.ID()]
if exists {
return KnownOrphanErr
}
missingParent[b.ID()] = b
return UnknownOrphanErr
}
// earliestChildTimestamp returns the earliest timestamp that a child node
// can have while still being valid. See section 'Timestamp Rules' in
// Consensus.md.
......@@ -181,7 +150,7 @@ func (s *State) AcceptBlock(b Block) (err error) {
// See if the block is an orphan.
_, exists = s.blockMap[b.ParentID]
if !exists {
err = s.handleOrphanBlock(b)
err = OrphanErr
return
}
......
package consensus
import (
"math/big"
"testing"
"time"
)
......@@ -72,7 +71,6 @@ func testEmptyBlock(t *testing.T, s *State) {
// Get prior stats about the state.
bbLen := len(s.badBlocks)
bmLen := len(s.blockMap)
mpLen := len(s.missingParents)
cpLen := len(s.currentPath)
uoLen := len(s.unspentOutputs)
ocLen := len(s.openContracts)
......@@ -101,7 +99,6 @@ func testEmptyBlock(t *testing.T, s *State) {
// openContracts should not grow (contracts may close during the block though)
if bbLen != len(s.badBlocks) ||
bmLen != len(s.blockMap)-1 ||
mpLen != len(s.missingParents) ||
cpLen != len(s.currentPath)-1 ||
uoLen > len(s.unspentOutputs)-1 ||
ocLen < len(s.openContracts) {
......@@ -342,178 +339,6 @@ func testMissedTarget(t *testing.T, s *State) {
}
}
// testMultiOrphanBlock creates multiple orphans to a single parent, with one
// set of orphans that goes two deep. It then checks that after forking, all
// orphans have been added to the tree and removed from the orphan map.
func testMultiOrphanBlock(t *testing.T, s *State) {
// Mine the parent block.
parent, err := mineValidBlock(s)
if err != nil {
t.Fatal(err)
}
// Get the orphan and orphan2 targets.
parentTarget := s.CurrentTarget()
orphanRat := new(big.Rat).Mul(parentTarget.Rat(), MaxAdjustmentDown)
orphanTarget := RatToTarget(orphanRat)
orphan2Rat := new(big.Rat).Mul(orphanRat, MaxAdjustmentDown)
orphan2Target := RatToTarget(orphan2Rat)
// Mine 3 orphans with 'parent' as parent, and one orphan with another
// orphan as a parent.
//
// The timestamp gets incremented each time so that we don't accidentally
// mine the same block twice or end up with a too early block.
orphanA, err := mineTestingBlock(parent.ID(), Timestamp(time.Now().Unix()), nullMinerPayouts(s.Height()+2), nil, orphanTarget)
if err != nil {
t.Fatal(err)
}
orphanB, err := mineTestingBlock(parent.ID(), Timestamp(time.Now().Unix()+1), nullMinerPayouts(s.Height()+2), nil, orphanTarget)
if err != nil {
t.Fatal(err)
}
orphanC, err := mineTestingBlock(parent.ID(), Timestamp(time.Now().Unix()+2), nullMinerPayouts(s.Height()+2), nil, orphanTarget)
if err != nil {
t.Fatal(err)
}
orphan2, err := mineTestingBlock(orphanB.ID(), Timestamp(time.Now().Unix()+3), nullMinerPayouts(s.Height()+3), nil, orphan2Target)
if err != nil {
t.Fatal(err)
}
// Submit the orphans to the state, followed by the parent.
err = s.AcceptBlock(orphan2)
if err != UnknownOrphanErr {
t.Error("unexpected error, expecting UnknownOrphanErr:", err)
}
err = s.AcceptBlock(orphanA)
if err != UnknownOrphanErr {
t.Error("unexpected error, expecting UnknownOrphanErr:", err)
}
err = s.AcceptBlock(orphanB)
if err != UnknownOrphanErr {
t.Error("unexpected error, expecting UnknownOrphanErr:", err)
}
err = s.AcceptBlock(orphanC)
if err != UnknownOrphanErr {
t.Error("unexpected error, expecting UnknownOrphanErr:", err)
}
err = s.AcceptBlock(parent)
if err != nil {
t.Error("unexpected error:", err)
}
// Check that all blocks made it into the block tree and that the state
// forked to the orphan2 block.
_, exists := s.blockMap[orphan2.ID()]
if !exists {
t.Error("second layer orphan never made it into block map")
}
_, exists = s.blockMap[orphanA.ID()]
if !exists {
t.Error("first layer orphan never made it into block map")
}
_, exists = s.blockMap[orphanB.ID()]
if !exists {
t.Error("first layer orphan never made it into block map")
}
_, exists = s.blockMap[orphanC.ID()]
if !exists {
t.Error("first layer orphan never made it into block map")
}
_, exists = s.blockMap[parent.ID()]
if !exists {
t.Error("parent never made it into block map")
}
if s.currentBlockID != orphan2.ID() {
t.Error("orphan 2 is not updates as the head block")
}
_, exists = s.missingParents[orphanA.ID()]
if exists {
t.Error("first orphan was never deleted from missing parents")
}
_, exists = s.missingParents[parent.ID()]
if exists {
t.Error("first orphan was never deleted from missing parents")
}
}
// testOrphanBlock creates an orphan block and submits it to the state to check
// that orphans are handled correctly. Then it sumbmits the orphan's parent to
// check that the reconnection happens correctly.
func testOrphanBlock(t *testing.T, s *State) {
beforeStateHash := s.StateHash()
beforeHeight := s.Height()
// Mine the parent of the orphan.
parent, err := mineValidBlock(s)
if err != nil {
t.Fatal(err)
}
// Mine the orphan using a target that's guaranteed to be sufficient.
parentTarget := s.CurrentTarget()
orphanRat := new(big.Rat).Mul(parentTarget.Rat(), MaxAdjustmentDown)
orphanTarget := RatToTarget(orphanRat)
orphan, err := mineTestingBlock(parent.ID(), Timestamp(time.Now().Unix()), nullMinerPayouts(s.Height()+2), nil, orphanTarget)
if err != nil {
t.Fatal(err)
}
// Submit the orphan and check that the block was ignored.
err = s.AcceptBlock(orphan)
if err != UnknownOrphanErr {
t.Error("unexpected error upon submitting an unknown orphan block:", err)
}
if s.StateHash() != beforeStateHash {
t.Error("state hash changed after submitting an orphan block")
}
_, exists := s.blockMap[orphan.ID()]
if exists {
t.Error("orphan got added to the block map")
}
// Check that the KnownOrphan code is working as well.
err = s.AcceptBlock(orphan)
if err != KnownOrphanErr {
t.Error("unexpected error upong submitting a known orphan:", err)
}
if s.StateHash() != beforeStateHash {
t.Error("state hash changed after submitting an orphan block")
}
_, exists = s.blockMap[orphan.ID()]
if exists {
t.Error("orphan got added to the block map")
}
// Submit the parent and check that both the orphan and the parent get
// accepted.
err = s.AcceptBlock(parent)
if err != nil {
t.Error("unexpected error upon submitting the parent to an orphan:", err)
}
_, exists = s.blockMap[parent.ID()]
if !exists {
t.Error("parent block is not in the block map")
}
_, exists = s.blockMap[orphan.ID()]
if !exists {
t.Error("orphan block is not in the block map after being reconnected")
}
if s.currentBlockID != orphan.ID() {
t.Error("the states current block is not the reconnected orphan")
}
if beforeHeight != s.Height()-2 {
t.Error("height should now be reporting 2 new blocks.")
}
// Check that the orphan has been removed from the orphan map.
_, exists = s.missingParents[parent.ID()]
if exists {
t.Error("orphan map was not cleaned out after orphans were connected")
}
}
// testRepeatBlock submits a block to the state, and then submits the same
// block to the state. If anything in the state has changed, an error is noted.
func testRepeatBlock(t *testing.T, s *State) {
......@@ -530,7 +355,6 @@ func testRepeatBlock(t *testing.T, s *State) {
// Collect metrics about the state.
bbLen := len(s.badBlocks)
bmLen := len(s.blockMap)
mpLen := len(s.missingParents)
cpLen := len(s.currentPath)
uoLen := len(s.unspentOutputs)
ocLen := len(s.openContracts)
......@@ -545,7 +369,6 @@ func testRepeatBlock(t *testing.T, s *State) {
// Compare the metrics and report an error if something has changed.
if bbLen != len(s.badBlocks) ||
bmLen != len(s.blockMap) ||
mpLen != len(s.missingParents) ||
cpLen != len(s.currentPath) ||
uoLen != len(s.unspentOutputs) ||
ocLen != len(s.openContracts) ||
......@@ -585,27 +408,6 @@ func TestMissedTarget(t *testing.T) {
testMissedTarget(t, s)
}
// TestDoubleOrphanBlock creates a new state and used it to call
// testDoubleOrphanBlock.
func TestMultiOrphanBlock(t *testing.T) {
if testing.Short() {
t.Skip()
}
s := CreateGenesisState()
testMultiOrphanBlock(t, s)
consistencyChecks(t, s)
}
// TestOrphanBlock creates a new state and uses it to call testOrphanBlock.
func TestOrphanBlock(t *testing.T) {
if testing.Short() {
t.Skip()
}
s := CreateGenesisState()
testOrphanBlock(t, s)
consistencyChecks(t, s)
}
// TestRepeatBlock creates a new state and uses it to call testRepeatBlock.
func TestRepeatBlock(t *testing.T) {
s := CreateGenesisState()
......@@ -630,3 +432,8 @@ func TestRepeatBlock(t *testing.T) {
// TODO: Probe the target adjustments, make sure that they are happening
// according to specification, moving as much as they should and that the
// clamps are being effective.
// TODO: Submit orphan blocks that have errors in them.
// TODO: Make sure that the code operates correctly when a block is found with
// an error halfway through validation.
......@@ -67,8 +67,6 @@ func orderedTestBattery(t *testing.T, states ...*State) {
testLargeBlock(t, s)
testMinerPayouts(t, s)
testMissedTarget(t, s)
testMultiOrphanBlock(t, s)
testOrphanBlock(t, s)
testRepeatBlock(t, s)
}
}
......
......@@ -28,9 +28,8 @@ type State struct {
// necessary so that if a parent is found, all the children can be added to
// the parent. The second is necessary for checking if a new block is a
// known orphan.
badBlocks map[BlockID]struct{} // A list of blocks that don't verify.
blockMap map[BlockID]*blockNode // A list of all blocks in the blocktree.
missingParents map[BlockID]map[BlockID]Block // A list of all missing parents and their known children.
badBlocks map[BlockID]struct{} // A list of blocks that don't verify.
blockMap map[BlockID]*blockNode // A list of all blocks in the blocktree.
// Consensus Variables - the current state of consensus according to the
// longest fork.
......@@ -49,7 +48,6 @@ func CreateGenesisState() (s *State) {
s = &State{
badBlocks: make(map[BlockID]struct{}),
blockMap: make(map[BlockID]*blockNode),
missingParents: make(map[BlockID]map[BlockID]Block),
currentPath: make(map[BlockHeight]BlockID),
openContracts: make(map[ContractID]FileContract),
unspentOutputs: make(map[OutputID]Output),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment