Merge pull request #24 from maticnetwork/dev-test-commit-span

MAT-869: Allow commitSpan/State actions if they are underpriced + Test commit span
This commit is contained in:
Arpit Agarwal 2020-03-18 14:58:21 +05:30 committed by GitHub
commit c69ad1342b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 617 additions and 72 deletions

View file

@ -16,3 +16,5 @@ jobs:
- checkout # check out source code to working directory
- run:
command: make bor
- run:
command: make test

View file

@ -29,8 +29,8 @@ ios:
@echo "Done building."
@echo "Import \"$(GOBIN)/bor.framework\" to use the library."
test: all
build/env.sh go run build/ci.go test
test: bor
go test github.com/maticnetwork/bor/consensus/bor_test
lint: ## Run linters.
build/env.sh go run build/ci.go lint

View file

@ -10,7 +10,7 @@ import (
"io"
"math"
"math/big"
"net/http"
"sort"
"strconv"
"strings"
@ -243,7 +243,7 @@ type Bor struct {
ethAPI *ethapi.PublicBlockChainAPI
validatorSetABI abi.ABI
stateReceiverABI abi.ABI
httpClient http.Client
HeimdallClient IHeimdallClient
// The fields below are for testing only
fakeDiff bool // Skip difficulty verifications
@ -268,6 +268,8 @@ func New(
signatures, _ := lru.NewARC(inmemorySignatures)
vABI, _ := abi.JSON(strings.NewReader(validatorsetABI))
sABI, _ := abi.JSON(strings.NewReader(stateReceiverABI))
heimdallClient, _ := NewHeimdallClient(chainConfig.Bor.Heimdall)
c := &Bor{
chainConfig: chainConfig,
config: borConfig,
@ -277,9 +279,7 @@ func New(
signatures: signatures,
validatorSetABI: vABI,
stateReceiverABI: sABI,
httpClient: http.Client{
Timeout: time.Duration(5 * time.Second),
},
HeimdallClient: heimdallClient,
}
return c
@ -563,7 +563,6 @@ func (c *Bor) verifySeal(chain consensus.ChainReader, header *types.Header, pare
if err != nil {
return err
}
if !snap.ValidatorSet.HasAddress(signer.Bytes()) {
return errUnauthorizedSigner
}
@ -662,15 +661,15 @@ func (c *Bor) Prepare(chain consensus.ChainReader, header *types.Header) error {
// rewards given.
func (c *Bor) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) {
// commit span
if header.Number.Uint64()%c.config.Sprint == 0 {
cx := chainContext{Chain: chain, Bor: c}
headerNumber := header.Number.Uint64()
if headerNumber%c.config.Sprint == 0 {
cx := chainContext{Chain: chain, Bor: c}
// check and commit span
if err := c.checkAndCommitSpan(state, header, cx); err != nil {
log.Error("Error while committing span", "error", err)
return
}
// commit statees
if err := c.CommitStates(state, header, cx); err != nil {
log.Error("Error while committing states", "error", err)
@ -986,33 +985,42 @@ func (c *Bor) checkAndCommitSpan(
header *types.Header,
chain core.ChainContext,
) error {
headerNumber := header.Number.Uint64()
pending := false
var span *Span = nil
var wg sync.WaitGroup
wg.Add(1)
errors := make(chan error)
go func() {
pending, _ = c.isSpanPending(header.Number.Uint64())
wg.Done()
var err error
pending, err = c.isSpanPending(headerNumber - 1)
errors <- err
}()
wg.Add(1)
go func() {
span, _ = c.GetCurrentSpan(header.Number.Uint64() - 1)
wg.Done()
var err error
span, err = c.GetCurrentSpan(headerNumber - 1)
errors <- err
}()
wg.Wait()
var err error
for i := 0; i < 2; i++ {
err = <-errors
if err != nil {
close(errors)
return err
}
}
close(errors)
// commit span if there is new span pending or span is ending or end block is not set
if pending || c.needToCommitSpan(span, header) {
err := c.commitSpan(span, state, header, chain)
if pending || c.needToCommitSpan(span, headerNumber) {
err := c.fetchAndCommitSpan(span.ID+1, state, header, chain)
return err
}
return nil
}
func (c *Bor) needToCommitSpan(span *Span, header *types.Header) bool {
func (c *Bor) needToCommitSpan(span *Span, headerNumber uint64) bool {
// if span is nil
if span == nil {
return false
@ -1024,21 +1032,21 @@ func (c *Bor) needToCommitSpan(span *Span, header *types.Header) bool {
}
// if current block is first block of last sprint in current span
h := header.Number.Uint64()
if span.EndBlock > c.config.Sprint && span.EndBlock-c.config.Sprint+1 == h {
if span.EndBlock > c.config.Sprint && span.EndBlock-c.config.Sprint+1 == headerNumber {
return true
}
return false
}
func (c *Bor) commitSpan(
span *Span,
func (c *Bor) fetchAndCommitSpan(
newSpanID uint64,
state *state.StateDB,
header *types.Header,
chain core.ChainContext,
) error {
response, err := FetchFromHeimdallWithRetry(c.httpClient, c.chainConfig.Bor.Heimdall, "bor", "span", strconv.FormatUint(span.ID+1, 10))
response, err := c.HeimdallClient.FetchWithRetry("bor", "span", strconv.FormatUint(newSpanID, 10))
if err != nil {
return err
}
@ -1166,7 +1174,7 @@ func (c *Bor) CommitStates(
// itereate through state ids
for _, stateID := range stateIds {
// fetch from heimdall
response, err := FetchFromHeimdallWithRetry(c.httpClient, c.chainConfig.Bor.Heimdall, "clerk", "event-record", strconv.FormatUint(stateID.Uint64(), 10))
response, err := c.HeimdallClient.FetchWithRetry("clerk", "event-record", strconv.FormatUint(stateID.Uint64(), 10))
if err != nil {
return err
}
@ -1218,6 +1226,44 @@ func (c *Bor) CommitStates(
return nil
}
func (c *Bor) SetHeimdallClient(h IHeimdallClient) {
c.HeimdallClient = h
}
func (c *Bor) IsValidatorAction(chain consensus.ChainReader, from common.Address, tx *types.Transaction) bool {
header := chain.CurrentHeader()
validators, err := c.GetCurrentValidators(header.Number.Uint64(), header.Number.Uint64()+1)
if err != nil {
log.Error("Failed fetching snapshot", err)
return false
}
isValidator := false
for _, validator := range validators {
if bytes.Compare(validator.Address.Bytes(), from.Bytes()) == 0 {
isValidator = true
break
}
}
return isValidator && (isProposeSpanAction(tx, chain.Config().Bor.ValidatorContract) ||
isProposeStateAction(tx, chain.Config().Bor.StateReceiverContract))
}
func isProposeSpanAction(tx *types.Transaction, validatorContract string) bool {
// keccak256('proposeSpan()').slice(0, 4)
proposeSpanSig, _ := hex.DecodeString("4b0e4d17")
return bytes.Compare(proposeSpanSig, tx.Data()[:4]) == 0 &&
tx.To().String() == validatorContract
}
func isProposeStateAction(tx *types.Transaction, stateReceiverContract string) bool {
// keccak256('proposeState(uint256)').slice(0, 4)
proposeStateSig, _ := hex.DecodeString("ede01f17")
return bytes.Compare(proposeStateSig, tx.Data()[:4]) == 0 &&
tx.To().String() == stateReceiverContract
}
//
// Private methods
//

View file

@ -19,9 +19,61 @@ type ResponseWithHeight struct {
Result json.RawMessage `json:"result"`
}
type IHeimdallClient interface {
Fetch(paths ...string) (*ResponseWithHeight, error)
FetchWithRetry(paths ...string) (*ResponseWithHeight, error)
}
type HeimdallClient struct {
u *url.URL
client http.Client
}
func NewHeimdallClient(urlString string) (*HeimdallClient, error) {
u, err := url.Parse(urlString)
if err != nil {
return nil, err
}
h := &HeimdallClient{
u: u,
client: http.Client{
Timeout: time.Duration(5 * time.Second),
},
}
return h, nil
}
func (h *HeimdallClient) Fetch(paths ...string) (*ResponseWithHeight, error) {
for _, e := range paths {
if e != "" {
h.u.Path = path.Join(h.u.Path, e)
}
}
return h.internalFetch()
}
// FetchWithRetry returns data from heimdall with retry
func (h *HeimdallClient) FetchWithRetry(paths ...string) (*ResponseWithHeight, error) {
for _, e := range paths {
if e != "" {
h.u.Path = path.Join(h.u.Path, e)
}
}
for {
res, err := h.internalFetch()
if err == nil && res != nil {
return res, nil
}
log.Info("Retrying again in 5 seconds for next Heimdall span", "path", h.u.Path)
time.Sleep(5 * time.Second)
}
}
// internal fetch method
func internalFetch(client http.Client, u *url.URL) (*ResponseWithHeight, error) {
res, err := client.Get(u.String())
func (h *HeimdallClient) internalFetch() (*ResponseWithHeight, error) {
res, err := h.client.Get(h.u.String())
if err != nil {
return nil, err
}
@ -46,42 +98,3 @@ func internalFetch(client http.Client, u *url.URL) (*ResponseWithHeight, error)
return &response, nil
}
// FetchFromHeimdallWithRetry returns data from heimdall with retry
func FetchFromHeimdallWithRetry(client http.Client, urlString string, paths ...string) (*ResponseWithHeight, error) {
u, err := url.Parse(urlString)
if err != nil {
return nil, err
}
for _, e := range paths {
if e != "" {
u.Path = path.Join(u.Path, e)
}
}
for {
res, err := internalFetch(client, u)
if err == nil && res != nil {
return res, nil
}
log.Info("Retrying again in 5 seconds for next Heimdall span", "path", u.Path)
time.Sleep(5 * time.Second)
}
}
// FetchFromHeimdall returns data from heimdall
func FetchFromHeimdall(client http.Client, urlString string, paths ...string) (*ResponseWithHeight, error) {
u, err := url.Parse(urlString)
if err != nil {
return nil, err
}
for _, e := range paths {
if e != "" {
u.Path = path.Join(u.Path, e)
}
}
return internalFetch(client, u)
}

View file

@ -0,0 +1,253 @@
package bor_test
import (
"encoding/hex"
"encoding/json"
"io/ioutil"
"math/big"
"testing"
"github.com/maticnetwork/bor/common"
"github.com/maticnetwork/bor/consensus/bor"
"github.com/maticnetwork/bor/core"
"github.com/maticnetwork/bor/core/rawdb"
"github.com/maticnetwork/bor/core/state"
"github.com/maticnetwork/bor/crypto/secp256k1"
"github.com/stretchr/testify/assert"
"github.com/maticnetwork/bor/core/types"
"github.com/maticnetwork/bor/crypto"
"github.com/maticnetwork/bor/eth"
"github.com/maticnetwork/bor/ethdb"
"github.com/maticnetwork/bor/mocks"
"github.com/maticnetwork/bor/node"
)
var (
extraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal
privKey = "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291"
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
addr = crypto.PubkeyToAddress(key.PublicKey) // 0x71562b71999873DB5b286dF957af199Ec94617F7
)
type initializeData struct {
genesis *core.Genesis
ethereum *eth.Ethereum
}
func TestCommitSpan(t *testing.T) {
init := buildEthereumInstance(t, rawdb.NewMemoryDatabase())
chain := init.ethereum.BlockChain()
engine := init.ethereum.Engine()
_bor := engine.(*bor.Bor)
// Mock HeimdallClient.FetchWithRetry to return span data from span.json
res, heimdallSpan := loadSpanFromFile(t)
h := &mocks.IHeimdallClient{}
h.On("FetchWithRetry", "bor", "span", "1").
Return(res, nil).
Times(2) // both FinalizeAndAssemble and chain.InsertChain call HeimdallClient.FetchWithRetry. @todo Investigate this in depth
_bor.SetHeimdallClient(h)
db := init.ethereum.ChainDb()
block := init.genesis.ToBlock(db)
// Build 1st block's header
header := buildMinimalNextHeader(t, block, init.genesis.Config.Bor.Period)
statedb, err := chain.State()
if err != nil {
t.Fatalf("%s", err)
}
_key, _ := hex.DecodeString(privKey)
insertNewBlock(t, _bor, chain, header, statedb, _key)
assert.True(t, h.AssertNumberOfCalls(t, "FetchWithRetry", 2))
validators, err := _bor.GetCurrentValidators(1, 256) // new span starts at 256
if err != nil {
t.Fatalf("%s", err)
}
assert.Equal(t, len(validators), 3)
for i, validator := range validators {
assert.Equal(t, validator.Address.Bytes(), heimdallSpan.SelectedProducers[i].Address.Bytes())
assert.Equal(t, validator.VotingPower, heimdallSpan.SelectedProducers[i].VotingPower)
}
}
func TestIsValidatorAction(t *testing.T) {
init := buildEthereumInstance(t, rawdb.NewMemoryDatabase())
chain := init.ethereum.BlockChain()
engine := init.ethereum.Engine()
_bor := engine.(*bor.Bor)
proposeStateData, _ := hex.DecodeString("ede01f170000000000000000000000000000000000000000000000000000000000000000")
proposeSpanData, _ := hex.DecodeString("4b0e4d17")
var tx *types.Transaction
tx = types.NewTransaction(
0,
common.HexToAddress(chain.Config().Bor.StateReceiverContract),
big.NewInt(0), 0, big.NewInt(0),
proposeStateData,
)
assert.True(t, _bor.IsValidatorAction(chain, addr, tx))
tx = types.NewTransaction(
0,
common.HexToAddress(chain.Config().Bor.ValidatorContract),
big.NewInt(0), 0, big.NewInt(0),
proposeSpanData,
)
assert.True(t, _bor.IsValidatorAction(chain, addr, tx))
res, heimdallSpan := loadSpanFromFile(t)
h := &mocks.IHeimdallClient{}
h.On("FetchWithRetry", "bor", "span", "1").Return(res, nil)
_bor.SetHeimdallClient(h)
// Build 1st block's header
db := init.ethereum.ChainDb()
block := init.genesis.ToBlock(db)
header := buildMinimalNextHeader(t, block, init.genesis.Config.Bor.Period)
statedb, err := chain.State()
if err != nil {
t.Fatalf("%s", err)
}
_key, _ := hex.DecodeString(privKey)
insertNewBlock(t, _bor, chain, header, statedb, _key)
block = types.NewBlockWithHeader(header)
var headers []*types.Header
for i := int64(2); i <= 255; i++ {
header := buildMinimalNextHeader(t, block, init.genesis.Config.Bor.Period)
headers = append(headers, header)
block = types.NewBlockWithHeader(header)
}
t.Logf("inserting %v headers", len(headers))
if _, err := chain.InsertHeaderChain(headers, 0); err != nil {
t.Fatalf("%s", err)
}
for _, validator := range heimdallSpan.SelectedProducers {
_addr := validator.Address
tx = types.NewTransaction(
0,
common.HexToAddress(chain.Config().Bor.StateReceiverContract),
big.NewInt(0), 0, big.NewInt(0),
proposeStateData,
)
assert.True(t, _bor.IsValidatorAction(chain, _addr, tx))
tx = types.NewTransaction(
0,
common.HexToAddress(chain.Config().Bor.ValidatorContract),
big.NewInt(0), 0, big.NewInt(0),
proposeSpanData,
)
assert.True(t, _bor.IsValidatorAction(chain, _addr, tx))
}
}
func buildEthereumInstance(t *testing.T, db ethdb.Database) *initializeData {
genesisData, err := ioutil.ReadFile("genesis.json")
if err != nil {
t.Fatalf("%s", err)
}
gen := &core.Genesis{}
if err := json.Unmarshal(genesisData, gen); err != nil {
t.Fatalf("%s", err)
}
ethConf := &eth.Config{
Genesis: gen,
}
ethConf.Genesis.MustCommit(db)
// Create a temporary storage for the node keys and initialize it
workspace, err := ioutil.TempDir("", "console-tester-")
if err != nil {
t.Fatalf("failed to create temporary keystore: %v", err)
}
// Create a networkless protocol stack and start an Ethereum service within
stack, err := node.New(&node.Config{DataDir: workspace, UseLightweightKDF: true, Name: "console-tester"})
if err != nil {
t.Fatalf("failed to create node: %v", err)
}
err = stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
s, err := eth.New(ctx, ethConf)
return s, err
})
if err != nil {
t.Fatalf("failed to register Ethereum protocol: %v", err)
}
// Start the node and assemble the JavaScript console around it
if err = stack.Start(); err != nil {
t.Fatalf("failed to start test stack: %v", err)
}
_, err = stack.Attach()
if err != nil {
t.Fatalf("failed to attach to node: %v", err)
}
var ethereum *eth.Ethereum
stack.Service(&ethereum)
ethConf.Genesis.MustCommit(ethereum.ChainDb())
return &initializeData{
genesis: gen,
ethereum: ethereum,
}
}
func insertNewBlock(t *testing.T, _bor *bor.Bor, chain *core.BlockChain, header *types.Header, statedb *state.StateDB, privKey []byte) {
_, err := _bor.FinalizeAndAssemble(chain, header, statedb, nil, nil, nil)
if err != nil {
t.Fatalf("%s", err)
}
sig, err := secp256k1.Sign(crypto.Keccak256(bor.BorRLP(header)), privKey)
if err != nil {
t.Fatalf("%s", err)
}
copy(header.Extra[len(header.Extra)-extraSeal:], sig)
block := types.NewBlockWithHeader(header)
if _, err := chain.InsertChain([]*types.Block{block}); err != nil {
t.Fatalf("%s", err)
}
}
func buildMinimalNextHeader(t *testing.T, block *types.Block, period uint64) *types.Header {
header := block.Header()
header.Number.Add(header.Number, big.NewInt(1))
header.ParentHash = block.Hash()
header.Time += (period + 1)
header.Extra = make([]byte, 97) // vanity (32) + extraSeal (65)
_key, _ := hex.DecodeString(privKey)
sig, err := secp256k1.Sign(crypto.Keccak256(bor.BorRLP(header)), _key)
if err != nil {
t.Fatalf("%s", err)
}
copy(header.Extra[len(header.Extra)-extraSeal:], sig)
return header
}
func loadSpanFromFile(t *testing.T) (*bor.ResponseWithHeight, *bor.HeimdallSpan) {
spanData, err := ioutil.ReadFile("span.json")
if err != nil {
t.Fatalf("%s", err)
}
res := &bor.ResponseWithHeight{}
if err := json.Unmarshal(spanData, res); err != nil {
t.Fatalf("%s", err)
}
heimdallSpan := &bor.HeimdallSpan{}
if err := json.Unmarshal(res.Result, heimdallSpan); err != nil {
t.Fatalf("%s", err)
}
return res, heimdallSpan
}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,95 @@
{
"height": "42841",
"result": {
"span_id": 1,
"start_block": 256,
"end_block": 6655,
"validator_set": {
"validators": [{
"ID": 3,
"startEpoch": 0,
"endEpoch": 0,
"power": 10000,
"pubKey": "0x046434e10a34ade13c4fea917346a9fd1473eac2138a0b4e2a36426871918be63188fde4edbf598457592c9a49fe3b0036dd5497079495d132e5045bf499c4bdb1",
"signer": "0xc787af4624cb3e80ee23ae7faac0f2acea2be34c",
"last_updated": 0,
"accum": -40000
}, {
"ID": 4,
"startEpoch": 0,
"endEpoch": 0,
"power": 10000,
"pubKey": "0x04d9d09f2afc9da3cccc164e8112eb6911a63f5ede10169768f800df83cf99c73f944411e9d4fac3543b11c5f84a82e56b36cfcd34f1d065855c1e2b27af8b5247",
"signer": "0x461295d3d9249215e758e939a150ab180950720b",
"last_updated": 0,
"accum": 10000
}, {
"ID": 5,
"startEpoch": 0,
"endEpoch": 0,
"power": 10000,
"pubKey": "0x04a36f6ed1f93acb0a38f4cacbe2467c72458ac41ce3b12b34d758205b2bc5d930a4e059462da7a0976c32fce766e1f7e8d73933ae72ac2af231fe161187743932",
"signer": "0x836fe3e3dd0a5f77d9d5b0f67e48048aaafcd5a0",
"last_updated": 0,
"accum": 10000
}, {
"ID": 1,
"startEpoch": 0,
"endEpoch": 0,
"power": 10000,
"pubKey": "0x04a312814042a6655c8e5ecf0c52cba0b6a6f3291c87cc42260a3c0222410c0d0d59b9139d1c56542e5df0ce2fce3a86ce13e93bd9bde0dc8ff664f8dd5294dead",
"signer": "0x925a91f8003aaeabea6037103123b93c50b86ca3",
"last_updated": 0,
"accum": 10000
}, {
"ID": 2,
"startEpoch": 0,
"endEpoch": 0,
"power": 10000,
"pubKey": "0x0469536ae98030a7e83ec5ef3baffed2d05a32e31d978e58486f6bdb0fbbf240293838325116090190c0639db03f9cbd8b9aecfd269d016f46e3a2287fbf9ad232",
"signer": "0x1c4f0f054a0d6a1415382dc0fd83c6535188b220",
"last_updated": 0,
"accum": 10000
}],
"proposer": {
"ID": 3,
"startEpoch": 0,
"endEpoch": 0,
"power": 10000,
"pubKey": "0x046434e10a34ade13c4fea917346a9fd1473eac2138a0b4e2a36426871918be63188fde4edbf598457592c9a49fe3b0036dd5497079495d132e5045bf499c4bdb1",
"signer": "0x1c4f0f054a0d6a1415382dc0fd83c6535188b220",
"last_updated": 0,
"accum": -40000
}
},
"selected_producers": [{
"ID": 5,
"startEpoch": 0,
"endEpoch": 0,
"power": 1,
"pubKey": "0x04a36f6ed1f93acb0a38f4cacbe2467c72458ac41ce3b12b34d758205b2bc5d930a4e059462da7a0976c32fce766e1f7e8d73933ae72ac2af231fe161187743932",
"signer": "0x836fe3e3dd0a5f77d9d5b0f67e48048aaafcd5a0",
"last_updated": 0,
"accum": 10000
}, {
"ID": 1,
"startEpoch": 0,
"endEpoch": 0,
"power": 1,
"pubKey": "0x04a312814042a6655c8e5ecf0c52cba0b6a6f3291c87cc42260a3c0222410c0d0d59b9139d1c56542e5df0ce2fce3a86ce13e93bd9bde0dc8ff664f8dd5294dead",
"signer": "0x925a91f8003aaeabea6037103123b93c50b86ca3",
"last_updated": 0,
"accum": 10000
}, {
"ID": 2,
"startEpoch": 0,
"endEpoch": 0,
"power": 2,
"pubKey": "0x0469536ae98030a7e83ec5ef3baffed2d05a32e31d978e58486f6bdb0fbbf240293838325116090190c0639db03f9cbd8b9aecfd269d016f46e3a2287fbf9ad232",
"signer": "0xc787af4624cb3e80ee23ae7faac0f2acea2be34c",
"last_updated": 0,
"accum": 5000
}],
"bor_chain_id": "15001"
}
}

View file

@ -27,6 +27,7 @@ import (
"github.com/maticnetwork/bor/common"
"github.com/maticnetwork/bor/common/prque"
"github.com/maticnetwork/bor/consensus"
"github.com/maticnetwork/bor/core/state"
"github.com/maticnetwork/bor/core/types"
"github.com/maticnetwork/bor/event"
@ -116,14 +117,22 @@ const (
TxStatusIncluded
)
// bor acts as a way to be able to type cast consensus.Engine;
// since importing "github.com/maticnetwork/bor/consensus/bor" results in a cyclic dependency
type bor interface {
IsValidatorAction(chain consensus.ChainReader, from common.Address, tx *types.Transaction) bool
}
// blockChain provides the state of blockchain and current gas limit to do
// some pre checks in tx pool and event subscribers.
type blockChain interface {
CurrentBlock() *types.Block
GetBlock(hash common.Hash, number uint64) *types.Block
StateAt(root common.Hash) (*state.StateDB, error)
SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Subscription
Engine() consensus.Engine
CurrentHeader() *types.Header
}
// TxPoolConfig are the configuration parameters of the transaction pool.
@ -527,7 +536,9 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
}
// Drop non-local transactions under our own minimal accepted gas price
local = local || pool.locals.contains(from) // account may be local even if the transaction arrived from the network
if !local && pool.gasPrice.Cmp(tx.GasPrice()) > 0 {
if !local &&
!pool.chain.Engine().(bor).IsValidatorAction(pool.chain.(consensus.ChainReader), from, tx) &&
pool.gasPrice.Cmp(tx.GasPrice()) > 0 {
return ErrUnderpriced
}
// Ensure the transaction adheres to nonce ordering

3
go.mod
View file

@ -51,7 +51,8 @@ require (
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4
github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570
github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 // indirect
github.com/stretchr/testify v1.2.2
github.com/stretchr/objx v0.2.0 // indirect
github.com/stretchr/testify v1.3.0
github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef
github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208

6
go.sum
View file

@ -19,6 +19,7 @@ github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnC
github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk=
github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea h1:j4317fAZh7X6GqbFowYdYdI0L9bwxL07jyPZIdepyZ0=
@ -131,8 +132,13 @@ github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570 h1:gIlAHnH1
github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570/go.mod h1:8OR4w3TdeIHIh1g6EMY5p0gVNOovcWC+1vpc7naMuAw=
github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 h1:njlZPzLwU639dk2kqnCPPv+wNjq7Xb6EfUxe/oX0/NM=
github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3/go.mod h1:hpGUWaI9xL8pRQCTXQgocU38Qw1g0Us7n5PxxTwTCYU=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965 h1:1oFLiOyVl+W7bnBzGhf7BbIv9loSFQcieWWYIjLqcAw=
github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA=
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef h1:wHSqTBrZW24CsNJDfeh9Ex6Pm0Rcpc7qrgKBiL44vF4=

71
mocks/IHeimdallClient.go Normal file
View file

@ -0,0 +1,71 @@
// Code generated by mockery v1.0.0. DO NOT EDIT.
package mocks
import (
bor "github.com/maticnetwork/bor/consensus/bor"
mock "github.com/stretchr/testify/mock"
)
// IHeimdallClient is an autogenerated mock type for the IHeimdallClient type
type IHeimdallClient struct {
mock.Mock
}
// Fetch provides a mock function with given fields: paths
func (_m *IHeimdallClient) Fetch(paths ...string) (*bor.ResponseWithHeight, error) {
_va := make([]interface{}, len(paths))
for _i := range paths {
_va[_i] = paths[_i]
}
var _ca []interface{}
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *bor.ResponseWithHeight
if rf, ok := ret.Get(0).(func(...string) *bor.ResponseWithHeight); ok {
r0 = rf(paths...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*bor.ResponseWithHeight)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(...string) error); ok {
r1 = rf(paths...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// FetchWithRetry provides a mock function with given fields: paths
func (_m *IHeimdallClient) FetchWithRetry(paths ...string) (*bor.ResponseWithHeight, error) {
_va := make([]interface{}, len(paths))
for _i := range paths {
_va[_i] = paths[_i]
}
var _ca []interface{}
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *bor.ResponseWithHeight
if rf, ok := ret.Get(0).(func(...string) *bor.ResponseWithHeight); ok {
r0 = rf(paths...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*bor.ResponseWithHeight)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(...string) error); ok {
r1 = rf(paths...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}