Skip to content

Commit 3656354

Browse files
authored
Merge pull request #833 from jshufro/devnet-3
Fix build by adding latestblocktime to execution client and percolating it through the api. Put ethclient behind a facade
2 parents 4868a8d + 1999ed9 commit 3656354

14 files changed

Lines changed: 102 additions & 71 deletions

File tree

bindings/rocketpool/ec-interface.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package rocketpool
33
import (
44
"context"
55
"math/big"
6+
"time"
67

78
"github.com/ethereum/go-ethereum"
89
"github.com/ethereum/go-ethereum/common"
@@ -102,4 +103,11 @@ type ExecutionClient interface {
102103
// SyncProgress retrieves the current progress of the sync algorithm. If there's
103104
// no sync currently running, it returns nil.
104105
SyncProgress(ctx context.Context) (*ethereum.SyncProgress, error)
106+
107+
/// =================
108+
/// Utility functions
109+
/// =================
110+
111+
// LatestBlockTime returns the timestamp of the latest block
112+
LatestBlockTime(ctx context.Context) (time.Time, error)
105113
}

rocketpool-cli/node/withdraw-rpl.go

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
"github.com/rocket-pool/smartnode/bindings/utils/eth"
1010
"github.com/urfave/cli"
1111

12-
"github.com/rocket-pool/smartnode/shared/services"
1312
"github.com/rocket-pool/smartnode/shared/services/gas"
1413
"github.com/rocket-pool/smartnode/shared/services/rocketpool"
1514
cliutils "github.com/rocket-pool/smartnode/shared/utils/cli"
@@ -27,10 +26,6 @@ func nodeWithdrawRpl(c *cli.Context) error {
2726
return err
2827
}
2928
defer rp.Close()
30-
ec, err := services.GetEthClient(c)
31-
if err != nil {
32-
return err
33-
}
3429

3530
// Get node status
3631
status, err := rp.NodeStatus()
@@ -41,12 +36,6 @@ func nodeWithdrawRpl(c *cli.Context) error {
4136
var unstakingPeriodEnd time.Time
4237

4338
if status.IsSaturnDeployed {
44-
// Get the latest block time
45-
latestBlockTimeUnix, err := services.GetEthClientLatestBlockTimestamp(ec)
46-
if err != nil {
47-
return err
48-
}
49-
latestBlockTime := time.Unix(int64(latestBlockTimeUnix), 0)
5039
fmt.Print("The RPL withdrawal process has changed in Saturn. It is now a 2-step process:")
5140
fmt.Println()
5241
fmt.Print("1. Request to unstake a certain RPL amount;")
@@ -69,7 +58,7 @@ func nodeWithdrawRpl(c *cli.Context) error {
6958
fmt.Printf("")
7059
if status.UnstakingRPL.Cmp(big.NewInt(0)) > 0 {
7160

72-
if unstakingPeriodEnd.After(latestBlockTime) {
61+
if unstakingPeriodEnd.After(status.LatestBlockTime) {
7362
fmt.Printf("You have %.6f RPL currently unstaking until %s.\n", status.UnstakingRPL, unstakingPeriodEnd.Format(TimeFormat))
7463
} else {
7564
if !c.Bool("yes") || prompt.Confirm(fmt.Sprintf("You have %.6f RPL already unstaked. Would you like to withdraw it now?", eth.WeiToEth(status.UnstakingRPL))) {

rocketpool/api/node/smoothing-pool.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package node
33
import (
44
"context"
55
"fmt"
6-
"time"
76

87
"github.com/rocket-pool/smartnode/bindings/node"
98
"github.com/rocket-pool/smartnode/bindings/rewards"
@@ -66,11 +65,10 @@ func getSmoothingPoolRegistrationStatus(c *cli.Context) (*api.GetSmoothingPoolRe
6665
}
6766

6867
// Get the time the user can next change their opt-in status
69-
latestBlockTimeUnix, err := services.GetEthClientLatestBlockTimestamp(ec)
68+
latestBlockTime, err := ec.LatestBlockTime(context.Background())
7069
if err != nil {
7170
return nil, err
7271
}
73-
latestBlockTime := time.Unix(int64(latestBlockTimeUnix), 0)
7472
changeAvailableTime := regChangeTime.Add(intervalTime)
7573
response.TimeLeftUntilChangeable = changeAvailableTime.Sub(latestBlockTime)
7674

rocketpool/api/node/status.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ func getStatus(c *cli.Context) (*api.NodeStatusResponse, error) {
9595

9696
// Sync
9797
var wg errgroup.Group
98+
ctx, cancel := context.WithCancel(context.Background())
99+
defer cancel()
98100

99101
if saturnDeployed {
100102
wg.Go(func() error {
@@ -189,6 +191,11 @@ func getStatus(c *cli.Context) (*api.NodeStatusResponse, error) {
189191
})
190192

191193
if saturnDeployed {
194+
wg.Go(func() error {
195+
var err error
196+
response.LatestBlockTime, err = rp.Client.LatestBlockTime(ctx)
197+
return err
198+
})
192199
// Get the node's locked RPL
193200
wg.Go(func() error {
194201
var err error
@@ -417,6 +424,8 @@ func getStatus(c *cli.Context) (*api.NodeStatusResponse, error) {
417424

418425
// Wait for data
419426
if err := wg.Wait(); err != nil {
427+
// Cancel in-flight requests.
428+
cancel()
420429
return nil, err
421430
}
422431

rocketpool/watchtower/generate-rewards-tree.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import (
1414
"github.com/ethereum/go-ethereum/common"
1515
"github.com/ethereum/go-ethereum/core/types"
1616
"github.com/ethereum/go-ethereum/crypto"
17-
"github.com/ethereum/go-ethereum/ethclient"
1817
"github.com/rocket-pool/smartnode/bindings/rewards"
1918
"github.com/rocket-pool/smartnode/bindings/rocketpool"
2019
"github.com/rocket-pool/smartnode/shared/services"
@@ -183,7 +182,7 @@ func (t *generateRewardsTree) generateRewardsTree(index uint64) {
183182
archiveEcUrl := t.cfg.Smartnode.ArchiveECUrl.Value.(string)
184183
if archiveEcUrl != "" {
185184
t.log.Printlnf("%s Primary EC cannot retrieve state for historical block %d, using archive EC [%s]", generationPrefix, elBlockHeader.Number.Uint64(), archiveEcUrl)
186-
ec, err := ethclient.Dial(archiveEcUrl)
185+
ec, err := services.NewEthClient(archiveEcUrl)
187186
if err != nil {
188187
t.handleError(fmt.Errorf("Error connecting to archive EC: %w", err))
189188
return

shared/services/ec-manager.go

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,16 @@ import (
2323
type ExecutionClientManager struct {
2424
primaryEcUrl string
2525
fallbackEcUrl string
26-
primaryEc *ethclient.Client
27-
fallbackEc *ethclient.Client
26+
primaryEc *ethClient
27+
fallbackEc *ethClient
2828
logger log.ColorLogger
2929
primaryReady bool
3030
fallbackReady bool
3131
ignoreSyncCheck bool
3232
}
3333

3434
// This is a signature for a wrapped ethclient.Client function
35-
type ecFunction func(*ethclient.Client) (interface{}, error)
35+
type ecFunction func(*ethClient) (interface{}, error)
3636

3737
// Creates a new ExecutionClientManager instance based on the Rocket Pool config
3838
func NewExecutionClientManager(cfg *config.RocketPoolConfig) (*ExecutionClientManager, error) {
@@ -80,8 +80,8 @@ func NewExecutionClientManager(cfg *config.RocketPoolConfig) (*ExecutionClientMa
8080
return &ExecutionClientManager{
8181
primaryEcUrl: primaryEcUrl,
8282
fallbackEcUrl: fallbackEcUrl,
83-
primaryEc: primaryEc,
84-
fallbackEc: fallbackEc,
83+
primaryEc: &ethClient{primaryEc},
84+
fallbackEc: &ethClient{fallbackEc},
8585
logger: log.NewColorLogger(color.FgYellow),
8686
primaryReady: true,
8787
fallbackReady: fallbackEc != nil,
@@ -96,7 +96,7 @@ func NewExecutionClientManager(cfg *config.RocketPoolConfig) (*ExecutionClientMa
9696
// CodeAt returns the code of the given account. This is needed to differentiate
9797
// between contract internal errors and the local chain being out of sync.
9898
func (p *ExecutionClientManager) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
99-
result, err := p.runFunction(func(client *ethclient.Client) (interface{}, error) {
99+
result, err := p.runFunction(func(client *ethClient) (interface{}, error) {
100100
return client.CodeAt(ctx, contract, blockNumber)
101101
})
102102
if err != nil {
@@ -108,7 +108,7 @@ func (p *ExecutionClientManager) CodeAt(ctx context.Context, contract common.Add
108108
// CallContract executes an Ethereum contract call with the specified data as the
109109
// input.
110110
func (p *ExecutionClientManager) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
111-
result, err := p.runFunction(func(client *ethclient.Client) (interface{}, error) {
111+
result, err := p.runFunction(func(client *ethClient) (interface{}, error) {
112112
return client.CallContract(ctx, call, blockNumber)
113113
})
114114
if err != nil {
@@ -123,7 +123,7 @@ func (p *ExecutionClientManager) CallContract(ctx context.Context, call ethereum
123123

124124
// HeaderByHash returns the block header with the given hash.
125125
func (p *ExecutionClientManager) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
126-
result, err := p.runFunction(func(client *ethclient.Client) (interface{}, error) {
126+
result, err := p.runFunction(func(client *ethClient) (interface{}, error) {
127127
return client.HeaderByHash(ctx, hash)
128128
})
129129
if err != nil {
@@ -135,7 +135,7 @@ func (p *ExecutionClientManager) HeaderByHash(ctx context.Context, hash common.H
135135
// HeaderByNumber returns a block header from the current canonical chain. If number is
136136
// nil, the latest known header is returned.
137137
func (p *ExecutionClientManager) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) {
138-
result, err := p.runFunction(func(client *ethclient.Client) (interface{}, error) {
138+
result, err := p.runFunction(func(client *ethClient) (interface{}, error) {
139139
return client.HeaderByNumber(ctx, number)
140140
})
141141
if err != nil {
@@ -146,7 +146,7 @@ func (p *ExecutionClientManager) HeaderByNumber(ctx context.Context, number *big
146146

147147
// PendingCodeAt returns the code of the given account in the pending state.
148148
func (p *ExecutionClientManager) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) {
149-
result, err := p.runFunction(func(client *ethclient.Client) (interface{}, error) {
149+
result, err := p.runFunction(func(client *ethClient) (interface{}, error) {
150150
return client.PendingCodeAt(ctx, account)
151151
})
152152
if err != nil {
@@ -157,7 +157,7 @@ func (p *ExecutionClientManager) PendingCodeAt(ctx context.Context, account comm
157157

158158
// PendingNonceAt retrieves the current pending nonce associated with an account.
159159
func (p *ExecutionClientManager) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) {
160-
result, err := p.runFunction(func(client *ethclient.Client) (interface{}, error) {
160+
result, err := p.runFunction(func(client *ethClient) (interface{}, error) {
161161
return client.PendingNonceAt(ctx, account)
162162
})
163163
if err != nil {
@@ -169,7 +169,7 @@ func (p *ExecutionClientManager) PendingNonceAt(ctx context.Context, account com
169169
// SuggestGasPrice retrieves the currently suggested gas price to allow a timely
170170
// execution of a transaction.
171171
func (p *ExecutionClientManager) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
172-
result, err := p.runFunction(func(client *ethclient.Client) (interface{}, error) {
172+
result, err := p.runFunction(func(client *ethClient) (interface{}, error) {
173173
return client.SuggestGasPrice(ctx)
174174
})
175175
if err != nil {
@@ -181,7 +181,7 @@ func (p *ExecutionClientManager) SuggestGasPrice(ctx context.Context) (*big.Int,
181181
// SuggestGasTipCap retrieves the currently suggested 1559 priority fee to allow
182182
// a timely execution of a transaction.
183183
func (p *ExecutionClientManager) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
184-
result, err := p.runFunction(func(client *ethclient.Client) (interface{}, error) {
184+
result, err := p.runFunction(func(client *ethClient) (interface{}, error) {
185185
return client.SuggestGasTipCap(ctx)
186186
})
187187
if err != nil {
@@ -196,7 +196,7 @@ func (p *ExecutionClientManager) SuggestGasTipCap(ctx context.Context) (*big.Int
196196
// transactions may be added or removed by miners, but it should provide a basis
197197
// for setting a reasonable default.
198198
func (p *ExecutionClientManager) EstimateGas(ctx context.Context, call ethereum.CallMsg) (gas uint64, err error) {
199-
result, err := p.runFunction(func(client *ethclient.Client) (interface{}, error) {
199+
result, err := p.runFunction(func(client *ethClient) (interface{}, error) {
200200
return client.EstimateGas(ctx, call)
201201
})
202202
if err != nil {
@@ -207,7 +207,7 @@ func (p *ExecutionClientManager) EstimateGas(ctx context.Context, call ethereum.
207207

208208
// SendTransaction injects the transaction into the pending pool for execution.
209209
func (p *ExecutionClientManager) SendTransaction(ctx context.Context, tx *types.Transaction) error {
210-
_, err := p.runFunction(func(client *ethclient.Client) (interface{}, error) {
210+
_, err := p.runFunction(func(client *ethClient) (interface{}, error) {
211211
return nil, client.SendTransaction(ctx, tx)
212212
})
213213
return err
@@ -222,7 +222,7 @@ func (p *ExecutionClientManager) SendTransaction(ctx context.Context, tx *types.
222222
//
223223
// TODO(karalabe): Deprecate when the subscription one can return past data too.
224224
func (p *ExecutionClientManager) FilterLogs(ctx context.Context, query ethereum.FilterQuery) ([]types.Log, error) {
225-
result, err := p.runFunction(func(client *ethclient.Client) (interface{}, error) {
225+
result, err := p.runFunction(func(client *ethClient) (interface{}, error) {
226226
return client.FilterLogs(ctx, query)
227227
})
228228
if err != nil {
@@ -234,7 +234,7 @@ func (p *ExecutionClientManager) FilterLogs(ctx context.Context, query ethereum.
234234
// SubscribeFilterLogs creates a background log filtering operation, returning
235235
// a subscription immediately, which can be used to stream the found events.
236236
func (p *ExecutionClientManager) SubscribeFilterLogs(ctx context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) {
237-
result, err := p.runFunction(func(client *ethclient.Client) (interface{}, error) {
237+
result, err := p.runFunction(func(client *ethClient) (interface{}, error) {
238238
return client.SubscribeFilterLogs(ctx, query, ch)
239239
})
240240
if err != nil {
@@ -250,7 +250,7 @@ func (p *ExecutionClientManager) SubscribeFilterLogs(ctx context.Context, query
250250
// TransactionReceipt returns the receipt of a transaction by transaction hash.
251251
// Note that the receipt is not available for pending transactions.
252252
func (p *ExecutionClientManager) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
253-
result, err := p.runFunction(func(client *ethclient.Client) (interface{}, error) {
253+
result, err := p.runFunction(func(client *ethClient) (interface{}, error) {
254254
return client.TransactionReceipt(ctx, txHash)
255255
})
256256
if err != nil {
@@ -265,7 +265,7 @@ func (p *ExecutionClientManager) TransactionReceipt(ctx context.Context, txHash
265265

266266
// BlockNumber returns the most recent block number
267267
func (p *ExecutionClientManager) BlockNumber(ctx context.Context) (uint64, error) {
268-
result, err := p.runFunction(func(client *ethclient.Client) (interface{}, error) {
268+
result, err := p.runFunction(func(client *ethClient) (interface{}, error) {
269269
return client.BlockNumber(ctx)
270270
})
271271
if err != nil {
@@ -277,7 +277,7 @@ func (p *ExecutionClientManager) BlockNumber(ctx context.Context) (uint64, error
277277
// BalanceAt returns the wei balance of the given account.
278278
// The block number can be nil, in which case the balance is taken from the latest known block.
279279
func (p *ExecutionClientManager) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) {
280-
result, err := p.runFunction(func(client *ethclient.Client) (interface{}, error) {
280+
result, err := p.runFunction(func(client *ethClient) (interface{}, error) {
281281
return client.BalanceAt(ctx, account, blockNumber)
282282
})
283283
if err != nil {
@@ -288,7 +288,7 @@ func (p *ExecutionClientManager) BalanceAt(ctx context.Context, account common.A
288288

289289
// TransactionByHash returns the transaction with the given hash.
290290
func (p *ExecutionClientManager) TransactionByHash(ctx context.Context, hash common.Hash) (tx *types.Transaction, isPending bool, err error) {
291-
result, err := p.runFunction(func(client *ethclient.Client) (interface{}, error) {
291+
result, err := p.runFunction(func(client *ethClient) (interface{}, error) {
292292
tx, isPending, err := client.TransactionByHash(ctx, hash)
293293
result := []interface{}{tx, isPending}
294294
return result, err
@@ -307,7 +307,7 @@ func (p *ExecutionClientManager) TransactionByHash(ctx context.Context, hash com
307307
// NonceAt returns the account nonce of the given account.
308308
// The block number can be nil, in which case the nonce is taken from the latest known block.
309309
func (p *ExecutionClientManager) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) {
310-
result, err := p.runFunction(func(client *ethclient.Client) (interface{}, error) {
310+
result, err := p.runFunction(func(client *ethClient) (interface{}, error) {
311311
return client.NonceAt(ctx, account, blockNumber)
312312
})
313313
if err != nil {
@@ -319,7 +319,7 @@ func (p *ExecutionClientManager) NonceAt(ctx context.Context, account common.Add
319319
// SyncProgress retrieves the current progress of the sync algorithm. If there's
320320
// no sync currently running, it returns nil.
321321
func (p *ExecutionClientManager) SyncProgress(ctx context.Context) (*ethereum.SyncProgress, error) {
322-
result, err := p.runFunction(func(client *ethclient.Client) (interface{}, error) {
322+
result, err := p.runFunction(func(client *ethClient) (interface{}, error) {
323323
return client.SyncProgress(ctx)
324324
})
325325
if err != nil {
@@ -328,6 +328,16 @@ func (p *ExecutionClientManager) SyncProgress(ctx context.Context) (*ethereum.Sy
328328
return result.(*ethereum.SyncProgress), err
329329
}
330330

331+
func (p *ExecutionClientManager) LatestBlockTime(ctx context.Context) (time.Time, error) {
332+
result, err := p.runFunction(func(client *ethClient) (interface{}, error) {
333+
return client.LatestBlockTime(ctx)
334+
})
335+
if err != nil {
336+
return time.Time{}, err
337+
}
338+
return result.(time.Time), err
339+
}
340+
331341
/// ==================
332342
/// Internal functions
333343
/// ==================
@@ -387,7 +397,7 @@ func getNetworkNameFromId(networkId uint) string {
387397
}
388398

389399
// Check the client status
390-
func checkEcStatus(client *ethclient.Client) api.ClientStatus {
400+
func checkEcStatus(client *ethClient) api.ClientStatus {
391401

392402
status := api.ClientStatus{}
393403

shared/services/eth1.go

Lines changed: 0 additions & 18 deletions
This file was deleted.

shared/services/ethclient.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package services
2+
3+
import (
4+
"context"
5+
"time"
6+
7+
"github.com/ethereum/go-ethereum/ethclient"
8+
)
9+
10+
type ethClient struct {
11+
*ethclient.Client
12+
}
13+
14+
func NewEthClient(url string) (*ethClient, error) {
15+
ec, err := ethclient.Dial(url)
16+
if err != nil {
17+
return nil, err
18+
}
19+
return &ethClient{ec}, nil
20+
}
21+
22+
func (c *ethClient) LatestBlockTime(ctx context.Context) (time.Time, error) {
23+
header, err := c.HeaderByNumber(ctx, nil)
24+
if err != nil {
25+
return time.Time{}, err
26+
}
27+
28+
return time.Unix(int64(header.Time), 0), nil
29+
}

0 commit comments

Comments
 (0)