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
493903ee
Unverified
Commit
493903ee
authored
Oct 20, 2018
by
Martin Holst Swende
Committed by
Péter Szilágyi
Nov 20, 2018
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
core: better side-chain importing
parent
3d997b6d
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
275 additions
and
98 deletions
+275
-98
blockchain.go
core/blockchain.go
+273
-96
node.go
node/node.go
+1
-1
table.go
p2p/discover/table.go
+1
-1
No files found.
core/blockchain.go
View file @
493903ee
...
@@ -1048,6 +1048,80 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
...
@@ -1048,6 +1048,80 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
return
n
,
err
return
n
,
err
}
}
// addFutureBlock checks if the block is within the max allowed window to get accepted for future processing, and
// returns an error if the block is too far ahead and was not added.
func
(
bc
*
BlockChain
)
addFutureBlock
(
block
*
types
.
Block
)
error
{
max
:=
big
.
NewInt
(
time
.
Now
()
.
Unix
()
+
maxTimeFutureBlocks
)
if
block
.
Time
()
.
Cmp
(
max
)
>
0
{
return
fmt
.
Errorf
(
"future block: %v > %v"
,
block
.
Time
(),
max
)
}
bc
.
futureBlocks
.
Add
(
block
.
Hash
(),
block
)
return
nil
}
// importBatch is a helper function to assist during chain import
type
importBatch
struct
{
chain
types
.
Blocks
results
<-
chan
error
index
int
validator
Validator
}
// newBatch creates a new batch based on the given blocks, which are assumed to be a contiguous chain
func
newBatch
(
chain
types
.
Blocks
,
results
<-
chan
error
,
validator
Validator
)
*
importBatch
{
return
&
importBatch
{
chain
:
chain
,
results
:
results
,
index
:
-
1
,
validator
:
validator
,
}
}
// next returns the next block in the batch, along with any potential validation error for that block
// When the end is reached, it will return (nil, nil), but Current() will always return the last element.
func
(
batch
*
importBatch
)
next
()
(
*
types
.
Block
,
error
)
{
if
batch
.
index
+
1
>=
len
(
batch
.
chain
)
{
return
nil
,
nil
}
batch
.
index
++
if
err
:=
<-
batch
.
results
;
err
!=
nil
{
return
batch
.
chain
[
batch
.
index
],
err
}
return
batch
.
chain
[
batch
.
index
],
batch
.
validator
.
ValidateBody
(
batch
.
chain
[
batch
.
index
])
}
// current returns the current block that's being processed. Even after the next() has progressed the entire
// chain, current will always return the last element
func
(
batch
*
importBatch
)
current
()
*
types
.
Block
{
if
batch
.
index
<
0
{
return
nil
}
return
batch
.
chain
[
batch
.
index
]
}
// previous returns the previous block was being processed, or nil
func
(
batch
*
importBatch
)
previous
()
*
types
.
Block
{
if
batch
.
index
<
1
{
return
nil
}
return
batch
.
chain
[
batch
.
index
-
1
]
}
// first returns the first block in the batch
func
(
batch
*
importBatch
)
first
()
*
types
.
Block
{
return
batch
.
chain
[
0
]
}
// remaining returns the number of remaining blocks
func
(
batch
*
importBatch
)
remaining
()
int
{
return
len
(
batch
.
chain
)
-
batch
.
index
}
// processed returns the number of processed blocks
func
(
batch
*
importBatch
)
processed
()
int
{
return
batch
.
index
+
1
}
// insertChain will execute the actual chain insertion and event aggregation. The
// insertChain will execute the actual chain insertion and event aggregation. The
// only reason this method exists as a separate one is to make locking cleaner
// only reason this method exists as a separate one is to make locking cleaner
// with deferred statements.
// with deferred statements.
...
@@ -1067,12 +1141,27 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty
...
@@ -1067,12 +1141,27 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty
chain
[
i
-
1
]
.
Hash
()
.
Bytes
()[
:
4
],
i
,
chain
[
i
]
.
NumberU64
(),
chain
[
i
]
.
Hash
()
.
Bytes
()[
:
4
],
chain
[
i
]
.
ParentHash
()
.
Bytes
()[
:
4
])
chain
[
i
-
1
]
.
Hash
()
.
Bytes
()[
:
4
],
i
,
chain
[
i
]
.
NumberU64
(),
chain
[
i
]
.
Hash
()
.
Bytes
()[
:
4
],
chain
[
i
]
.
ParentHash
()
.
Bytes
()[
:
4
])
}
}
}
}
log
.
Info
(
"insertChain"
,
"from"
,
chain
[
0
]
.
NumberU64
(),
"to"
,
chain
[
len
(
chain
)
-
1
]
.
NumberU64
())
// Pre-checks passed, start the full block imports
// Pre-checks passed, start the full block imports
bc
.
wg
.
Add
(
1
)
bc
.
wg
.
Add
(
1
)
defer
bc
.
wg
.
Done
()
defer
bc
.
wg
.
Done
()
bc
.
chainmu
.
Lock
()
bc
.
chainmu
.
Lock
()
defer
bc
.
chainmu
.
Unlock
()
defer
bc
.
chainmu
.
Unlock
()
return
bc
.
insertChainInternal
(
chain
,
true
)
}
//insertChainInternal is the internal implementation of insertChain, which assumes that
// 1. chains are contiguous, and
// 2. The `chainMu` lock is held
// This method is split out so that import batches that require re-injecting historical blocks can do
// so without releasing the lock, which could lead to racey behaviour. If a sidechain import is in progress,
// and the historic state is imported, but then new canon-head is added before the actual sidechain completes,
// then the historic state could be pruned again
func
(
bc
*
BlockChain
)
insertChainInternal
(
chain
types
.
Blocks
,
verifySeals
bool
)
(
int
,
[]
interface
{},
[]
*
types
.
Log
,
error
)
{
// Start a parallel signature recovery (signer will fluke on fork transition, minimal perf loss)
senderCacher
.
recoverFromBlocks
(
types
.
MakeSigner
(
bc
.
chainConfig
,
chain
[
0
]
.
Number
()),
chain
)
// A queued approach to delivering events. This is generally
// A queued approach to delivering events. This is generally
// faster than direct delivery and requires much less mutex
// faster than direct delivery and requires much less mutex
...
@@ -1082,6 +1171,8 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty
...
@@ -1082,6 +1171,8 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty
events
=
make
([]
interface
{},
0
,
len
(
chain
))
events
=
make
([]
interface
{},
0
,
len
(
chain
))
lastCanon
*
types
.
Block
lastCanon
*
types
.
Block
coalescedLogs
[]
*
types
.
Log
coalescedLogs
[]
*
types
.
Log
block
*
types
.
Block
err
error
)
)
// Start the parallel header verifier
// Start the parallel header verifier
headers
:=
make
([]
*
types
.
Header
,
len
(
chain
))
headers
:=
make
([]
*
types
.
Header
,
len
(
chain
))
...
@@ -1089,16 +1180,57 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty
...
@@ -1089,16 +1180,57 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty
for
i
,
block
:=
range
chain
{
for
i
,
block
:=
range
chain
{
headers
[
i
]
=
block
.
Header
()
headers
[
i
]
=
block
.
Header
()
seals
[
i
]
=
true
seals
[
i
]
=
verifySeals
}
}
abort
,
results
:=
bc
.
engine
.
VerifyHeaders
(
bc
,
headers
,
seals
)
abort
,
results
:=
bc
.
engine
.
VerifyHeaders
(
bc
,
headers
,
seals
)
defer
close
(
abort
)
defer
close
(
abort
)
// Start a parallel signature recovery (signer will fluke on fork transition, minimal perf loss)
// Peek the error for the first block
senderCacher
.
recoverFromBlocks
(
types
.
MakeSigner
(
bc
.
chainConfig
,
chain
[
0
]
.
Number
()),
chain
)
batch
:=
newBatch
(
chain
,
results
,
bc
.
Validator
())
if
block
,
err
=
batch
.
next
();
err
!=
nil
{
// Iterate over the blocks and insert when the verifier permits
if
err
==
consensus
.
ErrPrunedAncestor
{
for
i
,
block
:=
range
chain
{
return
bc
.
insertSidechainInternal
(
batch
,
err
)
}
else
if
err
==
consensus
.
ErrFutureBlock
||
(
err
==
consensus
.
ErrUnknownAncestor
&&
bc
.
futureBlocks
.
Contains
(
batch
.
first
()
.
ParentHash
()))
{
// The first block is a future block
// We can shove that one and any child blocks (that fail because of UnknownAncestor) into the future-queue
for
block
!=
nil
&&
(
batch
.
index
==
0
||
err
==
consensus
.
ErrUnknownAncestor
)
{
block
:=
batch
.
current
()
if
futureError
:=
bc
.
addFutureBlock
(
block
);
futureError
!=
nil
{
return
batch
.
index
,
events
,
coalescedLogs
,
futureError
}
block
,
err
=
batch
.
next
()
}
stats
.
queued
+=
batch
.
processed
()
stats
.
ignored
+=
batch
.
remaining
()
// If there are any still remaining, mark as ignored
return
batch
.
index
,
events
,
coalescedLogs
,
err
}
else
if
err
==
ErrKnownBlock
{
// Block and state both already known -- there can be two explanations.
// 1. We did a roll-back, and should now do a re-import
// 2. The block is stored as a sidechain, and is lying about it's stateroot, and passes a stateroot
// from the canonical chain, which has not been verified.
// Skip all known blocks that are blocks behind us
currentNum
:=
bc
.
CurrentBlock
()
.
NumberU64
()
for
block
!=
nil
&&
err
==
ErrKnownBlock
&&
currentNum
>=
block
.
NumberU64
()
{
// We ignore these
stats
.
ignored
++
block
,
err
=
batch
.
next
()
}
// Falls through to the block import
}
else
{
// Some other error
stats
.
ignored
+=
len
(
batch
.
chain
)
bc
.
reportBlock
(
block
,
nil
,
err
)
return
batch
.
index
,
events
,
coalescedLogs
,
err
}
}
// No validation errors
for
;
block
!=
nil
&&
err
==
nil
;
block
,
err
=
batch
.
next
()
{
// If the chain is terminating, stop processing blocks
// If the chain is terminating, stop processing blocks
if
atomic
.
LoadInt32
(
&
bc
.
procInterrupt
)
==
1
{
if
atomic
.
LoadInt32
(
&
bc
.
procInterrupt
)
==
1
{
log
.
Debug
(
"Premature abort during blocks processing"
)
log
.
Debug
(
"Premature abort during blocks processing"
)
...
@@ -1107,115 +1239,45 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty
...
@@ -1107,115 +1239,45 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty
// If the header is a banned one, straight out abort
// If the header is a banned one, straight out abort
if
BadHashes
[
block
.
Hash
()]
{
if
BadHashes
[
block
.
Hash
()]
{
bc
.
reportBlock
(
block
,
nil
,
ErrBlacklistedHash
)
bc
.
reportBlock
(
block
,
nil
,
ErrBlacklistedHash
)
return
i
,
events
,
coalescedLogs
,
ErrBlacklistedHash
return
batch
.
index
,
events
,
coalescedLogs
,
ErrBlacklistedHash
}
}
// Wait for the block's verification to complete
bstart
:=
time
.
Now
()
bstart
:=
time
.
Now
()
err
:=
<-
results
if
err
==
nil
{
err
=
bc
.
Validator
()
.
ValidateBody
(
block
)
}
switch
{
case
err
==
ErrKnownBlock
:
// Block and state both already known. However if the current block is below
// this number we did a rollback and we should reimport it nonetheless.
if
bc
.
CurrentBlock
()
.
NumberU64
()
>=
block
.
NumberU64
()
{
stats
.
ignored
++
continue
}
case
err
==
consensus
.
ErrFutureBlock
:
// Allow up to MaxFuture second in the future blocks. If this limit is exceeded
// the chain is discarded and processed at a later time if given.
max
:=
big
.
NewInt
(
time
.
Now
()
.
Unix
()
+
maxTimeFutureBlocks
)
if
block
.
Time
()
.
Cmp
(
max
)
>
0
{
return
i
,
events
,
coalescedLogs
,
fmt
.
Errorf
(
"future block: %v > %v"
,
block
.
Time
(),
max
)
}
bc
.
futureBlocks
.
Add
(
block
.
Hash
(),
block
)
stats
.
queued
++
continue
case
err
==
consensus
.
ErrUnknownAncestor
&&
bc
.
futureBlocks
.
Contains
(
block
.
ParentHash
())
:
bc
.
futureBlocks
.
Add
(
block
.
Hash
(),
block
)
stats
.
queued
++
continue
case
err
==
consensus
.
ErrPrunedAncestor
:
// Block competing with the canonical chain, store in the db, but don't process
// until the competitor TD goes above the canonical TD
currentBlock
:=
bc
.
CurrentBlock
()
localTd
:=
bc
.
GetTd
(
currentBlock
.
Hash
(),
currentBlock
.
NumberU64
())
externTd
:=
new
(
big
.
Int
)
.
Add
(
bc
.
GetTd
(
block
.
ParentHash
(),
block
.
NumberU64
()
-
1
),
block
.
Difficulty
())
if
localTd
.
Cmp
(
externTd
)
>
0
{
if
err
=
bc
.
WriteBlockWithoutState
(
block
,
externTd
);
err
!=
nil
{
return
i
,
events
,
coalescedLogs
,
err
}
continue
}
// Competitor chain beat canonical, gather all blocks from the common ancestor
var
winner
[]
*
types
.
Block
parent
:=
bc
.
GetBlock
(
block
.
ParentHash
(),
block
.
NumberU64
()
-
1
)
for
!
bc
.
HasState
(
parent
.
Root
())
{
winner
=
append
(
winner
,
parent
)
parent
=
bc
.
GetBlock
(
parent
.
ParentHash
(),
parent
.
NumberU64
()
-
1
)
}
for
j
:=
0
;
j
<
len
(
winner
)
/
2
;
j
++
{
winner
[
j
],
winner
[
len
(
winner
)
-
1
-
j
]
=
winner
[
len
(
winner
)
-
1
-
j
],
winner
[
j
]
}
// Import all the pruned blocks to make the state available
bc
.
chainmu
.
Unlock
()
_
,
evs
,
logs
,
err
:=
bc
.
insertChain
(
winner
)
bc
.
chainmu
.
Lock
()
events
,
coalescedLogs
=
evs
,
logs
if
err
!=
nil
{
return
i
,
events
,
coalescedLogs
,
err
}
case
err
!=
nil
:
bc
.
reportBlock
(
block
,
nil
,
err
)
return
i
,
events
,
coalescedLogs
,
err
}
// Create a new statedb using the parent block and report an
// error if it fails.
var
parent
*
types
.
Block
var
parent
*
types
.
Block
if
i
==
0
{
parent
=
batch
.
previous
()
if
parent
==
nil
{
parent
=
bc
.
GetBlock
(
block
.
ParentHash
(),
block
.
NumberU64
()
-
1
)
parent
=
bc
.
GetBlock
(
block
.
ParentHash
(),
block
.
NumberU64
()
-
1
)
}
else
{
parent
=
chain
[
i
-
1
]
}
}
state
,
err
:=
state
.
New
(
parent
.
Root
(),
bc
.
stateCache
)
state
,
err
:=
state
.
New
(
parent
.
Root
(),
bc
.
stateCache
)
if
err
!=
nil
{
if
err
!=
nil
{
return
i
,
events
,
coalescedLogs
,
err
return
batch
.
index
,
events
,
coalescedLogs
,
err
}
}
// Process block using the parent state as reference point.
// Process block using the parent state as reference point.
receipts
,
logs
,
usedGas
,
err
:=
bc
.
processor
.
Process
(
block
,
state
,
bc
.
vmConfig
)
receipts
,
logs
,
usedGas
,
err
:=
bc
.
processor
.
Process
(
block
,
state
,
bc
.
vmConfig
)
if
err
!=
nil
{
if
err
!=
nil
{
bc
.
reportBlock
(
block
,
receipts
,
err
)
bc
.
reportBlock
(
block
,
receipts
,
err
)
return
i
,
events
,
coalescedLogs
,
err
return
batch
.
index
,
events
,
coalescedLogs
,
err
}
}
// Validate the state using the default validator
// Validate the state using the default validator
err
=
bc
.
Validator
()
.
ValidateState
(
block
,
parent
,
state
,
receipts
,
usedGas
)
if
err
:=
bc
.
Validator
()
.
ValidateState
(
block
,
parent
,
state
,
receipts
,
usedGas
);
err
!=
nil
{
if
err
!=
nil
{
bc
.
reportBlock
(
block
,
receipts
,
err
)
bc
.
reportBlock
(
block
,
receipts
,
err
)
return
i
,
events
,
coalescedLogs
,
err
return
batch
.
index
,
events
,
coalescedLogs
,
err
}
}
proctime
:=
time
.
Since
(
bstart
)
proctime
:=
time
.
Since
(
bstart
)
// Write the block to the chain and get the status.
// Write the block to the chain and get the status.
status
,
err
:=
bc
.
WriteBlockWithState
(
block
,
receipts
,
state
)
status
,
err
:=
bc
.
WriteBlockWithState
(
block
,
receipts
,
state
)
if
err
!=
nil
{
if
err
!=
nil
{
return
i
,
events
,
coalescedLogs
,
err
return
batch
.
index
,
events
,
coalescedLogs
,
err
}
}
switch
status
{
switch
status
{
case
CanonStatTy
:
case
CanonStatTy
:
log
.
Debug
(
"Inserted new block"
,
"number"
,
block
.
Number
(),
"hash"
,
block
.
Hash
(),
"uncles"
,
len
(
block
.
Uncles
()),
log
.
Debug
(
"Inserted new block"
,
"number"
,
block
.
Number
(),
"hash"
,
block
.
Hash
(),
"txs"
,
len
(
block
.
Transactions
()),
"gas"
,
block
.
GasUsed
(),
"elapsed"
,
common
.
PrettyDuration
(
time
.
Since
(
bstart
)))
"uncles"
,
len
(
block
.
Uncles
()),
"txs"
,
len
(
block
.
Transactions
()),
"gas"
,
block
.
GasUsed
(),
"elapsed"
,
common
.
PrettyDuration
(
time
.
Since
(
bstart
)),
"root"
,
block
.
Root
()
.
String
())
coalescedLogs
=
append
(
coalescedLogs
,
logs
...
)
coalescedLogs
=
append
(
coalescedLogs
,
logs
...
)
blockInsertTimer
.
UpdateSince
(
bstart
)
events
=
append
(
events
,
ChainEvent
{
block
,
block
.
Hash
(),
logs
})
events
=
append
(
events
,
ChainEvent
{
block
,
block
.
Hash
(),
logs
})
lastCanon
=
block
lastCanon
=
block
...
@@ -1223,23 +1285,138 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty
...
@@ -1223,23 +1285,138 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty
bc
.
gcproc
+=
proctime
bc
.
gcproc
+=
proctime
case
SideStatTy
:
case
SideStatTy
:
log
.
Debug
(
"Inserted forked block"
,
"number"
,
block
.
Number
(),
"hash"
,
block
.
Hash
(),
"diff"
,
block
.
Difficulty
(),
"elapsed"
,
log
.
Debug
(
"Inserted forked block"
,
"number"
,
block
.
Number
(),
"hash"
,
block
.
Hash
(),
common
.
PrettyDuration
(
time
.
Since
(
bstart
)),
"txs"
,
len
(
block
.
Transactions
()),
"gas"
,
block
.
GasUsed
(),
"uncles"
,
len
(
block
.
Uncles
()))
"diff"
,
block
.
Difficulty
(),
"elapsed"
,
common
.
PrettyDuration
(
time
.
Since
(
bstart
)),
"txs"
,
len
(
block
.
Transactions
()),
"gas"
,
block
.
GasUsed
(),
"uncles"
,
len
(
block
.
Uncles
()),
blockInsertTimer
.
UpdateSince
(
bstart
)
"root"
,
block
.
Root
()
.
String
()
)
events
=
append
(
events
,
ChainSideEvent
{
block
})
events
=
append
(
events
,
ChainSideEvent
{
block
})
}
}
blockInsertTimer
.
UpdateSince
(
bstart
)
stats
.
processed
++
stats
.
processed
++
stats
.
usedGas
+=
usedGas
stats
.
usedGas
+=
usedGas
cache
,
_
:=
bc
.
stateCache
.
TrieDB
()
.
Size
()
cache
,
_
:=
bc
.
stateCache
.
TrieDB
()
.
Size
()
stats
.
report
(
chain
,
i
,
cache
)
stats
.
report
(
chain
,
batch
.
index
,
cache
)
}
// Any blocks remaining here? If so, the only ones we need to care about are
// shoving future blocks into queue
if
block
!=
nil
&&
err
==
consensus
.
ErrFutureBlock
{
if
futureErr
:=
bc
.
addFutureBlock
(
block
);
futureErr
!=
nil
{
return
batch
.
index
,
events
,
coalescedLogs
,
futureErr
}
for
block
,
err
=
batch
.
next
();
block
!=
nil
&&
err
==
consensus
.
ErrUnknownAncestor
;
{
if
futureErr
:=
bc
.
addFutureBlock
(
block
);
futureErr
!=
nil
{
return
batch
.
index
,
events
,
coalescedLogs
,
futureErr
}
stats
.
queued
++
block
,
err
=
batch
.
next
()
}
}
}
stats
.
ignored
+=
batch
.
remaining
()
// Append a single chain head event if we've progressed the chain
// Append a single chain head event if we've progressed the chain
if
lastCanon
!=
nil
&&
bc
.
CurrentBlock
()
.
Hash
()
==
lastCanon
.
Hash
()
{
if
lastCanon
!=
nil
&&
bc
.
CurrentBlock
()
.
Hash
()
==
lastCanon
.
Hash
()
{
events
=
append
(
events
,
ChainHeadEvent
{
lastCanon
})
events
=
append
(
events
,
ChainHeadEvent
{
lastCanon
})
}
}
return
0
,
events
,
coalescedLogs
,
nil
return
0
,
events
,
coalescedLogs
,
err
}
// insertSidechainInternal should be called when an import batch hits upon a pruned ancestor error, which happens when
// an sidechain with a sufficiently old fork-block is found. It writes all (header-and-body-valid) blocks to disk, then
// tries to switch over to the new chain if the TD exceeded the current chain.
// It assumes that relevant locks are held already (hence 'Internal')
func
(
bc
*
BlockChain
)
insertSidechainInternal
(
batch
*
importBatch
,
err
error
)
(
int
,
[]
interface
{},
[]
*
types
.
Log
,
error
)
{
// If we're given a chain of blocks, and the first one is pruned, that means we're getting a
// sidechain imported. On the sidechain, we validate headers, but do not validate body and state
// (and actually import them) until the sidechain reaches a higher TD.
// Until then, we store them in the database (assuming that the header PoW check works out)
var
(
externTd
*
big
.
Int
canonHeadNumber
=
bc
.
CurrentBlock
()
.
NumberU64
()
events
=
make
([]
interface
{},
0
)
coalescedLogs
[]
*
types
.
Log
)
// The first sidechain block error is already verified to be ErrPrunedAncestor. Since we don't import
// them here, we expect ErrUnknownAncestor for the remaining ones. Any other errors means that
// the block is invalid, and should not be written to disk.
block
:=
batch
.
current
()
for
block
!=
nil
&&
(
err
==
consensus
.
ErrPrunedAncestor
)
{
// Check the canonical stateroot for that number
if
remoteNum
:=
block
.
NumberU64
();
canonHeadNumber
>=
remoteNum
{
canonBlock
:=
bc
.
GetBlockByNumber
(
remoteNum
)
if
canonBlock
!=
nil
&&
canonBlock
.
Root
()
==
block
.
Root
()
{
// This is most likely a shadow-state attack.
// When a fork is imported into the database, and it eventually reaches a block height which is
// not pruned, we just found that the state already exist! This means that the sidechain block
// refers to a state which already exists in our canon chain.
// If left unchecked, we would now proceed importing the blocks, without actually having verified
// the state of the previous blocks.
log
.
Warn
(
"Sidechain ghost-state attack detected"
,
"blocknum"
,
block
.
NumberU64
(),
"sidechain root"
,
block
.
Root
(),
"canon root"
,
canonBlock
.
Root
())
// If someone legitimately side-mines blocks, they would still be imported as usual. However,
// we cannot risk writing unverified blocks to disk when they obviously target the pruning
// mechanism.
return
batch
.
index
,
events
,
coalescedLogs
,
fmt
.
Errorf
(
"sidechain ghost-state attack detected"
)
}
}
if
externTd
==
nil
{
externTd
=
bc
.
GetTd
(
block
.
ParentHash
(),
block
.
NumberU64
()
-
1
)
}
externTd
=
new
(
big
.
Int
)
.
Add
(
externTd
,
block
.
Difficulty
())
if
!
bc
.
HasBlock
(
block
.
Hash
(),
block
.
NumberU64
())
{
if
err
:=
bc
.
WriteBlockWithoutState
(
block
,
externTd
);
err
!=
nil
{
return
batch
.
index
,
events
,
coalescedLogs
,
err
}
}
block
,
err
=
batch
.
next
()
}
// At this point, we've written all sidechain blocks to database. Loop ended either on some other error,
// or all were processed. If there was some other error, we can ignore the rest of those blocks.
//
// If the externTd was larger than our local TD, we now need to reimport the previous
// blocks to regenerate the required state
currentBlock
:=
bc
.
CurrentBlock
()
localTd
:=
bc
.
GetTd
(
currentBlock
.
Hash
(),
currentBlock
.
NumberU64
())
// don't process until the competitor TD goes above the canonical TD
if
localTd
.
Cmp
(
externTd
)
>
0
{
// If we have hit a sidechain, we may have to reimport pruned blocks
log
.
Info
(
"Sidechain stored"
,
"start"
,
batch
.
first
()
.
NumberU64
(),
"end"
,
batch
.
current
()
.
NumberU64
(),
"sidechain TD"
,
externTd
,
"local TD"
,
localTd
)
return
batch
.
index
,
events
,
coalescedLogs
,
err
}
// Competitor chain beat canonical. Before we reprocess to get the common ancestor, investigate if
// any blocks in the chain are 'known bad' blocks.
for
index
,
b
:=
range
batch
.
chain
{
if
bc
.
badBlocks
.
Contains
(
b
.
Hash
())
{
log
.
Info
(
"Sidechain import aborted, bad block found"
,
"index"
,
index
,
"hash"
,
b
.
Hash
())
return
index
,
events
,
coalescedLogs
,
fmt
.
Errorf
(
"known bad block %d 0x%x"
,
b
.
NumberU64
(),
b
.
Hash
())
}
}
// gather all blocks from the common ancestor
var
parents
[]
*
types
.
Block
// Import all the pruned blocks to make the state available
parent
:=
bc
.
GetBlock
(
batch
.
first
()
.
ParentHash
(),
batch
.
first
()
.
NumberU64
()
-
1
)
for
!
bc
.
HasState
(
parent
.
Root
())
{
if
bc
.
badBlocks
.
Contains
(
parent
.
Hash
())
{
log
.
Info
(
"Sidechain parent processing aborted, bad block found"
,
"number"
,
parent
.
NumberU64
(),
"hash"
,
parent
.
Hash
())
return
0
,
events
,
coalescedLogs
,
fmt
.
Errorf
(
"known bad block %d 0x%x"
,
parent
.
NumberU64
(),
parent
.
Hash
())
}
parents
=
append
(
parents
,
parent
)
parent
=
bc
.
GetBlock
(
parent
.
ParentHash
(),
parent
.
NumberU64
()
-
1
)
}
for
j
:=
0
;
j
<
len
(
parents
)
/
2
;
j
++
{
parents
[
j
],
parents
[
len
(
parents
)
-
1
-
j
]
=
parents
[
len
(
parents
)
-
1
-
j
],
parents
[
j
]
}
// Import all the pruned blocks to make the state available
// During re-import, we can disable PoW-verification, since these are already verified
log
.
Info
(
"Inserting parent blocks for reprocessing"
,
"first"
,
parents
[
0
]
.
NumberU64
(),
"count"
,
len
(
parents
),
"last"
,
parents
[
len
(
parents
)
-
1
]
.
NumberU64
)
_
,
evs
,
logs
,
err
:=
bc
.
insertChainInternal
(
parents
,
false
)
events
,
coalescedLogs
=
evs
,
logs
if
err
!=
nil
{
return
0
,
events
,
coalescedLogs
,
err
}
log
.
Info
(
"Inserting sidechain blocks for processing"
)
errindex
,
events
,
coalescedLogs
,
err
:=
bc
.
insertChainInternal
(
batch
.
chain
[
0
:
batch
.
index
],
false
)
return
errindex
,
events
,
coalescedLogs
,
err
}
}
// insertStats tracks and reports on block insertion.
// insertStats tracks and reports on block insertion.
...
...
node/node.go
View file @
493903ee
...
@@ -287,7 +287,7 @@ func (n *Node) startInProc(apis []rpc.API) error {
...
@@ -287,7 +287,7 @@ func (n *Node) startInProc(apis []rpc.API) error {
if
err
:=
handler
.
RegisterName
(
api
.
Namespace
,
api
.
Service
);
err
!=
nil
{
if
err
:=
handler
.
RegisterName
(
api
.
Namespace
,
api
.
Service
);
err
!=
nil
{
return
err
return
err
}
}
n
.
log
.
Debug
(
"InProc registered"
,
"
service"
,
api
.
Service
,
"
namespace"
,
api
.
Namespace
)
n
.
log
.
Debug
(
"InProc registered"
,
"namespace"
,
api
.
Namespace
)
}
}
n
.
inprocHandler
=
handler
n
.
inprocHandler
=
handler
return
nil
return
nil
...
...
p2p/discover/table.go
View file @
493903ee
...
@@ -434,7 +434,7 @@ func (tab *Table) loadSeedNodes() {
...
@@ -434,7 +434,7 @@ func (tab *Table) loadSeedNodes() {
for
i
:=
range
seeds
{
for
i
:=
range
seeds
{
seed
:=
seeds
[
i
]
seed
:=
seeds
[
i
]
age
:=
log
.
Lazy
{
Fn
:
func
()
interface
{}
{
return
time
.
Since
(
tab
.
db
.
LastPongReceived
(
seed
.
ID
()))
}}
age
:=
log
.
Lazy
{
Fn
:
func
()
interface
{}
{
return
time
.
Since
(
tab
.
db
.
LastPongReceived
(
seed
.
ID
()))
}}
log
.
Debug
(
"Found seed node in database"
,
"id"
,
seed
.
ID
(),
"addr"
,
seed
.
addr
(),
"age"
,
age
)
log
.
Trace
(
"Found seed node in database"
,
"id"
,
seed
.
ID
(),
"addr"
,
seed
.
addr
(),
"age"
,
age
)
tab
.
add
(
seed
)
tab
.
add
(
seed
)
}
}
}
}
...
...
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