Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
G
Geth-Modification
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
张蕾
Geth-Modification
Commits
73d21ea6
Commit
73d21ea6
authored
Dec 16, 2015
by
zsfelfoldi
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
core: create a header chain structure shared by core.BlockChain and light.LightChain
parent
bff9ceb6
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
575 additions
and
345 deletions
+575
-345
blockchain.go
core/blockchain.go
+125
-340
blockchain_test.go
core/blockchain_test.go
+8
-3
headerchain.go
core/headerchain.go
+432
-0
types.go
core/types.go
+10
-2
No files found.
core/blockchain.go
View file @
73d21ea6
...
...
@@ -18,11 +18,9 @@
package
core
import
(
crand
"crypto/rand"
"errors"
"fmt"
"io"
"math"
"math/big"
mrand
"math/rand"
"runtime"
...
...
@@ -82,24 +80,22 @@ const (
// included in the canonical one where as GetBlockByNumber always represents the
// canonical chain.
type
BlockChain
struct
{
hc
*
HeaderChain
chainDb
ethdb
.
Database
eventMux
*
event
.
TypeMux
genesisBlock
*
types
.
Block
// Last known total difficulty
mu
sync
.
RWMutex
chainmu
sync
.
RWMutex
tsmu
sync
.
RWMutex
procmu
sync
.
RWMutex
checkpoint
int
// checkpoint counts towards the new checkpoint
currentHeader
*
types
.
Header
// Current head of the header chain (may be above the block chain!)
currentBlock
*
types
.
Block
// Current head of the block chain
currentFastBlock
*
types
.
Block
// Current head of the fast-sync chain (may be above the block chain!)
headerCache
*
lru
.
Cache
// Cache for the most recent block headers
bodyCache
*
lru
.
Cache
// Cache for the most recent block bodies
bodyRLPCache
*
lru
.
Cache
// Cache for the most recent block bodies in RLP encoded format
tdCache
*
lru
.
Cache
// Cache for the most recent block total difficulties
blockCache
*
lru
.
Cache
// Cache for the most recent entire blocks
futureBlocks
*
lru
.
Cache
// future blocks are blocks added for later processing
...
...
@@ -110,7 +106,6 @@ type BlockChain struct {
wg
sync
.
WaitGroup
pow
pow
.
PoW
rand
*
mrand
.
Rand
processor
Processor
validator
Validator
}
...
...
@@ -119,10 +114,8 @@ type BlockChain struct {
// available in the database. It initialiser the default Ethereum Validator and
// Processor.
func
NewBlockChain
(
chainDb
ethdb
.
Database
,
pow
pow
.
PoW
,
mux
*
event
.
TypeMux
)
(
*
BlockChain
,
error
)
{
headerCache
,
_
:=
lru
.
New
(
headerCacheLimit
)
bodyCache
,
_
:=
lru
.
New
(
bodyCacheLimit
)
bodyRLPCache
,
_
:=
lru
.
New
(
bodyCacheLimit
)
tdCache
,
_
:=
lru
.
New
(
tdCacheLimit
)
blockCache
,
_
:=
lru
.
New
(
blockCacheLimit
)
futureBlocks
,
_
:=
lru
.
New
(
maxFutureBlocks
)
...
...
@@ -130,22 +123,21 @@ func NewBlockChain(chainDb ethdb.Database, pow pow.PoW, mux *event.TypeMux) (*Bl
chainDb
:
chainDb
,
eventMux
:
mux
,
quit
:
make
(
chan
struct
{}),
headerCache
:
headerCache
,
bodyCache
:
bodyCache
,
bodyRLPCache
:
bodyRLPCache
,
tdCache
:
tdCache
,
blockCache
:
blockCache
,
futureBlocks
:
futureBlocks
,
pow
:
pow
,
}
// Seed a fast but crypto originating random generator
seed
,
err
:=
crand
.
Int
(
crand
.
Reader
,
big
.
NewInt
(
math
.
MaxInt64
))
bc
.
SetValidator
(
NewBlockValidator
(
bc
,
pow
))
bc
.
SetProcessor
(
NewStateProcessor
(
bc
))
gv
:=
func
()
HeaderValidator
{
return
bc
.
Validator
()
}
var
err
error
bc
.
hc
,
err
=
NewHeaderChain
(
chainDb
,
gv
,
bc
.
getProcInterrupt
)
if
err
!=
nil
{
return
nil
,
err
}
bc
.
rand
=
mrand
.
New
(
mrand
.
NewSource
(
seed
.
Int64
()))
bc
.
SetValidator
(
NewBlockValidator
(
bc
,
pow
))
bc
.
SetProcessor
(
NewStateProcessor
(
bc
))
bc
.
genesisBlock
=
bc
.
GetBlockByNumber
(
0
)
if
bc
.
genesisBlock
==
nil
{
...
...
@@ -171,6 +163,10 @@ func NewBlockChain(chainDb ethdb.Database, pow pow.PoW, mux *event.TypeMux) (*Bl
return
bc
,
nil
}
func
(
self
*
BlockChain
)
getProcInterrupt
()
bool
{
return
atomic
.
LoadInt32
(
&
self
.
procInterrupt
)
==
1
}
// loadLastState loads the last known chain state from the database. This method
// assumes that the chain manager mutex is held.
func
(
self
*
BlockChain
)
loadLastState
()
error
{
...
...
@@ -189,12 +185,13 @@ func (self *BlockChain) loadLastState() error {
}
}
// Restore the last known head header
self
.
currentHeader
=
self
.
currentBlock
.
Header
()
currentHeader
:
=
self
.
currentBlock
.
Header
()
if
head
:=
GetHeadHeaderHash
(
self
.
chainDb
);
head
!=
(
common
.
Hash
{})
{
if
header
:=
self
.
GetHeader
(
head
);
header
!=
nil
{
self
.
currentHeader
=
header
currentHeader
=
header
}
}
self
.
hc
.
SetCurrentHeader
(
currentHeader
)
// Restore the last known head fast block
self
.
currentFastBlock
=
self
.
currentBlock
if
head
:=
GetHeadFastBlockHash
(
self
.
chainDb
);
head
!=
(
common
.
Hash
{})
{
...
...
@@ -203,11 +200,11 @@ func (self *BlockChain) loadLastState() error {
}
}
// Issue a status log and return
headerTd
:=
self
.
GetTd
(
self
.
currentHeader
.
Hash
())
headerTd
:=
self
.
GetTd
(
self
.
hc
.
CurrentHeader
()
.
Hash
())
blockTd
:=
self
.
GetTd
(
self
.
currentBlock
.
Hash
())
fastTd
:=
self
.
GetTd
(
self
.
currentFastBlock
.
Hash
())
glog
.
V
(
logger
.
Info
)
.
Infof
(
"Last header: #%d [%x…] TD=%v"
,
self
.
currentHeader
.
Number
,
self
.
currentHeader
.
Hash
()
.
Bytes
()[
:
4
],
headerTd
)
glog
.
V
(
logger
.
Info
)
.
Infof
(
"Last header: #%d [%x…] TD=%v"
,
self
.
hc
.
CurrentHeader
()
.
Number
,
self
.
hc
.
CurrentHeader
()
.
Hash
()
.
Bytes
()[
:
4
],
headerTd
)
glog
.
V
(
logger
.
Info
)
.
Infof
(
"Last block: #%d [%x…] TD=%v"
,
self
.
currentBlock
.
Number
(),
self
.
currentBlock
.
Hash
()
.
Bytes
()[
:
4
],
blockTd
)
glog
.
V
(
logger
.
Info
)
.
Infof
(
"Fast block: #%d [%x…] TD=%v"
,
self
.
currentFastBlock
.
Number
(),
self
.
currentFastBlock
.
Hash
()
.
Bytes
()[
:
4
],
fastTd
)
...
...
@@ -222,71 +219,35 @@ func (bc *BlockChain) SetHead(head uint64) {
bc
.
mu
.
Lock
()
defer
bc
.
mu
.
Unlock
()
// Figure out the highest known canonical headers and/or blocks
height
:=
uint64
(
0
)
if
bc
.
currentHeader
!=
nil
{
if
hh
:=
bc
.
currentHeader
.
Number
.
Uint64
();
hh
>
height
{
height
=
hh
}
}
if
bc
.
currentBlock
!=
nil
{
if
bh
:=
bc
.
currentBlock
.
NumberU64
();
bh
>
height
{
height
=
bh
}
}
if
bc
.
currentFastBlock
!=
nil
{
if
fbh
:=
bc
.
currentFastBlock
.
NumberU64
();
fbh
>
height
{
height
=
fbh
}
}
// Gather all the hashes that need deletion
drop
:=
make
(
map
[
common
.
Hash
]
struct
{})
for
bc
.
currentHeader
!=
nil
&&
bc
.
currentHeader
.
Number
.
Uint64
()
>
head
{
drop
[
bc
.
currentHeader
.
Hash
()]
=
struct
{}{}
bc
.
currentHeader
=
bc
.
GetHeader
(
bc
.
currentHeader
.
ParentHash
)
}
for
bc
.
currentBlock
!=
nil
&&
bc
.
currentBlock
.
NumberU64
()
>
head
{
drop
[
bc
.
currentBlock
.
Hash
()]
=
struct
{}{}
bc
.
currentBlock
=
bc
.
GetBlock
(
bc
.
currentBlock
.
ParentHash
())
}
for
bc
.
currentFastBlock
!=
nil
&&
bc
.
currentFastBlock
.
NumberU64
()
>
head
{
drop
[
bc
.
currentFastBlock
.
Hash
()]
=
struct
{}{}
bc
.
currentFastBlock
=
bc
.
GetBlock
(
bc
.
currentFastBlock
.
ParentHash
())
}
// Roll back the canonical chain numbering
for
i
:=
height
;
i
>
head
;
i
--
{
DeleteCanonicalHash
(
bc
.
chainDb
,
i
)
}
// Delete everything found by the above rewind
for
hash
,
_
:=
range
drop
{
DeleteHeader
(
bc
.
chainDb
,
hash
)
delFn
:=
func
(
hash
common
.
Hash
)
{
DeleteBody
(
bc
.
chainDb
,
hash
)
DeleteTd
(
bc
.
chainDb
,
hash
)
}
bc
.
hc
.
SetHead
(
head
,
delFn
)
// Clear out any stale content from the caches
bc
.
headerCache
.
Purge
()
bc
.
bodyCache
.
Purge
()
bc
.
bodyRLPCache
.
Purge
()
bc
.
blockCache
.
Purge
()
bc
.
futureBlocks
.
Purge
()
// Update all computed fields to the new head
if
bc
.
currentBlock
!=
nil
&&
bc
.
hc
.
CurrentHeader
()
.
Number
.
Uint64
()
<
bc
.
currentBlock
.
NumberU64
()
{
bc
.
currentBlock
=
bc
.
GetBlock
(
bc
.
hc
.
CurrentHeader
()
.
Hash
())
}
if
bc
.
currentFastBlock
!=
nil
&&
bc
.
hc
.
CurrentHeader
()
.
Number
.
Uint64
()
<
bc
.
currentFastBlock
.
NumberU64
()
{
bc
.
currentFastBlock
=
bc
.
GetBlock
(
bc
.
hc
.
CurrentHeader
()
.
Hash
())
}
if
bc
.
currentBlock
==
nil
{
bc
.
currentBlock
=
bc
.
genesisBlock
}
if
bc
.
currentHeader
==
nil
{
bc
.
currentHeader
=
bc
.
genesisBlock
.
Header
()
}
if
bc
.
currentFastBlock
==
nil
{
bc
.
currentFastBlock
=
bc
.
genesisBlock
}
if
err
:=
WriteHeadBlockHash
(
bc
.
chainDb
,
bc
.
currentBlock
.
Hash
());
err
!=
nil
{
glog
.
Fatalf
(
"failed to reset head block hash: %v"
,
err
)
}
if
err
:=
WriteHeadHeaderHash
(
bc
.
chainDb
,
bc
.
currentHeader
.
Hash
());
err
!=
nil
{
glog
.
Fatalf
(
"failed to reset head header hash: %v"
,
err
)
}
if
err
:=
WriteHeadFastBlockHash
(
bc
.
chainDb
,
bc
.
currentFastBlock
.
Hash
());
err
!=
nil
{
glog
.
Fatalf
(
"failed to reset head fast block hash: %v"
,
err
)
}
...
...
@@ -329,15 +290,6 @@ func (self *BlockChain) LastBlockHash() common.Hash {
return
self
.
currentBlock
.
Hash
()
}
// CurrentHeader retrieves the current head header of the canonical chain. The
// header is retrieved from the blockchain's internal cache.
func
(
self
*
BlockChain
)
CurrentHeader
()
*
types
.
Header
{
self
.
mu
.
RLock
()
defer
self
.
mu
.
RUnlock
()
return
self
.
currentHeader
}
// CurrentBlock retrieves the current head block of the canonical chain. The
// block is retrieved from the blockchain's internal cache.
func
(
self
*
BlockChain
)
CurrentBlock
()
*
types
.
Block
{
...
...
@@ -425,7 +377,8 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) {
bc
.
genesisBlock
=
genesis
bc
.
insert
(
bc
.
genesisBlock
)
bc
.
currentBlock
=
bc
.
genesisBlock
bc
.
currentHeader
=
bc
.
genesisBlock
.
Header
()
bc
.
hc
.
SetGenesis
(
bc
.
genesisBlock
.
Header
())
bc
.
hc
.
SetCurrentHeader
(
bc
.
genesisBlock
.
Header
())
bc
.
currentFastBlock
=
bc
.
genesisBlock
}
...
...
@@ -483,10 +436,7 @@ func (bc *BlockChain) insert(block *types.Block) {
// If the block is better than out head or is on a different chain, force update heads
if
updateHeads
{
if
err
:=
WriteHeadHeaderHash
(
bc
.
chainDb
,
block
.
Hash
());
err
!=
nil
{
glog
.
Fatalf
(
"failed to insert head header hash: %v"
,
err
)
}
bc
.
currentHeader
=
block
.
Header
()
bc
.
hc
.
SetCurrentHeader
(
block
.
Header
())
if
err
:=
WriteHeadFastBlockHash
(
bc
.
chainDb
,
block
.
Hash
());
err
!=
nil
{
glog
.
Fatalf
(
"failed to insert head fast block hash: %v"
,
err
)
...
...
@@ -500,38 +450,6 @@ func (bc *BlockChain) Genesis() *types.Block {
return
bc
.
genesisBlock
}
// HasHeader checks if a block header is present in the database or not, caching
// it if present.
func
(
bc
*
BlockChain
)
HasHeader
(
hash
common
.
Hash
)
bool
{
return
bc
.
GetHeader
(
hash
)
!=
nil
}
// GetHeader retrieves a block header from the database by hash, caching it if
// found.
func
(
self
*
BlockChain
)
GetHeader
(
hash
common
.
Hash
)
*
types
.
Header
{
// Short circuit if the header's already in the cache, retrieve otherwise
if
header
,
ok
:=
self
.
headerCache
.
Get
(
hash
);
ok
{
return
header
.
(
*
types
.
Header
)
}
header
:=
GetHeader
(
self
.
chainDb
,
hash
)
if
header
==
nil
{
return
nil
}
// Cache the found header for next time and return
self
.
headerCache
.
Add
(
header
.
Hash
(),
header
)
return
header
}
// GetHeaderByNumber retrieves a block header from the database by number,
// caching it (associated with its hash) if found.
func
(
self
*
BlockChain
)
GetHeaderByNumber
(
number
uint64
)
*
types
.
Header
{
hash
:=
GetCanonicalHash
(
self
.
chainDb
,
number
)
if
hash
==
(
common
.
Hash
{})
{
return
nil
}
return
self
.
GetHeader
(
hash
)
}
// GetBody retrieves a block body (transactions and uncles) from the database by
// hash, caching it if found.
func
(
self
*
BlockChain
)
GetBody
(
hash
common
.
Hash
)
*
types
.
Body
{
...
...
@@ -565,22 +483,6 @@ func (self *BlockChain) GetBodyRLP(hash common.Hash) rlp.RawValue {
return
body
}
// GetTd retrieves a block's total difficulty in the canonical chain from the
// database by hash, caching it if found.
func
(
self
*
BlockChain
)
GetTd
(
hash
common
.
Hash
)
*
big
.
Int
{
// Short circuit if the td's already in the cache, retrieve otherwise
if
cached
,
ok
:=
self
.
tdCache
.
Get
(
hash
);
ok
{
return
cached
.
(
*
big
.
Int
)
}
td
:=
GetTd
(
self
.
chainDb
,
hash
)
if
td
==
nil
{
return
nil
}
// Cache the found body for next time and return
self
.
tdCache
.
Add
(
hash
,
td
)
return
td
}
// HasBlock checks if a block is fully present in the database or not, caching
// it if present.
func
(
bc
*
BlockChain
)
HasBlock
(
hash
common
.
Hash
)
bool
{
...
...
@@ -625,28 +527,6 @@ func (self *BlockChain) GetBlockByNumber(number uint64) *types.Block {
return
self
.
GetBlock
(
hash
)
}
// GetBlockHashesFromHash retrieves a number of block hashes starting at a given
// hash, fetching towards the genesis block.
func
(
self
*
BlockChain
)
GetBlockHashesFromHash
(
hash
common
.
Hash
,
max
uint64
)
[]
common
.
Hash
{
// Get the origin header from which to fetch
header
:=
self
.
GetHeader
(
hash
)
if
header
==
nil
{
return
nil
}
// Iterate the headers until enough is collected or the genesis reached
chain
:=
make
([]
common
.
Hash
,
0
,
max
)
for
i
:=
uint64
(
0
);
i
<
max
;
i
++
{
if
header
=
self
.
GetHeader
(
header
.
ParentHash
);
header
==
nil
{
break
}
chain
=
append
(
chain
,
header
.
Hash
())
if
header
.
Number
.
Cmp
(
common
.
Big0
)
==
0
{
break
}
}
return
chain
}
// [deprecated by eth/62]
// GetBlocksFromHash returns the block corresponding to hash and up to n-1 ancestors.
func
(
self
*
BlockChain
)
GetBlocksFromHash
(
hash
common
.
Hash
,
n
int
)
(
blocks
[]
*
types
.
Block
)
{
...
...
@@ -698,195 +578,15 @@ func (self *BlockChain) procFutureBlocks() {
}
}
type
w
riteStatus
byte
type
W
riteStatus
byte
const
(
NonStatTy
w
riteStatus
=
iota
NonStatTy
W
riteStatus
=
iota
CanonStatTy
SplitStatTy
SideStatTy
)
// writeHeader writes a header into the local chain, given that its parent is
// already known. If the total difficulty of the newly inserted header becomes
// greater than the current known TD, the canonical chain is re-routed.
//
// Note: This method is not concurrent-safe with inserting blocks simultaneously
// into the chain, as side effects caused by reorganisations cannot be emulated
// without the real blocks. Hence, writing headers directly should only be done
// in two scenarios: pure-header mode of operation (light clients), or properly
// separated header/block phases (non-archive clients).
func
(
self
*
BlockChain
)
writeHeader
(
header
*
types
.
Header
)
error
{
self
.
wg
.
Add
(
1
)
defer
self
.
wg
.
Done
()
// Calculate the total difficulty of the header
ptd
:=
self
.
GetTd
(
header
.
ParentHash
)
if
ptd
==
nil
{
return
ParentError
(
header
.
ParentHash
)
}
localTd
:=
self
.
GetTd
(
self
.
currentHeader
.
Hash
())
externTd
:=
new
(
big
.
Int
)
.
Add
(
header
.
Difficulty
,
ptd
)
// Make sure no inconsistent state is leaked during insertion
self
.
mu
.
Lock
()
defer
self
.
mu
.
Unlock
()
// If the total difficulty is higher than our known, add it to the canonical chain
// Second clause in the if statement reduces the vulnerability to selfish mining.
// Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf
if
externTd
.
Cmp
(
localTd
)
>
0
||
(
externTd
.
Cmp
(
localTd
)
==
0
&&
mrand
.
Float64
()
<
0.5
)
{
// Delete any canonical number assignments above the new head
for
i
:=
header
.
Number
.
Uint64
()
+
1
;
GetCanonicalHash
(
self
.
chainDb
,
i
)
!=
(
common
.
Hash
{});
i
++
{
DeleteCanonicalHash
(
self
.
chainDb
,
i
)
}
// Overwrite any stale canonical number assignments
head
:=
self
.
GetHeader
(
header
.
ParentHash
)
for
GetCanonicalHash
(
self
.
chainDb
,
head
.
Number
.
Uint64
())
!=
head
.
Hash
()
{
WriteCanonicalHash
(
self
.
chainDb
,
head
.
Hash
(),
head
.
Number
.
Uint64
())
head
=
self
.
GetHeader
(
head
.
ParentHash
)
}
// Extend the canonical chain with the new header
if
err
:=
WriteCanonicalHash
(
self
.
chainDb
,
header
.
Hash
(),
header
.
Number
.
Uint64
());
err
!=
nil
{
glog
.
Fatalf
(
"failed to insert header number: %v"
,
err
)
}
if
err
:=
WriteHeadHeaderHash
(
self
.
chainDb
,
header
.
Hash
());
err
!=
nil
{
glog
.
Fatalf
(
"failed to insert head header hash: %v"
,
err
)
}
self
.
currentHeader
=
types
.
CopyHeader
(
header
)
}
// Irrelevant of the canonical status, write the header itself to the database
if
err
:=
WriteTd
(
self
.
chainDb
,
header
.
Hash
(),
externTd
);
err
!=
nil
{
glog
.
Fatalf
(
"failed to write header total difficulty: %v"
,
err
)
}
if
err
:=
WriteHeader
(
self
.
chainDb
,
header
);
err
!=
nil
{
glog
.
Fatalf
(
"filed to write header contents: %v"
,
err
)
}
return
nil
}
// InsertHeaderChain attempts to insert the given header chain in to the local
// chain, possibly creating a reorg. If an error is returned, it will return the
// index number of the failing header as well an error describing what went wrong.
//
// The verify parameter can be used to fine tune whether nonce verification
// should be done or not. The reason behind the optional check is because some
// of the header retrieval mechanisms already need to verfy nonces, as well as
// because nonces can be verified sparsely, not needing to check each.
func
(
self
*
BlockChain
)
InsertHeaderChain
(
chain
[]
*
types
.
Header
,
checkFreq
int
)
(
int
,
error
)
{
self
.
wg
.
Add
(
1
)
defer
self
.
wg
.
Done
()
// Make sure only one thread manipulates the chain at once
self
.
chainmu
.
Lock
()
defer
self
.
chainmu
.
Unlock
()
// Collect some import statistics to report on
stats
:=
struct
{
processed
,
ignored
int
}{}
start
:=
time
.
Now
()
// Generate the list of headers that should be POW verified
verify
:=
make
([]
bool
,
len
(
chain
))
for
i
:=
0
;
i
<
len
(
verify
)
/
checkFreq
;
i
++
{
index
:=
i
*
checkFreq
+
self
.
rand
.
Intn
(
checkFreq
)
if
index
>=
len
(
verify
)
{
index
=
len
(
verify
)
-
1
}
verify
[
index
]
=
true
}
verify
[
len
(
verify
)
-
1
]
=
true
// Last should always be verified to avoid junk
// Create the header verification task queue and worker functions
tasks
:=
make
(
chan
int
,
len
(
chain
))
for
i
:=
0
;
i
<
len
(
chain
);
i
++
{
tasks
<-
i
}
close
(
tasks
)
errs
,
failed
:=
make
([]
error
,
len
(
tasks
)),
int32
(
0
)
process
:=
func
(
worker
int
)
{
for
index
:=
range
tasks
{
header
,
hash
:=
chain
[
index
],
chain
[
index
]
.
Hash
()
// Short circuit insertion if shutting down or processing failed
if
atomic
.
LoadInt32
(
&
self
.
procInterrupt
)
==
1
{
return
}
if
atomic
.
LoadInt32
(
&
failed
)
>
0
{
return
}
// Short circuit if the header is bad or already known
if
BadHashes
[
hash
]
{
errs
[
index
]
=
BadHashError
(
hash
)
atomic
.
AddInt32
(
&
failed
,
1
)
return
}
if
self
.
HasHeader
(
hash
)
{
continue
}
// Verify that the header honors the chain parameters
checkPow
:=
verify
[
index
]
var
err
error
if
index
==
0
{
err
=
self
.
Validator
()
.
ValidateHeader
(
header
,
self
.
GetHeader
(
header
.
ParentHash
),
checkPow
)
}
else
{
err
=
self
.
Validator
()
.
ValidateHeader
(
header
,
chain
[
index
-
1
],
checkPow
)
}
if
err
!=
nil
{
errs
[
index
]
=
err
atomic
.
AddInt32
(
&
failed
,
1
)
return
}
}
}
// Start as many worker threads as goroutines allowed
pending
:=
new
(
sync
.
WaitGroup
)
for
i
:=
0
;
i
<
runtime
.
GOMAXPROCS
(
0
);
i
++
{
pending
.
Add
(
1
)
go
func
(
id
int
)
{
defer
pending
.
Done
()
process
(
id
)
}(
i
)
}
pending
.
Wait
()
// If anything failed, report
if
failed
>
0
{
for
i
,
err
:=
range
errs
{
if
err
!=
nil
{
return
i
,
err
}
}
}
// All headers passed verification, import them into the database
for
i
,
header
:=
range
chain
{
// Short circuit insertion if shutting down
if
atomic
.
LoadInt32
(
&
self
.
procInterrupt
)
==
1
{
glog
.
V
(
logger
.
Debug
)
.
Infoln
(
"premature abort during header chain processing"
)
break
}
hash
:=
header
.
Hash
()
// If the header's already known, skip it, otherwise store
if
self
.
HasHeader
(
hash
)
{
stats
.
ignored
++
continue
}
if
err
:=
self
.
writeHeader
(
header
);
err
!=
nil
{
return
i
,
err
}
stats
.
processed
++
}
// Report some public statistics so the user has a clue what's going on
first
,
last
:=
chain
[
0
],
chain
[
len
(
chain
)
-
1
]
glog
.
V
(
logger
.
Info
)
.
Infof
(
"imported %d header(s) (%d ignored) in %v. #%v [%x… / %x…]"
,
stats
.
processed
,
stats
.
ignored
,
time
.
Since
(
start
),
last
.
Number
,
first
.
Hash
()
.
Bytes
()[
:
4
],
last
.
Hash
()
.
Bytes
()[
:
4
])
return
0
,
nil
}
// Rollback is designed to remove a chain of links from the database that aren't
// certain enough to be valid.
func
(
self
*
BlockChain
)
Rollback
(
chain
[]
common
.
Hash
)
{
...
...
@@ -896,9 +596,8 @@ func (self *BlockChain) Rollback(chain []common.Hash) {
for
i
:=
len
(
chain
)
-
1
;
i
>=
0
;
i
--
{
hash
:=
chain
[
i
]
if
self
.
currentHeader
.
Hash
()
==
hash
{
self
.
currentHeader
=
self
.
GetHeader
(
self
.
currentHeader
.
ParentHash
)
WriteHeadHeaderHash
(
self
.
chainDb
,
self
.
currentHeader
.
Hash
())
if
self
.
hc
.
CurrentHeader
()
.
Hash
()
==
hash
{
self
.
hc
.
SetCurrentHeader
(
self
.
GetHeader
(
self
.
hc
.
CurrentHeader
()
.
ParentHash
))
}
if
self
.
currentFastBlock
.
Hash
()
==
hash
{
self
.
currentFastBlock
=
self
.
GetBlock
(
self
.
currentFastBlock
.
ParentHash
())
...
...
@@ -1055,7 +754,7 @@ func (self *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain
}
// WriteBlock writes the block to the chain.
func
(
self
*
BlockChain
)
WriteBlock
(
block
*
types
.
Block
)
(
status
w
riteStatus
,
err
error
)
{
func
(
self
*
BlockChain
)
WriteBlock
(
block
*
types
.
Block
)
(
status
W
riteStatus
,
err
error
)
{
self
.
wg
.
Add
(
1
)
defer
self
.
wg
.
Done
()
...
...
@@ -1427,3 +1126,89 @@ func reportBlock(block *types.Block, err error) {
}
go
ReportBlock
(
block
,
err
)
}
// InsertHeaderChain attempts to insert the given header chain in to the local
// chain, possibly creating a reorg. If an error is returned, it will return the
// index number of the failing header as well an error describing what went wrong.
//
// The verify parameter can be used to fine tune whether nonce verification
// should be done or not. The reason behind the optional check is because some
// of the header retrieval mechanisms already need to verfy nonces, as well as
// because nonces can be verified sparsely, not needing to check each.
func
(
self
*
BlockChain
)
InsertHeaderChain
(
chain
[]
*
types
.
Header
,
checkFreq
int
)
(
int
,
error
)
{
// Make sure only one thread manipulates the chain at once
self
.
chainmu
.
Lock
()
defer
self
.
chainmu
.
Unlock
()
self
.
wg
.
Add
(
1
)
defer
self
.
wg
.
Done
()
whFunc
:=
func
(
header
*
types
.
Header
)
error
{
self
.
mu
.
Lock
()
defer
self
.
mu
.
Unlock
()
_
,
err
:=
self
.
hc
.
WriteHeader
(
header
)
return
err
}
return
self
.
hc
.
InsertHeaderChain
(
chain
,
checkFreq
,
whFunc
)
}
// writeHeader writes a header into the local chain, given that its parent is
// already known. If the total difficulty of the newly inserted header becomes
// greater than the current known TD, the canonical chain is re-routed.
//
// Note: This method is not concurrent-safe with inserting blocks simultaneously
// into the chain, as side effects caused by reorganisations cannot be emulated
// without the real blocks. Hence, writing headers directly should only be done
// in two scenarios: pure-header mode of operation (light clients), or properly
// separated header/block phases (non-archive clients).
func
(
self
*
BlockChain
)
writeHeader
(
header
*
types
.
Header
)
error
{
self
.
wg
.
Add
(
1
)
defer
self
.
wg
.
Done
()
self
.
mu
.
Lock
()
defer
self
.
mu
.
Unlock
()
_
,
err
:=
self
.
hc
.
WriteHeader
(
header
)
return
err
}
// CurrentHeader retrieves the current head header of the canonical chain. The
// header is retrieved from the HeaderChain's internal cache.
func
(
self
*
BlockChain
)
CurrentHeader
()
*
types
.
Header
{
self
.
mu
.
RLock
()
defer
self
.
mu
.
RUnlock
()
return
self
.
hc
.
CurrentHeader
()
}
// GetTd retrieves a block's total difficulty in the canonical chain from the
// database by hash, caching it if found.
func
(
self
*
BlockChain
)
GetTd
(
hash
common
.
Hash
)
*
big
.
Int
{
return
self
.
hc
.
GetTd
(
hash
)
}
// GetHeader retrieves a block header from the database by hash, caching it if
// found.
func
(
self
*
BlockChain
)
GetHeader
(
hash
common
.
Hash
)
*
types
.
Header
{
return
self
.
hc
.
GetHeader
(
hash
)
}
// HasHeader checks if a block header is present in the database or not, caching
// it if present.
func
(
bc
*
BlockChain
)
HasHeader
(
hash
common
.
Hash
)
bool
{
return
bc
.
hc
.
HasHeader
(
hash
)
}
// GetBlockHashesFromHash retrieves a number of block hashes starting at a given
// hash, fetching towards the genesis block.
func
(
self
*
BlockChain
)
GetBlockHashesFromHash
(
hash
common
.
Hash
,
max
uint64
)
[]
common
.
Hash
{
return
self
.
hc
.
GetBlockHashesFromHash
(
hash
,
max
)
}
// GetHeaderByNumber retrieves a block header from the database by number,
// caching it (associated with its hash) if found.
func
(
self
*
BlockChain
)
GetHeaderByNumber
(
number
uint64
)
*
types
.
Header
{
return
self
.
hc
.
GetHeaderByNumber
(
number
)
}
core/blockchain_test.go
View file @
73d21ea6
...
...
@@ -472,11 +472,16 @@ func makeBlockChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.B
func
chm
(
genesis
*
types
.
Block
,
db
ethdb
.
Database
)
*
BlockChain
{
var
eventMux
event
.
TypeMux
bc
:=
&
BlockChain
{
chainDb
:
db
,
genesisBlock
:
genesis
,
eventMux
:
&
eventMux
,
pow
:
FakePow
{},
rand
:
rand
.
New
(
rand
.
NewSource
(
0
))}
bc
.
headerCache
,
_
=
lru
.
New
(
100
)
bc
:=
&
BlockChain
{
chainDb
:
db
,
genesisBlock
:
genesis
,
eventMux
:
&
eventMux
,
pow
:
FakePow
{},
}
valFn
:=
func
()
HeaderValidator
{
return
bc
.
Validator
()
}
bc
.
hc
,
_
=
NewHeaderChain
(
db
,
valFn
,
bc
.
getProcInterrupt
)
bc
.
bodyCache
,
_
=
lru
.
New
(
100
)
bc
.
bodyRLPCache
,
_
=
lru
.
New
(
100
)
bc
.
tdCache
,
_
=
lru
.
New
(
100
)
bc
.
blockCache
,
_
=
lru
.
New
(
100
)
bc
.
futureBlocks
,
_
=
lru
.
New
(
100
)
bc
.
SetValidator
(
bproc
{})
...
...
core/headerchain.go
0 → 100644
View file @
73d21ea6
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package
core
import
(
crand
"crypto/rand"
"math"
"math/big"
mrand
"math/rand"
"runtime"
"sync"
"sync/atomic"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/pow"
"github.com/hashicorp/golang-lru"
)
// HeaderChain implements the basic block header chain logic that is shared by
// core.BlockChain and light.LightChain. It is not usable in itself, only as
// a part of either structure.
// It is not thread safe either, the encapsulating chain structures should do
// the necessary mutex locking/unlocking.
type
HeaderChain
struct
{
chainDb
ethdb
.
Database
genesisHeader
*
types
.
Header
currentHeader
*
types
.
Header
// Current head of the header chain (may be above the block chain!)
headerCache
*
lru
.
Cache
// Cache for the most recent block headers
tdCache
*
lru
.
Cache
// Cache for the most recent block total difficulties
procInterrupt
func
()
bool
rand
*
mrand
.
Rand
getValidator
getHeaderValidatorFn
}
// getHeaderValidatorFn returns a HeaderValidator interface
type
getHeaderValidatorFn
func
()
HeaderValidator
// NewHeaderChain creates a new HeaderChain structure.
// getValidator should return the parent's validator
// procInterrupt points to the parent's interrupt semaphore
// wg points to the parent's shutdown wait group
func
NewHeaderChain
(
chainDb
ethdb
.
Database
,
getValidator
getHeaderValidatorFn
,
procInterrupt
func
()
bool
)
(
*
HeaderChain
,
error
)
{
headerCache
,
_
:=
lru
.
New
(
headerCacheLimit
)
tdCache
,
_
:=
lru
.
New
(
tdCacheLimit
)
// Seed a fast but crypto originating random generator
seed
,
err
:=
crand
.
Int
(
crand
.
Reader
,
big
.
NewInt
(
math
.
MaxInt64
))
if
err
!=
nil
{
return
nil
,
err
}
hc
:=
&
HeaderChain
{
chainDb
:
chainDb
,
headerCache
:
headerCache
,
tdCache
:
tdCache
,
procInterrupt
:
procInterrupt
,
rand
:
mrand
.
New
(
mrand
.
NewSource
(
seed
.
Int64
())),
getValidator
:
getValidator
,
}
hc
.
genesisHeader
=
hc
.
GetHeaderByNumber
(
0
)
if
hc
.
genesisHeader
==
nil
{
genesisBlock
,
err
:=
WriteDefaultGenesisBlock
(
chainDb
)
if
err
!=
nil
{
return
nil
,
err
}
glog
.
V
(
logger
.
Info
)
.
Infoln
(
"WARNING: Wrote default ethereum genesis block"
)
hc
.
genesisHeader
=
genesisBlock
.
Header
()
}
hc
.
currentHeader
=
hc
.
genesisHeader
if
head
:=
GetHeadBlockHash
(
chainDb
);
head
!=
(
common
.
Hash
{})
{
if
chead
:=
hc
.
GetHeader
(
head
);
chead
!=
nil
{
hc
.
currentHeader
=
chead
}
}
return
hc
,
nil
}
// WriteHeader writes a header into the local chain, given that its parent is
// already known. If the total difficulty of the newly inserted header becomes
// greater than the current known TD, the canonical chain is re-routed.
//
// Note: This method is not concurrent-safe with inserting blocks simultaneously
// into the chain, as side effects caused by reorganisations cannot be emulated
// without the real blocks. Hence, writing headers directly should only be done
// in two scenarios: pure-header mode of operation (light clients), or properly
// separated header/block phases (non-archive clients).
func
(
hc
*
HeaderChain
)
WriteHeader
(
header
*
types
.
Header
)
(
status
WriteStatus
,
err
error
)
{
// Calculate the total difficulty of the header
ptd
:=
hc
.
GetTd
(
header
.
ParentHash
)
if
ptd
==
nil
{
return
NonStatTy
,
ParentError
(
header
.
ParentHash
)
}
localTd
:=
hc
.
GetTd
(
hc
.
currentHeader
.
Hash
())
externTd
:=
new
(
big
.
Int
)
.
Add
(
header
.
Difficulty
,
ptd
)
// If the total difficulty is higher than our known, add it to the canonical chain
// Second clause in the if statement reduces the vulnerability to selfish mining.
// Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf
if
externTd
.
Cmp
(
localTd
)
>
0
||
(
externTd
.
Cmp
(
localTd
)
==
0
&&
mrand
.
Float64
()
<
0.5
)
{
// Delete any canonical number assignments above the new head
for
i
:=
header
.
Number
.
Uint64
()
+
1
;
GetCanonicalHash
(
hc
.
chainDb
,
i
)
!=
(
common
.
Hash
{});
i
++
{
DeleteCanonicalHash
(
hc
.
chainDb
,
i
)
}
// Overwrite any stale canonical number assignments
head
:=
hc
.
GetHeader
(
header
.
ParentHash
)
for
GetCanonicalHash
(
hc
.
chainDb
,
head
.
Number
.
Uint64
())
!=
head
.
Hash
()
{
WriteCanonicalHash
(
hc
.
chainDb
,
head
.
Hash
(),
head
.
Number
.
Uint64
())
head
=
hc
.
GetHeader
(
head
.
ParentHash
)
}
// Extend the canonical chain with the new header
if
err
:=
WriteCanonicalHash
(
hc
.
chainDb
,
header
.
Hash
(),
header
.
Number
.
Uint64
());
err
!=
nil
{
glog
.
Fatalf
(
"failed to insert header number: %v"
,
err
)
}
if
err
:=
WriteHeadHeaderHash
(
hc
.
chainDb
,
header
.
Hash
());
err
!=
nil
{
glog
.
Fatalf
(
"failed to insert head header hash: %v"
,
err
)
}
hc
.
currentHeader
=
types
.
CopyHeader
(
header
)
status
=
CanonStatTy
}
else
{
status
=
SideStatTy
}
// Irrelevant of the canonical status, write the header itself to the database
if
err
:=
WriteTd
(
hc
.
chainDb
,
header
.
Hash
(),
externTd
);
err
!=
nil
{
glog
.
Fatalf
(
"failed to write header total difficulty: %v"
,
err
)
}
if
err
:=
WriteHeader
(
hc
.
chainDb
,
header
);
err
!=
nil
{
glog
.
Fatalf
(
"failed to write header contents: %v"
,
err
)
}
return
}
// WhCallback is a callback function for inserting individual headers.
// A callback is used for two reasons: first, in a LightChain, status should be
// processed and light chain events sent, while in a BlockChain this is not
// necessary since chain events are sent after inserting blocks. Second, the
// header writes should be protected by the parent chain mutex individually.
type
WhCallback
func
(
*
types
.
Header
)
error
// InsertHeaderChain attempts to insert the given header chain in to the local
// chain, possibly creating a reorg. If an error is returned, it will return the
// index number of the failing header as well an error describing what went wrong.
//
// The verify parameter can be used to fine tune whether nonce verification
// should be done or not. The reason behind the optional check is because some
// of the header retrieval mechanisms already need to verfy nonces, as well as
// because nonces can be verified sparsely, not needing to check each.
func
(
hc
*
HeaderChain
)
InsertHeaderChain
(
chain
[]
*
types
.
Header
,
checkFreq
int
,
writeHeader
WhCallback
)
(
int
,
error
)
{
// Collect some import statistics to report on
stats
:=
struct
{
processed
,
ignored
int
}{}
start
:=
time
.
Now
()
// Generate the list of headers that should be POW verified
verify
:=
make
([]
bool
,
len
(
chain
))
for
i
:=
0
;
i
<
len
(
verify
)
/
checkFreq
;
i
++
{
index
:=
i
*
checkFreq
+
hc
.
rand
.
Intn
(
checkFreq
)
if
index
>=
len
(
verify
)
{
index
=
len
(
verify
)
-
1
}
verify
[
index
]
=
true
}
verify
[
len
(
verify
)
-
1
]
=
true
// Last should always be verified to avoid junk
// Create the header verification task queue and worker functions
tasks
:=
make
(
chan
int
,
len
(
chain
))
for
i
:=
0
;
i
<
len
(
chain
);
i
++
{
tasks
<-
i
}
close
(
tasks
)
errs
,
failed
:=
make
([]
error
,
len
(
tasks
)),
int32
(
0
)
process
:=
func
(
worker
int
)
{
for
index
:=
range
tasks
{
header
,
hash
:=
chain
[
index
],
chain
[
index
]
.
Hash
()
// Short circuit insertion if shutting down or processing failed
if
hc
.
procInterrupt
()
{
return
}
if
atomic
.
LoadInt32
(
&
failed
)
>
0
{
return
}
// Short circuit if the header is bad or already known
if
BadHashes
[
hash
]
{
errs
[
index
]
=
BadHashError
(
hash
)
atomic
.
AddInt32
(
&
failed
,
1
)
return
}
if
hc
.
HasHeader
(
hash
)
{
continue
}
// Verify that the header honors the chain parameters
checkPow
:=
verify
[
index
]
var
err
error
if
index
==
0
{
err
=
hc
.
getValidator
()
.
ValidateHeader
(
header
,
hc
.
GetHeader
(
header
.
ParentHash
),
checkPow
)
}
else
{
err
=
hc
.
getValidator
()
.
ValidateHeader
(
header
,
chain
[
index
-
1
],
checkPow
)
}
if
err
!=
nil
{
errs
[
index
]
=
err
atomic
.
AddInt32
(
&
failed
,
1
)
return
}
}
}
// Start as many worker threads as goroutines allowed
pending
:=
new
(
sync
.
WaitGroup
)
for
i
:=
0
;
i
<
runtime
.
GOMAXPROCS
(
0
);
i
++
{
pending
.
Add
(
1
)
go
func
(
id
int
)
{
defer
pending
.
Done
()
process
(
id
)
}(
i
)
}
pending
.
Wait
()
// If anything failed, report
if
failed
>
0
{
for
i
,
err
:=
range
errs
{
if
err
!=
nil
{
return
i
,
err
}
}
}
// All headers passed verification, import them into the database
for
i
,
header
:=
range
chain
{
// Short circuit insertion if shutting down
if
hc
.
procInterrupt
()
{
glog
.
V
(
logger
.
Debug
)
.
Infoln
(
"premature abort during header chain processing"
)
break
}
hash
:=
header
.
Hash
()
// If the header's already known, skip it, otherwise store
if
hc
.
HasHeader
(
hash
)
{
stats
.
ignored
++
continue
}
if
err
:=
writeHeader
(
header
);
err
!=
nil
{
return
i
,
err
}
stats
.
processed
++
}
// Report some public statistics so the user has a clue what's going on
first
,
last
:=
chain
[
0
],
chain
[
len
(
chain
)
-
1
]
glog
.
V
(
logger
.
Info
)
.
Infof
(
"imported %d header(s) (%d ignored) in %v. #%v [%x… / %x…]"
,
stats
.
processed
,
stats
.
ignored
,
time
.
Since
(
start
),
last
.
Number
,
first
.
Hash
()
.
Bytes
()[
:
4
],
last
.
Hash
()
.
Bytes
()[
:
4
])
return
0
,
nil
}
// GetBlockHashesFromHash retrieves a number of block hashes starting at a given
// hash, fetching towards the genesis block.
func
(
hc
*
HeaderChain
)
GetBlockHashesFromHash
(
hash
common
.
Hash
,
max
uint64
)
[]
common
.
Hash
{
// Get the origin header from which to fetch
header
:=
hc
.
GetHeader
(
hash
)
if
header
==
nil
{
return
nil
}
// Iterate the headers until enough is collected or the genesis reached
chain
:=
make
([]
common
.
Hash
,
0
,
max
)
for
i
:=
uint64
(
0
);
i
<
max
;
i
++
{
if
header
=
hc
.
GetHeader
(
header
.
ParentHash
);
header
==
nil
{
break
}
chain
=
append
(
chain
,
header
.
Hash
())
if
header
.
Number
.
Cmp
(
common
.
Big0
)
==
0
{
break
}
}
return
chain
}
// GetTd retrieves a block's total difficulty in the canonical chain from the
// database by hash, caching it if found.
func
(
hc
*
HeaderChain
)
GetTd
(
hash
common
.
Hash
)
*
big
.
Int
{
// Short circuit if the td's already in the cache, retrieve otherwise
if
cached
,
ok
:=
hc
.
tdCache
.
Get
(
hash
);
ok
{
return
cached
.
(
*
big
.
Int
)
}
td
:=
GetTd
(
hc
.
chainDb
,
hash
)
if
td
==
nil
{
return
nil
}
// Cache the found body for next time and return
hc
.
tdCache
.
Add
(
hash
,
td
)
return
td
}
// GetHeader retrieves a block header from the database by hash, caching it if
// found.
func
(
hc
*
HeaderChain
)
GetHeader
(
hash
common
.
Hash
)
*
types
.
Header
{
// Short circuit if the header's already in the cache, retrieve otherwise
if
header
,
ok
:=
hc
.
headerCache
.
Get
(
hash
);
ok
{
return
header
.
(
*
types
.
Header
)
}
header
:=
GetHeader
(
hc
.
chainDb
,
hash
)
if
header
==
nil
{
return
nil
}
// Cache the found header for next time and return
hc
.
headerCache
.
Add
(
header
.
Hash
(),
header
)
return
header
}
// HasHeader checks if a block header is present in the database or not, caching
// it if present.
func
(
hc
*
HeaderChain
)
HasHeader
(
hash
common
.
Hash
)
bool
{
return
hc
.
GetHeader
(
hash
)
!=
nil
}
// GetHeaderByNumber retrieves a block header from the database by number,
// caching it (associated with its hash) if found.
func
(
hc
*
HeaderChain
)
GetHeaderByNumber
(
number
uint64
)
*
types
.
Header
{
hash
:=
GetCanonicalHash
(
hc
.
chainDb
,
number
)
if
hash
==
(
common
.
Hash
{})
{
return
nil
}
return
hc
.
GetHeader
(
hash
)
}
// CurrentHeader retrieves the current head header of the canonical chain. The
// header is retrieved from the HeaderChain's internal cache.
func
(
hc
*
HeaderChain
)
CurrentHeader
()
*
types
.
Header
{
return
hc
.
currentHeader
}
// SetCurrentHeader sets the current head header of the canonical chain.
func
(
hc
*
HeaderChain
)
SetCurrentHeader
(
head
*
types
.
Header
)
{
if
err
:=
WriteHeadHeaderHash
(
hc
.
chainDb
,
head
.
Hash
());
err
!=
nil
{
glog
.
Fatalf
(
"failed to insert head header hash: %v"
,
err
)
}
hc
.
currentHeader
=
head
}
// DeleteCallback is a callback function that is called by SetHead before
// each header is deleted.
type
DeleteCallback
func
(
common
.
Hash
)
// SetHead rewinds the local chain to a new head. Everything above the new head
// will be deleted and the new one set.
func
(
hc
*
HeaderChain
)
SetHead
(
head
uint64
,
delFn
DeleteCallback
)
{
height
:=
uint64
(
0
)
if
hc
.
currentHeader
!=
nil
{
height
=
hc
.
currentHeader
.
Number
.
Uint64
()
}
for
hc
.
currentHeader
!=
nil
&&
hc
.
currentHeader
.
Number
.
Uint64
()
>
head
{
hash
:=
hc
.
currentHeader
.
Hash
()
if
delFn
!=
nil
{
delFn
(
hash
)
}
DeleteHeader
(
hc
.
chainDb
,
hash
)
DeleteTd
(
hc
.
chainDb
,
hash
)
hc
.
currentHeader
=
hc
.
GetHeader
(
hc
.
currentHeader
.
ParentHash
)
}
// Roll back the canonical chain numbering
for
i
:=
height
;
i
>
head
;
i
--
{
DeleteCanonicalHash
(
hc
.
chainDb
,
i
)
}
// Clear out any stale content from the caches
hc
.
headerCache
.
Purge
()
hc
.
tdCache
.
Purge
()
if
hc
.
currentHeader
==
nil
{
hc
.
currentHeader
=
hc
.
genesisHeader
}
if
err
:=
WriteHeadHeaderHash
(
hc
.
chainDb
,
hc
.
currentHeader
.
Hash
());
err
!=
nil
{
glog
.
Fatalf
(
"failed to reset head header hash: %v"
,
err
)
}
}
// SetGenesis sets a new genesis block header for the chain
func
(
hc
*
HeaderChain
)
SetGenesis
(
head
*
types
.
Header
)
{
hc
.
genesisHeader
=
head
}
// headerValidator is responsible for validating block headers
//
// headerValidator implements HeaderValidator.
type
headerValidator
struct
{
hc
*
HeaderChain
// Canonical header chain
Pow
pow
.
PoW
// Proof of work used for validating
}
// NewBlockValidator returns a new block validator which is safe for re-use
func
NewHeaderValidator
(
chain
*
HeaderChain
,
pow
pow
.
PoW
)
HeaderValidator
{
return
&
headerValidator
{
Pow
:
pow
,
hc
:
chain
,
}
}
// ValidateHeader validates the given header and, depending on the pow arg,
// checks the proof of work of the given header. Returns an error if the
// validation failed.
func
(
v
*
headerValidator
)
ValidateHeader
(
header
,
parent
*
types
.
Header
,
checkPow
bool
)
error
{
// Short circuit if the parent is missing.
if
parent
==
nil
{
return
ParentError
(
header
.
ParentHash
)
}
// Short circuit if the header's already known or its parent missing
if
v
.
hc
.
HasHeader
(
header
.
Hash
())
{
return
nil
}
return
ValidateHeader
(
v
.
Pow
,
header
,
parent
,
checkPow
,
false
)
}
core/types.go
View file @
73d21ea6
...
...
@@ -38,14 +38,22 @@ import (
// ValidateHeader validates the given header and parent and returns an error
// if it failed to do so.
//
// ValidateSta
ck
validates the given statedb and optionally the receipts and
// ValidateSta
te
validates the given statedb and optionally the receipts and
// gas used. The implementor should decide what to do with the given input.
type
Validator
interface
{
HeaderValidator
ValidateBlock
(
block
*
types
.
Block
)
error
ValidateHeader
(
header
,
parent
*
types
.
Header
,
checkPow
bool
)
error
ValidateState
(
block
,
parent
*
types
.
Block
,
state
*
state
.
StateDB
,
receipts
types
.
Receipts
,
usedGas
*
big
.
Int
)
error
}
// HeaderValidator is an interface for validating headers only
//
// ValidateHeader validates the given header and parent and returns an error
// if it failed to do so.
type
HeaderValidator
interface
{
ValidateHeader
(
header
,
parent
*
types
.
Header
,
checkPow
bool
)
error
}
// Processor is an interface for processing blocks using a given initial state.
//
// Process takes the block to be processed and the statedb upon which the
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment