Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
N
nebula
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
exchain
nebula
Commits
f4f3054a
Unverified
Commit
f4f3054a
authored
Mar 03, 2022
by
Conner Fromknecht
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: add teleportr API server
parent
bced4fa9
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
822 additions
and
1 deletion
+822
-1
mighty-sloths-float.md
.changeset/mighty-sloths-float.md
+5
-0
Makefile
go/teleportr/Makefile
+5
-0
metrics.go
go/teleportr/api/metrics.go
+43
-0
server.go
go/teleportr/api/server.go
+626
-0
main.go
go/teleportr/cmd/teleportr-api/main.go
+42
-0
api_flags.go
go/teleportr/flags/api_flags.go
+97
-0
go.mod
go/teleportr/go.mod
+3
-1
go.sum
go/teleportr/go.sum
+1
-0
No files found.
.changeset/mighty-sloths-float.md
0 → 100644
View file @
f4f3054a
---
'
@eth-optimism/teleportr'
:
patch
---
Add teleportr API server
go/teleportr/Makefile
View file @
f4f3054a
...
@@ -13,8 +13,12 @@ DISBURSER_ARTIFACT := ../../packages/contracts/artifacts/contracts/L2/teleportr/
...
@@ -13,8 +13,12 @@ DISBURSER_ARTIFACT := ../../packages/contracts/artifacts/contracts/L2/teleportr/
teleportr
:
teleportr
:
env
GO111MODULE
=
on go build
-v
$(LDFLAGS)
./cmd/teleportr
env
GO111MODULE
=
on go build
-v
$(LDFLAGS)
./cmd/teleportr
teleportr-api
:
env
GO111MODULE
=
on go build
-v
$(LDFLAGS)
./cmd/teleportr-api
clean
:
clean
:
rm
teleportr
rm
teleportr
rm
api
test
:
test
:
go
test
-v
./...
go
test
-v
./...
...
@@ -48,6 +52,7 @@ bindings-disburser:
...
@@ -48,6 +52,7 @@ bindings-disburser:
.PHONY
:
\
.PHONY
:
\
teleportr
\
teleportr
\
teleportr-api
\
bindings
\
bindings
\
bindings-deposit
\
bindings-deposit
\
bindings-disburser
\
bindings-disburser
\
...
...
go/teleportr/api/metrics.go
0 → 100644
View file @
f4f3054a
package
api
import
(
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)
const
TeleportrAPINamespace
=
"teleportr_api"
var
(
rpcRequestsTotal
=
promauto
.
NewCounter
(
prometheus
.
CounterOpts
{
Namespace
:
TeleportrAPINamespace
,
Name
:
"rpc_requests_total"
,
Help
:
"Count of total client RPC requests."
,
})
httpResponseCodesTotal
=
promauto
.
NewCounterVec
(
prometheus
.
CounterOpts
{
Namespace
:
TeleportrAPINamespace
,
Name
:
"http_response_codes_total"
,
Help
:
"Count of total HTTP response codes."
,
},
[]
string
{
"status_code"
,
})
httpRequestDurationSumm
=
promauto
.
NewSummary
(
prometheus
.
SummaryOpts
{
Namespace
:
TeleportrAPINamespace
,
Name
:
"http_request_duration_seconds"
,
Help
:
"Summary of HTTP request durations, in seconds."
,
Objectives
:
map
[
float64
]
float64
{
0.5
:
0.05
,
0.9
:
0.01
,
0.95
:
0.005
,
0.99
:
0.001
},
})
databaseErrorsTotal
=
promauto
.
NewCounterVec
(
prometheus
.
CounterOpts
{
Namespace
:
TeleportrAPINamespace
,
Name
:
"database_errors_total"
,
Help
:
"Count of total database failures."
,
},
[]
string
{
"method"
,
})
rpcErrorsTotal
=
promauto
.
NewCounterVec
(
prometheus
.
CounterOpts
{
Namespace
:
TeleportrAPINamespace
,
Name
:
"rpc_errors_total"
,
Help
:
"Count of total L1 rpc failures."
,
},
[]
string
{
"method"
,
})
)
go/teleportr/api/server.go
0 → 100644
View file @
f4f3054a
package
api
import
(
"context"
"encoding/json"
"errors"
"fmt"
"math/big"
"net"
"net/http"
"os"
"os/signal"
"strconv"
"sync"
"syscall"
"time"
bsscore
"github.com/ethereum-optimism/optimism/go/bss-core"
"github.com/ethereum-optimism/optimism/go/bss-core/dial"
"github.com/ethereum-optimism/optimism/go/bss-core/txmgr"
"github.com/ethereum-optimism/optimism/go/teleportr/bindings/deposit"
"github.com/ethereum-optimism/optimism/go/teleportr/db"
"github.com/ethereum-optimism/optimism/go/teleportr/flags"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
"github.com/google/uuid"
"github.com/gorilla/mux"
"github.com/prometheus/client_golang/prometheus"
"github.com/rs/cors"
"github.com/urfave/cli"
)
type
ContextKey
string
const
(
ContextKeyReqID
ContextKey
=
"req_id"
)
func
Main
(
gitVersion
string
)
func
(
*
cli
.
Context
)
error
{
return
func
(
cliCtx
*
cli
.
Context
)
error
{
cfg
,
err
:=
NewConfig
(
cliCtx
)
if
err
!=
nil
{
return
err
}
ctx
,
cancel
:=
context
.
WithCancel
(
context
.
Background
())
defer
cancel
()
depositAddr
,
err
:=
bsscore
.
ParseAddress
(
cfg
.
DepositAddress
)
if
err
!=
nil
{
return
err
}
l1Client
,
err
:=
dial
.
L1EthClientWithTimeout
(
ctx
,
cfg
.
L1EthRpc
,
cfg
.
DisableHTTP2
,
)
if
err
!=
nil
{
return
err
}
defer
l1Client
.
Close
()
depositContract
,
err
:=
deposit
.
NewTeleportrDeposit
(
depositAddr
,
l1Client
,
)
if
err
!=
nil
{
return
err
}
// TODO(conner): make read-only
database
,
err
:=
db
.
Open
(
db
.
Config
{
Host
:
cfg
.
PostgresHost
,
Port
:
uint16
(
cfg
.
PostgresPort
),
User
:
cfg
.
PostgresUser
,
Password
:
cfg
.
PostgresPassword
,
DBName
:
cfg
.
PostgresDBName
,
EnableSSL
:
cfg
.
PostgresEnableSSL
,
})
if
err
!=
nil
{
return
err
}
defer
database
.
Close
()
server
:=
NewServer
(
ctx
,
l1Client
,
database
,
depositAddr
,
depositContract
,
cfg
.
NumConfirmations
,
)
var
wg
sync
.
WaitGroup
wg
.
Add
(
1
)
go
func
()
{
defer
wg
.
Done
()
_
=
server
.
ListenAndServe
(
cfg
.
Hostname
,
cfg
.
Port
)
}()
interruptChannel
:=
make
(
chan
os
.
Signal
,
1
)
signal
.
Notify
(
interruptChannel
,
[]
os
.
Signal
{
os
.
Interrupt
,
os
.
Kill
,
syscall
.
SIGTERM
,
syscall
.
SIGQUIT
,
}
...
)
select
{
case
<-
interruptChannel
:
_
=
server
.
httpServer
.
Shutdown
(
ctx
)
time
.
AfterFunc
(
defaultTimeout
,
func
()
{
cancel
()
_
=
server
.
httpServer
.
Close
()
})
wg
.
Wait
()
case
<-
ctx
.
Done
()
:
}
return
nil
}
}
type
Config
struct
{
Hostname
string
Port
uint16
L1EthRpc
string
DepositAddress
string
NumConfirmations
uint64
PostgresHost
string
PostgresPort
uint16
PostgresUser
string
PostgresPassword
string
PostgresDBName
string
PostgresEnableSSL
bool
DisableHTTP2
bool
}
func
NewConfig
(
ctx
*
cli
.
Context
)
(
Config
,
error
)
{
return
Config
{
Hostname
:
ctx
.
GlobalString
(
flags
.
APIHostnameFlag
.
Name
),
Port
:
uint16
(
ctx
.
GlobalUint64
(
flags
.
APIPortFlag
.
Name
)),
L1EthRpc
:
ctx
.
GlobalString
(
flags
.
APIL1EthRpcFlag
.
Name
),
DepositAddress
:
ctx
.
GlobalString
(
flags
.
APIDepositAddressFlag
.
Name
),
NumConfirmations
:
ctx
.
GlobalUint64
(
flags
.
APINumConfirmationsFlag
.
Name
),
PostgresHost
:
ctx
.
GlobalString
(
flags
.
APIPostgresHostFlag
.
Name
),
PostgresPort
:
uint16
(
ctx
.
GlobalUint64
(
flags
.
APIPostgresPortFlag
.
Name
)),
PostgresUser
:
ctx
.
GlobalString
(
flags
.
APIPostgresUserFlag
.
Name
),
PostgresPassword
:
ctx
.
GlobalString
(
flags
.
APIPostgresPasswordFlag
.
Name
),
PostgresDBName
:
ctx
.
GlobalString
(
flags
.
APIPostgresDBNameFlag
.
Name
),
PostgresEnableSSL
:
ctx
.
GlobalBool
(
flags
.
APIPostgresEnableSSLFlag
.
Name
),
},
nil
}
const
(
ContentTypeHeader
=
"Content-Type"
ContentTypeJSON
=
"application/json"
defaultTimeout
=
10
*
time
.
Second
)
type
Server
struct
{
ctx
context
.
Context
l1Client
*
ethclient
.
Client
database
*
db
.
Database
depositAddr
common
.
Address
depositContract
*
deposit
.
TeleportrDeposit
numConfirmations
uint64
httpServer
*
http
.
Server
}
func
NewServer
(
ctx
context
.
Context
,
l1Client
*
ethclient
.
Client
,
database
*
db
.
Database
,
depositAddr
common
.
Address
,
depositContract
*
deposit
.
TeleportrDeposit
,
numConfirmations
uint64
,
)
*
Server
{
if
numConfirmations
==
0
{
panic
(
"NumConfirmations cannot be zero"
)
}
return
&
Server
{
ctx
:
ctx
,
l1Client
:
l1Client
,
database
:
database
,
depositAddr
:
depositAddr
,
depositContract
:
depositContract
,
numConfirmations
:
numConfirmations
,
}
}
func
(
s
*
Server
)
ListenAndServe
(
host
string
,
port
uint16
)
error
{
handler
:=
mux
.
NewRouter
()
handler
.
HandleFunc
(
"/healthz"
,
HandleHealthz
)
.
Methods
(
"GET"
)
handler
.
HandleFunc
(
"/status"
,
instrumentedErrorHandler
(
s
.
HandleStatus
),
)
.
Methods
(
"GET"
)
handler
.
HandleFunc
(
"/estimate/{addr:0x[0-9a-fA-F]{40}}/{amount:[0-9]{1,80}}"
,
instrumentedErrorHandler
(
s
.
HandleEstimate
),
)
.
Methods
(
"GET"
)
handler
.
HandleFunc
(
"/track/{txhash:0x[0-9a-fA-F]{64}}"
,
instrumentedErrorHandler
(
s
.
HandleTrack
),
)
.
Methods
(
"GET"
)
handler
.
HandleFunc
(
"/history/{addr:0x[0-9a-fA-F]{40}}"
,
instrumentedErrorHandler
(
s
.
HandleHistory
),
)
.
Methods
(
"GET"
)
c
:=
cors
.
New
(
cors
.
Options
{
AllowedOrigins
:
[]
string
{
"*"
},
})
addr
:=
fmt
.
Sprintf
(
"%s:%d"
,
host
,
port
)
s
.
httpServer
=
&
http
.
Server
{
Handler
:
c
.
Handler
(
handler
),
Addr
:
addr
,
BaseContext
:
func
(
_
net
.
Listener
)
context
.
Context
{
return
s
.
ctx
},
}
log
.
Info
(
"Starting HTTP server"
,
"addr"
,
addr
)
return
s
.
httpServer
.
ListenAndServe
()
}
func
HandleHealthz
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
_
,
_
=
w
.
Write
([]
byte
(
"OK"
))
}
type
StatusResponse
struct
{
CurrentBalanceWei
string
`json:"current_balance_wei"`
MaximumBalanceWei
string
`json:"maximum_balance_wei"`
MinDepositAmountWei
string
`json:"min_deposit_amount_wei"`
MaxDepositAmountWei
string
`json:"max_deposit_amount_wei"`
IsAvailable
bool
`json:"is_available"`
}
func
(
s
*
Server
)
HandleStatus
(
ctx
context
.
Context
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
,
)
error
{
maxBalance
,
err
:=
s
.
depositContract
.
MaxBalance
(
&
bind
.
CallOpts
{
Context
:
ctx
,
})
if
err
!=
nil
{
rpcErrorsTotal
.
WithLabelValues
(
"max_balance"
)
.
Inc
()
return
err
}
minDepositAmount
,
err
:=
s
.
depositContract
.
MinDepositAmount
(
&
bind
.
CallOpts
{
Context
:
ctx
,
})
if
err
!=
nil
{
rpcErrorsTotal
.
WithLabelValues
(
"min_deposit_amount"
)
.
Inc
()
return
err
}
maxDepositAmount
,
err
:=
s
.
depositContract
.
MaxDepositAmount
(
&
bind
.
CallOpts
{
Context
:
ctx
,
})
if
err
!=
nil
{
rpcErrorsTotal
.
WithLabelValues
(
"max_deposit_amount"
)
.
Inc
()
return
err
}
curBalance
,
err
:=
s
.
l1Client
.
BalanceAt
(
ctx
,
s
.
depositAddr
,
nil
)
if
err
!=
nil
{
rpcErrorsTotal
.
WithLabelValues
(
"balance_at"
)
.
Inc
()
return
err
}
balanceAfterMaxDeposit
:=
new
(
big
.
Int
)
.
Add
(
curBalance
,
maxDepositAmount
,
)
isAvailable
:=
curBalance
.
Cmp
(
balanceAfterMaxDeposit
)
>=
0
resp
:=
StatusResponse
{
CurrentBalanceWei
:
curBalance
.
String
(),
MaximumBalanceWei
:
maxBalance
.
String
(),
MinDepositAmountWei
:
minDepositAmount
.
String
(),
MaxDepositAmountWei
:
maxDepositAmount
.
String
(),
IsAvailable
:
isAvailable
,
}
jsonResp
,
err
:=
json
.
Marshal
(
resp
)
if
err
!=
nil
{
return
err
}
w
.
Header
()
.
Set
(
ContentTypeHeader
,
ContentTypeJSON
)
_
,
err
=
w
.
Write
(
jsonResp
)
return
err
}
type
EstimateResponse
struct
{
BaseFee
string
`json:"base_fee"`
GasTipCap
string
`json:"gas_tip_cap"`
GasFeeCap
string
`json:"gas_fee_cap"`
GasEstimate
string
`json:"gas_estimate"`
}
func
(
s
*
Server
)
HandleEstimate
(
ctx
context
.
Context
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
,
)
error
{
vars
:=
mux
.
Vars
(
r
)
addressStr
,
ok
:=
vars
[
"addr"
]
if
!
ok
{
return
StatusError
{
Err
:
errors
.
New
(
"missing address parameter"
),
Code
:
http
.
StatusBadRequest
,
}
}
address
,
err
:=
bsscore
.
ParseAddress
(
addressStr
)
if
err
!=
nil
{
return
StatusError
{
Err
:
err
,
Code
:
http
.
StatusBadRequest
,
}
}
amountStr
,
ok
:=
vars
[
"amount"
]
if
!
ok
{
return
StatusError
{
Err
:
errors
.
New
(
"missing amount parameter"
),
Code
:
http
.
StatusBadRequest
,
}
}
amount
,
ok
:=
new
(
big
.
Int
)
.
SetString
(
amountStr
,
10
)
if
!
ok
{
return
StatusError
{
Err
:
errors
.
New
(
"unable to parse amount"
),
Code
:
http
.
StatusBadRequest
,
}
}
gasTipCap
,
err
:=
s
.
l1Client
.
SuggestGasTipCap
(
ctx
)
if
err
!=
nil
{
rpcErrorsTotal
.
WithLabelValues
(
"suggest_gas_tip_cap"
)
.
Inc
()
return
err
}
header
,
err
:=
s
.
l1Client
.
HeaderByNumber
(
ctx
,
nil
)
if
err
!=
nil
{
rpcErrorsTotal
.
WithLabelValues
(
"header_by_number"
)
.
Inc
()
return
err
}
gasFeeCap
:=
txmgr
.
CalcGasFeeCap
(
header
.
BaseFee
,
gasTipCap
)
gasUsed
,
err
:=
s
.
l1Client
.
EstimateGas
(
ctx
,
ethereum
.
CallMsg
{
From
:
address
,
To
:
&
s
.
depositAddr
,
Gas
:
0
,
GasFeeCap
:
gasFeeCap
,
GasTipCap
:
gasTipCap
,
Value
:
amount
,
Data
:
nil
,
})
if
err
!=
nil
{
rpcErrorsTotal
.
WithLabelValues
(
"estimate_gas"
)
.
Inc
()
return
err
}
resp
:=
EstimateResponse
{
BaseFee
:
header
.
BaseFee
.
String
(),
GasTipCap
:
gasTipCap
.
String
(),
GasFeeCap
:
gasFeeCap
.
String
(),
GasEstimate
:
new
(
big
.
Int
)
.
SetUint64
(
gasUsed
)
.
String
(),
}
jsonResp
,
err
:=
json
.
Marshal
(
resp
)
if
err
!=
nil
{
return
err
}
w
.
Header
()
.
Set
(
ContentTypeHeader
,
ContentTypeJSON
)
_
,
err
=
w
.
Write
(
jsonResp
)
return
err
}
type
RPCTeleport
struct
{
ID
string
`json:"id"`
Address
string
`json:"address"`
AmountWei
string
`json:"amount_wei"`
TxHash
string
`json:"tx_hash"`
BlockNumber
string
`json:"block_number"`
BlockTimestamp
string
`json:"block_timestamp_unix"`
Disbursement
*
RPCDisbursement
`json:"disbursement,omitempty"`
}
func
makeRPCTeleport
(
teleport
*
db
.
Teleport
)
RPCTeleport
{
rpcTeleport
:=
RPCTeleport
{
ID
:
strconv
.
FormatUint
(
teleport
.
ID
,
10
),
Address
:
teleport
.
Address
.
String
(),
AmountWei
:
teleport
.
Amount
.
String
(),
TxHash
:
teleport
.
Deposit
.
TxnHash
.
String
(),
BlockNumber
:
strconv
.
FormatUint
(
teleport
.
Deposit
.
BlockNumber
,
10
),
BlockTimestamp
:
strconv
.
FormatInt
(
teleport
.
Deposit
.
BlockTimestamp
.
Unix
(),
10
),
}
if
rpcTeleport
.
Disbursement
!=
nil
{
rpcTeleport
.
Disbursement
=
&
RPCDisbursement
{
TxHash
:
teleport
.
Disbursement
.
TxnHash
.
String
(),
BlockNumber
:
strconv
.
FormatUint
(
teleport
.
Disbursement
.
BlockNumber
,
10
),
BlockTimestamp
:
strconv
.
FormatInt
(
teleport
.
Disbursement
.
BlockTimestamp
.
Unix
(),
10
),
Success
:
teleport
.
Disbursement
.
Success
,
}
}
return
rpcTeleport
}
type
RPCDisbursement
struct
{
TxHash
string
`json:"tx_hash"`
BlockNumber
string
`json:"block_number"`
BlockTimestamp
string
`json:"block_timestamp_unix"`
Success
bool
`json:"success"`
}
type
TrackResponse
struct
{
CurrentBlockNumber
string
`json:"current_block_number"`
ConfirmationsRequired
string
`json:"confirmations_required"`
ConfirmationsRemaining
string
`json:"confirmations_remaining"`
Teleport
RPCTeleport
}
func
(
s
*
Server
)
HandleTrack
(
ctx
context
.
Context
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
,
)
error
{
vars
:=
mux
.
Vars
(
r
)
txHashStr
,
ok
:=
vars
[
"txhash"
]
if
!
ok
{
return
StatusError
{
Err
:
errors
.
New
(
"missing txhash parameter"
),
Code
:
http
.
StatusBadRequest
,
}
}
txHash
:=
common
.
HexToHash
(
txHashStr
)
blockNumber
,
err
:=
s
.
l1Client
.
BlockNumber
(
ctx
)
if
err
!=
nil
{
rpcErrorsTotal
.
WithLabelValues
(
"block_number"
)
.
Inc
()
return
err
}
teleport
,
err
:=
s
.
database
.
LoadTeleportByDepositHash
(
txHash
)
if
err
!=
nil
{
databaseErrorsTotal
.
WithLabelValues
(
"load_teleport_by_deposit_hash"
)
.
Inc
()
return
err
}
if
teleport
==
nil
{
return
StatusError
{
Code
:
http
.
StatusNotFound
,
}
}
var
confsRemaining
uint64
if
teleport
.
Deposit
.
BlockNumber
+
s
.
numConfirmations
>
blockNumber
+
1
{
confsRemaining
=
blockNumber
+
1
-
(
teleport
.
Deposit
.
BlockNumber
+
s
.
numConfirmations
)
}
resp
:=
TrackResponse
{
CurrentBlockNumber
:
strconv
.
FormatUint
(
blockNumber
,
10
),
ConfirmationsRequired
:
strconv
.
FormatUint
(
s
.
numConfirmations
,
10
),
ConfirmationsRemaining
:
strconv
.
FormatUint
(
confsRemaining
,
10
),
Teleport
:
makeRPCTeleport
(
teleport
),
}
jsonResp
,
err
:=
json
.
Marshal
(
resp
)
if
err
!=
nil
{
return
err
}
w
.
Header
()
.
Set
(
ContentTypeHeader
,
ContentTypeJSON
)
_
,
err
=
w
.
Write
(
jsonResp
)
return
err
}
type
HistoryResponse
struct
{
Teleports
[]
RPCTeleport
`json:"teleports"`
}
func
(
s
*
Server
)
HandleHistory
(
ctx
context
.
Context
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
,
)
error
{
vars
:=
mux
.
Vars
(
r
)
addrStr
,
ok
:=
vars
[
"addr"
]
if
!
ok
{
return
StatusError
{
Err
:
errors
.
New
(
"missing addr parameter"
),
Code
:
http
.
StatusBadRequest
,
}
}
addr
:=
common
.
HexToAddress
(
addrStr
)
teleports
,
err
:=
s
.
database
.
LoadTeleportsByAddress
(
addr
)
if
err
!=
nil
{
databaseErrorsTotal
.
WithLabelValues
(
"load_teleports_by_address"
)
.
Inc
()
return
err
}
rpcTeleports
:=
make
([]
RPCTeleport
,
0
,
len
(
teleports
))
for
_
,
teleport
:=
range
teleports
{
rpcTeleports
=
append
(
rpcTeleports
,
makeRPCTeleport
(
&
teleport
))
}
resp
:=
HistoryResponse
{
Teleports
:
rpcTeleports
,
}
jsonResp
,
err
:=
json
.
Marshal
(
resp
)
if
err
!=
nil
{
return
err
}
w
.
Header
()
.
Set
(
ContentTypeHeader
,
ContentTypeJSON
)
_
,
err
=
w
.
Write
(
jsonResp
)
return
err
}
type
Error
interface
{
error
Status
()
int
}
type
StatusError
struct
{
Code
int
Err
error
}
func
(
se
StatusError
)
Error
()
string
{
if
se
.
Err
!=
nil
{
msg
:=
se
.
Err
.
Error
()
if
msg
!=
""
{
return
msg
}
}
return
http
.
StatusText
(
se
.
Code
)
}
func
(
se
StatusError
)
Status
()
int
{
return
se
.
Code
}
func
instrumentedErrorHandler
(
h
func
(
context
.
Context
,
http
.
ResponseWriter
,
*
http
.
Request
)
error
,
)
func
(
http
.
ResponseWriter
,
*
http
.
Request
)
{
return
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
rpcRequestsTotal
.
Inc
()
ctx
,
cancel
:=
populateContext
(
w
,
r
)
defer
cancel
()
reqID
:=
GetReqID
(
ctx
)
log
.
Info
(
"HTTP request"
,
"req_id"
,
reqID
,
"path"
,
r
.
URL
.
Path
,
"user_agent"
,
r
.
UserAgent
())
respTimer
:=
prometheus
.
NewTimer
(
httpRequestDurationSumm
)
err
:=
h
(
ctx
,
w
,
r
)
elapsed
:=
respTimer
.
ObserveDuration
()
var
statusCode
int
switch
e
:=
err
.
(
type
)
{
case
nil
:
statusCode
=
200
log
.
Info
(
"HTTP success"
,
"req_id"
,
reqID
,
"elapsed"
,
elapsed
)
case
Error
:
statusCode
=
e
.
Status
()
log
.
Warn
(
"HTTP error"
,
"req_id"
,
reqID
,
"elapsed"
,
elapsed
,
"status"
,
statusCode
,
"err"
,
e
.
Error
())
http
.
Error
(
w
,
e
.
Error
(),
statusCode
)
default
:
statusCode
=
http
.
StatusInternalServerError
log
.
Warn
(
"HTTP internal error"
,
"req_id"
,
reqID
,
"elapsed"
,
elapsed
,
"status"
,
statusCode
,
"err"
,
err
)
http
.
Error
(
w
,
http
.
StatusText
(
statusCode
),
statusCode
)
}
httpResponseCodesTotal
.
WithLabelValues
(
strconv
.
Itoa
(
statusCode
))
.
Inc
()
}
}
func
populateContext
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
,
)
(
context
.
Context
,
func
())
{
ctx
:=
context
.
WithValue
(
r
.
Context
(),
ContextKeyReqID
,
uuid
.
NewString
())
return
context
.
WithTimeout
(
ctx
,
defaultTimeout
)
}
func
GetReqID
(
ctx
context
.
Context
)
string
{
if
reqID
,
ok
:=
ctx
.
Value
(
ContextKeyReqID
)
.
(
string
);
ok
{
return
reqID
}
return
""
}
go/teleportr/cmd/teleportr-api/main.go
0 → 100644
View file @
f4f3054a
package
main
import
(
"fmt"
"os"
"github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli"
"github.com/ethereum-optimism/optimism/go/teleportr/api"
"github.com/ethereum-optimism/optimism/go/teleportr/flags"
)
var
(
GitVersion
=
""
GitCommit
=
""
GitDate
=
""
)
func
main
()
{
// Set up logger with a default INFO level in case we fail to parse flags.
// Otherwise the final critical log won't show what the parsing error was.
log
.
Root
()
.
SetHandler
(
log
.
LvlFilterHandler
(
log
.
LvlInfo
,
log
.
StreamHandler
(
os
.
Stdout
,
log
.
TerminalFormat
(
true
)),
),
)
app
:=
cli
.
NewApp
()
app
.
Flags
=
flags
.
APIFlags
app
.
Version
=
fmt
.
Sprintf
(
"%s-%s-%s"
,
GitVersion
,
GitCommit
,
GitDate
)
app
.
Name
=
"teleportr-api"
app
.
Usage
=
"Teleportr API server"
app
.
Description
=
"API serving teleportr data"
app
.
Action
=
api
.
Main
(
GitVersion
)
err
:=
app
.
Run
(
os
.
Args
)
if
err
!=
nil
{
log
.
Crit
(
"Application failed"
,
"message"
,
err
)
}
}
go/teleportr/flags/api_flags.go
0 → 100644
View file @
f4f3054a
package
flags
import
(
"fmt"
"strings"
"github.com/urfave/cli"
)
func
prefixAPIEnvVar
(
name
string
)
string
{
return
fmt
.
Sprintf
(
"TELEPORTR_API_%s"
,
strings
.
ToUpper
(
name
))
}
var
(
APIHostnameFlag
=
cli
.
StringFlag
{
Name
:
"hostname"
,
Usage
:
"The hostname of the API server"
,
Required
:
true
,
EnvVar
:
prefixAPIEnvVar
(
"HOSTNAME"
),
}
APIPortFlag
=
cli
.
StringFlag
{
Name
:
"port"
,
Usage
:
"The hostname of the API server"
,
Required
:
true
,
EnvVar
:
prefixAPIEnvVar
(
"PORT"
),
}
APIL1EthRpcFlag
=
cli
.
StringFlag
{
Name
:
"l1-eth-rpc"
,
Usage
:
"The endpoint for the L1 ETH provider"
,
Required
:
true
,
EnvVar
:
prefixAPIEnvVar
(
"L1_ETH_RPC"
),
}
APIDepositAddressFlag
=
cli
.
StringFlag
{
Name
:
"deposit-address"
,
Usage
:
"Address of the TeleportrDeposit contract"
,
Required
:
true
,
EnvVar
:
prefixAPIEnvVar
(
"DEPOSIT_ADDRESS"
),
}
APINumConfirmationsFlag
=
cli
.
StringFlag
{
Name
:
"num-confirmations"
,
Usage
:
"Number of confirmations required until deposits are "
+
"considered confirmed"
,
Required
:
true
,
EnvVar
:
prefixAPIEnvVar
(
"NUM_CONFIRMATIONS"
),
}
APIPostgresHostFlag
=
cli
.
StringFlag
{
Name
:
"postgres-host"
,
Usage
:
"Host of the teleportr postgres instance"
,
Required
:
true
,
EnvVar
:
prefixAPIEnvVar
(
"POSTGRES_HOST"
),
}
APIPostgresPortFlag
=
cli
.
Uint64Flag
{
Name
:
"postgres-port"
,
Usage
:
"Port of the teleportr postgres instance"
,
Required
:
true
,
EnvVar
:
prefixAPIEnvVar
(
"POSTGRES_PORT"
),
}
APIPostgresUserFlag
=
cli
.
StringFlag
{
Name
:
"postgres-user"
,
Usage
:
"Username of the teleportr postgres instance"
,
Required
:
true
,
EnvVar
:
prefixAPIEnvVar
(
"POSTGRES_USER"
),
}
APIPostgresPasswordFlag
=
cli
.
StringFlag
{
Name
:
"postgres-password"
,
Usage
:
"Password of the teleportr postgres instance"
,
Required
:
true
,
EnvVar
:
prefixAPIEnvVar
(
"POSTGRES_PASSWORD"
),
}
APIPostgresDBNameFlag
=
cli
.
StringFlag
{
Name
:
"postgres-db-name"
,
Usage
:
"Database name of the teleportr postgres instance"
,
Required
:
true
,
EnvVar
:
prefixAPIEnvVar
(
"POSTGRES_DB_NAME"
),
}
APIPostgresEnableSSLFlag
=
cli
.
BoolFlag
{
Name
:
"postgres-enable-ssl"
,
Usage
:
"Whether or not to enable SSL on connections to "
+
"teleportr postgres instance"
,
Required
:
true
,
EnvVar
:
prefixAPIEnvVar
(
"POSTGRES_ENABLE_SSL"
),
}
)
var
APIFlags
=
[]
cli
.
Flag
{
APIHostnameFlag
,
APIPortFlag
,
APIL1EthRpcFlag
,
APIDepositAddressFlag
,
APINumConfirmationsFlag
,
APIPostgresHostFlag
,
APIPostgresPortFlag
,
APIPostgresUserFlag
,
APIPostgresPasswordFlag
,
APIPostgresDBNameFlag
,
APIPostgresEnableSSLFlag
,
}
go/teleportr/go.mod
View file @
f4f3054a
...
@@ -6,7 +6,10 @@ require (
...
@@ -6,7 +6,10 @@ require (
github.com/ethereum-optimism/optimism/go/bss-core v0.0.0-20220218171106-67a0414d7606
github.com/ethereum-optimism/optimism/go/bss-core v0.0.0-20220218171106-67a0414d7606
github.com/ethereum/go-ethereum v1.10.15
github.com/ethereum/go-ethereum v1.10.15
github.com/google/uuid v1.3.0
github.com/google/uuid v1.3.0
github.com/gorilla/mux v1.8.0
github.com/lib/pq v1.10.4
github.com/lib/pq v1.10.4
github.com/prometheus/client_golang v1.11.0
github.com/rs/cors v1.7.0
github.com/stretchr/testify v1.7.0
github.com/stretchr/testify v1.7.0
github.com/urfave/cli v1.22.5
github.com/urfave/cli v1.22.5
)
)
...
@@ -39,7 +42,6 @@ require (
...
@@ -39,7 +42,6 @@ require (
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.11.0 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.26.0 // indirect
github.com/prometheus/common v0.26.0 // indirect
github.com/prometheus/procfs v0.6.0 // indirect
github.com/prometheus/procfs v0.6.0 // indirect
...
...
go/teleportr/go.sum
View file @
f4f3054a
...
@@ -265,6 +265,7 @@ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
...
@@ -265,6 +265,7 @@ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment