This commit is contained in:
DinhLN 2018-12-11 16:20:50 +07:00
commit dfcf2df19d
32 changed files with 766 additions and 404 deletions

View file

@ -27,6 +27,7 @@ ENV NETWORK_ID '89'
ENV WS_SECRET '' ENV WS_SECRET ''
ENV NETSTATS_HOST 'netstats-server' ENV NETSTATS_HOST 'netstats-server'
ENV NETSTATS_PORT '3000' ENV NETSTATS_PORT '3000'
ENV ANNOUNCE_TXS ''
RUN apk add --no-cache ca-certificates RUN apk add --no-cache ca-certificates

View file

@ -120,6 +120,7 @@ var (
//utils.GpoPercentileFlag, //utils.GpoPercentileFlag,
//utils.ExtraDataFlag, //utils.ExtraDataFlag,
configFileFlag, configFileFlag,
utils.AnnounceTxsFlag,
} }
rpcFlags = []cli.Flag{ rpcFlags = []cli.Flag{

View file

@ -113,6 +113,10 @@ func NewApp(gitCommit, usage string) *cli.App {
var ( var (
// General settings // General settings
AnnounceTxsFlag = cli.BoolFlag{
Name: "announce-txs",
Usage: "Always commit transactions",
}
DataDirFlag = DirectoryFlag{ DataDirFlag = DirectoryFlag{
Name: "datadir", Name: "datadir",
Usage: "Data directory for the databases and keystore", Usage: "Data directory for the databases and keystore",
@ -897,6 +901,9 @@ func SetNodeConfig(ctx *cli.Context, cfg *node.Config) {
if ctx.GlobalIsSet(NoUSBFlag.Name) { if ctx.GlobalIsSet(NoUSBFlag.Name) {
cfg.NoUSB = ctx.GlobalBool(NoUSBFlag.Name) cfg.NoUSB = ctx.GlobalBool(NoUSBFlag.Name)
} }
if ctx.GlobalIsSet(AnnounceTxsFlag.Name) {
cfg.AnnounceTxs = ctx.GlobalBool(AnnounceTxsFlag.Name)
}
} }
func setGPO(ctx *cli.Context, cfg *gasprice.Config) { func setGPO(ctx *cli.Context, cfg *gasprice.Config) {

View file

@ -58,7 +58,7 @@ type Engine interface {
// VerifyHeader checks whether a header conforms to the consensus rules of a // VerifyHeader checks whether a header conforms to the consensus rules of a
// given engine. Verifying the seal may be done optionally here, or explicitly // given engine. Verifying the seal may be done optionally here, or explicitly
// via the VerifySeal method. // via the VerifySeal method.
VerifyHeader(chain ChainReader, header *types.Header, seal bool) error VerifyHeader(chain ChainReader, header *types.Header, fullVerify bool) error
// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers // VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers
// concurrently. The method returns a quit channel to abort the operations and // concurrently. The method returns a quit channel to abort the operations and

View file

@ -46,8 +46,6 @@ import (
const ( const (
inmemorySnapshots = 128 // Number of recent vote snapshots to keep in memory inmemorySnapshots = 128 // Number of recent vote snapshots to keep in memory
inmemorySignatures = 4096 // Number of recent block signatures to keep in memory
wiggleTime = 500 * time.Millisecond // Random delay (per signer) to allow concurrent signers
M2ByteLength = 4 M2ByteLength = 4
) )
@ -58,8 +56,7 @@ type Masternode struct {
// Posv proof-of-stake-voting protocol constants. // Posv proof-of-stake-voting protocol constants.
var ( var (
epochLength = uint64(30000) // Default number of blocks after which to checkpoint and reset the pending votes epochLength = uint64(900) // Default number of blocks after which to checkpoint and reset the pending votes
blockPeriod = uint64(15) // Default minimum difference between two consecutive block's timestamps
extraVanity = 32 // Fixed number of extra-data prefix bytes reserved for signer vanity extraVanity = 32 // Fixed number of extra-data prefix bytes reserved for signer vanity
extraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal extraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal
@ -226,11 +223,11 @@ type Posv struct {
HookReward func(chain consensus.ChainReader, state *state.StateDB, header *types.Header) error HookReward func(chain consensus.ChainReader, state *state.StateDB, header *types.Header) error
HookPenalty func(chain consensus.ChainReader, blockNumberEpoc uint64) ([]common.Address, error) HookPenalty func(chain consensus.ChainReader, blockNumberEpoc uint64) ([]common.Address, error)
HookValidator func(header *types.Header, signers []common.Address) error HookValidator func(header *types.Header, signers []common.Address) ([]byte, error)
HookVerifyMNs func(header *types.Header, signers []common.Address) error HookVerifyMNs func(header *types.Header, signers []common.Address) error
} }
// New creates a Posv proof-of-stake-voting consensus engine with the initial // New creates a PoSV proof-of-stake-voting consensus engine with the initial
// signers set to the ones provided by the user. // signers set to the ones provided by the user.
func New(config *params.PosvConfig, db ethdb.Database) *Posv { func New(config *params.PosvConfig, db ethdb.Database) *Posv {
// Set any missing consensus parameters to their defaults // Set any missing consensus parameters to their defaults
@ -261,20 +258,20 @@ func (c *Posv) Author(header *types.Header) (common.Address, error) {
} }
// VerifyHeader checks whether a header conforms to the consensus rules. // VerifyHeader checks whether a header conforms to the consensus rules.
func (c *Posv) VerifyHeader(chain consensus.ChainReader, header *types.Header, seal bool) error { func (c *Posv) VerifyHeader(chain consensus.ChainReader, header *types.Header, fullVerify bool) error {
return c.verifyHeaderWithCache(chain, header, nil) return c.verifyHeaderWithCache(chain, header, nil, fullVerify)
} }
// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers. The // VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers. The
// method returns a quit channel to abort the operations and a results channel to // method returns a quit channel to abort the operations and a results channel to
// retrieve the async verifications (the order is that of the input slice). // retrieve the async verifications (the order is that of the input slice).
func (c *Posv) VerifyHeaders(chain consensus.ChainReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) { func (c *Posv) VerifyHeaders(chain consensus.ChainReader, headers []*types.Header, fullVerifies []bool) (chan<- struct{}, <-chan error) {
abort := make(chan struct{}) abort := make(chan struct{})
results := make(chan error, len(headers)) results := make(chan error, len(headers))
go func() { go func() {
for i, header := range headers { for i, header := range headers {
err := c.verifyHeaderWithCache(chain, header, headers[:i]) err := c.verifyHeaderWithCache(chain, header, headers[:i], fullVerifies[i])
select { select {
case <-abort: case <-abort:
@ -286,12 +283,12 @@ func (c *Posv) VerifyHeaders(chain consensus.ChainReader, headers []*types.Heade
return abort, results return abort, results
} }
func (c *Posv) verifyHeaderWithCache(chain consensus.ChainReader, header *types.Header, parents []*types.Header) error { func (c *Posv) verifyHeaderWithCache(chain consensus.ChainReader, header *types.Header, parents []*types.Header, fullVerify bool) error {
_, check := c.verifiedHeaders.Get(header.Hash()) _, check := c.verifiedHeaders.Get(header.Hash())
if check { if check {
return nil return nil
} }
err := c.verifyHeader(chain, header, parents) err := c.verifyHeader(chain, header, parents, fullVerify)
if err == nil { if err == nil {
c.verifiedHeaders.Add(header.Hash(), true) c.verifiedHeaders.Add(header.Hash(), true)
} }
@ -302,16 +299,20 @@ func (c *Posv) verifyHeaderWithCache(chain consensus.ChainReader, header *types.
// caller may optionally pass in a batch of parents (ascending order) to avoid // caller may optionally pass in a batch of parents (ascending order) to avoid
// looking those up from the database. This is useful for concurrently verifying // looking those up from the database. This is useful for concurrently verifying
// a batch of new headers. // a batch of new headers.
func (c *Posv) verifyHeader(chain consensus.ChainReader, header *types.Header, parents []*types.Header) error { func (c *Posv) verifyHeader(chain consensus.ChainReader, header *types.Header, parents []*types.Header, fullVerify bool) error {
if header.Number == nil { if header.Number == nil {
return errUnknownBlock return errUnknownBlock
} }
number := header.Number.Uint64() number := header.Number.Uint64()
if fullVerify {
if header.Number.Uint64() > c.config.Epoch && len(header.Validator) == 0 {
return consensus.ErrNoValidatorSignature
}
// Don't waste time checking blocks from the future // Don't waste time checking blocks from the future
if header.Time.Cmp(big.NewInt(time.Now().Unix())) > 0 { if header.Time.Cmp(big.NewInt(time.Now().Unix())) > 0 {
return consensus.ErrFutureBlock return consensus.ErrFutureBlock
} }
}
// Checkpoint blocks need to enforce zero beneficiary // Checkpoint blocks need to enforce zero beneficiary
checkpoint := (number % c.config.Epoch) == 0 checkpoint := (number % c.config.Epoch) == 0
if checkpoint && header.Coinbase != (common.Address{}) { if checkpoint && header.Coinbase != (common.Address{}) {
@ -344,29 +345,23 @@ func (c *Posv) verifyHeader(chain consensus.ChainReader, header *types.Header, p
if header.MixDigest != (common.Hash{}) { if header.MixDigest != (common.Hash{}) {
return errInvalidMixDigest return errInvalidMixDigest
} }
// Ensure that the block doesn't contain any uncles which are meaningless in PoA // Ensure that the block doesn't contain any uncles which are meaningless in PoSV
if header.UncleHash != uncleHash { if header.UncleHash != uncleHash {
return errInvalidUncleHash return errInvalidUncleHash
} }
// Ensure that the block's difficulty is meaningful (may not be correct at this point)
if number > 0 {
if header.Difficulty == nil || (header.Difficulty.Cmp(diffInTurn) != 0 && header.Difficulty.Cmp(diffNoTurn) != 0) {
return errInvalidDifficulty
}
}
// If all checks passed, validate any special fields for hard forks // If all checks passed, validate any special fields for hard forks
if err := misc.VerifyForkHashes(chain.Config(), header, false); err != nil { if err := misc.VerifyForkHashes(chain.Config(), header, false); err != nil {
return err return err
} }
// All basic checks passed, verify cascading fields // All basic checks passed, verify cascading fields
return c.verifyCascadingFields(chain, header, parents) return c.verifyCascadingFields(chain, header, parents, fullVerify)
} }
// verifyCascadingFields verifies all the header fields that are not standalone, // verifyCascadingFields verifies all the header fields that are not standalone,
// rather depend on a batch of previous headers. The caller may optionally pass // rather depend on a batch of previous headers. The caller may optionally pass
// in a batch of parents (ascending order) to avoid looking those up from the // in a batch of parents (ascending order) to avoid looking those up from the
// database. This is useful for concurrently verifying a batch of new headers. // database. This is useful for concurrently verifying a batch of new headers.
func (c *Posv) verifyCascadingFields(chain consensus.ChainReader, header *types.Header, parents []*types.Header) error { func (c *Posv) verifyCascadingFields(chain consensus.ChainReader, header *types.Header, parents []*types.Header, fullVerify bool) error {
// The genesis block is the always valid dead-end // The genesis block is the always valid dead-end
number := header.Number.Uint64() number := header.Number.Uint64()
if number == 0 { if number == 0 {
@ -385,9 +380,6 @@ func (c *Posv) verifyCascadingFields(chain consensus.ChainReader, header *types.
if parent.Time.Uint64()+c.config.Period > header.Time.Uint64() { if parent.Time.Uint64()+c.config.Period > header.Time.Uint64() {
return ErrInvalidTimestamp return ErrInvalidTimestamp
} }
if header.Number.Uint64() > c.config.Epoch && len(header.Validator) == 0 {
return consensus.ErrNoValidatorSignature
}
// Retrieve the snapshot needed to verify this header and cache it // Retrieve the snapshot needed to verify this header and cache it
snap, err := c.snapshot(chain, number-1, header.ParentHash, parents) snap, err := c.snapshot(chain, number-1, header.ParentHash, parents)
if err != nil { if err != nil {
@ -429,7 +421,7 @@ func (c *Posv) verifyCascadingFields(chain consensus.ChainReader, header *types.
} }
} }
// All basic checks passed, verify the seal and return // All basic checks passed, verify the seal and return
return c.verifySeal(chain, header, parents) return c.verifySeal(chain, header, parents, fullVerify)
} }
func (c *Posv) GetSnapshot(chain consensus.ChainReader, header *types.Header) (*Snapshot, error) { func (c *Posv) GetSnapshot(chain consensus.ChainReader, header *types.Header) (*Snapshot, error) {
@ -471,7 +463,7 @@ func (c *Posv) GetMasternodes(chain consensus.ChainReader, header *types.Header)
func (c *Posv) GetPeriod() uint64 { return c.config.Period } func (c *Posv) GetPeriod() uint64 { return c.config.Period }
func WhoIsCreator(snap *Snapshot, header *types.Header) (common.Address, error) { func whoIsCreator(snap *Snapshot, header *types.Header) (common.Address, error) {
if header.Number.Uint64() == 0 { if header.Number.Uint64() == 0 {
return common.Address{}, errors.New("Don't take block 0") return common.Address{}, errors.New("Don't take block 0")
} }
@ -482,31 +474,38 @@ func WhoIsCreator(snap *Snapshot, header *types.Header) (common.Address, error)
return m, nil return m, nil
} }
func YourTurn(masternodes []common.Address, snap *Snapshot, header *types.Header, cur common.Address) (int, int, bool, error) { func (c *Posv) YourTurn(chain consensus.ChainReader, parent *types.Header, signer common.Address) (int, int, int, bool, error) {
masternodes := c.GetMasternodes(chain, parent)
masternodes = masternodes[:len(masternodes)/2+1] masternodes = masternodes[:len(masternodes)/2+1]
snap, err := c.GetSnapshot(chain, parent)
if err != nil {
log.Error("Failed when trying to commit new work", "err", err)
return 0, -1, -1, false, err
}
if len(masternodes) == 0 { if len(masternodes) == 0 {
return -1, -1, true, nil return 0, -1, -1, false, errors.New("Masternodes not found")
} }
pre := common.Address{} pre := common.Address{}
// masternode[0] has chance to create block 1 // masternode[0] has chance to create block 1
var err error
preIndex := -1 preIndex := -1
if header.Number.Uint64() != 0 { if parent.Number.Uint64() != 0 {
pre, err = WhoIsCreator(snap, header) pre, err = whoIsCreator(snap, parent)
if err != nil { if err != nil {
return 0, 0, false, err return 0, 0, 0, false, err
} }
preIndex = position(masternodes, pre) preIndex = position(masternodes, pre)
} }
curIndex := position(masternodes, cur) curIndex := position(masternodes, signer)
log.Info("Masternodes cycle info", "number of masternodes", len(masternodes), "previous", pre, "position", preIndex, "current", cur, "position", curIndex) if signer == c.signer {
log.Info("Masternodes cycle info", "number of masternodes", len(masternodes), "previous", pre, "position", preIndex, "current", signer, "position", curIndex)
}
for i, s := range masternodes { for i, s := range masternodes {
fmt.Printf("%d - %s\n", i, s.String()) fmt.Printf("%d - %s\n", i, s.String())
} }
if (preIndex+1)%len(masternodes) == curIndex { if (preIndex+1)%len(masternodes) == curIndex {
return preIndex, curIndex, true, nil return len(masternodes), preIndex, curIndex, true, nil
} }
return preIndex, curIndex, false, nil return len(masternodes), preIndex, curIndex, false, nil
} }
// snapshot retrieves the authorization snapshot at a given point in time. // snapshot retrieves the authorization snapshot at a given point in time.
@ -534,7 +533,7 @@ func (c *Posv) snapshot(chain consensus.ChainReader, number uint64, hash common.
// If we're at block zero, make a snapshot // If we're at block zero, make a snapshot
if number == 0 { if number == 0 {
genesis := chain.GetHeaderByNumber(0) genesis := chain.GetHeaderByNumber(0)
if err := c.VerifyHeader(chain, genesis, false); err != nil { if err := c.VerifyHeader(chain, genesis, true); err != nil {
return nil, err return nil, err
} }
signers := make([]common.Address, (len(genesis.Extra)-extraVanity-extraSeal)/common.AddressLength) signers := make([]common.Address, (len(genesis.Extra)-extraVanity-extraSeal)/common.AddressLength)
@ -599,7 +598,7 @@ func (c *Posv) VerifyUncles(chain consensus.ChainReader, block *types.Block) err
// VerifySeal implements consensus.Engine, checking whether the signature contained // VerifySeal implements consensus.Engine, checking whether the signature contained
// in the header satisfies the consensus protocol requirements. // in the header satisfies the consensus protocol requirements.
func (c *Posv) VerifySeal(chain consensus.ChainReader, header *types.Header) error { func (c *Posv) VerifySeal(chain consensus.ChainReader, header *types.Header) error {
return c.verifySeal(chain, header, nil) return c.verifySeal(chain, header, nil, true)
} }
// verifySeal checks whether the signature contained in the header satisfies the // verifySeal checks whether the signature contained in the header satisfies the
@ -608,7 +607,7 @@ func (c *Posv) VerifySeal(chain consensus.ChainReader, header *types.Header) err
// from. // from.
// verifySeal also checks the pair of creator-validator set in the header satisfies // verifySeal also checks the pair of creator-validator set in the header satisfies
// the double validation. // the double validation.
func (c *Posv) verifySeal(chain consensus.ChainReader, header *types.Header, parents []*types.Header) error { func (c *Posv) verifySeal(chain consensus.ChainReader, header *types.Header, parents []*types.Header, fullVerify bool) error {
// Verifying the genesis block is not supported // Verifying the genesis block is not supported
number := header.Number.Uint64() number := header.Number.Uint64()
if number == 0 { if number == 0 {
@ -625,6 +624,20 @@ func (c *Posv) verifySeal(chain consensus.ChainReader, header *types.Header, par
if err != nil { if err != nil {
return err return err
} }
var parent *types.Header
if len(parents) > 0 {
parent = parents[len(parents)-1]
} else {
parent = chain.GetHeader(header.ParentHash, number-1)
}
difficulty := c.calcDifficulty(chain, parent, creator)
log.Debug("verify seal block", "number", header.Number, "hash", header.Hash(), "block difficulty", header.Difficulty, "calc difficulty", difficulty, "creator", creator)
// Ensure that the block's difficulty is meaningful (may not be correct at this point)
if number > 0 {
if header.Difficulty.Int64() != difficulty.Int64() {
return errInvalidDifficulty
}
}
masternodes := c.GetMasternodes(chain, header) masternodes := c.GetMasternodes(chain, header)
mstring := []string{} mstring := []string{}
for _, m := range masternodes { for _, m := range masternodes {
@ -664,7 +677,7 @@ func (c *Posv) verifySeal(chain consensus.ChainReader, header *types.Header, par
// header must contain validator info following double validation design // header must contain validator info following double validation design
// start checking from epoch 2nd. // start checking from epoch 2nd.
if header.Number.Uint64() > c.config.Epoch { if header.Number.Uint64() > c.config.Epoch && fullVerify {
validator, err := c.RecoverValidator(header) validator, err := c.RecoverValidator(header)
if err != nil { if err != nil {
return err return err
@ -742,38 +755,49 @@ func (c *Posv) Prepare(chain consensus.ChainReader, header *types.Header) error
} }
c.lock.RUnlock() c.lock.RUnlock()
} }
parent := chain.GetHeader(header.ParentHash, number-1)
if parent == nil {
return consensus.ErrUnknownAncestor
}
// Set the correct difficulty // Set the correct difficulty
header.Difficulty = big.NewInt(1) header.Difficulty = c.calcDifficulty(chain, parent, c.signer)
log.Debug("CalcDifficulty ", "number", header.Number, "difficulty", header.Difficulty)
// Ensure the extra data has all it's components // Ensure the extra data has all it's components
if len(header.Extra) < extraVanity { if len(header.Extra) < extraVanity {
header.Extra = append(header.Extra, bytes.Repeat([]byte{0x00}, extraVanity-len(header.Extra))...) header.Extra = append(header.Extra, bytes.Repeat([]byte{0x00}, extraVanity-len(header.Extra))...)
} }
header.Extra = header.Extra[:extraVanity] header.Extra = header.Extra[:extraVanity]
signers := snap.GetSigners() masternodes := snap.GetSigners()
if number%c.config.Epoch == 0 { if number > 0 && number%c.config.Epoch == 0 {
if c.HookPenalty != nil { if c.HookPenalty != nil {
penSigners, err := c.HookPenalty(chain, number) penMasternodes, err := c.HookPenalty(chain, number)
if err != nil { if err != nil {
return err return err
} }
if len(penSigners) > 0 { if len(penMasternodes) > 0 {
// Keep remove penalty signer out of signer list. // penalize bad masternode(s)
signers = common.RemoveItemFromArray(signers, penSigners) masternodes = common.RemoveItemFromArray(masternodes, penMasternodes)
for _, address := range penSigners { for _, address := range penMasternodes {
log.Debug("Penalty Info", "address", address, "number", number) log.Debug("Penalty status", "address", address, "block number", number)
} }
header.Penalties = common.ExtractAddressToBytes(penSigners) header.Penalties = common.ExtractAddressToBytes(penMasternodes)
} }
} }
// Prevent penaltied signer in 4 epocs ago jump into signer list. // Prevent penalized masternode(s) within 4 recent epochs
for i := 1; i <= common.LimitPenaltyEpoch; i++ { for i := 1; i <= common.LimitPenaltyEpoch; i++ {
if number > uint64(i)*c.config.Epoch { if number > uint64(i)*c.config.Epoch {
signers = RemovePenaltiesFromBlock(chain, signers, number-uint64(i)*c.config.Epoch) masternodes = RemovePenaltiesFromBlock(chain, masternodes, number-uint64(i)*c.config.Epoch)
} }
} }
for _, signer := range signers { for _, masternode := range masternodes {
header.Extra = append(header.Extra, signer[:]...) header.Extra = append(header.Extra, masternode[:]...)
}
if c.HookValidator != nil {
validators, err := c.HookValidator(header, masternodes)
if err != nil {
return err
}
header.Validators = validators
} }
} }
header.Extra = append(header.Extra, make([]byte, extraSeal)...) header.Extra = append(header.Extra, make([]byte, extraSeal)...)
@ -782,20 +806,11 @@ func (c *Posv) Prepare(chain consensus.ChainReader, header *types.Header) error
header.MixDigest = common.Hash{} header.MixDigest = common.Hash{}
// Ensure the timestamp has the correct delay // Ensure the timestamp has the correct delay
parent := chain.GetHeader(header.ParentHash, number-1)
if parent == nil {
return consensus.ErrUnknownAncestor
}
header.Time = new(big.Int).Add(parent.Time, new(big.Int).SetUint64(c.config.Period)) header.Time = new(big.Int).Add(parent.Time, new(big.Int).SetUint64(c.config.Period))
if header.Time.Int64() < time.Now().Unix() { if header.Time.Int64() < time.Now().Unix() {
header.Time = big.NewInt(time.Now().Unix()) header.Time = big.NewInt(time.Now().Unix())
} }
if c.HookValidator != nil {
c.HookValidator(header, signers)
if err != nil {
return err
}
}
return nil return nil
} }
@ -807,23 +822,14 @@ func (c *Posv) UpdateMasternodes(chain consensus.ChainReader, header *types.Head
if err != nil { if err != nil {
return err return err
} }
currentSigners := snap.GetSigners() newMasternodes := make(map[common.Address]struct{})
proposedSigners := make(map[common.Address]struct{})
// count all addresses in ms to be masternode
for _, m := range ms { for _, m := range ms {
proposedSigners[m.Address] = struct{}{} newMasternodes[m.Address] = struct{}{}
snap.Signers[m.Address] = struct{}{}
}
// deactivate current masternodes which aren't in ms
for _, s := range currentSigners {
if _, ok := proposedSigners[s]; !ok {
delete(snap.Signers, s)
}
} }
snap.Signers = newMasternodes
nm := []string{} nm := []string{}
newSigners := snap.GetSigners() for _, n := range ms {
for _, n := range newSigners { nm = append(nm, n.Address.String())
nm = append(nm, n.String())
} }
c.recents.Add(snap.Hash, snap) c.recents.Add(snap.Hash, snap)
log.Info("New set of masternodes has been updated to snapshot", "number", snap.Number, "hash", snap.Hash, "new masternodes", nm) log.Info("New set of masternodes has been updated to snapshot", "number", snap.Number, "hash", snap.Hash, "new masternodes", nm)
@ -834,7 +840,6 @@ func (c *Posv) UpdateMasternodes(chain consensus.ChainReader, header *types.Head
// rewards given, and returns the final block. // rewards given, and returns the final block.
func (c *Posv) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { func (c *Posv) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
// set block reward // set block reward
// FIXME: unit Ether could be too plump
number := header.Number.Uint64() number := header.Number.Uint64()
rCheckpoint := chain.Config().Posv.RewardCheckpoint rCheckpoint := chain.Config().Posv.RewardCheckpoint
@ -844,7 +849,7 @@ func (c *Posv) Finalize(chain consensus.ChainReader, header *types.Header, state
} }
} }
// No block rewards in PoA, so the state remains as is and uncles are dropped // the state remains as is and uncles are dropped
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
header.UncleHash = types.CalcUncleHash(nil) header.UncleHash = types.CalcUncleHash(nil)
@ -909,7 +914,7 @@ func (c *Posv) Seal(chain consensus.ChainReader, block *types.Block, stop <-chan
if limit := uint64(2); number < limit || seen > number-limit { if limit := uint64(2); number < limit || seen > number-limit {
// Only take into account the non-epoch blocks // Only take into account the non-epoch blocks
if number%c.config.Epoch != 0 { if number%c.config.Epoch != 0 {
log.Info("Debugging", "len(masternodes)", len(masternodes), "number", number, "limit", limit, "seen", seen, "recent", recent.String(), "snap.Recents", snap.Recents) log.Info("len(masternodes)", len(masternodes), "number", number, "limit", limit, "seen", seen, "recent", recent.String(), "snap.Recents", snap.Recents)
log.Info("Signed recently, must wait for others") log.Info("Signed recently, must wait for others")
<-stop <-stop
return nil, nil return nil, nil
@ -937,21 +942,15 @@ func (c *Posv) Seal(chain consensus.ChainReader, block *types.Block, stop <-chan
// that a new block should have based on the previous blocks in the chain and the // that a new block should have based on the previous blocks in the chain and the
// current signer. // current signer.
func (c *Posv) CalcDifficulty(chain consensus.ChainReader, time uint64, parent *types.Header) *big.Int { func (c *Posv) CalcDifficulty(chain consensus.ChainReader, time uint64, parent *types.Header) *big.Int {
snap, err := c.snapshot(chain, parent.Number.Uint64(), parent.Hash(), nil) return c.calcDifficulty(chain, parent, c.signer)
if err != nil {
return nil
}
return CalcDifficulty(snap, c.signer)
} }
// CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty func (c *Posv) calcDifficulty(chain consensus.ChainReader, parent *types.Header, signer common.Address) *big.Int {
// that a new block should have based on the previous blocks in the chain and the len, preIndex, curIndex, _, err := c.YourTurn(chain, parent, signer)
// current signer. if err != nil {
func CalcDifficulty(snap *Snapshot, signer common.Address) *big.Int { return big.NewInt(int64(len + curIndex - preIndex))
if snap.inturn(snap.Number+1, signer) {
return new(big.Int).Set(diffInTurn)
} }
return new(big.Int).Set(diffNoTurn) return big.NewInt(int64(len - Hop(len, preIndex, curIndex)))
} }
// APIs implements consensus.Engine, returning the user facing RPC API to allow // APIs implements consensus.Engine, returning the user facing RPC API to allow
@ -1006,18 +1005,18 @@ func (c *Posv) GetMasternodesFromCheckpointHeader(preCheckpointHeader *types.Hea
} }
// Extract validators from byte array. // Extract validators from byte array.
func RemovePenaltiesFromBlock(chain consensus.ChainReader, signers []common.Address, epochNumber uint64) []common.Address { func RemovePenaltiesFromBlock(chain consensus.ChainReader, masternodes []common.Address, epochNumber uint64) []common.Address {
if epochNumber <= 0 { if epochNumber <= 0 {
return signers return masternodes
} }
header := chain.GetHeaderByNumber(epochNumber) header := chain.GetHeaderByNumber(epochNumber)
block := chain.GetBlock(header.Hash(), epochNumber) block := chain.GetBlock(header.Hash(), epochNumber)
penalties := block.Penalties() penalties := block.Penalties()
if penalties != nil { if penalties != nil {
prevPenalties := common.ExtractAddressFromBytes(penalties) prevPenalties := common.ExtractAddressFromBytes(penalties)
signers = common.RemoveItemFromArray(signers, prevPenalties) masternodes = common.RemoveItemFromArray(masternodes, prevPenalties)
} }
return signers return masternodes
} }
// Get masternodes address from checkpoint Header. // Get masternodes address from checkpoint Header.
@ -1066,3 +1065,14 @@ func ExtractValidatorsFromBytes(byteValidators []byte) []int64 {
return validators return validators
} }
func Hop(len, pre, cur int) int {
switch {
case pre < cur:
return cur - (pre + 1)
case pre > cur:
return (len - pre) + (cur - 1)
default:
return len - 1
}
}

View file

@ -377,7 +377,7 @@ func (_BlockSigner *BlockSignerFilterer) WatchSign(opts *bind.WatchOpts, sink ch
const SafeMathABI = "[]" const SafeMathABI = "[]"
// SafeMathBin is the compiled bytecode used for deploying new contracts. // SafeMathBin is the compiled bytecode used for deploying new contracts.
const SafeMathBin = `0x604c602c600b82828239805160001a60731460008114601c57601e565bfe5b5030600052607381538281f30073000000000000000000000000000000000000000030146080604052600080fd00a165627a7a72305820a3f63b465e1cf25f306b1eb1efefc8dac3c38993a7340f69d8b470c3bf599ff30029` const SafeMathBin = `0x604c602c600b82828239805160001a60731460008114601c57601e565bfe5b5030600052607381538281f30073000000000000000000000000000000000000000030146060604052600080fd00a165627a7a72305820b9407d48ebc7efee5c9f08b3b3a957df2939281f5913225e8c1291f069b900490029`
// DeploySafeMath deploys a new Ethereum contract, binding an instance of SafeMath to it. // DeploySafeMath deploys a new Ethereum contract, binding an instance of SafeMath to it.
func DeploySafeMath(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *SafeMath, error) { func DeploySafeMath(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *SafeMath, error) {

File diff suppressed because one or more lines are too long

View file

@ -16,7 +16,7 @@ import (
const SafeMathABI = "[]" const SafeMathABI = "[]"
// SafeMathBin is the compiled bytecode used for deploying new contracts. // SafeMathBin is the compiled bytecode used for deploying new contracts.
const SafeMathBin = `0x604c602c600b82828239805160001a60731460008114601c57601e565bfe5b5030600052607381538281f30073000000000000000000000000000000000000000030146080604052600080fd00a165627a7a72305820ea3f7b50706b29b7324f2b7bc6c5eec464df491d2c006d76decd3a344ef24b5b0029` const SafeMathBin = `0x604c602c600b82828239805160001a60731460008114601c57601e565bfe5b5030600052607381538281f30073000000000000000000000000000000000000000030146060604052600080fd00a165627a7a72305820b9407d48ebc7efee5c9f08b3b3a957df2939281f5913225e8c1291f069b900490029`
// DeploySafeMath deploys a new Ethereum contract, binding an instance of SafeMath to it. // DeploySafeMath deploys a new Ethereum contract, binding an instance of SafeMath to it.
func DeploySafeMath(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *SafeMath, error) { func DeploySafeMath(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *SafeMath, error) {
@ -177,7 +177,7 @@ func (_SafeMath *SafeMathTransactorRaw) Transact(opts *bind.TransactOpts, method
const TomoRandomizeABI = "[{\"constant\":true,\"inputs\":[{\"name\":\"_validator\",\"type\":\"address\"}],\"name\":\"getSecret\",\"outputs\":[{\"name\":\"\",\"type\":\"bytes32[]\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_secret\",\"type\":\"bytes32[]\"}],\"name\":\"setSecret\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_validator\",\"type\":\"address\"}],\"name\":\"getOpening\",\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_opening\",\"type\":\"bytes32\"}],\"name\":\"setOpening\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"}]" const TomoRandomizeABI = "[{\"constant\":true,\"inputs\":[{\"name\":\"_validator\",\"type\":\"address\"}],\"name\":\"getSecret\",\"outputs\":[{\"name\":\"\",\"type\":\"bytes32[]\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_secret\",\"type\":\"bytes32[]\"}],\"name\":\"setSecret\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_validator\",\"type\":\"address\"}],\"name\":\"getOpening\",\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_opening\",\"type\":\"bytes32\"}],\"name\":\"setOpening\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"}]"
// TomoRandomizeBin is the compiled bytecode used for deploying new contracts. // TomoRandomizeBin is the compiled bytecode used for deploying new contracts.
const TomoRandomizeBin = `0x608060405234801561001057600080fd5b50610335806100206000396000f3006080604052600436106100615763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663284180fc811461006657806334d38600146100e4578063d442d6cc1461013b578063e11f5ba21461017b575b600080fd5b34801561007257600080fd5b5061009473ffffffffffffffffffffffffffffffffffffffff60043516610193565b60408051602080825283518183015283519192839290830191858101910280838360005b838110156100d05781810151838201526020016100b8565b505050509050019250505060405180910390f35b3480156100f057600080fd5b50604080516020600480358082013583810280860185019096528085526101399536959394602494938501929182918501908490808284375094975061020b9650505050505050565b005b34801561014757600080fd5b5061016973ffffffffffffffffffffffffffffffffffffffff60043516610250565b60408051918252519081900360200190f35b34801561018757600080fd5b50610139600435610278565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260208181526040918290208054835181840281018401909452808452606093928301828280156101ff57602002820191906000526020600020905b815481526001909101906020018083116101ea575b50505050509050919050565b610384430661032081101561021f57600080fd5b610352811061022d57600080fd5b33600090815260208181526040909120835161024b9285019061029f565b505050565b73ffffffffffffffffffffffffffffffffffffffff1660009081526001602052604090205490565b610384430661035281101561028c57600080fd5b5033600090815260016020526040902055565b8280548282559060005260206000209081019282156102dc579160200282015b828111156102dc57825182556020909201916001909101906102bf565b506102e89291506102ec565b5090565b61030691905b808211156102e857600081556001016102f2565b905600a165627a7a72305820524cb2eeb0cc4214180425f822fd315cd15fd8352a830bbd9846b46133730a100029` const TomoRandomizeBin = `0x6060604052341561000f57600080fd5b6103368061001e6000396000f3006060604052600436106100615763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663284180fc811461006657806334d38600146100d8578063d442d6cc14610129578063e11f5ba21461015a575b600080fd5b341561007157600080fd5b610085600160a060020a0360043516610170565b60405160208082528190810183818151815260200191508051906020019060200280838360005b838110156100c45780820151838201526020016100ac565b505050509050019250505060405180910390f35b34156100e357600080fd5b61012760046024813581810190830135806020818102016040519081016040528093929190818152602001838360200280828437509496506101f395505050505050565b005b341561013457600080fd5b610148600160a060020a0360043516610243565b60405190815260200160405180910390f35b341561016557600080fd5b61012760043561025e565b61017861028e565b60008083600160a060020a0316600160a060020a031681526020019081526020016000208054806020026020016040519081016040528092919081815260200182805480156101e757602002820191906000526020600020905b815481526001909101906020018083116101d2575b50505050509050919050565b610384430661032081101561020757600080fd5b610352811061021557600080fd5b600160a060020a033316600090815260208190526040902082805161023e9291602001906102a0565b505050565b600160a060020a031660009081526001602052604090205490565b610384430661035281101561027257600080fd5b50600160a060020a033316600090815260016020526040902055565b60206040519081016040526000815290565b8280548282559060005260206000209081019282156102dd579160200282015b828111156102dd57825182556020909201916001909101906102c0565b506102e99291506102ed565b5090565b61030791905b808211156102e957600081556001016102f3565b905600a165627a7a7230582034991c8dc4001fc254f3ba2811c05d2e7d29bee3908946ca56d1545b2c852de20029`
// DeployTomoRandomize deploys a new Ethereum contract, binding an instance of TomoRandomize to it. // DeployTomoRandomize deploys a new Ethereum contract, binding an instance of TomoRandomize to it.
func DeployTomoRandomize(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *TomoRandomize, error) { func DeployTomoRandomize(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *TomoRandomize, error) {

View file

@ -98,7 +98,7 @@ contract TomoValidator {
maxValidatorNumber = _maxValidatorNumber; maxValidatorNumber = _maxValidatorNumber;
candidateWithdrawDelay = _candidateWithdrawDelay; candidateWithdrawDelay = _candidateWithdrawDelay;
voterWithdrawDelay = _voterWithdrawDelay; voterWithdrawDelay = _voterWithdrawDelay;
candidateCount = candidateCount.add(_candidates.length); candidateCount = _candidates.length;
for (uint256 i = 0; i < _candidates.length; i++) { for (uint256 i = 0; i < _candidates.length; i++) {
candidates.push(_candidates[i]); candidates.push(_candidates[i]);
@ -108,7 +108,7 @@ contract TomoValidator {
cap: _caps[i] cap: _caps[i]
}); });
voters[_candidates[i]].push(_firstOwner); voters[_candidates[i]].push(_firstOwner);
validatorsState[candidates[i]].voters[_firstOwner] = minCandidateCap; validatorsState[_candidates[i]].voters[_firstOwner] = minCandidateCap;
} }
} }
@ -120,7 +120,7 @@ contract TomoValidator {
isCandidate: true, isCandidate: true,
cap: cap cap: cap
}); });
validatorsState[_candidate].voters[msg.sender] = msg.value; validatorsState[_candidate].voters[msg.sender] = validatorsState[_candidate].voters[msg.sender].add(msg.value);
candidateCount = candidateCount.add(1); candidateCount = candidateCount.add(1);
voters[_candidate].push(msg.sender); voters[_candidate].push(msg.sender);
emit Propose(msg.sender, _candidate, msg.value); emit Propose(msg.sender, _candidate, msg.value);

File diff suppressed because one or more lines are too long

View file

@ -75,6 +75,13 @@ type CacheConfig struct {
TrieNodeLimit int // Memory limit (MB) at which to flush the current in-memory trie to disk TrieNodeLimit int // Memory limit (MB) at which to flush the current in-memory trie to disk
TrieTimeLimit time.Duration // Time limit after which to flush the current in-memory trie to disk TrieTimeLimit time.Duration // Time limit after which to flush the current in-memory trie to disk
} }
type ResultProcessBlock struct {
logs []*types.Log
receipts []*types.Receipt
state *state.StateDB
proctime time.Duration
usedGas uint64
}
// BlockChain represents the canonical chain given a database with a genesis // BlockChain represents the canonical chain given a database with a genesis
// block. The Blockchain manages chain imports, reverts, chain reorganisations. // block. The Blockchain manages chain imports, reverts, chain reorganisations.
@ -120,7 +127,9 @@ type BlockChain struct {
bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format
blockCache *lru.Cache // Cache for the most recent entire blocks blockCache *lru.Cache // Cache for the most recent entire blocks
futureBlocks *lru.Cache // future blocks are blocks added for later processing futureBlocks *lru.Cache // future blocks are blocks added for later processing
resultProcess *lru.Cache // Cache for processed blocks
calculatingBlock *lru.Cache // Cache for processing blocks
downloadingBlock *lru.Cache // Cache for downloading blocks (avoid duplication from fetcher)
quit chan struct{} // blockchain quit channel quit chan struct{} // blockchain quit channel
running int32 // running must be called atomically running int32 // running must be called atomically
// procInterrupt must be atomically called // procInterrupt must be atomically called
@ -152,7 +161,9 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
blockCache, _ := lru.New(blockCacheLimit) blockCache, _ := lru.New(blockCacheLimit)
futureBlocks, _ := lru.New(maxFutureBlocks) futureBlocks, _ := lru.New(maxFutureBlocks)
badBlocks, _ := lru.New(badBlockLimit) badBlocks, _ := lru.New(badBlockLimit)
resultProcess, _ := lru.New(blockCacheLimit)
preparingBlock, _ := lru.New(blockCacheLimit)
downloadingBlock, _ := lru.New(blockCacheLimit)
bc := &BlockChain{ bc := &BlockChain{
chainConfig: chainConfig, chainConfig: chainConfig,
cacheConfig: cacheConfig, cacheConfig: cacheConfig,
@ -164,6 +175,9 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
bodyRLPCache: bodyRLPCache, bodyRLPCache: bodyRLPCache,
blockCache: blockCache, blockCache: blockCache,
futureBlocks: futureBlocks, futureBlocks: futureBlocks,
resultProcess: resultProcess,
calculatingBlock: preparingBlock,
downloadingBlock: downloadingBlock,
engine: engine, engine: engine,
vmConfig: vmConfig, vmConfig: vmConfig,
badBlocks: badBlocks, badBlocks: badBlocks,
@ -892,7 +906,7 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
defer bc.mu.Unlock() defer bc.mu.Unlock()
currentBlock := bc.CurrentBlock() currentBlock := bc.CurrentBlock()
//localTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64()) localTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64())
externTd := new(big.Int).Add(block.Difficulty(), ptd) externTd := new(big.Int).Add(block.Difficulty(), ptd)
// Irrelevant of the canonical status, write the block itself to the database // Irrelevant of the canonical status, write the block itself to the database
@ -966,8 +980,12 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
// If the total difficulty is higher than our known, add it to the canonical chain // 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. // Second clause in the if statement reduces the vulnerability to selfish mining.
// Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf // Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf
reorg := externTd.Cmp(localTd) > 0
reorg := block.NumberU64() > currentBlock.NumberU64() currentBlock = bc.CurrentBlock()
if !reorg && externTd.Cmp(localTd) == 0 {
// Split same-difficulty blocks by number
reorg = block.NumberU64() > currentBlock.NumberU64()
}
if reorg { if reorg {
// Reorganise the chain if the parent is not the head block // Reorganise the chain if the parent is not the head block
if block.ParentHash() != currentBlock.Hash() { if block.ParentHash() != currentBlock.Hash() {
@ -1049,6 +1067,7 @@ 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] = true
bc.downloadingBlock.Add(block.Hash(), true)
} }
abort, results := bc.engine.VerifyHeaders(bc, headers, seals) abort, results := bc.engine.VerifyHeaders(bc, headers, seals)
defer close(abort) defer close(abort)
@ -1168,7 +1187,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty
} }
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 from downloader", "number", block.Number(), "hash", block.Hash(), "uncles", len(block.Uncles()),
"txs", len(block.Transactions()), "gas", block.GasUsed(), "elapsed", common.PrettyDuration(time.Since(bstart))) "txs", len(block.Transactions()), "gas", block.GasUsed(), "elapsed", common.PrettyDuration(time.Since(bstart)))
coalescedLogs = append(coalescedLogs, logs...) coalescedLogs = append(coalescedLogs, logs...)
@ -1180,7 +1199,7 @@ 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 from downloader", "number", block.Number(), "hash", block.Hash(), "diff", block.Difficulty(), "elapsed",
common.PrettyDuration(time.Since(bstart)), "txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles())) common.PrettyDuration(time.Since(bstart)), "txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles()))
blockInsertTimer.UpdateSince(bstart) blockInsertTimer.UpdateSince(bstart)
@ -1189,7 +1208,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty
stats.processed++ stats.processed++
stats.usedGas += usedGas stats.usedGas += usedGas
stats.report(chain, i, bc.stateCache.TrieDB().Size()) stats.report(chain, i, bc.stateCache.TrieDB().Size())
if bc.chainConfig.Posv != nil { if status == CanonStatTy && bc.chainConfig.Posv != nil {
// epoch block // epoch block
if (chain[i].NumberU64() % bc.chainConfig.Posv.Epoch) == 0 { if (chain[i].NumberU64() % bc.chainConfig.Posv.Epoch) == 0 {
CheckpointCh <- 1 CheckpointCh <- 1
@ -1206,11 +1225,212 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty
} }
// 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() {
log.Debug("New ChainHeadEvent ", "number", lastCanon.NumberU64(), "hash", lastCanon.Hash())
events = append(events, ChainHeadEvent{lastCanon}) events = append(events, ChainHeadEvent{lastCanon})
} }
return 0, events, coalescedLogs, nil return 0, events, coalescedLogs, nil
} }
func (bc *BlockChain) InsertBlock(block *types.Block) error {
events, logs, err := bc.insertBlock(block)
bc.PostChainEvents(events, logs)
return err
}
func (bc *BlockChain) PrepareBlock(block *types.Block) (err error) {
defer log.Debug("Done prepare block ", "number", block.NumberU64(), "hash", block.Hash(), "validator", block.Header().Validator, "err", err)
if _, check := bc.resultProcess.Get(block.Hash()); check {
log.Debug("Stop prepare a block because the result cached", "number", block.NumberU64(), "hash", block.Hash(), "validator", block.Header().Validator)
return nil
}
if _, check := bc.calculatingBlock.Get(block.Hash()); check {
log.Debug("Stop prepare a block because inserting", "number", block.NumberU64(), "hash", block.Hash(), "validator", block.Header().Validator)
return nil
}
err = bc.engine.VerifyHeader(bc, block.Header(), false)
if err != nil {
return err
}
result, err := bc.getResultBlock(block, false)
if err == nil {
bc.resultProcess.Add(block.Hash(), result)
return nil
} else if err == ErrKnownBlock {
return nil
} else if err == ErrStopPreparingBlock {
log.Debug("Stop prepare a block because calculating", "number", block.NumberU64(), "hash", block.Hash(), "validator", block.Header().Validator)
return nil
}
return err
}
func (bc *BlockChain) getResultBlock(block *types.Block, verifiedM2 bool) (*ResultProcessBlock, error) {
var calculatedBlock *CalculatedBlock
if verifiedM2 {
if result, check := bc.resultProcess.Get(block.HashNoValidator()); check {
log.Debug("Get result block from cache ", "number", block.NumberU64(), "hash", block.Hash(), "hash no validator", block.HashNoValidator())
return result.(*ResultProcessBlock), nil
}
log.Debug("Not found cache prepare block ", "number", block.NumberU64(), "hash", block.Hash(), "validator", block.HashNoValidator())
if calculatedBlock, _ := bc.calculatingBlock.Get(block.HashNoValidator()); calculatedBlock != nil {
calculatedBlock.(*CalculatedBlock).stop = true
}
}
calculatedBlock = &CalculatedBlock{block, false}
bc.calculatingBlock.Add(block.HashNoValidator(), calculatedBlock)
// Start the parallel header verifier
// If the chain is terminating, stop processing blocks
if atomic.LoadInt32(&bc.procInterrupt) == 1 {
log.Debug("Premature abort during blocks processing")
return nil, ErrBlacklistedHash
}
// If the header is a banned one, straight out abort
if BadHashes[block.Hash()] {
bc.reportBlock(block, nil, ErrBlacklistedHash)
return nil, ErrBlacklistedHash
}
// Wait for the block's verification to complete
bstart := time.Now()
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() {
return nil, ErrKnownBlock
}
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 {
return nil, err
}
// 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]
}
log.Debug("Number block need calculated again", "number", block.NumberU64(), "hash", block.Hash().Hex(), "winners", len(winner))
// Import all the pruned blocks to make the state available
_, _, _, err := bc.insertChain(winner)
if err != nil {
return nil, err
}
case err != nil:
bc.reportBlock(block, nil, err)
return nil, err
}
// Create a new statedb using the parent block and report an
// error if it fails.
var parent = bc.GetBlock(block.ParentHash(), block.NumberU64()-1)
state, err := state.New(parent.Root(), bc.stateCache)
if err != nil {
return nil, err
}
// Process block using the parent state as reference point.
receipts, logs, usedGas, err := bc.processor.ProcessBlockNoValidator(calculatedBlock, state, bc.vmConfig)
process := time.Since(bstart)
if err != nil {
if err != ErrStopPreparingBlock {
bc.reportBlock(block, receipts, err)
}
return nil, err
}
// Validate the state using the default validator
err = bc.Validator().ValidateState(block, parent, state, receipts, usedGas)
if err != nil {
bc.reportBlock(block, receipts, err)
return nil, err
}
proctime := time.Since(bstart)
log.Debug("Calculate new block", "number", block.Number(), "hash", block.Hash(), "uncles", len(block.Uncles()),
"txs", len(block.Transactions()), "gas", block.GasUsed(), "elapsed", common.PrettyDuration(time.Since(bstart)), "process", process)
return &ResultProcessBlock{receipts: receipts, logs: logs, state: state, proctime: proctime, usedGas: usedGas}, nil
}
// 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
// with deferred statements.
func (bc *BlockChain) insertBlock(block *types.Block) ([]interface{}, []*types.Log, error) {
var (
stats = insertStats{startTime: mclock.Now()}
events = make([]interface{}, 0, 1)
coalescedLogs []*types.Log
)
if _, check := bc.downloadingBlock.Get(block.Hash()); check {
log.Debug("Stop fetcher a block because downloading", "number", block.NumberU64(), "hash", block.Hash())
return events, coalescedLogs, nil
}
result, err := bc.getResultBlock(block, true)
if err != nil {
return events, coalescedLogs, err
}
defer bc.resultProcess.Remove(block.HashNoValidator())
bc.wg.Add(1)
defer bc.wg.Done()
// Write the block to the chain and get the status.
bc.chainmu.Lock()
defer bc.chainmu.Unlock()
if bc.HasBlockAndState(block.Hash(), block.NumberU64()) {
return events, coalescedLogs, nil
}
status, err := bc.WriteBlockWithState(block, result.receipts, result.state)
if err != nil {
return events, coalescedLogs, err
}
switch status {
case CanonStatTy:
log.Debug("Inserted new block from fetcher", "number", block.Number(), "hash", block.Hash(), "uncles", len(block.Uncles()),
"txs", len(block.Transactions()), "gas", block.GasUsed(), "elapsed", common.PrettyDuration(time.Since(block.ReceivedAt)))
coalescedLogs = append(coalescedLogs, result.logs...)
events = append(events, ChainEvent{block, block.Hash(), result.logs})
// Only count canonical blocks for GC processing time
bc.gcproc += result.proctime
case SideStatTy:
log.Debug("Inserted forked block from fetcher", "number", block.Number(), "hash", block.Hash(), "diff", block.Difficulty(), "elapsed",
common.PrettyDuration(time.Since(block.ReceivedAt)), "txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles()))
blockInsertTimer.Update(result.proctime)
events = append(events, ChainSideEvent{block})
}
stats.processed++
stats.usedGas += result.usedGas
stats.report(types.Blocks{block}, 0, bc.stateCache.TrieDB().Size())
if status == CanonStatTy && bc.chainConfig.Posv != nil {
// epoch block
if (block.NumberU64() % bc.chainConfig.Posv.Epoch) == 0 {
CheckpointCh <- 1
}
// prepare set of masternodes for the next epoch
if (block.NumberU64() % bc.chainConfig.Posv.Epoch) == (bc.chainConfig.Posv.Epoch - bc.chainConfig.Posv.Gap) {
err := bc.UpdateM1()
if err != nil {
log.Error("Error when update masternodes set. Stopping node", "err", err)
os.Exit(1)
}
}
}
// Append a single chain head event if we've progressed the chain
if status == CanonStatTy && bc.CurrentBlock().Hash() == block.Hash() {
events = append(events, ChainHeadEvent{block})
log.Debug("New ChainHeadEvent from fetcher ", "number", block.NumberU64(), "hash", block.Hash())
}
return events, coalescedLogs, nil
}
// insertStats tracks and reports on block insertion. // insertStats tracks and reports on block insertion.
type insertStats struct { type insertStats struct {
queued, processed, ignored int queued, processed, ignored int

View file

@ -36,4 +36,6 @@ var (
ErrNotPoSV = errors.New("Posv not found in config") ErrNotPoSV = errors.New("Posv not found in config")
ErrNotFoundM1 = errors.New("list M1 not found ") ErrNotFoundM1 = errors.New("list M1 not found ")
ErrStopPreparingBlock = errors.New("stop calculating a block not verified by M2")
) )

View file

@ -40,6 +40,10 @@ type StateProcessor struct {
bc *BlockChain // Canonical block chain bc *BlockChain // Canonical block chain
engine consensus.Engine // Consensus engine used for block rewards engine consensus.Engine // Consensus engine used for block rewards
} }
type CalculatedBlock struct {
block *types.Block
stop bool
}
// NewStateProcessor initialises a new StateProcessor. // NewStateProcessor initialises a new StateProcessor.
func NewStateProcessor(config *params.ChainConfig, bc *BlockChain, engine consensus.Engine) *StateProcessor { func NewStateProcessor(config *params.ChainConfig, bc *BlockChain, engine consensus.Engine) *StateProcessor {
@ -69,9 +73,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 { if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 {
misc.ApplyDAOHardFork(statedb) misc.ApplyDAOHardFork(statedb)
} }
InitSignerInTransactions(p.config, header, block.Transactions()) InitSignerInTransactions(p.config, header, block.Transactions())
// Iterate over and process the individual transactions
for i, tx := range block.Transactions() { for i, tx := range block.Transactions() {
statedb.Prepare(tx.Hash(), block.Hash(), i) statedb.Prepare(tx.Hash(), block.Hash(), i)
receipt, _, err := ApplyTransaction(p.config, p.bc, nil, gp, statedb, header, tx, usedGas, cfg) receipt, _, err := ApplyTransaction(p.config, p.bc, nil, gp, statedb, header, tx, usedGas, cfg)
@ -83,7 +85,44 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
} }
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards) // Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.Uncles(), receipts) p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.Uncles(), receipts)
return receipts, allLogs, *usedGas, nil
}
func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) {
block := cBlock.block
var (
receipts types.Receipts
usedGas = new(uint64)
header = block.Header()
allLogs []*types.Log
gp = new(GasPool).AddGas(block.GasLimit())
)
// Mutate the the block and state according to any hard-fork specs
if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 {
misc.ApplyDAOHardFork(statedb)
}
if cBlock.stop {
return nil, nil, 0, ErrStopPreparingBlock
}
InitSignerInTransactions(p.config, header, block.Transactions())
if cBlock.stop {
return nil, nil, 0, ErrStopPreparingBlock
}
// Iterate over and process the individual transactions
receipts = make([]*types.Receipt, block.Transactions().Len())
for i, tx := range block.Transactions() {
statedb.Prepare(tx.Hash(), block.Hash(), i)
receipt, _, err := ApplyTransaction(p.config, p.bc, nil, gp, statedb, header, tx, usedGas, cfg)
if err != nil {
return nil, nil, 0, err
}
if cBlock.stop {
return nil, nil, 0, ErrStopPreparingBlock
}
receipts[i] = receipt
}
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.Uncles(), receipts)
return receipts, allLogs, *usedGas, nil return receipts, allLogs, *usedGas, nil
} }

View file

@ -193,7 +193,6 @@ type TxPool struct {
chain blockChain chain blockChain
gasPrice *big.Int gasPrice *big.Int
txFeed event.Feed txFeed event.Feed
specialTxFeed event.Feed
scope event.SubscriptionScope scope event.SubscriptionScope
chainHeadCh chan ChainHeadEvent chainHeadCh chan ChainHeadEvent
chainHeadSub event.Subscription chainHeadSub event.Subscription
@ -458,12 +457,6 @@ func (pool *TxPool) SubscribeTxPreEvent(ch chan<- TxPreEvent) event.Subscription
return pool.scope.Track(pool.txFeed.Subscribe(ch)) return pool.scope.Track(pool.txFeed.Subscribe(ch))
} }
// SubscribeSpecialTxPreEvent registers a subscription of TxPreEvent and
// starts sending event to the given channel.
func (pool *TxPool) SubscribeSpecialTxPreEvent(ch chan<- TxPreEvent) event.Subscription {
return pool.scope.Track(pool.specialTxFeed.Subscribe(ch))
}
// GasPrice returns the current gas price enforced by the transaction pool. // GasPrice returns the current gas price enforced by the transaction pool.
func (pool *TxPool) GasPrice() *big.Int { func (pool *TxPool) GasPrice() *big.Int {
pool.mu.RLock() pool.mu.RLock()
@ -654,11 +647,12 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (bool, error) {
return false, err return false, err
} }
from, _ := types.Sender(pool.signer, tx) // already validated from, _ := types.Sender(pool.signer, tx) // already validated
if tx.IsSpecialTransaction() && pool.IsMasterNode != nil && pool.IsMasterNode(from) { if tx.IsSpecialTransaction() && pool.IsMasterNode != nil && pool.IsMasterNode(from) && pool.pendingState.GetNonce(from) == tx.Nonce() {
return pool.promoteSpecialTx(from, tx) return pool.promoteSpecialTx(from, tx)
} }
// If the transaction pool is full, discard underpriced transactions // If the transaction pool is full, discard underpriced transactions
if uint64(len(pool.all)) >= pool.config.GlobalSlots+pool.config.GlobalQueue { if uint64(len(pool.all)) >= pool.config.GlobalSlots+pool.config.GlobalQueue {
log.Debug("Add transaction to pool full", "hash", hash, "nonce", tx.Nonce())
// If the new transaction is underpriced, don't accept it // If the new transaction is underpriced, don't accept it
if pool.priced.Underpriced(tx, pool.locals) { if pool.priced.Underpriced(tx, pool.locals) {
log.Trace("Discarding underpriced transaction", "hash", hash, "price", tx.GasPrice()) log.Trace("Discarding underpriced transaction", "hash", hash, "price", tx.GasPrice())
@ -819,21 +813,7 @@ func (pool *TxPool) promoteSpecialTx(addr common.Address, tx *types.Transaction)
// Set the potentially new pending nonce and notify any subsystems of the new tx // Set the potentially new pending nonce and notify any subsystems of the new tx
pool.beats[addr] = time.Now() pool.beats[addr] = time.Now()
pool.pendingState.SetNonce(addr, tx.Nonce()+1) pool.pendingState.SetNonce(addr, tx.Nonce()+1)
broadcastTxs := types.Transactions{} go pool.txFeed.Send(TxPreEvent{tx})
for i := tx.Nonce() - 1; i > 0; i-- {
before := list.txs.Get(i)
if before == nil || before.IsSpecialTransaction() {
break
}
broadcastTxs = append(broadcastTxs, before)
}
broadcastTxs = append(broadcastTxs, tx)
go func() {
for _, btx := range broadcastTxs {
pool.specialTxFeed.Send(TxPreEvent{btx})
log.Trace("Pooled new special transaction", "hash", tx.Hash(), "from", addr, "to", tx.To(), "nonce", tx.Nonce())
}
}()
return true, nil return true, nil
} }
@ -887,9 +867,6 @@ func (pool *TxPool) addTx(tx *types.Transaction, local bool) error {
// addTxs attempts to queue a batch of transactions if they are valid. // addTxs attempts to queue a batch of transactions if they are valid.
func (pool *TxPool) addTxs(txs []*types.Transaction, local bool) []error { func (pool *TxPool) addTxs(txs []*types.Transaction, local bool) []error {
for _, tx := range txs {
types.CacheSigner(pool.signer, tx)
}
pool.mu.Lock() pool.mu.Lock()
defer pool.mu.Unlock() defer pool.mu.Unlock()

View file

@ -43,4 +43,5 @@ type Validator interface {
// failed. // failed.
type Processor interface { type Processor interface {
Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error)
ProcessBlockNoValidator(block *CalculatedBlock, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error)
} }

View file

@ -125,6 +125,30 @@ func (h *Header) HashNoNonce() common.Hash {
}) })
} }
// HashNoNonce returns the hash which is used as input for the proof-of-work search.
func (h *Header) HashNoValidator() common.Hash {
return rlpHash([]interface{}{
h.ParentHash,
h.UncleHash,
h.Coinbase,
h.Root,
h.TxHash,
h.ReceiptHash,
h.Bloom,
h.Difficulty,
h.Number,
h.GasLimit,
h.GasUsed,
h.Time,
h.Extra,
h.MixDigest,
h.Nonce,
h.Validators,
[]byte{},
h.Penalties,
})
}
// Size returns the approximate memory used by all internal contents. It is used // Size returns the approximate memory used by all internal contents. It is used
// to approximate and limit the memory consumption of various caches. // to approximate and limit the memory consumption of various caches.
func (h *Header) Size() common.StorageSize { func (h *Header) Size() common.StorageSize {
@ -337,6 +361,9 @@ func (b *Block) Body() *Body { return &Body{b.transactions, b.uncles} }
func (b *Block) HashNoNonce() common.Hash { func (b *Block) HashNoNonce() common.Hash {
return b.header.HashNoNonce() return b.header.HashNoNonce()
} }
func (b *Block) HashNoValidator() common.Hash {
return b.header.HashNoValidator()
}
// Size returns the true RLP encoded storage size of the block, either by encoding // Size returns the true RLP encoded storage size of the block, either by encoding
// and returning it, or returning a previsouly cached value. // and returning it, or returning a previsouly cached value.

View file

@ -144,7 +144,7 @@ func TestTransactionPriceNonceSort(t *testing.T) {
} }
} }
// Sort the transactions and cross check the nonce ordering // Sort the transactions and cross check the nonce ordering
txset, _ := NewTransactionsByPriceAndNonce(signer, groups,nil) txset, _ := NewTransactionsByPriceAndNonce(signer, groups, nil)
txs := Transactions{} txs := Transactions{}
for tx := txset.Peek(); tx != nil; tx = txset.Peek() { for tx := txset.Peek(); tx != nil; tx = txset.Peek() {

View file

@ -28,7 +28,7 @@ accountsCount=$(
# file to env # file to env
for env in IDENTITY PASSWORD PRIVATE_KEY BOOTNODES WS_SECRET NETSTATS_HOST \ for env in IDENTITY PASSWORD PRIVATE_KEY BOOTNODES WS_SECRET NETSTATS_HOST \
NETSTATS_PORT EXTIP SYNC_MODE NETWORK_ID; do NETSTATS_PORT EXTIP SYNC_MODE NETWORK_ID ANNOUNCE_TXS; do
file=$(eval echo "\$${env}_FILE") file=$(eval echo "\$${env}_FILE")
if [[ -f $file ]] && [[ ! -z $file ]]; then if [[ -f $file ]] && [[ ! -z $file ]]; then
echo "Replacing $env by $file" echo "Replacing $env by $file"
@ -139,6 +139,11 @@ else
echo "WS_SECRET not set, will not report to netstats server." echo "WS_SECRET not set, will not report to netstats server."
fi fi
# annonce txs
if [[ ! -z $ANNOUNCE_TXS ]]; then
params="$params --announce-txs"
fi
# dump # dump
echo "dump: $IDENTITY $account $BOOTNODES" echo "dump: $IDENTITY $account $BOOTNODES"
@ -151,7 +156,7 @@ exec tomo $params \
--identity $IDENTITY \ --identity $IDENTITY \
--password ./password \ --password ./password \
--port 30303 \ --port 30303 \
--maxpeers 50 \ --maxpeers 25 \
--txpool.globalqueue 5000 \ --txpool.globalqueue 5000 \
--txpool.globalslots 5000 \ --txpool.globalslots 5000 \
--rpc \ --rpc \

View file

@ -173,7 +173,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
if eth.protocolManager, err = NewProtocolManager(eth.chainConfig, config.SyncMode, config.NetworkId, eth.eventMux, eth.txPool, eth.engine, eth.blockchain, chainDb); err != nil { if eth.protocolManager, err = NewProtocolManager(eth.chainConfig, config.SyncMode, config.NetworkId, eth.eventMux, eth.txPool, eth.engine, eth.blockchain, chainDb); err != nil {
return nil, err return nil, err
} }
eth.miner = miner.New(eth, eth.chainConfig, eth.EventMux(), eth.engine) eth.miner = miner.New(eth, eth.chainConfig, eth.EventMux(), eth.engine, ctx.GetConfig().AnnounceTxs)
eth.miner.SetExtra(makeExtraData(config.ExtraData)) eth.miner.SetExtra(makeExtraData(config.ExtraData))
eth.ApiBackend = &EthApiBackend{eth, nil} eth.ApiBackend = &EthApiBackend{eth, nil}
@ -203,47 +203,43 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
return nil return nil
} }
appendM2HeaderHook := func(block *types.Block) (*types.Block, error) { appendM2HeaderHook := func(block *types.Block) (*types.Block, bool, error) {
eb, err := eth.Etherbase() eb, err := eth.Etherbase()
if err != nil { if err != nil {
log.Error("Cannot get etherbase for append m2 header", "err", err) log.Error("Cannot get etherbase for append m2 header", "err", err)
return block, fmt.Errorf("etherbase missing: %v", err) return block, false, fmt.Errorf("etherbase missing: %v", err)
} }
m1, err := c.RecoverSigner(block.Header()) m1, err := c.RecoverSigner(block.Header())
if err != nil { if err != nil {
return block, fmt.Errorf("can't get block creator: %v", err) return block, false, fmt.Errorf("can't get block creator: %v", err)
} }
m2, err := c.GetValidator(m1, eth.blockchain, block.Header()) m2, err := c.GetValidator(m1, eth.blockchain, block.Header())
if err != nil { if err != nil {
return block, fmt.Errorf("can't get block validator: %v", err) return block, false, fmt.Errorf("can't get block validator: %v", err)
} }
if m2 == eb { if m2 == eb {
wallet, _ := eth.accountManager.Find(accounts.Account{Address: eb}) wallet, _ := eth.accountManager.Find(accounts.Account{Address: eb})
header := block.Header() header := block.Header()
sighash, _ := wallet.SignHash(accounts.Account{Address: eb}, posv.SigHash(header).Bytes()) sighash, _ := wallet.SignHash(accounts.Account{Address: eb}, posv.SigHash(header).Bytes())
header.Validator = sighash header.Validator = sighash
block = types.NewBlockWithHeader(header).WithBody(block.Transactions(), block.Uncles()) return types.NewBlockWithHeader(header).WithBody(block.Transactions(), block.Uncles()), true, nil
} }
return block, false, nil
return block, nil
} }
eth.protocolManager.fetcher.SetSignHook(signHook) eth.protocolManager.fetcher.SetSignHook(signHook)
eth.protocolManager.fetcher.SetAppendM2HeaderHook(appendM2HeaderHook) eth.protocolManager.fetcher.SetAppendM2HeaderHook(appendM2HeaderHook)
// Hook prepares validators M2 for the current epoch // Hook prepares validators M2 for the current epoch at checkpoint block
c.HookValidator = func(header *types.Header, signers []common.Address) error { c.HookValidator = func(header *types.Header, signers []common.Address) ([]byte, error) {
start := time.Now() start := time.Now()
number := header.Number.Int64()
if number > 0 && number%common.EpocBlockRandomize == 0 {
validators, err := GetValidators(eth.blockchain, signers) validators, err := GetValidators(eth.blockchain, signers)
if err != nil { if err != nil {
return err return []byte{}, err
} }
header.Validators = validators header.Validators = validators
}
log.Debug("Time Calculated HookValidator ", "block", header.Number.Uint64(), "time", common.PrettyDuration(time.Since(start))) log.Debug("Time Calculated HookValidator ", "block", header.Number.Uint64(), "time", common.PrettyDuration(time.Since(start)))
return nil return validators, nil
} }
// Hook scans for bad masternodes and decide to penalty them // Hook scans for bad masternodes and decide to penalty them
@ -301,8 +297,8 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
if foudationWalletAddr == (common.Address{}) { if foudationWalletAddr == (common.Address{}) {
log.Error("Foundation Wallet Address is empty", "error", foudationWalletAddr) log.Error("Foundation Wallet Address is empty", "error", foudationWalletAddr)
} }
start := time.Now()
if number > 0 && number-rCheckpoint > 0 && foudationWalletAddr != (common.Address{}) { if number > 0 && number-rCheckpoint > 0 && foudationWalletAddr != (common.Address{}) {
start := time.Now()
// Get signers in blockSigner smartcontract. // Get signers in blockSigner smartcontract.
addr := common.HexToAddress(common.BlockSigners) addr := common.HexToAddress(common.BlockSigners)
// Get reward inflation. // Get reward inflation.
@ -334,8 +330,8 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
} }
} }
} }
}
log.Debug("Time Calculated HookReward ", "block", header.Number.Uint64(), "time", common.PrettyDuration(time.Since(start))) log.Debug("Time Calculated HookReward ", "block", header.Number.Uint64(), "time", common.PrettyDuration(time.Since(start)))
}
return nil return nil
} }
@ -359,7 +355,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
currentHeader := eth.blockchain.CurrentHeader() currentHeader := eth.blockchain.CurrentHeader()
snap, err := c.GetSnapshot(eth.blockchain, currentHeader) snap, err := c.GetSnapshot(eth.blockchain, currentHeader)
if err != nil { if err != nil {
log.Error("Can't get snap shot with current header ", "number", currentHeader.Number, "hash", currentHeader.Hash().Hex()) log.Error("Can't get snapshot with current header ", "number", currentHeader.Number, "hash", currentHeader.Hash().Hex())
return false return false
} }
if _, ok := snap.Signers[address]; ok { if _, ok := snap.Signers[address]; ok {

View file

@ -47,10 +47,10 @@ var (
MaxForkAncestry = 3 * params.EpochDuration // Maximum chain reorganisation MaxForkAncestry = 3 * params.EpochDuration // Maximum chain reorganisation
rttMinEstimate = 2 * time.Second // Minimum round-trip time to target for download requests rttMinEstimate = 2 * time.Second // Minimum round-trip time to target for download requests
rttMaxEstimate = 20 * time.Second // Maximum rount-trip time to target for download requests rttMaxEstimate = 5 * time.Second // Maximum rount-trip time to target for download requests
rttMinConfidence = 0.1 // Worse confidence factor in our estimated RTT value rttMinConfidence = 0.1 // Worse confidence factor in our estimated RTT value
ttlScaling = 3 // Constant scaling factor for RTT -> TTL conversion ttlScaling = 2 // Constant scaling factor for RTT -> TTL conversion
ttlLimit = time.Minute // Maximum TTL allowance to prevent reaching crazy timeouts ttlLimit = 5 * time.Second // Maximum TTL allowance to prevent reaching crazy timeouts
qosTuningPeers = 5 // Number of peers to tune based on (best peers) qosTuningPeers = 5 // Number of peers to tune based on (best peers)
qosConfidenceCap = 10 // Number of peers above which not to modify RTT confidence qosConfidenceCap = 10 // Number of peers above which not to modify RTT confidence

View file

@ -146,9 +146,7 @@ func (q *queue) Reset() {
// Close marks the end of the sync, unblocking WaitResults. // Close marks the end of the sync, unblocking WaitResults.
// It may be called even if the queue is already closed. // It may be called even if the queue is already closed.
func (q *queue) Close() { func (q *queue) Close() {
q.lock.Lock()
q.closed = true q.closed = true
q.lock.Unlock()
q.active.Broadcast() q.active.Broadcast()
} }

View file

@ -62,8 +62,10 @@ type blockBroadcasterFn func(block *types.Block, propagate bool)
// chainHeightFn is a callback type to retrieve the current chain height. // chainHeightFn is a callback type to retrieve the current chain height.
type chainHeightFn func() uint64 type chainHeightFn func() uint64
// chainInsertFn is a callback type to insert a batch of blocks into the local chain. // blockInsertFn is a callback type to insert a batch of blocks into the local chain.
type chainInsertFn func(blocks types.Blocks) (int, error) type blockInsertFn func(block *types.Block) error
type blockPrepareFn func(block *types.Block) error
// peerDropFn is a callback type for dropping a peer detected as malicious. // peerDropFn is a callback type for dropping a peer detected as malicious.
type peerDropFn func(id string) type peerDropFn func(id string)
@ -135,7 +137,8 @@ type Fetcher struct {
verifyHeader headerVerifierFn // Checks if a block's headers have a valid proof of work verifyHeader headerVerifierFn // Checks if a block's headers have a valid proof of work
broadcastBlock blockBroadcasterFn // Broadcasts a block to connected peers broadcastBlock blockBroadcasterFn // Broadcasts a block to connected peers
chainHeight chainHeightFn // Retrieves the current chain's height chainHeight chainHeightFn // Retrieves the current chain's height
insertChain chainInsertFn // Injects a batch of blocks into the chain insertBlock blockInsertFn // Injects a batch of blocks into the chain
prepareBlock blockPrepareFn
dropPeer peerDropFn // Drops a peer for misbehaving dropPeer peerDropFn // Drops a peer for misbehaving
// Testing hooks // Testing hooks
@ -144,11 +147,11 @@ type Fetcher struct {
fetchingHook func([]common.Hash) // Method to call upon starting a block (eth/61) or header (eth/62) fetch fetchingHook func([]common.Hash) // Method to call upon starting a block (eth/61) or header (eth/62) fetch
completingHook func([]common.Hash) // Method to call upon starting a block body fetch (eth/62) completingHook func([]common.Hash) // Method to call upon starting a block body fetch (eth/62)
signHook func(*types.Block) error signHook func(*types.Block) error
appendM2HeaderHook func(*types.Block) (*types.Block, error) appendM2HeaderHook func(*types.Block) (*types.Block, bool, error)
} }
// New creates a block fetcher to retrieve blocks based on hash announcements. // New creates a block fetcher to retrieve blocks based on hash announcements.
func New(getBlock blockRetrievalFn, verifyHeader headerVerifierFn, broadcastBlock blockBroadcasterFn, chainHeight chainHeightFn, insertChain chainInsertFn, dropPeer peerDropFn) *Fetcher { func New(getBlock blockRetrievalFn, verifyHeader headerVerifierFn, broadcastBlock blockBroadcasterFn, chainHeight chainHeightFn, insertBlock blockInsertFn, prepareBlock blockPrepareFn, dropPeer peerDropFn) *Fetcher {
knownBlocks, _ := lru.NewARC(blockLimit) knownBlocks, _ := lru.NewARC(blockLimit)
return &Fetcher{ return &Fetcher{
notify: make(chan *announce), notify: make(chan *announce),
@ -171,7 +174,8 @@ func New(getBlock blockRetrievalFn, verifyHeader headerVerifierFn, broadcastBloc
verifyHeader: verifyHeader, verifyHeader: verifyHeader,
broadcastBlock: broadcastBlock, broadcastBlock: broadcastBlock,
chainHeight: chainHeight, chainHeight: chainHeight,
insertChain: insertChain, insertBlock: insertBlock,
prepareBlock: prepareBlock,
dropPeer: dropPeer, dropPeer: dropPeer,
} }
} }
@ -605,7 +609,7 @@ func (f *Fetcher) rescheduleComplete(complete *time.Timer) {
func (f *Fetcher) enqueue(peer string, block *types.Block) { func (f *Fetcher) enqueue(peer string, block *types.Block) {
hash := block.Hash() hash := block.Hash()
if f.knowns.Contains(hash) { if f.knowns.Contains(hash) {
log.Debug("Discarded propagated block, known block", "peer", peer, "number", block.Number(), "hash", hash, "limit", blockLimit) log.Trace("Discarded propagated block, known block", "peer", peer, "number", block.Number(), "hash", hash, "limit", blockLimit)
return return
} }
// Ensure the peer isn't DOSing us // Ensure the peer isn't DOSing us
@ -657,40 +661,56 @@ func (f *Fetcher) insert(peer string, block *types.Block) {
log.Debug("Unknown parent of propagated block", "peer", peer, "number", block.Number(), "hash", hash, "parent", block.ParentHash()) log.Debug("Unknown parent of propagated block", "peer", peer, "number", block.Number(), "hash", hash, "parent", block.ParentHash())
return return
} }
fastBroadCast := true
again: again:
err := f.verifyHeader(block.Header())
// Quickly validate the header and propagate the block if it passes // Quickly validate the header and propagate the block if it passes
switch err := f.verifyHeader(block.Header()); err { switch err {
case nil: case nil:
// All ok, quickly propagate to our peers // All ok, quickly propagate to our peers
propBroadcastOutTimer.UpdateSince(block.ReceivedAt) propBroadcastOutTimer.UpdateSince(block.ReceivedAt)
if fastBroadCast {
go f.broadcastBlock(block, true) go f.broadcastBlock(block, true)
}
case consensus.ErrFutureBlock: case consensus.ErrFutureBlock:
delay := time.Unix(block.Time().Int64(), 0).Sub(time.Now()) // nolint: gosimple delay := time.Unix(block.Time().Int64(), 0).Sub(time.Now()) // nolint: gosimple
time.Sleep(delay)
log.Info("Receive future block", "number", block.NumberU64(), "hash", block.Hash().Hex(), "delay", delay) log.Info("Receive future block", "number", block.NumberU64(), "hash", block.Hash().Hex(), "delay", delay)
time.Sleep(delay)
goto again goto again
case consensus.ErrNoValidatorSignature: case consensus.ErrNoValidatorSignature:
newBlock := block newBlock := block
var errM2 error
isM2 := false
if f.appendM2HeaderHook != nil { if f.appendM2HeaderHook != nil {
if newBlock, err = f.appendM2HeaderHook(block); err != nil { if newBlock, isM2, errM2 = f.appendM2HeaderHook(block); errM2 != nil {
log.Error("Append m2 to block header fail", "err", err) log.Error("Append m2 to block header fail", "err", errM2)
return return
} }
} }
if newBlock.Hash() == block.Hash() { if !isM2 {
go f.broadcastBlock(block, true) go f.broadcastBlock(block, true)
if err := f.prepareBlock(block); err != nil {
log.Debug("Propagated block prepare failed", "peer", peer, "number", block.Number(), "hash", hash, "err", err)
return
}
return
}
log.Debug("Append M2 to header block", "numer", block.NumberU64(), "hahs", block.Hash())
if err := f.prepareBlock(block); err != nil {
log.Debug("Propagated block prepare failed", "peer", peer, "number", block.Number(), "hash", hash, "err", err)
return return
} }
block = newBlock block = newBlock
fastBroadCast = false
goto again
default: default:
// Something went very wrong, drop the peer // Something went very wrong, drop the peer
log.Debug("Propagated block verification failed", "peer", peer, "number", block.Number(), "hash", hash, "err", err) log.Debug("Propagated block verification failed", "peer", peer, "number", block.Number(), "hash", hash, "err", err)
f.dropPeer(peer) f.dropPeer(peer)
return return
} }
// Run the actual import and log any issues // Run the actual import and log any issues
if _, err := f.insertChain(types.Blocks{block}); err != nil { if err := f.insertBlock(block); err != nil {
log.Debug("Propagated block import failed", "peer", peer, "number", block.Number(), "hash", hash, "err", err) log.Debug("Propagated block import failed", "peer", peer, "number", block.Number(), "hash", hash, "err", err)
return return
} }
@ -703,8 +723,9 @@ func (f *Fetcher) insert(peer string, block *types.Block) {
} }
// If import succeeded, broadcast the block // If import succeeded, broadcast the block
propAnnounceOutTimer.UpdateSince(block.ReceivedAt) propAnnounceOutTimer.UpdateSince(block.ReceivedAt)
if !fastBroadCast {
go f.broadcastBlock(block, true) go f.broadcastBlock(block, true)
go f.broadcastBlock(block, false) }
}() }()
} }
@ -768,6 +789,6 @@ func (f *Fetcher) SetSignHook(signHook func(*types.Block) error) {
} }
// Bind append m2 to block header hook when imported into chain. // Bind append m2 to block header hook when imported into chain.
func (f *Fetcher) SetAppendM2HeaderHook(appendM2HeaderHook func(*types.Block) (*types.Block, error)) { func (f *Fetcher) SetAppendM2HeaderHook(appendM2HeaderHook func(*types.Block) (*types.Block, bool, error)) {
f.appendM2HeaderHook = appendM2HeaderHook f.appendM2HeaderHook = appendM2HeaderHook
} }

View file

@ -92,7 +92,7 @@ func newTester() *fetcherTester {
blocks: map[common.Hash]*types.Block{genesis.Hash(): genesis}, blocks: map[common.Hash]*types.Block{genesis.Hash(): genesis},
drops: make(map[string]bool), drops: make(map[string]bool),
} }
tester.fetcher = New(tester.getBlock, tester.verifyHeader, tester.broadcastBlock, tester.chainHeight, tester.insertChain, tester.dropPeer) tester.fetcher = New(tester.getBlock, tester.verifyHeader, tester.broadcastBlock, tester.chainHeight, tester.insertBlock, tester.prepareBlock, tester.dropPeer)
tester.fetcher.Start() tester.fetcher.Start()
return tester return tester
@ -123,7 +123,7 @@ func (f *fetcherTester) chainHeight() uint64 {
return f.blocks[f.hashes[len(f.hashes)-1]].NumberU64() return f.blocks[f.hashes[len(f.hashes)-1]].NumberU64()
} }
// insertChain injects a new blocks into the simulated chain. // insertBlock injects a new blocks into the simulated chain.
func (f *fetcherTester) insertChain(blocks types.Blocks) (int, error) { func (f *fetcherTester) insertChain(blocks types.Blocks) (int, error) {
f.lock.Lock() f.lock.Lock()
defer f.lock.Unlock() defer f.lock.Unlock()
@ -144,6 +144,31 @@ func (f *fetcherTester) insertChain(blocks types.Blocks) (int, error) {
return 0, nil return 0, nil
} }
// insertBlock injects a new blocks into the simulated chain.
func (f *fetcherTester) insertBlock(block *types.Block) error {
f.lock.Lock()
defer f.lock.Unlock()
// Make sure the parent in known
if _, ok := f.blocks[block.ParentHash()]; !ok {
return errors.New("unknown parent")
}
// Discard any new blocks if the same height already exists
if block.NumberU64() <= f.blocks[f.hashes[len(f.hashes)-1]].NumberU64() {
return nil
}
// Otherwise build our current chain
f.hashes = append(f.hashes, block.Hash())
f.blocks[block.Hash()] = block
return nil
}
// insertBlock injects a new blocks into the simulated chain.
func (f *fetcherTester) prepareBlock(block *types.Block) error {
return nil
}
// dropPeer is an emulator for the peer removal, simply accumulating the various // dropPeer is an emulator for the peer removal, simply accumulating the various
// peers dropped by the fetcher. // peers dropped by the fetcher.
func (f *fetcherTester) dropPeer(peer string) { func (f *fetcherTester) dropPeer(peer string) {
@ -512,9 +537,9 @@ func testImportDeduplication(t *testing.T, protocol int) {
bodyFetcher := tester.makeBodyFetcher("valid", blocks, 0) bodyFetcher := tester.makeBodyFetcher("valid", blocks, 0)
counter := uint32(0) counter := uint32(0)
tester.fetcher.insertChain = func(blocks types.Blocks) (int, error) { tester.fetcher.insertBlock = func(block *types.Block) error {
atomic.AddUint32(&counter, uint32(len(blocks))) atomic.AddUint32(&counter, uint32(1))
return tester.insertChain(blocks) return tester.insertBlock(block)
} }
// Instrument the fetching and imported events // Instrument the fetching and imported events
fetching := make(chan []common.Hash) fetching := make(chan []common.Hash)

View file

@ -20,6 +20,7 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"github.com/hashicorp/golang-lru"
"math/big" "math/big"
"sync" "sync"
"sync/atomic" "sync/atomic"
@ -82,8 +83,6 @@ type ProtocolManager struct {
eventMux *event.TypeMux eventMux *event.TypeMux
txCh chan core.TxPreEvent txCh chan core.TxPreEvent
txSub event.Subscription txSub event.Subscription
specialTxCh chan core.TxPreEvent
specialTxSub event.Subscription
minedBlockSub *event.TypeMuxSubscription minedBlockSub *event.TypeMuxSubscription
// channels for fetcher, syncer, txsyncLoop // channels for fetcher, syncer, txsyncLoop
@ -95,11 +94,13 @@ type ProtocolManager struct {
// wait group is used for graceful shutdowns during downloading // wait group is used for graceful shutdowns during downloading
// and processing // and processing
wg sync.WaitGroup wg sync.WaitGroup
knownTxs *lru.Cache
} }
// NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable // NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable
// with the ethereum network. // with the ethereum network.
func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, networkId uint64, mux *event.TypeMux, txpool txPool, engine consensus.Engine, blockchain *core.BlockChain, chaindb ethdb.Database) (*ProtocolManager, error) { func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, networkId uint64, mux *event.TypeMux, txpool txPool, engine consensus.Engine, blockchain *core.BlockChain, chaindb ethdb.Database) (*ProtocolManager, error) {
knownTxs, _ := lru.New(maxKnownTxs)
// Create the protocol manager with the base fields // Create the protocol manager with the base fields
manager := &ProtocolManager{ manager := &ProtocolManager{
networkId: networkId, networkId: networkId,
@ -112,6 +113,7 @@ func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, ne
noMorePeers: make(chan struct{}), noMorePeers: make(chan struct{}),
txsyncCh: make(chan *txsync), txsyncCh: make(chan *txsync),
quitSync: make(chan struct{}), quitSync: make(chan struct{}),
knownTxs: knownTxs,
} }
// Figure out whether to allow fast sync or not // Figure out whether to allow fast sync or not
if mode == downloader.FastSync && blockchain.CurrentBlock().NumberU64() > 0 { if mode == downloader.FastSync && blockchain.CurrentBlock().NumberU64() > 0 {
@ -168,16 +170,26 @@ func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, ne
heighter := func() uint64 { heighter := func() uint64 {
return blockchain.CurrentBlock().NumberU64() return blockchain.CurrentBlock().NumberU64()
} }
inserter := func(blocks types.Blocks) (int, error) { inserter := func(block *types.Block) error {
// If fast sync is running, deny importing weird blocks // If fast sync is running, deny importing weird blocks
if atomic.LoadUint32(&manager.fastSync) == 1 { if atomic.LoadUint32(&manager.fastSync) == 1 {
log.Warn("Discarded bad propagated block", "number", blocks[0].Number(), "hash", blocks[0].Hash()) log.Warn("Discarded bad propagated block", "number", block.Number(), "hash", block.Hash())
return 0, nil return nil
} }
atomic.StoreUint32(&manager.acceptTxs, 1) // Mark initial sync done on any fetcher import atomic.StoreUint32(&manager.acceptTxs, 1) // Mark initial sync done on any fetcher import
return manager.blockchain.InsertChain(blocks) return manager.blockchain.InsertBlock(block)
} }
manager.fetcher = fetcher.New(blockchain.GetBlockByHash, validator, manager.BroadcastBlock, heighter, inserter, manager.removePeer)
prepare := func(block *types.Block) error {
// If fast sync is running, deny importing weird blocks
if atomic.LoadUint32(&manager.fastSync) == 1 {
log.Warn("Discarded bad propagated block", "number", block.Number(), "hash", block.Hash())
return nil
}
atomic.StoreUint32(&manager.acceptTxs, 1) // Mark initial sync done on any fetcher import
return manager.blockchain.PrepareBlock(block)
}
manager.fetcher = fetcher.New(blockchain.GetBlockByHash, validator, manager.BroadcastBlock, heighter, inserter, prepare, manager.removePeer)
return manager, nil return manager, nil
} }
@ -209,11 +221,6 @@ func (pm *ProtocolManager) Start(maxPeers int) {
pm.txSub = pm.txpool.SubscribeTxPreEvent(pm.txCh) pm.txSub = pm.txpool.SubscribeTxPreEvent(pm.txCh)
go pm.txBroadcastLoop() go pm.txBroadcastLoop()
// broadcast special transactions
pm.specialTxCh = make(chan core.TxPreEvent, txChanSize)
pm.specialTxSub = pm.txpool.SubscribeSpecialTxPreEvent(pm.specialTxCh)
go pm.specialTxBroadcastLoop()
// broadcast mined blocks // broadcast mined blocks
pm.minedBlockSub = pm.eventMux.Subscribe(core.NewMinedBlockEvent{}) pm.minedBlockSub = pm.eventMux.Subscribe(core.NewMinedBlockEvent{})
go pm.minedBroadcastLoop() go pm.minedBroadcastLoop()
@ -227,7 +234,6 @@ func (pm *ProtocolManager) Stop() {
log.Info("Stopping Ethereum protocol") log.Info("Stopping Ethereum protocol")
pm.txSub.Unsubscribe() // quits txBroadcastLoop pm.txSub.Unsubscribe() // quits txBroadcastLoop
pm.specialTxSub.Unsubscribe() // quits specialTxBroadcastLoop
pm.minedBlockSub.Unsubscribe() // quits blockBroadcastLoop pm.minedBlockSub.Unsubscribe() // quits blockBroadcastLoop
// Quit the sync loop. // Quit the sync loop.
@ -651,17 +657,14 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
trueTD = new(big.Int).Sub(request.TD, request.Block.Difficulty()) trueTD = new(big.Int).Sub(request.TD, request.Block.Difficulty())
) )
// Update the peers total difficulty if better than the previous // Update the peers total difficulty if better than the previous
_, td := p.Head() if _, td := p.Head(); trueTD.Cmp(td) > 0 {
currentBlock := pm.blockchain.CurrentBlock()
currentTd := pm.blockchain.GetTd(currentBlock.Hash(), currentBlock.NumberU64())
log.Debug("NewBlockMsg", "p", p, "number", request.Block.NumberU64(), "trueTD", trueTD, "td", td, "currentTd", currentTd)
if trueTD.Cmp(td) > 0 {
p.SetHead(trueHead, trueTD) p.SetHead(trueHead, trueTD)
// Schedule a sync if above ours. Note, this will not fire a sync for a gap of // Schedule a sync if above ours. Note, this will not fire a sync for a gap of
// a singe block (as the true TD is below the propagated block), however this // a singe block (as the true TD is below the propagated block), however this
// scenario should easily be covered by the fetcher. // scenario should easily be covered by the fetcher.
if trueTD.Cmp(currentTd) > 0 { currentBlock := pm.blockchain.CurrentBlock()
if trueTD.Cmp(pm.blockchain.GetTd(currentBlock.Hash(), currentBlock.NumberU64())) > 0 {
go pm.synchronise(p) go pm.synchronise(p)
} }
} }
@ -676,12 +679,19 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
if err := msg.Decode(&txs); err != nil { if err := msg.Decode(&txs); err != nil {
return errResp(ErrDecode, "msg %v: %v", msg, err) return errResp(ErrDecode, "msg %v: %v", msg, err)
} }
var unkownTxs []*types.Transaction
for i, tx := range txs { for i, tx := range txs {
// Validate and mark the remote transaction // Validate and mark the remote transaction
if tx == nil { if tx == nil {
return errResp(ErrDecode, "transaction %d is nil", i) return errResp(ErrDecode, "transaction %d is nil", i)
} }
p.MarkTransaction(tx.Hash()) p.MarkTransaction(tx.Hash())
exist, _ := pm.knownTxs.ContainsOrAdd(tx.Hash(), true)
if !exist {
unkownTxs = append(unkownTxs, tx)
} else {
log.Trace("Discard known tx", "hash", tx.Hash(), "nonce", tx.Nonce(), "to", tx.To())
}
} }
pm.txpool.AddRemotes(txs) pm.txpool.AddRemotes(txs)
@ -735,16 +745,6 @@ func (pm *ProtocolManager) BroadcastTx(hash common.Hash, tx *types.Transaction)
log.Trace("Broadcast transaction", "hash", hash, "recipients", len(peers)) log.Trace("Broadcast transaction", "hash", hash, "recipients", len(peers))
} }
func (pm *ProtocolManager) BroadcastSpecialTx(hash common.Hash, tx *types.Transaction) {
// Broadcast transaction to a batch of peers not knowing about it
peers := pm.peers.PeersWithoutTx(hash)
//FIXME include this again: peers = peers[:int(math.Sqrt(float64(len(peers))))]
for _, peer := range peers {
peer.SendSpecialTransactions(tx)
}
log.Trace("Broadcast special transaction", "hash", hash, "recipients", len(peers))
}
// Mined broadcast loop // Mined broadcast loop
func (self *ProtocolManager) minedBroadcastLoop() { func (self *ProtocolManager) minedBroadcastLoop() {
// automatically stops if unsubscribe // automatically stops if unsubscribe
@ -752,7 +752,7 @@ func (self *ProtocolManager) minedBroadcastLoop() {
switch ev := obj.Data.(type) { switch ev := obj.Data.(type) {
case core.NewMinedBlockEvent: case core.NewMinedBlockEvent:
self.BroadcastBlock(ev.Block, true) // First propagate block to peers self.BroadcastBlock(ev.Block, true) // First propagate block to peers
self.BroadcastBlock(ev.Block, false) // Only then announce to the rest //self.BroadcastBlock(ev.Block, false) // Only then announce to the rest
} }
} }
} }
@ -770,19 +770,6 @@ func (self *ProtocolManager) txBroadcastLoop() {
} }
} }
func (self *ProtocolManager) specialTxBroadcastLoop() {
for {
select {
case event := <-self.specialTxCh:
self.BroadcastSpecialTx(event.Tx.Hash(), event.Tx)
// Err() channel will be closed when unsubscribing.
case <-self.specialTxSub.Err():
return
}
}
}
// NodeInfo represents a short summary of the Ethereum sub-protocol metadata // NodeInfo represents a short summary of the Ethereum sub-protocol metadata
// known about the host peer. // known about the host peer.
type NodeInfo struct { type NodeInfo struct {

View file

@ -128,10 +128,6 @@ func (p *testTxPool) SubscribeTxPreEvent(ch chan<- core.TxPreEvent) event.Subscr
return p.txFeed.Subscribe(ch) return p.txFeed.Subscribe(ch)
} }
func (p *testTxPool) SubscribeSpecialTxPreEvent(ch chan<- core.TxPreEvent) event.Subscription {
return p.txFeed.Subscribe(ch)
}
// newTestTransaction create a new dummy transaction. // newTestTransaction create a new dummy transaction.
func newTestTransaction(from *ecdsa.PrivateKey, nonce uint64, datasize int) *types.Transaction { func newTestTransaction(from *ecdsa.PrivateKey, nonce uint64, datasize int) *types.Transaction {
tx := types.NewTransaction(nonce, common.Address{}, big.NewInt(0), 100000, big.NewInt(0), make([]byte, datasize)) tx := types.NewTransaction(nonce, common.Address{}, big.NewInt(0), 100000, big.NewInt(0), make([]byte, datasize))

View file

@ -25,7 +25,6 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"gopkg.in/fatih/set.v0" "gopkg.in/fatih/set.v0"
@ -141,15 +140,6 @@ func (p *peer) SendTransactions(txs types.Transactions) error {
return p2p.Send(p.rw, TxMsg, txs) return p2p.Send(p.rw, TxMsg, txs)
} }
func (p *peer) SendSpecialTransactions(tx *types.Transaction) error {
p.knownTxs.Add(tx.Hash())
if p.pairRw != nil {
return p2p.Send(p.pairRw, TxMsg, types.Transactions{tx})
} else {
return p2p.Send(p.rw, TxMsg, types.Transactions{tx})
}
}
// SendNewBlockHashes announces the availability of a number of blocks through // SendNewBlockHashes announces the availability of a number of blocks through
// a hash notification. // a hash notification.
func (p *peer) SendNewBlockHashes(hashes []common.Hash, numbers []uint64) error { func (p *peer) SendNewBlockHashes(hashes []common.Hash, numbers []uint64) error {
@ -168,81 +158,123 @@ func (p *peer) SendNewBlockHashes(hashes []common.Hash, numbers []uint64) error
func (p *peer) SendNewBlock(block *types.Block, td *big.Int) error { func (p *peer) SendNewBlock(block *types.Block, td *big.Int) error {
p.knownBlocks.Add(block.Hash()) p.knownBlocks.Add(block.Hash())
if p.pairRw != nil { if p.pairRw != nil {
log.Trace("p2p send new block to the pairRw connection", "p", p, "number", block.NumberU64())
return p2p.Send(p.pairRw, NewBlockMsg, []interface{}{block, td}) return p2p.Send(p.pairRw, NewBlockMsg, []interface{}{block, td})
} else { } else {
return p2p.Send(p.rw, NewBlockMsg, []interface{}{block, td}) return p2p.Send(p.rw, NewBlockMsg, []interface{}{block, td})
} }
} }
// SendBlockHeaders sends a batch of block headers to the remote peer. // SendBlockHeaders sends a batch of block headers to the remote peer.
func (p *peer) SendBlockHeaders(headers []*types.Header) error { func (p *peer) SendBlockHeaders(headers []*types.Header) error {
if p.pairRw != nil {
return p2p.Send(p.pairRw, BlockHeadersMsg, headers)
} else {
return p2p.Send(p.rw, BlockHeadersMsg, headers) return p2p.Send(p.rw, BlockHeadersMsg, headers)
}
} }
// SendBlockBodies sends a batch of block contents to the remote peer. // SendBlockBodies sends a batch of block contents to the remote peer.
func (p *peer) SendBlockBodies(bodies []*blockBody) error { func (p *peer) SendBlockBodies(bodies []*blockBody) error {
if p.pairRw != nil {
return p2p.Send(p.pairRw, BlockBodiesMsg, blockBodiesData(bodies))
} else {
return p2p.Send(p.rw, BlockBodiesMsg, blockBodiesData(bodies)) return p2p.Send(p.rw, BlockBodiesMsg, blockBodiesData(bodies))
}
} }
// SendBlockBodiesRLP sends a batch of block contents to the remote peer from // SendBlockBodiesRLP sends a batch of block contents to the remote peer from
// an already RLP encoded format. // an already RLP encoded format.
func (p *peer) SendBlockBodiesRLP(bodies []rlp.RawValue) error { func (p *peer) SendBlockBodiesRLP(bodies []rlp.RawValue) error {
if p.pairRw != nil {
return p2p.Send(p.pairRw, BlockBodiesMsg, bodies)
} else {
return p2p.Send(p.rw, BlockBodiesMsg, bodies) return p2p.Send(p.rw, BlockBodiesMsg, bodies)
}
} }
// SendNodeDataRLP sends a batch of arbitrary internal data, corresponding to the // SendNodeDataRLP sends a batch of arbitrary internal data, corresponding to the
// hashes requested. // hashes requested.
func (p *peer) SendNodeData(data [][]byte) error { func (p *peer) SendNodeData(data [][]byte) error {
if p.pairRw != nil {
return p2p.Send(p.pairRw, NodeDataMsg, data)
} else {
return p2p.Send(p.rw, NodeDataMsg, data) return p2p.Send(p.rw, NodeDataMsg, data)
}
} }
// SendReceiptsRLP sends a batch of transaction receipts, corresponding to the // SendReceiptsRLP sends a batch of transaction receipts, corresponding to the
// ones requested from an already RLP encoded format. // ones requested from an already RLP encoded format.
func (p *peer) SendReceiptsRLP(receipts []rlp.RawValue) error { func (p *peer) SendReceiptsRLP(receipts []rlp.RawValue) error {
if p.pairRw != nil {
return p2p.Send(p.pairRw, ReceiptsMsg, receipts)
} else {
return p2p.Send(p.rw, ReceiptsMsg, receipts) return p2p.Send(p.rw, ReceiptsMsg, receipts)
}
} }
// RequestOneHeader is a wrapper around the header query functions to fetch a // RequestOneHeader is a wrapper around the header query functions to fetch a
// single header. It is used solely by the fetcher. // single header. It is used solely by the fetcher.
func (p *peer) RequestOneHeader(hash common.Hash) error { func (p *peer) RequestOneHeader(hash common.Hash) error {
p.Log().Debug("Fetching single header", "hash", hash) p.Log().Debug("Fetching single header", "hash", hash)
if p.pairRw != nil {
return p2p.Send(p.pairRw, GetBlockHeadersMsg, &getBlockHeadersData{Origin: hashOrNumber{Hash: hash}, Amount: uint64(1), Skip: uint64(0), Reverse: false})
} else {
return p2p.Send(p.rw, GetBlockHeadersMsg, &getBlockHeadersData{Origin: hashOrNumber{Hash: hash}, Amount: uint64(1), Skip: uint64(0), Reverse: false}) return p2p.Send(p.rw, GetBlockHeadersMsg, &getBlockHeadersData{Origin: hashOrNumber{Hash: hash}, Amount: uint64(1), Skip: uint64(0), Reverse: false})
}
} }
// RequestHeadersByHash fetches a batch of blocks' headers corresponding to the // RequestHeadersByHash fetches a batch of blocks' headers corresponding to the
// specified header query, based on the hash of an origin block. // specified header query, based on the hash of an origin block.
func (p *peer) RequestHeadersByHash(origin common.Hash, amount int, skip int, reverse bool) error { func (p *peer) RequestHeadersByHash(origin common.Hash, amount int, skip int, reverse bool) error {
p.Log().Debug("Fetching batch of headers", "count", amount, "fromhash", origin, "skip", skip, "reverse", reverse) p.Log().Debug("Fetching batch of headers", "count", amount, "fromhash", origin, "skip", skip, "reverse", reverse)
if p.pairRw != nil {
return p2p.Send(p.pairRw, GetBlockHeadersMsg, &getBlockHeadersData{Origin: hashOrNumber{Hash: origin}, Amount: uint64(amount), Skip: uint64(skip), Reverse: reverse})
} else {
return p2p.Send(p.rw, GetBlockHeadersMsg, &getBlockHeadersData{Origin: hashOrNumber{Hash: origin}, Amount: uint64(amount), Skip: uint64(skip), Reverse: reverse}) return p2p.Send(p.rw, GetBlockHeadersMsg, &getBlockHeadersData{Origin: hashOrNumber{Hash: origin}, Amount: uint64(amount), Skip: uint64(skip), Reverse: reverse})
}
} }
// RequestHeadersByNumber fetches a batch of blocks' headers corresponding to the // RequestHeadersByNumber fetches a batch of blocks' headers corresponding to the
// specified header query, based on the number of an origin block. // specified header query, based on the number of an origin block.
func (p *peer) RequestHeadersByNumber(origin uint64, amount int, skip int, reverse bool) error { func (p *peer) RequestHeadersByNumber(origin uint64, amount int, skip int, reverse bool) error {
p.Log().Debug("Fetching batch of headers", "count", amount, "fromnum", origin, "skip", skip, "reverse", reverse) p.Log().Debug("Fetching batch of headers", "count", amount, "fromnum", origin, "skip", skip, "reverse", reverse)
if p.pairRw != nil {
return p2p.Send(p.pairRw, GetBlockHeadersMsg, &getBlockHeadersData{Origin: hashOrNumber{Number: origin}, Amount: uint64(amount), Skip: uint64(skip), Reverse: reverse})
} else {
return p2p.Send(p.rw, GetBlockHeadersMsg, &getBlockHeadersData{Origin: hashOrNumber{Number: origin}, Amount: uint64(amount), Skip: uint64(skip), Reverse: reverse}) return p2p.Send(p.rw, GetBlockHeadersMsg, &getBlockHeadersData{Origin: hashOrNumber{Number: origin}, Amount: uint64(amount), Skip: uint64(skip), Reverse: reverse})
}
} }
// RequestBodies fetches a batch of blocks' bodies corresponding to the hashes // RequestBodies fetches a batch of blocks' bodies corresponding to the hashes
// specified. // specified.
func (p *peer) RequestBodies(hashes []common.Hash) error { func (p *peer) RequestBodies(hashes []common.Hash) error {
p.Log().Debug("Fetching batch of block bodies", "count", len(hashes)) p.Log().Debug("Fetching batch of block bodies", "count", len(hashes))
if p.pairRw != nil {
return p2p.Send(p.pairRw, GetBlockBodiesMsg, hashes)
} else {
return p2p.Send(p.rw, GetBlockBodiesMsg, hashes) return p2p.Send(p.rw, GetBlockBodiesMsg, hashes)
}
} }
// RequestNodeData fetches a batch of arbitrary data from a node's known state // RequestNodeData fetches a batch of arbitrary data from a node's known state
// data, corresponding to the specified hashes. // data, corresponding to the specified hashes.
func (p *peer) RequestNodeData(hashes []common.Hash) error { func (p *peer) RequestNodeData(hashes []common.Hash) error {
p.Log().Debug("Fetching batch of state data", "count", len(hashes)) p.Log().Debug("Fetching batch of state data", "count", len(hashes))
if p.pairRw != nil {
return p2p.Send(p.pairRw, GetNodeDataMsg, hashes)
} else {
return p2p.Send(p.rw, GetNodeDataMsg, hashes) return p2p.Send(p.rw, GetNodeDataMsg, hashes)
}
} }
// RequestReceipts fetches a batch of transaction receipts from a remote node. // RequestReceipts fetches a batch of transaction receipts from a remote node.
func (p *peer) RequestReceipts(hashes []common.Hash) error { func (p *peer) RequestReceipts(hashes []common.Hash) error {
p.Log().Debug("Fetching batch of receipts", "count", len(hashes)) p.Log().Debug("Fetching batch of receipts", "count", len(hashes))
if p.pairRw != nil {
return p2p.Send(p.pairRw, GetReceiptsMsg, hashes)
} else {
return p2p.Send(p.rw, GetReceiptsMsg, hashes) return p2p.Send(p.rw, GetReceiptsMsg, hashes)
}
} }
// Handshake executes the eth protocol handshake, negotiating version number, // Handshake executes the eth protocol handshake, negotiating version number,

View file

@ -106,7 +106,6 @@ type txPool interface {
// SubscribeTxPreEvent should return an event subscription of // SubscribeTxPreEvent should return an event subscription of
// TxPreEvent and send events to the given channel. // TxPreEvent and send events to the given channel.
SubscribeTxPreEvent(chan<- core.TxPreEvent) event.Subscription SubscribeTxPreEvent(chan<- core.TxPreEvent) event.Subscription
SubscribeSpecialTxPreEvent(chan<- core.TxPreEvent) event.Subscription
} }
// statusData is the network packet for the status message. // statusData is the network packet for the status message.

View file

@ -170,7 +170,6 @@ func (pm *ProtocolManager) synchronise(peer *peer) {
currentBlock := pm.blockchain.CurrentBlock() currentBlock := pm.blockchain.CurrentBlock()
td := pm.blockchain.GetTd(currentBlock.Hash(), currentBlock.NumberU64()) td := pm.blockchain.GetTd(currentBlock.Hash(), currentBlock.NumberU64())
pHead, pTd := peer.Head() pHead, pTd := peer.Head()
log.Debug("ProtocolManager synchronise ", "p", peer, "pTd", pTd, "currentTd", td)
if pTd.Cmp(td) <= 0 { if pTd.Cmp(td) <= 0 {
return return
} }
@ -205,13 +204,13 @@ func (pm *ProtocolManager) synchronise(peer *peer) {
atomic.StoreUint32(&pm.fastSync, 0) atomic.StoreUint32(&pm.fastSync, 0)
} }
atomic.StoreUint32(&pm.acceptTxs, 1) // Mark initial sync done atomic.StoreUint32(&pm.acceptTxs, 1) // Mark initial sync done
if head := pm.blockchain.CurrentBlock(); head.NumberU64() > 0 { //if head := pm.blockchain.CurrentBlock(); head.NumberU64() > 0 {
// We've completed a sync cycle, notify all peers of new state. This path is // // We've completed a sync cycle, notify all peers of new state. This path is
// essential in star-topology networks where a gateway node needs to notify // // essential in star-topology networks where a gateway node needs to notify
// all its out-of-date peers of the availability of a new block. This failure // // all its out-of-date peers of the availability of a new block. This failure
// scenario will most often crop up in private and hackathon networks with // // scenario will most often crop up in private and hackathon networks with
// degenerate connectivity, but it should be healthy for the mainnet too to // // degenerate connectivity, but it should be healthy for the mainnet too to
// more reliably update peers or the local TD state. // // more reliably update peers or the local TD state.
go pm.BroadcastBlock(head, false) // go pm.BroadcastBlock(head, false)
} //}
} }

View file

@ -57,12 +57,12 @@ type Miner struct {
shouldStart int32 // should start indicates whether we should start after sync shouldStart int32 // should start indicates whether we should start after sync
} }
func New(eth Backend, config *params.ChainConfig, mux *event.TypeMux, engine consensus.Engine) *Miner { func New(eth Backend, config *params.ChainConfig, mux *event.TypeMux, engine consensus.Engine, announceTxs bool) *Miner {
miner := &Miner{ miner := &Miner{
eth: eth, eth: eth,
mux: mux, mux: mux,
engine: engine, engine: engine,
worker: newWorker(config, engine, common.Address{}, eth, mux), worker: newWorker(config, engine, common.Address{}, eth, mux, announceTxs),
canStart: 1, canStart: 1,
} }
miner.Register(NewCpuAgent(eth.BlockChain(), engine)) miner.Register(NewCpuAgent(eth.BlockChain(), engine))
@ -77,7 +77,6 @@ func New(eth Backend, config *params.ChainConfig, mux *event.TypeMux, engine con
// and halt your mining operation for as long as the DOS continues. // and halt your mining operation for as long as the DOS continues.
func (self *Miner) update() { func (self *Miner) update() {
events := self.mux.Subscribe(downloader.StartEvent{}, downloader.DoneEvent{}, downloader.FailedEvent{}) events := self.mux.Subscribe(downloader.StartEvent{}, downloader.DoneEvent{}, downloader.FailedEvent{})
out:
for ev := range events.Chan() { for ev := range events.Chan() {
switch ev.Data.(type) { switch ev.Data.(type) {
case downloader.StartEvent: case downloader.StartEvent:
@ -95,10 +94,6 @@ out:
if shouldStart { if shouldStart {
self.Start(self.coinbase) self.Start(self.coinbase)
} }
// unsubscribe. we're only interested in this event once
events.Unsubscribe()
// stop immediately and ignore all further pending events
break out
} }
} }
} }

View file

@ -55,7 +55,7 @@ const (
// timeout waiting for M1 // timeout waiting for M1
waitPeriod = 10 waitPeriod = 10
// timeout for checkpoint. // timeout for checkpoint.
waitPeriodCheckpoint = 60 waitPeriodCheckpoint = 30
) )
// Agent can register themself with the worker // Agent can register themself with the worker
@ -132,9 +132,11 @@ type worker struct {
// atomic status counters // atomic status counters
mining int32 mining int32
atWork int32 atWork int32
announceTxs bool
lastParentBlockCommit string
} }
func newWorker(config *params.ChainConfig, engine consensus.Engine, coinbase common.Address, eth Backend, mux *event.TypeMux) *worker { func newWorker(config *params.ChainConfig, engine consensus.Engine, coinbase common.Address, eth Backend, mux *event.TypeMux, announceTxs bool) *worker {
worker := &worker{ worker := &worker{
config: config, config: config,
engine: engine, engine: engine,
@ -151,9 +153,12 @@ func newWorker(config *params.ChainConfig, engine consensus.Engine, coinbase com
coinbase: coinbase, coinbase: coinbase,
agents: make(map[Agent]struct{}), agents: make(map[Agent]struct{}),
unconfirmed: newUnconfirmedBlocks(eth.BlockChain(), miningLogAtDepth), unconfirmed: newUnconfirmedBlocks(eth.BlockChain(), miningLogAtDepth),
announceTxs: announceTxs,
} }
if worker.announceTxs {
// Subscribe TxPreEvent for tx pool // Subscribe TxPreEvent for tx pool
worker.txSub = eth.TxPool().SubscribeTxPreEvent(worker.txCh) worker.txSub = eth.TxPool().SubscribeTxPreEvent(worker.txCh)
}
// Subscribe events for blockchain // Subscribe events for blockchain
worker.chainHeadSub = eth.BlockChain().SubscribeChainHeadEvent(worker.chainHeadCh) worker.chainHeadSub = eth.BlockChain().SubscribeChainHeadEvent(worker.chainHeadCh)
worker.chainSideSub = eth.BlockChain().SubscribeChainSideEvent(worker.chainSideCh) worker.chainSideSub = eth.BlockChain().SubscribeChainSideEvent(worker.chainSideCh)
@ -248,23 +253,47 @@ func (self *worker) unregister(agent Agent) {
} }
func (self *worker) update() { func (self *worker) update() {
if self.announceTxs {
defer self.txSub.Unsubscribe() defer self.txSub.Unsubscribe()
}
defer self.chainHeadSub.Unsubscribe() defer self.chainHeadSub.Unsubscribe()
defer self.chainSideSub.Unsubscribe() defer self.chainSideSub.Unsubscribe()
timeout := time.NewTimer(waitPeriod * time.Second)
c := make(chan struct{})
finish := make(chan struct{})
defer close(finish)
defer timeout.Stop()
go func() {
for { for {
// A real event arrived, process interesting content // A real event arrived, process interesting content
select { select {
case <-timeout.C:
c <- struct{}{}
case <-finish:
return
}
}
}()
for {
// A real event arrived, process interesting content
select {
case <-c:
if atomic.LoadInt32(&self.mining) == 1 {
self.commitNewWork()
}
timeout.Reset(waitPeriod * time.Second)
// Handle ChainHeadEvent // Handle ChainHeadEvent
case <-self.chainHeadCh: case <-self.chainHeadCh:
self.commitNewWork() self.commitNewWork()
timeout.Reset(waitPeriod * time.Second)
// Handle ChainSideEvent // Handle ChainSideEvent
case ev := <-self.chainSideCh: case ev := <-self.chainSideCh:
if self.config.Posv == nil {
self.uncleMu.Lock() self.uncleMu.Lock()
self.possibleUncles[ev.Block.Hash()] = ev.Block self.possibleUncles[ev.Block.Hash()] = ev.Block
self.uncleMu.Unlock() self.uncleMu.Unlock()
}
// Handle TxPreEvent // Handle TxPreEvent
case ev := <-self.txCh: case ev := <-self.txCh:
// Apply transaction to the pending state if we're not mining // Apply transaction to the pending state if we're not mining
@ -282,9 +311,6 @@ func (self *worker) update() {
self.commitNewWork() self.commitNewWork()
} }
} }
// System stopped
case <-self.txSub.Err():
return
case <-self.chainHeadSub.Err(): case <-self.chainHeadSub.Err():
return return
case <-self.chainSideSub.Err(): case <-self.chainSideSub.Err():
@ -422,6 +448,7 @@ func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error
createdAt: time.Now(), createdAt: time.Now(),
} }
if self.config.Posv == nil {
// when 08 is processed ancestors contain 07 (quick block) // when 08 is processed ancestors contain 07 (quick block)
for _, ancestor := range self.chain.GetBlocksFromHash(parent.Hash(), 7) { for _, ancestor := range self.chain.GetBlocksFromHash(parent.Hash(), 7) {
for _, uncle := range ancestor.Uncles() { for _, uncle := range ancestor.Uncles() {
@ -430,6 +457,7 @@ func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error
work.family.Add(ancestor.Hash()) work.family.Add(ancestor.Hash())
work.ancestors.Add(ancestor.Hash()) work.ancestors.Add(ancestor.Hash())
} }
}
// Keep track of transactions which return errors so they can be removed // Keep track of transactions which return errors so they can be removed
work.tcount = 0 work.tcount = 0
@ -444,17 +472,6 @@ func abs(x int64) int64 {
return x return x
} }
func hop(len, pre, cur int) int {
switch {
case pre < cur:
return cur - (pre + 1)
case pre > cur:
return (len - pre) + (cur - 1)
default:
return len - 1
}
}
func (self *worker) commitNewWork() { func (self *worker) commitNewWork() {
self.mu.Lock() self.mu.Lock()
defer self.mu.Unlock() defer self.mu.Unlock()
@ -466,6 +483,13 @@ func (self *worker) commitNewWork() {
tstart := time.Now() tstart := time.Now()
parent := self.chain.CurrentBlock() parent := self.chain.CurrentBlock()
var signers map[common.Address]struct{} var signers map[common.Address]struct{}
if parent.Hash().Hex() == self.lastParentBlockCommit {
return
}
if !self.announceTxs && atomic.LoadInt32(&self.mining) == 0 {
return
}
// Only try to commit new work if we are mining // Only try to commit new work if we are mining
if atomic.LoadInt32(&self.mining) == 1 { if atomic.LoadInt32(&self.mining) == 1 {
// check if we are right after parent's coinbase in the list // check if we are right after parent's coinbase in the list
@ -473,14 +497,7 @@ func (self *worker) commitNewWork() {
if self.config.Posv != nil { if self.config.Posv != nil {
// get masternodes set from latest checkpoint // get masternodes set from latest checkpoint
c := self.engine.(*posv.Posv) c := self.engine.(*posv.Posv)
masternodes := c.GetMasternodes(self.chain, parent.Header()) len, preIndex, curIndex, ok, err := c.YourTurn(self.chain, parent.Header(),self.coinbase)
snap, err := c.GetSnapshot(self.chain, parent.Header())
if err != nil {
log.Error("Failed when trying to commit new work", "err", err)
return
}
signers = snap.Signers
preIndex, curIndex, ok, err := posv.YourTurn(masternodes, snap, parent.Header(), self.coinbase)
if err != nil { if err != nil {
log.Error("Failed when trying to commit new work", "err", err) log.Error("Failed when trying to commit new work", "err", err)
return return
@ -496,31 +513,22 @@ func (self *worker) commitNewWork() {
// you're not allowed to create this block // you're not allowed to create this block
return return
} }
h := hop(len(masternodes), preIndex, curIndex) h := posv.Hop(len, preIndex, curIndex)
gap := waitPeriod * int64(h) gap := waitPeriod * int64(h)
// Check nearest checkpoint block in hop range. // Check nearest checkpoint block in hop range.
nearest := self.config.Posv.Epoch - (parent.Header().Number.Uint64() % self.config.Posv.Epoch) nearest := self.config.Posv.Epoch - (parent.Header().Number.Uint64() % self.config.Posv.Epoch)
if uint64(h) >= nearest { if uint64(h) >= nearest {
gap += waitPeriodCheckpoint gap = waitPeriodCheckpoint * int64(h)
} }
log.Info("Distance from the parent block", "seconds", gap, "hops", h) log.Info("Distance from the parent block", "seconds", gap, "hops", h)
L: waitedTime := time.Now().Unix() - parent.Header().Time.Int64()
select { if gap > waitedTime {
case newBlock := <-self.chainHeadCh:
self.chainHeadCh <- newBlock
if newBlock.Block.NumberU64() > parent.NumberU64() {
log.Info("New block has came already. Skip this turn", "new block", newBlock.Block.NumberU64(), "current block", parent.NumberU64())
return return
} }
case <-time.After(time.Duration(gap) * time.Second): log.Info("Wait enough. It's my turn", "waited seconds", waitedTime)
// wait enough. It's my turn
log.Info("Wait enough. It's my turn", "waited seconds", gap)
break L
} }
} }
} }
}
tstamp := tstart.Unix() tstamp := tstart.Unix()
if parent.Time().Cmp(new(big.Int).SetInt64(tstamp)) >= 0 { if parent.Time().Cmp(new(big.Int).SetInt64(tstamp)) >= 0 {
tstamp = parent.Time().Int64() + 1 tstamp = parent.Time().Int64() + 1
@ -545,7 +553,7 @@ func (self *worker) commitNewWork() {
header.Coinbase = self.coinbase header.Coinbase = self.coinbase
} }
if err := self.engine.Prepare(self.chain, header); err != nil { if err := self.engine.Prepare(self.chain, header); err != nil {
log.Error("Failed to prepare header for mining", "err", err) log.Error("Failed to prepare header for new block", "err", err)
return return
} }
// If we are care about TheDAO hard-fork check whether to override the extra-data or not // If we are care about TheDAO hard-fork check whether to override the extra-data or not
@ -585,6 +593,7 @@ func (self *worker) commitNewWork() {
uncles []*types.Header uncles []*types.Header
badUncles []common.Hash badUncles []common.Hash
) )
if self.config.Posv == nil {
for hash, uncle := range self.possibleUncles { for hash, uncle := range self.possibleUncles {
if len(uncles) == 2 { if len(uncles) == 2 {
break break
@ -602,15 +611,16 @@ func (self *worker) commitNewWork() {
for _, hash := range badUncles { for _, hash := range badUncles {
delete(self.possibleUncles, hash) delete(self.possibleUncles, hash)
} }
}
// Create the new block to seal with the consensus engine // Create the new block to seal with the consensus engine
if work.Block, err = self.engine.Finalize(self.chain, header, work.state, work.txs, uncles, work.receipts); err != nil { if work.Block, err = self.engine.Finalize(self.chain, header, work.state, work.txs, uncles, work.receipts); err != nil {
log.Error("Failed to finalize block for sealing", "err", err) log.Error("Failed to finalize block for sealing", "err", err)
return return
} }
// We only care about logging if we're actually mining.
if atomic.LoadInt32(&self.mining) == 1 { if atomic.LoadInt32(&self.mining) == 1 {
log.Info("Commit new mining work", "number", work.Block.Number(), "txs", work.tcount, "special txs", len(specialTxs), "uncles", len(uncles), "elapsed", common.PrettyDuration(time.Since(tstart))) log.Info("Committing new block", "number", work.Block.Number(), "txs", work.tcount, "special txs", len(specialTxs), "uncles", len(uncles), "elapsed", common.PrettyDuration(time.Since(tstart)))
self.unconfirmed.Shift(work.Block.NumberU64() - 1) self.unconfirmed.Shift(work.Block.NumberU64() - 1)
self.lastParentBlockCommit = parent.Hash().Hex()
} }
self.push(work) self.push(work)
} }
@ -634,7 +644,6 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB
gp := new(core.GasPool).AddGas(env.header.GasLimit) gp := new(core.GasPool).AddGas(env.header.GasLimit)
var coalescedLogs []*types.Log var coalescedLogs []*types.Log
// first priority for special Txs // first priority for special Txs
for _, tx := range specialTxs { for _, tx := range specialTxs {
if gp.Gas() < params.TxGas && tx.Gas() > 0 { if gp.Gas() < params.TxGas && tx.Gas() > 0 {
@ -654,6 +663,11 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB
} }
// Start executing the transaction // Start executing the transaction
env.state.Prepare(tx.Hash(), common.Hash{}, env.tcount) env.state.Prepare(tx.Hash(), common.Hash{}, env.tcount)
nonce := env.state.GetNonce(from)
if nonce != tx.Nonce() {
log.Trace("Skipping account with special transaction invalide nonce", "sender", from, "nonce", nonce, "tx nonce ", tx.Nonce(), "to", tx.To())
continue
}
err, logs := env.commitTransaction(tx, bc, coinbase, gp) err, logs := env.commitTransaction(tx, bc, coinbase, gp)
switch err { switch err {
case core.ErrNonceTooLow: case core.ErrNonceTooLow:
@ -663,7 +677,6 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB
case core.ErrNonceTooHigh: case core.ErrNonceTooHigh:
// Reorg notification data race between the transaction pool and miner, skip account = // Reorg notification data race between the transaction pool and miner, skip account =
log.Trace("Skipping account with special transaction hight nonce", "sender", from, "nonce", tx.Nonce(), "to", tx.To()) log.Trace("Skipping account with special transaction hight nonce", "sender", from, "nonce", tx.Nonce(), "to", tx.To())
case nil: case nil:
// Everything ok, collect the logs and shift in the next transaction from the same account // Everything ok, collect the logs and shift in the next transaction from the same account
coalescedLogs = append(coalescedLogs, logs...) coalescedLogs = append(coalescedLogs, logs...)
@ -675,7 +688,6 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB
log.Debug("Add Special Transaction failed, account skipped", "hash", tx.Hash(), "sender", from, "nonce", tx.Nonce(), "to", tx.To(), "err", err) log.Debug("Add Special Transaction failed, account skipped", "hash", tx.Hash(), "sender", from, "nonce", tx.Nonce(), "to", tx.To(), "err", err)
} }
} }
for { for {
// If we don't have enough gas for any further transactions then we're done // If we don't have enough gas for any further transactions then we're done
if gp.Gas() < params.TxGas { if gp.Gas() < params.TxGas {
@ -696,13 +708,24 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB
// phase, start ignoring the sender until we do. // phase, start ignoring the sender until we do.
if tx.Protected() && !env.config.IsEIP155(env.header.Number) { if tx.Protected() && !env.config.IsEIP155(env.header.Number) {
log.Trace("Ignoring reply protected transaction", "hash", tx.Hash(), "eip155", env.config.EIP155Block) log.Trace("Ignoring reply protected transaction", "hash", tx.Hash(), "eip155", env.config.EIP155Block)
txs.Pop() txs.Pop()
continue continue
} }
// Start executing the transaction // Start executing the transaction
env.state.Prepare(tx.Hash(), common.Hash{}, env.tcount) env.state.Prepare(tx.Hash(), common.Hash{}, env.tcount)
nonce := env.state.GetNonce(from)
if nonce > tx.Nonce() {
// New head notification data race between the transaction pool and miner, shift
log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce())
txs.Shift()
continue
}
if nonce < tx.Nonce() {
// Reorg notification data race between the transaction pool and miner, skip account =
log.Trace("Skipping account with hight nonce", "sender", from, "nonce", tx.Nonce())
txs.Pop()
continue
}
err, logs := env.commitTransaction(tx, bc, coinbase, gp) err, logs := env.commitTransaction(tx, bc, coinbase, gp)
switch err { switch err {
case core.ErrGasLimitReached: case core.ErrGasLimitReached:
@ -717,7 +740,7 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB
case core.ErrNonceTooHigh: case core.ErrNonceTooHigh:
// Reorg notification data race between the transaction pool and miner, skip account = // Reorg notification data race between the transaction pool and miner, skip account =
log.Trace("Skipping account with hight nonce", "sender", from, "nonce", tx.Nonce()) log.Trace("Skipping account with high nonce", "sender", from, "nonce", tx.Nonce())
txs.Pop() txs.Pop()
case nil: case nil:
@ -733,7 +756,6 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB
txs.Shift() txs.Shift()
} }
} }
if len(coalescedLogs) > 0 || env.tcount > 0 { if len(coalescedLogs) > 0 || env.tcount > 0 {
// make a copy, the state caches the logs and these logs get "upgraded" from pending to mined // make a copy, the state caches the logs and these logs get "upgraded" from pending to mined
// logs by filling in the block hash when the block was mined by the local miner. This can // logs by filling in the block hash when the block was mined by the local miner. This can

View file

@ -147,6 +147,8 @@ type Config struct {
// Logger is a custom logger to use with the p2p.Server. // Logger is a custom logger to use with the p2p.Server.
Logger log.Logger `toml:",omitempty"` Logger log.Logger `toml:",omitempty"`
AnnounceTxs bool `toml:",omitempty"`
} }
// IPCEndpoint resolves an IPC endpoint based on a configured value, taking into // IPCEndpoint resolves an IPC endpoint based on a configured value, taking into

View file

@ -22,7 +22,7 @@ import (
const ( const (
VersionMajor = 1 // Major version component of the current release VersionMajor = 1 // Major version component of the current release
VersionMinor = 0 // Minor version component of the current release VersionMinor = 1 // Minor version component of the current release
VersionPatch = 0 // Patch version component of the current release VersionPatch = 0 // Patch version component of the current release
VersionMeta = "stable" // Version metadata to append to the version string VersionMeta = "stable" // Version metadata to append to the version string
) )