diff --git a/consensus/bor/api.go b/consensus/bor/api.go index 98f7c46b6e..d1d151c216 100644 --- a/consensus/bor/api.go +++ b/consensus/bor/api.go @@ -30,6 +30,10 @@ type API struct { bor *Bor } +func NewBorApi(chain consensus.ChainReader, bor *Bor) API { + return API{chain: chain, bor: bor} +} + // GetSnapshot retrieves the state snapshot at a given block. func (api *API) GetSnapshot(number *rpc.BlockNumber) (*Snapshot, error) { // Retrieve the requested block number (or current if none requested) diff --git a/consensus/bor/bor.go b/consensus/bor/bor.go index f4223be702..68ecb36211 100644 --- a/consensus/bor/bor.go +++ b/consensus/bor/bor.go @@ -10,7 +10,8 @@ import ( "io" "math" "math/big" - "net/http" + + // "net/http" "sort" "strconv" "strings" @@ -243,7 +244,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 +269,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 +280,7 @@ func New( signatures: signatures, validatorSetABI: vABI, stateReceiverABI: sABI, - httpClient: http.Client{ - Timeout: time.Duration(5 * time.Second), - }, + HeimdallClient: heimdallClient, } return c @@ -662,20 +663,26 @@ 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 { + headerNumber := header.Number.Uint64() + fmt.Println(header.Number, headerNumber % c.config.Sprint) + if headerNumber > 0 && + headerNumber % c.config.Sprint == 0 { cx := chainContext{Chain: chain, Bor: c} - + fmt.Println("here 0.1") // check and commit span if err := c.checkAndCommitSpan(state, header, cx); err != nil { + fmt.Println("Error while committing span", err) log.Error("Error while committing span", "error", err) return } - + fmt.Println("here 11") // commit statees - if err := c.CommitStates(state, header, cx); err != nil { - log.Error("Error while committing states", "error", err) - return - } + // if err := c.CommitStates(state, header, cx); err != nil { + // log.Error("Error while committing states", "error", err) + // fmt.Println("here 12", err) + // return + // } + fmt.Println("here 12") } // No block rewards in PoA, so the state remains as is and uncles are dropped @@ -986,33 +993,53 @@ 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) + // var wg sync.WaitGroup + // wg.Add(1) go func() { - pending, _ = c.isSpanPending(header.Number.Uint64()) - wg.Done() + var err error + pending, err = c.isSpanPending(headerNumber - 1) + errors <- err + // wg.Done() }() - wg.Add(1) + // 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.Done() }() - wg.Wait() + var err error + for i := 0; i < 2; i++ { + err = <- errors + if err != nil { + // fmt.Println(i, err) + return err + } + } + + fmt.Println("here 1") // 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) { + fmt.Println("here 2", span) + err := c.fetchAndCommitSpan(span.ID + 1, state, header, chain) + // err := c.commitSpan(span, state, header, chain) + fmt.Println("here 10", err) return err } return nil } -func (c *Bor) needToCommitSpan(span *Span, header *types.Header) bool { +func (c *Bor) needToCommitSpan(span *Span, headerNumber uint64) bool { + fmt.Println("span", span) + // if span is nil if span == nil { return false @@ -1024,21 +1051,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)) + fmt.Println("here 3") if err != nil { return err } @@ -1047,7 +1074,7 @@ func (c *Bor) commitSpan( if err := json.Unmarshal(response.Result, &heimdallSpan); err != nil { return err } - + fmt.Println("here 4", heimdallSpan) // check if chain id matches with heimdall span if heimdallSpan.ChainID != c.chainConfig.ChainID.String() { return fmt.Errorf( @@ -1056,7 +1083,7 @@ func (c *Bor) commitSpan( c.chainConfig.ChainID, ) } - + fmt.Println("here 5") // get validators bytes var validators []MinimalVal for _, val := range heimdallSpan.ValidatorSet.Validators { @@ -1066,7 +1093,7 @@ func (c *Bor) commitSpan( if err != nil { return err } - + fmt.Println("here 6") // get producers bytes var producers []MinimalVal for _, val := range heimdallSpan.SelectedProducers { @@ -1086,7 +1113,7 @@ func (c *Bor) commitSpan( "validatorBytes", hex.EncodeToString(validatorBytes), "producerBytes", hex.EncodeToString(producerBytes), ) - + fmt.Println("here 7") // get packed data data, err := c.validatorSetABI.Pack(method, big.NewInt(0).SetUint64(heimdallSpan.ID), @@ -1099,10 +1126,10 @@ func (c *Bor) commitSpan( log.Error("Unable to pack tx for commitSpan", "error", err) return err } - + fmt.Println("here 8") // get system message msg := getSystemMessage(common.HexToAddress(c.config.ValidatorContract), data) - + fmt.Println("here 9") // apply message return applyMessage(msg, state, header, c.chainConfig, chain) } @@ -1166,7 +1193,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 +1245,10 @@ 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() snap, err := c.snapshot(chain, header.Number.Uint64(), header.Hash(), nil) @@ -1246,7 +1277,6 @@ func isProposeSpanAction(tx *types.Transaction, validatorContract string) bool { func isProposeStateAction(tx *types.Transaction, stateReceiverContract string) bool { // keccak256('proposeState(uint256)').slice(0, 4) proposeStateSig, _ := hex.DecodeString("ede01f17") - fmt.Println(bytes.Compare(proposeStateSig, tx.Data()[:4]) == 0, tx.To().String(), stateReceiverContract) return bytes.Compare(proposeStateSig, tx.Data()[:4]) == 0 && tx.To().String() == stateReceiverContract } @@ -1265,6 +1295,10 @@ type chainContext struct { Bor consensus.Engine } +// func NewChainContext(chain consensus.ChainReader, bor consensus.Engine) (chainContext) { +// return {Chain: chain, Bor: bor} +// } + func (c chainContext) Engine() consensus.Engine { return c.Bor } @@ -1322,11 +1356,12 @@ func applyMessage( msg.Gas(), msg.Value(), ) + fmt.Println("here 9.1", err) // Update the state with pending changes if err != nil { + fmt.Println("here 9.2", err) state.Finalise(true) } - return nil } diff --git a/consensus/bor/rest.go b/consensus/bor/rest.go index a312959132..6129037fc3 100644 --- a/consensus/bor/rest.go +++ b/consensus/bor/rest.go @@ -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) -} diff --git a/consensus/bor_test/bor_test.go b/consensus/bor_test/bor_test.go index 80163bc91e..f959f07cb0 100644 --- a/consensus/bor_test/bor_test.go +++ b/consensus/bor_test/bor_test.go @@ -3,34 +3,129 @@ package bor_test import ( "encoding/hex" "encoding/json" + "fmt" + + // "fmt" "io/ioutil" "math/big" "testing" "github.com/stretchr/testify/assert" + // "github.com/stretchr/testify/mock" "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/core/types" + // "github.com/maticnetwork/bor/core/vm" "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" ) +type initializeData struct { + genesis *core.Genesis + ethereum *eth.Ethereum +} + +func TestCommitSpan(t *testing.T) { + var ( + db = rawdb.NewMemoryDatabase() + // key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + // addr = crypto.PubkeyToAddress(key.PublicKey) + ) + init := buildEthereumInstance(t, db) + chain := init.ethereum.BlockChain() + engine := init.ethereum.Engine() + _bor := engine.(*bor.Bor) + + 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) + } + + h := &mocks.IHeimdallClient{} + h.On("FetchWithRetry", "bor", "span", "1").Return(res, nil) + _bor.SetHeimdallClient(h) + block := init.genesis.ToBlock(db) + statedb, _ := state.New(block.Root(), state.NewDatabase(db)) + // _, _, _, err = chain.Processor().Process(block, statedb, vm.Config{}) + // fmt.Println(err) + // _bor.Finalize(chain, block.Header(), statedb, nil, nil) + + api := bor.NewBorApi(chain, _bor) + validators, _ := api.GetCurrentValidators() + fmt.Println(1, validators) + + header := block.Header() + header.Number = big.NewInt(1) + header.ParentHash = block.Hash() + block = types.NewBlockWithHeader(header) + _bor.Finalize(chain, block.Header(), statedb, nil, nil) + // _, _, _, err = chain.Processor().Process(block, statedb, vm.Config{}) + if err != nil { + t.Fatalf("%s", err) + } + // fmt.Println(header.Number) + // _bor.Finalize(chain, header, statedb, nil, nil) + + // status, err := chain.writeBlockWithState(block, receipts, statedb) + + // chain.chainmu.Lock() + k := big.NewInt(0) + k.Add(block.Difficulty(), chain.GetTdByHash(block.ParentHash())) + fmt.Println("k", k) + + rawdb.WriteTd(db, + block.Hash(), + block.NumberU64(), + k, + ) + rawdb.WriteBlock(db, block) + root, err := statedb.Commit(true /* false ??*/) + fmt.Println("root", root) + if err != nil { + t.Fatalf("%s", err) + } + if err := statedb.Reset(root); err != nil { + t.Fatalf("state reset after block %d failed: %v", block.NumberU64(), err) + } + // blockchain.chainmu.Unlock() + + assert.True(t, h.AssertNumberOfCalls(t, "FetchWithRetry", 1)) + validators, _ = api.GetCurrentValidators() + fmt.Println(2, validators) + + validators, _ = _bor.GetCurrentValidators(0, 255) + fmt.Println(3, validators) + + validators, _ = _bor.GetCurrentValidators(0, 256) + fmt.Println(4, validators) + + // validators, _ = _bor.GetCurrentValidators(1, 256) + // fmt.Println(4, validators) + // engine +} + func TestIsValidatorAction(t *testing.T) { var ( db = rawdb.NewMemoryDatabase() key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") addr = crypto.PubkeyToAddress(key.PublicKey) ) - ethereum := buildEthereumInstance(t, db) + init := buildEthereumInstance(t, db) + ethereum := init.ethereum chain := ethereum.BlockChain() engine := ethereum.Engine() - bor := engine.(*bor.Bor) // proposeState data, _ := hex.DecodeString("ede01f170000000000000000000000000000000000000000000000000000000000000000") @@ -40,7 +135,7 @@ func TestIsValidatorAction(t *testing.T) { big.NewInt(0), 0, big.NewInt(0), data, ) - assert.True(t, bor.IsValidatorAction(chain, addr, tx)) + assert.True(t, engine.(*bor.Bor).IsValidatorAction(chain, addr, tx)) // proposeSpan data, _ = hex.DecodeString("4b0e4d17") @@ -50,10 +145,10 @@ func TestIsValidatorAction(t *testing.T) { big.NewInt(0), 0, big.NewInt(0), data, ) - assert.True(t, bor.IsValidatorAction(chain, addr, tx)) + assert.True(t, engine.(*bor.Bor).IsValidatorAction(chain, addr, tx)) } -func buildEthereumInstance(t *testing.T, db ethdb.Database) *eth.Ethereum { +func buildEthereumInstance(t *testing.T, db ethdb.Database) (*initializeData) { genesisData, err := ioutil.ReadFile("genesis.json") if err != nil { t.Fatalf("%s", err) @@ -98,5 +193,9 @@ func buildEthereumInstance(t *testing.T, db ethdb.Database) *eth.Ethereum { var ethereum *eth.Ethereum stack.Service(ðereum) - return ethereum + + return &initializeData{ + genesis: gen, + ethereum: ethereum, + } } diff --git a/consensus/bor_test/genesis.json b/consensus/bor_test/genesis.json index 84a293ff05..737cb600dd 100644 --- a/consensus/bor_test/genesis.json +++ b/consensus/bor_test/genesis.json @@ -11,7 +11,7 @@ "bor": { "period": 1, "producerDelay": 4, - "sprint": 64, + "sprint": 1, "validatorContract": "0x0000000000000000000000000000000000001000", "stateReceiverContract": "0x0000000000000000000000000000000000001001", "heimdall": "http://localhost:1317" diff --git a/consensus/bor_test/span.json b/consensus/bor_test/span.json new file mode 100644 index 0000000000..01e9427c47 --- /dev/null +++ b/consensus/bor_test/span.json @@ -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": "0x1c4f0f054a0d6a1415382dc0fd83c6535188b220", + "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": "0xc787af4624cb3e80ee23ae7faac0f2acea2be34c", + "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": 10000 + }], + "bor_chain_id": "15001" + } +} diff --git a/mocks/IHeimdallClient.go b/mocks/IHeimdallClient.go new file mode 100644 index 0000000000..cb09800c74 --- /dev/null +++ b/mocks/IHeimdallClient.go @@ -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 +}