mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-03 02:23:48 +00:00
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:
commit
c69ad1342b
11 changed files with 617 additions and 72 deletions
|
|
@ -16,3 +16,5 @@ jobs:
|
|||
- checkout # check out source code to working directory
|
||||
- run:
|
||||
command: make bor
|
||||
- run:
|
||||
command: make test
|
||||
|
|
|
|||
4
Makefile
4
Makefile
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
//
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
253
consensus/bor_test/bor_test.go
Normal file
253
consensus/bor_test/bor_test.go
Normal 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 := ð.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(ðereum)
|
||||
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
|
||||
}
|
||||
47
consensus/bor_test/genesis.json
Normal file
47
consensus/bor_test/genesis.json
Normal file
File diff suppressed because one or more lines are too long
95
consensus/bor_test/span.json
Normal file
95
consensus/bor_test/span.json
Normal 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"
|
||||
}
|
||||
}
|
||||
|
|
@ -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
3
go.mod
|
|
@ -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
6
go.sum
|
|
@ -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
71
mocks/IHeimdallClient.go
Normal 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
|
||||
}
|
||||
Loading…
Reference in a new issue