Commit 95dd5008 authored by Ethen Pociask's avatar Ethen Pociask

[indexer.api.supply_view] feat(indexer) Obervability for Bridge Supplies

parent 8644925c
...@@ -43,6 +43,8 @@ const ( ...@@ -43,6 +43,8 @@ const (
HealthPath = "/healthz" HealthPath = "/healthz"
DepositsPath = "/api/v0/deposits/" DepositsPath = "/api/v0/deposits/"
WithdrawalsPath = "/api/v0/withdrawals/" WithdrawalsPath = "/api/v0/withdrawals/"
SupplyPath = "/api/v0/supply"
) )
// chiMetricsMiddleware ... Injects a metrics recorder into request processing middleware // chiMetricsMiddleware ... Injects a metrics recorder into request processing middleware
...@@ -70,6 +72,8 @@ func NewApi(logger log.Logger, bv database.BridgeTransfersView, serverConfig con ...@@ -70,6 +72,8 @@ func NewApi(logger log.Logger, bv database.BridgeTransfersView, serverConfig con
apiRouter.Get(fmt.Sprintf(DepositsPath+addressParam, ethereumAddressRegex), h.L1DepositsHandler) apiRouter.Get(fmt.Sprintf(DepositsPath+addressParam, ethereumAddressRegex), h.L1DepositsHandler)
apiRouter.Get(fmt.Sprintf(WithdrawalsPath+addressParam, ethereumAddressRegex), h.L2WithdrawalsHandler) apiRouter.Get(fmt.Sprintf(WithdrawalsPath+addressParam, ethereumAddressRegex), h.L2WithdrawalsHandler)
apiRouter.Get(SupplyPath, h.SupplyView)
return &API{log: logger, router: apiRouter, metricsRegistry: mr, serverConfig: serverConfig, metricsConfig: metricsConfig} return &API{log: logger, router: apiRouter, metricsRegistry: mr, serverConfig: serverConfig, metricsConfig: metricsConfig}
} }
......
...@@ -93,6 +93,11 @@ func (mbv *MockBridgeTransfersView) L2BridgeWithdrawalsByAddress(address common. ...@@ -93,6 +93,11 @@ func (mbv *MockBridgeTransfersView) L2BridgeWithdrawalsByAddress(address common.
}, },
}, nil }, nil
} }
func (mbv *MockBridgeTransfersView) L1BridgeDepositSum() (float64, error) {
return 69, nil
}
func TestHealthz(t *testing.T) { func TestHealthz(t *testing.T) {
logger := testlog.Logger(t, log.LvlInfo) logger := testlog.Logger(t, log.LvlInfo)
api := NewApi(logger, &MockBridgeTransfersView{}, apiConfig, metricsConfig) api := NewApi(logger, &MockBridgeTransfersView{}, apiConfig, metricsConfig)
......
...@@ -43,3 +43,7 @@ type WithdrawalResponse struct { ...@@ -43,3 +43,7 @@ type WithdrawalResponse struct {
HasNextPage bool `json:"hasNextPage"` HasNextPage bool `json:"hasNextPage"`
Items []WithdrawalItem `json:"items"` Items []WithdrawalItem `json:"items"`
} }
type BridgeSupplyView struct {
L1DepositSum float64 `json:"l1DepositSum"`
}
...@@ -76,3 +76,28 @@ func (h Routes) L1DepositsHandler(w http.ResponseWriter, r *http.Request) { ...@@ -76,3 +76,28 @@ func (h Routes) L1DepositsHandler(w http.ResponseWriter, r *http.Request) {
h.logger.Error("Error writing response", "err", err) h.logger.Error("Error writing response", "err", err)
} }
} }
// SupplyView ... Handles /api/v0/supply GET requests
func (h Routes) SupplyView(w http.ResponseWriter, r *http.Request) {
sum, err := h.view.L1BridgeDepositSum()
if err != nil {
http.Error(w, "Internal server error reading deposits", http.StatusInternalServerError)
h.logger.Error("Unable to read deposits from DB", "err", err.Error())
return
}
view := models.BridgeSupplyView{
L1DepositSum: sum,
}
// TODO - Add support for:
// - L2DepositSum
// - L1WithdrawalProvenSum
// - L1WithdrawalFinalizedSum
// - L2WithdrawalInitiatedSum
err = jsonResponse(w, view, http.StatusOK)
if err != nil {
h.logger.Error("Error writing response", "err", err)
}
}
...@@ -23,6 +23,7 @@ const ( ...@@ -23,6 +23,7 @@ const (
healthz = "get_health" healthz = "get_health"
deposits = "get_deposits" deposits = "get_deposits"
withdrawals = "get_withdrawals" withdrawals = "get_withdrawals"
sum = "get_sum"
) )
// Option ... Provides configuration through callback injection // Option ... Provides configuration through callback injection
...@@ -164,6 +165,25 @@ func (c *Client) GetAllDepositsByAddress(l1Address common.Address) ([]models.Dep ...@@ -164,6 +165,25 @@ func (c *Client) GetAllDepositsByAddress(l1Address common.Address) ([]models.Dep
} }
// GetSupplyAssessment ... Returns an assessment of the current supply
// on both L1 and L2. This includes the individual sums of
// (L1/L2) deposits and withdrawals
func (c *Client) GetSupplyAssessment() (*models.BridgeSupplyView, error) {
url := c.cfg.BaseURL + api.SupplyPath
resp, err := c.doRecordRequest(sum, url)
if err != nil {
return nil, err
}
var bsv *models.BridgeSupplyView
if err := json.Unmarshal(resp, &bsv); err != nil {
return nil, err
}
return bsv, nil
}
// GetAllWithdrawalsByAddress ... Gets all withdrawals provided a L2 address // GetAllWithdrawalsByAddress ... Gets all withdrawals provided a L2 address
func (c *Client) GetAllWithdrawalsByAddress(l2Address common.Address) ([]models.WithdrawalItem, error) { func (c *Client) GetAllWithdrawalsByAddress(l2Address common.Address) ([]models.WithdrawalItem, error) {
var withdrawals []models.WithdrawalItem var withdrawals []models.WithdrawalItem
......
...@@ -59,6 +59,7 @@ type L2BridgeWithdrawalWithTransactionHashes struct { ...@@ -59,6 +59,7 @@ type L2BridgeWithdrawalWithTransactionHashes struct {
type BridgeTransfersView interface { type BridgeTransfersView interface {
L1BridgeDeposit(common.Hash) (*L1BridgeDeposit, error) L1BridgeDeposit(common.Hash) (*L1BridgeDeposit, error)
L1BridgeDepositSum() (float64, error)
L1BridgeDepositWithFilter(BridgeTransfer) (*L1BridgeDeposit, error) L1BridgeDepositWithFilter(BridgeTransfer) (*L1BridgeDeposit, error)
L1BridgeDepositsByAddress(common.Address, string, int) (*L1BridgeDepositsResponse, error) L1BridgeDepositsByAddress(common.Address, string, int) (*L1BridgeDepositsResponse, error)
...@@ -128,6 +129,18 @@ type L1BridgeDepositsResponse struct { ...@@ -128,6 +129,18 @@ type L1BridgeDepositsResponse struct {
HasNextPage bool HasNextPage bool
} }
// L1BridgeDepositSum ... returns the sum of all l1 bridge deposit mints in gwei
func (db *bridgeTransfersDB) L1BridgeDepositSum() (float64, error) {
// (1) Fetch the sum of all deposits in gwei
var sum float64
result := db.gorm.Model(&L1TransactionDeposit{}).Select("sum(amount)").Scan(&sum)
if result.Error != nil {
return 0, result.Error
}
return sum, nil
}
// L1BridgeDepositsByAddress retrieves a list of deposits initiated by the specified address, // L1BridgeDepositsByAddress retrieves a list of deposits initiated by the specified address,
// coupled with the L1/L2 transaction hashes that complete the bridge transaction. // coupled with the L1/L2 transaction hashes that complete the bridge transaction.
func (db *bridgeTransfersDB) L1BridgeDepositsByAddress(address common.Address, cursor string, limit int) (*L1BridgeDepositsResponse, error) { func (db *bridgeTransfersDB) L1BridgeDepositsByAddress(address common.Address, cursor string, limit int) (*L1BridgeDepositsResponse, error) {
......
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/ethereum-optimism/optimism/indexer/bigint"
e2etest_utils "github.com/ethereum-optimism/optimism/indexer/e2e_tests/utils" e2etest_utils "github.com/ethereum-optimism/optimism/indexer/e2e_tests/utils"
op_e2e "github.com/ethereum-optimism/optimism/op-e2e" op_e2e "github.com/ethereum-optimism/optimism/op-e2e"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/transactions" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/transactions"
...@@ -447,7 +448,7 @@ func TestE2EBridgeTransfersCursoredWithdrawals(t *testing.T) { ...@@ -447,7 +448,7 @@ func TestE2EBridgeTransfersCursoredWithdrawals(t *testing.T) {
} }
} }
func TestClientGetWithdrawals(t *testing.T) { func TestClientBridgeFunctions(t *testing.T) {
testSuite := createE2ETestSuite(t) testSuite := createE2ETestSuite(t)
// (1) Generate contract bindings for the L1 and L2 standard bridges // (1) Generate contract bindings for the L1 and L2 standard bridges
...@@ -459,12 +460,14 @@ func TestClientGetWithdrawals(t *testing.T) { ...@@ -459,12 +460,14 @@ func TestClientGetWithdrawals(t *testing.T) {
// (2) Create test actors that will deposit and withdraw using the standard bridge // (2) Create test actors that will deposit and withdraw using the standard bridge
aliceAddr := testSuite.OpCfg.Secrets.Addresses().Alice aliceAddr := testSuite.OpCfg.Secrets.Addresses().Alice
bobAddr := testSuite.OpCfg.Secrets.Addresses().Bob bobAddr := testSuite.OpCfg.Secrets.Addresses().Bob
malAddr := testSuite.OpCfg.Secrets.Addresses().Mallory
type actor struct { type actor struct {
addr common.Address addr common.Address
priv *ecdsa.PrivateKey priv *ecdsa.PrivateKey
} }
mintSum := bigint.Zero
actors := []actor{ actors := []actor{
{ {
addr: aliceAddr, addr: aliceAddr,
...@@ -474,6 +477,10 @@ func TestClientGetWithdrawals(t *testing.T) { ...@@ -474,6 +477,10 @@ func TestClientGetWithdrawals(t *testing.T) {
addr: bobAddr, addr: bobAddr,
priv: testSuite.OpCfg.Secrets.Bob, priv: testSuite.OpCfg.Secrets.Bob,
}, },
{
addr: malAddr,
priv: testSuite.OpCfg.Secrets.Mallory,
},
} }
// (3) Iterate over each actor and deposit / withdraw // (3) Iterate over each actor and deposit / withdraw
...@@ -491,6 +498,8 @@ func TestClientGetWithdrawals(t *testing.T) { ...@@ -491,6 +498,8 @@ func TestClientGetWithdrawals(t *testing.T) {
_, err = wait.ForReceiptOK(context.Background(), testSuite.L1Client, depositTx.Hash()) _, err = wait.ForReceiptOK(context.Background(), testSuite.L1Client, depositTx.Hash())
require.NoError(t, err) require.NoError(t, err)
mintSum = new(big.Int).Add(mintSum, depositTx.Value())
// (3.b) Initiate withdrawal transaction via L2ToL1MessagePasser contract // (3.b) Initiate withdrawal transaction via L2ToL1MessagePasser contract
l2ToL1MessagePasserWithdrawTx, err := l2ToL1MessagePasser.Receive(l2Opts) l2ToL1MessagePasserWithdrawTx, err := l2ToL1MessagePasser.Receive(l2Opts)
require.NoError(t, err) require.NoError(t, err)
...@@ -513,6 +522,14 @@ func TestClientGetWithdrawals(t *testing.T) { ...@@ -513,6 +522,14 @@ func TestClientGetWithdrawals(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Len(t, withdrawals, 1) require.Len(t, withdrawals, 1)
require.Equal(t, l2ToL1MessagePasserWithdrawTx.Hash().String(), withdrawals[0].TransactionHash) require.Equal(t, l2ToL1MessagePasserWithdrawTx.Hash().String(), withdrawals[0].TransactionHash)
} }
// (4) Ensure that supply assessment is correct
assessment, err := testSuite.Client.GetSupplyAssessment()
require.NoError(t, err)
asFloat, _ := mintSum.Float64()
require.Equal(t, asFloat, assessment.L1DepositSum)
} }
...@@ -94,7 +94,7 @@ CREATE TABLE IF NOT EXISTS l1_transaction_deposits ( ...@@ -94,7 +94,7 @@ CREATE TABLE IF NOT EXISTS l1_transaction_deposits (
-- transaction data. NOTE: `to_address` is the recipient of funds transferred in value field of the -- transaction data. NOTE: `to_address` is the recipient of funds transferred in value field of the
-- L2 deposit transaction and not the amount minted on L1 from the source address. Hence the `amount` -- L2 deposit transaction and not the amount minted on L1 from the source address. Hence the `amount`
-- column in this table does NOT indiciate the amount transferred to the recipient but instead funds -- column in this table does NOT indicate the amount transferred to the recipient but instead funds
-- bridged from L1 into `from_address`. -- bridged from L1 into `from_address`.
from_address VARCHAR NOT NULL, from_address VARCHAR NOT NULL,
to_address VARCHAR NOT NULL, to_address VARCHAR NOT NULL,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment