Add support for Dynamic txn(EIP-1559).
This commit is contained in:
parent
4f627f614f
commit
a1a02a097d
@ -16,6 +16,7 @@ CREATE TABLE eth.header_cids (
|
|||||||
bloom BYTEA NOT NULL,
|
bloom BYTEA NOT NULL,
|
||||||
timestamp NUMERIC NOT NULL,
|
timestamp NUMERIC NOT NULL,
|
||||||
times_validated INTEGER NOT NULL DEFAULT 1,
|
times_validated INTEGER NOT NULL DEFAULT 1,
|
||||||
|
base_fee BIGINT NOT NULL,
|
||||||
UNIQUE (block_number, block_hash)
|
UNIQUE (block_number, block_hash)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -229,6 +229,7 @@ func (sdi *StateDiffIndexer) processHeader(tx *sqlx.Tx, header *types.Header, he
|
|||||||
TxRoot: header.TxHash.String(),
|
TxRoot: header.TxHash.String(),
|
||||||
UncleRoot: header.UncleHash.String(),
|
UncleRoot: header.UncleHash.String(),
|
||||||
Timestamp: header.Time,
|
Timestamp: header.Time,
|
||||||
|
BaseFee: header.BaseFee.String(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +25,6 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
|
||||||
"github.com/ethereum/go-ethereum/statediff/indexer"
|
"github.com/ethereum/go-ethereum/statediff/indexer"
|
||||||
"github.com/ethereum/go-ethereum/statediff/indexer/ipfs/ipld"
|
"github.com/ethereum/go-ethereum/statediff/indexer/ipfs/ipld"
|
||||||
"github.com/ethereum/go-ethereum/statediff/indexer/mocks"
|
"github.com/ethereum/go-ethereum/statediff/indexer/mocks"
|
||||||
@ -45,10 +44,10 @@ var (
|
|||||||
ind *indexer.StateDiffIndexer
|
ind *indexer.StateDiffIndexer
|
||||||
ipfsPgGet = `SELECT data FROM public.blocks
|
ipfsPgGet = `SELECT data FROM public.blocks
|
||||||
WHERE key = $1`
|
WHERE key = $1`
|
||||||
tx1, tx2, tx3, tx4, rct1, rct2, rct3, rct4 []byte
|
tx1, tx2, tx3, tx4, tx5, rct1, rct2, rct3, rct4, rct5 []byte
|
||||||
mockBlock *types.Block
|
mockBlock *types.Block
|
||||||
headerCID, trx1CID, trx2CID, trx3CID, trx4CID cid.Cid
|
headerCID, trx1CID, trx2CID, trx3CID, trx4CID, trx5CID cid.Cid
|
||||||
rct1CID, rct2CID, rct3CID, rct4CID cid.Cid
|
rct1CID, rct2CID, rct3CID, rct4CID, rct5CID cid.Cid
|
||||||
state1CID, state2CID, storageCID cid.Cid
|
state1CID, state2CID, storageCID cid.Cid
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -88,6 +87,11 @@ func init() {
|
|||||||
copy(tx4, buf.Bytes())
|
copy(tx4, buf.Bytes())
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
|
|
||||||
|
txs.EncodeIndex(4, buf)
|
||||||
|
tx5 = make([]byte, buf.Len())
|
||||||
|
copy(tx5, buf.Bytes())
|
||||||
|
buf.Reset()
|
||||||
|
|
||||||
rcts.EncodeIndex(0, buf)
|
rcts.EncodeIndex(0, buf)
|
||||||
rct1 = make([]byte, buf.Len())
|
rct1 = make([]byte, buf.Len())
|
||||||
copy(rct1, buf.Bytes())
|
copy(rct1, buf.Bytes())
|
||||||
@ -108,15 +112,22 @@ func init() {
|
|||||||
copy(rct4, buf.Bytes())
|
copy(rct4, buf.Bytes())
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
|
|
||||||
|
rcts.EncodeIndex(4, buf)
|
||||||
|
rct5 = make([]byte, buf.Len())
|
||||||
|
copy(rct5, buf.Bytes())
|
||||||
|
buf.Reset()
|
||||||
|
|
||||||
headerCID, _ = ipld.RawdataToCid(ipld.MEthHeader, mocks.MockHeaderRlp, multihash.KECCAK_256)
|
headerCID, _ = ipld.RawdataToCid(ipld.MEthHeader, mocks.MockHeaderRlp, multihash.KECCAK_256)
|
||||||
trx1CID, _ = ipld.RawdataToCid(ipld.MEthTx, tx1, multihash.KECCAK_256)
|
trx1CID, _ = ipld.RawdataToCid(ipld.MEthTx, tx1, multihash.KECCAK_256)
|
||||||
trx2CID, _ = ipld.RawdataToCid(ipld.MEthTx, tx2, multihash.KECCAK_256)
|
trx2CID, _ = ipld.RawdataToCid(ipld.MEthTx, tx2, multihash.KECCAK_256)
|
||||||
trx3CID, _ = ipld.RawdataToCid(ipld.MEthTx, tx3, multihash.KECCAK_256)
|
trx3CID, _ = ipld.RawdataToCid(ipld.MEthTx, tx3, multihash.KECCAK_256)
|
||||||
trx4CID, _ = ipld.RawdataToCid(ipld.MEthTx, tx4, multihash.KECCAK_256)
|
trx4CID, _ = ipld.RawdataToCid(ipld.MEthTx, tx4, multihash.KECCAK_256)
|
||||||
|
trx5CID, _ = ipld.RawdataToCid(ipld.MEthTx, tx5, multihash.KECCAK_256)
|
||||||
rct1CID, _ = ipld.RawdataToCid(ipld.MEthTxReceipt, rct1, multihash.KECCAK_256)
|
rct1CID, _ = ipld.RawdataToCid(ipld.MEthTxReceipt, rct1, multihash.KECCAK_256)
|
||||||
rct2CID, _ = ipld.RawdataToCid(ipld.MEthTxReceipt, rct2, multihash.KECCAK_256)
|
rct2CID, _ = ipld.RawdataToCid(ipld.MEthTxReceipt, rct2, multihash.KECCAK_256)
|
||||||
rct3CID, _ = ipld.RawdataToCid(ipld.MEthTxReceipt, rct3, multihash.KECCAK_256)
|
rct3CID, _ = ipld.RawdataToCid(ipld.MEthTxReceipt, rct3, multihash.KECCAK_256)
|
||||||
rct4CID, _ = ipld.RawdataToCid(ipld.MEthTxReceipt, rct4, multihash.KECCAK_256)
|
rct4CID, _ = ipld.RawdataToCid(ipld.MEthTxReceipt, rct4, multihash.KECCAK_256)
|
||||||
|
rct5CID, _ = ipld.RawdataToCid(ipld.MEthTxReceipt, rct5, multihash.KECCAK_256)
|
||||||
state1CID, _ = ipld.RawdataToCid(ipld.MEthStateTrie, mocks.ContractLeafNode, multihash.KECCAK_256)
|
state1CID, _ = ipld.RawdataToCid(ipld.MEthStateTrie, mocks.ContractLeafNode, multihash.KECCAK_256)
|
||||||
state2CID, _ = ipld.RawdataToCid(ipld.MEthStateTrie, mocks.AccountLeafNode, multihash.KECCAK_256)
|
state2CID, _ = ipld.RawdataToCid(ipld.MEthStateTrie, mocks.AccountLeafNode, multihash.KECCAK_256)
|
||||||
storageCID, _ = ipld.RawdataToCid(ipld.MEthStorageTrie, mocks.StorageLeafNode, multihash.KECCAK_256)
|
storageCID, _ = ipld.RawdataToCid(ipld.MEthStorageTrie, mocks.StorageLeafNode, multihash.KECCAK_256)
|
||||||
@ -127,7 +138,7 @@ func setup(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
ind = indexer.NewStateDiffIndexer(params.MainnetChainConfig, db)
|
ind = indexer.NewStateDiffIndexer(mocks.TestConfig, db)
|
||||||
var tx *indexer.BlockTx
|
var tx *indexer.BlockTx
|
||||||
tx, err = ind.PushBlock(
|
tx, err = ind.PushBlock(
|
||||||
mockBlock,
|
mockBlock,
|
||||||
@ -198,11 +209,12 @@ func TestPublishAndIndexer(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
shared.ExpectEqual(t, len(trxs), 4)
|
shared.ExpectEqual(t, len(trxs), 5)
|
||||||
expectTrue(t, shared.ListContainsString(trxs, trx1CID.String()))
|
expectTrue(t, shared.ListContainsString(trxs, trx1CID.String()))
|
||||||
expectTrue(t, shared.ListContainsString(trxs, trx2CID.String()))
|
expectTrue(t, shared.ListContainsString(trxs, trx2CID.String()))
|
||||||
expectTrue(t, shared.ListContainsString(trxs, trx3CID.String()))
|
expectTrue(t, shared.ListContainsString(trxs, trx3CID.String()))
|
||||||
expectTrue(t, shared.ListContainsString(trxs, trx4CID.String()))
|
expectTrue(t, shared.ListContainsString(trxs, trx4CID.String()))
|
||||||
|
expectTrue(t, shared.ListContainsString(trxs, trx5CID.String()))
|
||||||
// and published
|
// and published
|
||||||
for _, c := range trxs {
|
for _, c := range trxs {
|
||||||
dc, err := cid.Decode(c)
|
dc, err := cid.Decode(c)
|
||||||
@ -281,6 +293,17 @@ func TestPublishAndIndexer(t *testing.T) {
|
|||||||
}
|
}
|
||||||
shared.ExpectEqual(t, model1, mocks.AccessListEntry1Model)
|
shared.ExpectEqual(t, model1, mocks.AccessListEntry1Model)
|
||||||
shared.ExpectEqual(t, model2, mocks.AccessListEntry2Model)
|
shared.ExpectEqual(t, model2, mocks.AccessListEntry2Model)
|
||||||
|
case trx5CID.String():
|
||||||
|
shared.ExpectEqual(t, data, tx5)
|
||||||
|
var txType *uint8
|
||||||
|
pgStr = `SELECT tx_type FROM eth.transaction_cids WHERE cid = $1`
|
||||||
|
err = db.Get(&txType, pgStr, c)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if *txType != types.DynamicFeeTxType {
|
||||||
|
t.Fatalf("expected DynamicFeeTxType (2), got %d", *txType)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -298,11 +321,12 @@ func TestPublishAndIndexer(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
shared.ExpectEqual(t, len(rcts), 4)
|
shared.ExpectEqual(t, len(rcts), 5)
|
||||||
expectTrue(t, shared.ListContainsString(rcts, rct1CID.String()))
|
expectTrue(t, shared.ListContainsString(rcts, rct1CID.String()))
|
||||||
expectTrue(t, shared.ListContainsString(rcts, rct2CID.String()))
|
expectTrue(t, shared.ListContainsString(rcts, rct2CID.String()))
|
||||||
expectTrue(t, shared.ListContainsString(rcts, rct3CID.String()))
|
expectTrue(t, shared.ListContainsString(rcts, rct3CID.String()))
|
||||||
expectTrue(t, shared.ListContainsString(rcts, rct4CID.String()))
|
expectTrue(t, shared.ListContainsString(rcts, rct4CID.String()))
|
||||||
|
expectTrue(t, shared.ListContainsString(rcts, rct5CID.String()))
|
||||||
// and published
|
// and published
|
||||||
for _, c := range rcts {
|
for _, c := range rcts {
|
||||||
dc, err := cid.Decode(c)
|
dc, err := cid.Decode(c)
|
||||||
@ -353,6 +377,15 @@ func TestPublishAndIndexer(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
shared.ExpectEqual(t, postState, mocks.ExpectedPostState3)
|
shared.ExpectEqual(t, postState, mocks.ExpectedPostState3)
|
||||||
|
case rct5CID.String():
|
||||||
|
shared.ExpectEqual(t, data, rct5)
|
||||||
|
var postState string
|
||||||
|
pgStr = `SELECT post_state FROM eth.receipt_cids WHERE cid = $1`
|
||||||
|
err = db.Get(&postState, pgStr, c)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
shared.ExpectEqual(t, postState, mocks.ExpectedPostState3)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -40,7 +40,9 @@ import (
|
|||||||
// Test variables
|
// Test variables
|
||||||
var (
|
var (
|
||||||
// block data
|
// block data
|
||||||
BlockNumber = big.NewInt(12244001)
|
// TODO: Update this to `MainnetChainConfig` when `LondonBlock` is added
|
||||||
|
TestConfig = params.RinkebyChainConfig
|
||||||
|
BlockNumber = TestConfig.LondonBlock
|
||||||
MockHeader = types.Header{
|
MockHeader = types.Header{
|
||||||
Time: 0,
|
Time: 0,
|
||||||
Number: new(big.Int).Set(BlockNumber),
|
Number: new(big.Int).Set(BlockNumber),
|
||||||
@ -49,6 +51,7 @@ var (
|
|||||||
ReceiptHash: common.HexToHash("0x0"),
|
ReceiptHash: common.HexToHash("0x0"),
|
||||||
Difficulty: big.NewInt(5000000),
|
Difficulty: big.NewInt(5000000),
|
||||||
Extra: []byte{},
|
Extra: []byte{},
|
||||||
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||||
}
|
}
|
||||||
MockTransactions, MockReceipts, SenderAddr = createTransactionsAndReceipts()
|
MockTransactions, MockReceipts, SenderAddr = createTransactionsAndReceipts()
|
||||||
MockBlock = types.NewBlock(&MockHeader, MockTransactions, nil, MockReceipts, new(trie.Trie))
|
MockBlock = types.NewBlock(&MockHeader, MockTransactions, nil, MockReceipts, new(trie.Trie))
|
||||||
@ -186,7 +189,7 @@ func createTransactionsAndReceipts() (types.Transactions, types.Receipts, common
|
|||||||
trx2 := types.NewTransaction(1, AnotherAddress, big.NewInt(2000), 100, big.NewInt(200), []byte{})
|
trx2 := types.NewTransaction(1, AnotherAddress, big.NewInt(2000), 100, big.NewInt(200), []byte{})
|
||||||
trx3 := types.NewContractCreation(2, big.NewInt(1500), 75, big.NewInt(150), MockContractByteCode)
|
trx3 := types.NewContractCreation(2, big.NewInt(1500), 75, big.NewInt(150), MockContractByteCode)
|
||||||
trx4 := types.NewTx(&types.AccessListTx{
|
trx4 := types.NewTx(&types.AccessListTx{
|
||||||
ChainID: big.NewInt(1),
|
ChainID: TestConfig.ChainID,
|
||||||
Nonce: 0,
|
Nonce: 0,
|
||||||
GasPrice: big.NewInt(100),
|
GasPrice: big.NewInt(100),
|
||||||
Gas: 50,
|
Gas: 50,
|
||||||
@ -198,8 +201,22 @@ func createTransactionsAndReceipts() (types.Transactions, types.Receipts, common
|
|||||||
AccessListEntry2,
|
AccessListEntry2,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
trx5 := types.NewTx(&types.DynamicFeeTx{
|
||||||
|
ChainID: TestConfig.ChainID,
|
||||||
|
Nonce: 0,
|
||||||
|
GasTipCap: big.NewInt(100),
|
||||||
|
GasFeeCap: big.NewInt(100),
|
||||||
|
Gas: 50,
|
||||||
|
To: &AnotherAddress,
|
||||||
|
Value: big.NewInt(1000),
|
||||||
|
Data: []byte{},
|
||||||
|
AccessList: types.AccessList{
|
||||||
|
AccessListEntry1,
|
||||||
|
AccessListEntry2,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
transactionSigner := types.NewEIP2930Signer(params.MainnetChainConfig.ChainID)
|
transactionSigner := types.MakeSigner(TestConfig, BlockNumber)
|
||||||
mockCurve := elliptic.P256()
|
mockCurve := elliptic.P256()
|
||||||
mockPrvKey, err := ecdsa.GenerateKey(mockCurve, rand.Reader)
|
mockPrvKey, err := ecdsa.GenerateKey(mockCurve, rand.Reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -219,13 +236,18 @@ func createTransactionsAndReceipts() (types.Transactions, types.Receipts, common
|
|||||||
}
|
}
|
||||||
signedTrx4, err := types.SignTx(trx4, transactionSigner, mockPrvKey)
|
signedTrx4, err := types.SignTx(trx4, transactionSigner, mockPrvKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
println(err.Error())
|
|
||||||
log.Crit(err.Error())
|
log.Crit(err.Error())
|
||||||
}
|
}
|
||||||
|
signedTrx5, err := types.SignTx(trx5, transactionSigner, mockPrvKey)
|
||||||
|
if err != nil {
|
||||||
|
log.Crit(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
senderAddr, err := types.Sender(transactionSigner, signedTrx1) // same for both trx
|
senderAddr, err := types.Sender(transactionSigner, signedTrx1) // same for both trx
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Crit(err.Error())
|
log.Crit(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// make receipts
|
// make receipts
|
||||||
mockReceipt1 := types.NewReceipt(nil, false, 50)
|
mockReceipt1 := types.NewReceipt(nil, false, 50)
|
||||||
mockReceipt1.Logs = []*types.Log{MockLog1}
|
mockReceipt1.Logs = []*types.Log{MockLog1}
|
||||||
@ -244,6 +266,14 @@ func createTransactionsAndReceipts() (types.Transactions, types.Receipts, common
|
|||||||
Logs: []*types.Log{},
|
Logs: []*types.Log{},
|
||||||
TxHash: signedTrx4.Hash(),
|
TxHash: signedTrx4.Hash(),
|
||||||
}
|
}
|
||||||
|
mockReceipt5 := &types.Receipt{
|
||||||
return types.Transactions{signedTrx1, signedTrx2, signedTrx3, signedTrx4}, types.Receipts{mockReceipt1, mockReceipt2, mockReceipt3, mockReceipt4}, senderAddr
|
Type: types.DynamicFeeTxType,
|
||||||
|
PostState: common.HexToHash("0x3").Bytes(),
|
||||||
|
Status: types.ReceiptStatusSuccessful,
|
||||||
|
CumulativeGasUsed: 175,
|
||||||
|
Logs: []*types.Log{},
|
||||||
|
TxHash: signedTrx5.Hash(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return types.Transactions{signedTrx1, signedTrx2, signedTrx3, signedTrx4, signedTrx5}, types.Receipts{mockReceipt1, mockReceipt2, mockReceipt3, mockReceipt4, mockReceipt5}, senderAddr
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,7 @@ type HeaderModel struct {
|
|||||||
Bloom []byte `db:"bloom"`
|
Bloom []byte `db:"bloom"`
|
||||||
Timestamp uint64 `db:"timestamp"`
|
Timestamp uint64 `db:"timestamp"`
|
||||||
TimesValidated int64 `db:"times_validated"`
|
TimesValidated int64 `db:"times_validated"`
|
||||||
|
BaseFee string `db:"base_fee"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// UncleModel is the db model for eth.uncle_cids
|
// UncleModel is the db model for eth.uncle_cids
|
||||||
@ -63,7 +64,7 @@ type TxModel struct {
|
|||||||
Type *uint8 `db:"tx_type"`
|
Type *uint8 `db:"tx_type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AccessListEntryModel is the db model for eth.access_list_entry
|
// AccessListElementModel is the db model for eth.access_list_entry
|
||||||
type AccessListElementModel struct {
|
type AccessListElementModel struct {
|
||||||
ID int64 `db:"id"`
|
ID int64 `db:"id"`
|
||||||
Index int64 `db:"index"`
|
Index int64 `db:"index"`
|
||||||
|
@ -44,12 +44,12 @@ func NewPostgresCIDWriter(db *postgres.DB) *PostgresCIDWriter {
|
|||||||
|
|
||||||
func (in *PostgresCIDWriter) upsertHeaderCID(tx *sqlx.Tx, header models.HeaderModel) (int64, error) {
|
func (in *PostgresCIDWriter) upsertHeaderCID(tx *sqlx.Tx, header models.HeaderModel) (int64, error) {
|
||||||
var headerID int64
|
var headerID int64
|
||||||
err := tx.QueryRowx(`INSERT INTO eth.header_cids (block_number, block_hash, parent_hash, cid, td, node_id, reward, state_root, tx_root, receipt_root, uncle_root, bloom, timestamp, mh_key, times_validated)
|
err := tx.QueryRowx(`INSERT INTO eth.header_cids (block_number, block_hash, parent_hash, cid, td, node_id, reward, state_root, tx_root, receipt_root, uncle_root, bloom, timestamp, mh_key, times_validated, base_fee)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)
|
||||||
ON CONFLICT (block_number, block_hash) DO UPDATE SET (parent_hash, cid, td, node_id, reward, state_root, tx_root, receipt_root, uncle_root, bloom, timestamp, mh_key, times_validated) = ($3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, eth.header_cids.times_validated + 1)
|
ON CONFLICT (block_number, block_hash) DO UPDATE SET (parent_hash, cid, td, node_id, reward, state_root, tx_root, receipt_root, uncle_root, bloom, timestamp, mh_key, times_validated, base_fee) = ($3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, eth.header_cids.times_validated + 1, $16)
|
||||||
RETURNING id`,
|
RETURNING id`,
|
||||||
header.BlockNumber, header.BlockHash, header.ParentHash, header.CID, header.TotalDifficulty, in.db.NodeID, header.Reward, header.StateRoot, header.TxRoot,
|
header.BlockNumber, header.BlockHash, header.ParentHash, header.CID, header.TotalDifficulty, in.db.NodeID, header.Reward, header.StateRoot, header.TxRoot,
|
||||||
header.RctRoot, header.UncleRoot, header.Bloom, header.Timestamp, header.MhKey, 1).Scan(&headerID)
|
header.RctRoot, header.UncleRoot, header.Bloom, header.Timestamp, header.MhKey, 1, header.BaseFee).Scan(&headerID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, fmt.Errorf("error upserting header_cids entry: %v", err)
|
return 0, fmt.Errorf("error upserting header_cids entry: %v", err)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user