Skip to content

Commit dae6c40

Browse files
authored
feat: latest Clearnode (#17)
- Faucet now exposes and logs both owner and session-key addresses separately. - Transaction responses include richer details (tx ID, asset, amount, source/destination). - Core platform and crypto-related libraries upgraded for stability and security. - Faucet backend now uses an RPC-driven request/response flow for operations.
1 parent b717abc commit dae6c40

11 files changed

Lines changed: 239 additions & 280 deletions

File tree

server/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.env

server/Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Build stage
2-
FROM golang:1.21-alpine AS builder
2+
FROM golang:1.25-alpine AS builder
33

44
WORKDIR /app
55

@@ -30,4 +30,4 @@ COPY --from=builder /app/faucet-server .
3030
EXPOSE 8080
3131

3232
# Run the binary
33-
CMD ["./faucet-server"]
33+
CMD ["./faucet-server"]

server/go.mod

Lines changed: 32 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,62 @@
11
module faucet-server
22

3-
go 1.21
3+
go 1.25
44

55
require (
6-
github.com/ethereum/go-ethereum v1.13.8
6+
github.com/erc7824/nitrolite/clearnode v0.4.0
7+
github.com/ethereum/go-ethereum v1.16.4
78
github.com/gin-gonic/gin v1.9.1
8-
github.com/gorilla/websocket v1.5.1
9+
github.com/gorilla/websocket v1.5.3
910
github.com/ilyakaznacheev/cleanenv v1.5.0
1011
github.com/shopspring/decimal v1.4.0
1112
github.com/sirupsen/logrus v1.9.3
12-
github.com/stretchr/testify v1.11.0
13+
github.com/stretchr/testify v1.11.1
1314
)
1415

1516
require (
16-
github.com/BurntSushi/toml v1.2.1 // indirect
17-
github.com/bits-and-blooms/bitset v1.10.0 // indirect
18-
github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect
17+
github.com/BurntSushi/toml v1.5.0 // indirect
18+
github.com/bits-and-blooms/bitset v1.22.0 // indirect
1919
github.com/bytedance/sonic v1.9.1 // indirect
2020
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
21-
github.com/consensys/bavard v0.1.13 // indirect
22-
github.com/consensys/gnark-crypto v0.12.1 // indirect
23-
github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect
21+
github.com/consensys/gnark-crypto v0.18.0 // indirect
22+
github.com/crate-crypto/go-eth-kzg v1.4.0 // indirect
23+
github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a // indirect
2424
github.com/davecgh/go-spew v1.1.1 // indirect
25-
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
26-
github.com/ethereum/c-kzg-4844 v0.4.0 // indirect
27-
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
25+
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
26+
github.com/ethereum/c-kzg-4844/v2 v2.1.3 // indirect
27+
github.com/ethereum/go-verkle v0.2.2 // indirect
28+
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
2829
github.com/gin-contrib/sse v0.1.0 // indirect
2930
github.com/go-playground/locales v0.14.1 // indirect
3031
github.com/go-playground/universal-translator v0.18.1 // indirect
31-
github.com/go-playground/validator/v10 v10.14.0 // indirect
32-
github.com/goccy/go-json v0.10.2 // indirect
33-
github.com/holiman/uint256 v1.2.4 // indirect
32+
github.com/go-playground/validator/v10 v10.27.0 // indirect
33+
github.com/goccy/go-json v0.10.4 // indirect
34+
github.com/google/uuid v1.6.0 // indirect
35+
github.com/holiman/uint256 v1.3.2 // indirect
3436
github.com/joho/godotenv v1.5.1 // indirect
3537
github.com/json-iterator/go v1.1.12 // indirect
38+
github.com/jsternberg/zap-logfmt v1.3.0 // indirect
3639
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
37-
github.com/leodido/go-urn v1.2.4 // indirect
38-
github.com/mattn/go-isatty v0.0.19 // indirect
39-
github.com/mmcloughlin/addchain v0.4.0 // indirect
40+
github.com/leodido/go-urn v1.4.0 // indirect
41+
github.com/mattn/go-isatty v0.0.20 // indirect
4042
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
4143
github.com/modern-go/reflect2 v1.0.2 // indirect
4244
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
4345
github.com/pmezard/go-difflib v1.0.0 // indirect
44-
github.com/supranational/blst v0.3.11 // indirect
46+
github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe // indirect
4547
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
4648
github.com/ugorji/go/codec v1.2.11 // indirect
49+
go.opentelemetry.io/otel v1.38.0 // indirect
50+
go.opentelemetry.io/otel/trace v1.38.0 // indirect
51+
go.uber.org/multierr v1.11.0 // indirect
52+
go.uber.org/zap v1.27.0 // indirect
4753
golang.org/x/arch v0.3.0 // indirect
48-
golang.org/x/crypto v0.17.0 // indirect
49-
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
50-
golang.org/x/net v0.19.0 // indirect
51-
golang.org/x/sync v0.5.0 // indirect
52-
golang.org/x/sys v0.15.0 // indirect
53-
golang.org/x/text v0.14.0 // indirect
54-
google.golang.org/protobuf v1.30.0 // indirect
54+
golang.org/x/crypto v0.41.0 // indirect
55+
golang.org/x/net v0.43.0 // indirect
56+
golang.org/x/sync v0.16.0 // indirect
57+
golang.org/x/sys v0.36.0 // indirect
58+
golang.org/x/text v0.28.0 // indirect
59+
google.golang.org/protobuf v1.36.8 // indirect
5560
gopkg.in/yaml.v3 v3.0.1 // indirect
5661
olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3 // indirect
57-
rsc.io/tmplfunc v0.0.3 // indirect
5862
)

server/go.sum

Lines changed: 100 additions & 130 deletions
Large diffs are not rendered by default.

server/internal/clearnode/client.go

Lines changed: 53 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"sync/atomic"
99
"time"
1010

11+
"github.com/erc7824/nitrolite/clearnode/pkg/rpc"
1112
"github.com/ethereum/go-ethereum/common"
1213
"github.com/ethereum/go-ethereum/common/hexutil"
1314
"github.com/ethereum/go-ethereum/crypto"
@@ -57,37 +58,6 @@ type RPCResponse struct {
5758
Timestamp uint64 `json:"timestamp"`
5859
}
5960

60-
// Structured response types
61-
type Asset struct {
62-
Token string `json:"token"`
63-
ChainID int `json:"chain_id"`
64-
Symbol string `json:"symbol"`
65-
Decimals int `json:"decimals"`
66-
}
67-
68-
type Balance struct {
69-
Asset string `json:"asset"`
70-
Amount decimal.Decimal `json:"amount"`
71-
}
72-
73-
type Allocation struct {
74-
Asset string `json:"asset"`
75-
Amount decimal.Decimal `json:"amount"`
76-
}
77-
78-
type TransferRequest struct {
79-
Destination string `json:"destination"`
80-
Allocations []Allocation `json:"allocations"`
81-
}
82-
83-
type TransferResult struct {
84-
TransactionID string `json:"transaction_id"`
85-
Amount decimal.Decimal `json:"amount"`
86-
Asset string `json:"asset"`
87-
Destination string `json:"destination"`
88-
Status string `json:"status"`
89-
}
90-
9161
func NewClient(ownerPrivateKeyHex, signerPrivateKeyHex, clearnodeURL string, tokenSymbol string, standardTipAmount decimal.Decimal, minTransferCount int) (*Client, error) {
9262
// Clean the owner private key (remove 0x prefix if present)
9363
if len(ownerPrivateKeyHex) > 2 && ownerPrivateKeyHex[:2] == "0x" {
@@ -156,24 +126,24 @@ func (c *Client) Authenticate() error {
156126
logger.Info("Starting authentication flow")
157127

158128
// Authentication parameters
159-
appName := "Nitrolite Faucet"
129+
appName := "clearnode" // "clearnode" to allow unlimited allowances
160130
scope := "app.transfer"
161-
expire := "36000000" // 10_000 hours
162-
sessionKey := c.signerAddress // Use signer address as session key
163-
application := common.Address{} // Zero address if no specific app
131+
expiresAt := uint64(time.Now().Add(10000 * time.Hour).Unix()) // 10_000 hours in seconds
132+
sessionKey := c.signerAddress // Use signer address as session key
133+
applicationAddress := common.Address{} // Zero address if no specific app
164134

165-
// Step 1: Send auth_request
166-
authRequestData := map[string]interface{}{
135+
// Step 1: Send auth_request using a map to match the local server's expectations
136+
// Note: The published rpc package types don't match the latest local server yet
137+
authRequest := map[string]interface{}{
167138
"address": c.ownerAddress.Hex(),
168139
"session_key": sessionKey.Hex(),
169-
"app_name": appName,
140+
"application": appName,
170141
"scope": scope,
171-
"expire": expire,
172-
"application": application.Hex(),
173-
"allowances": []map[string]interface{}{}, // Empty allowances for faucet
142+
"expires_at": expiresAt,
143+
"allowances": []rpc.Allowance{}, // Use rpc.Allowance type for consistency
174144
}
175145

176-
challengeResponse, err := c.sendRequest("auth_request", authRequestData)
146+
challengeResponse, err := c.sendRequest("auth_request", authRequest)
177147
if err != nil {
178148
return fmt.Errorf("auth_request failed: %w", err)
179149
}
@@ -186,15 +156,15 @@ func (c *Client) Authenticate() error {
186156
logger.Debugf("Received challenge: %s", challengeMessage)
187157

188158
// Step 2: Sign the challenge using EIP-712
189-
allowances := []Allowance{} // Empty allowances for faucet
159+
allowances := []rpc.Allowance{} // Empty allowances for faucet
190160
signature, err := c.eip712Signer.SignChallenge(
191161
challengeMessage,
192162
sessionKey,
193163
appName,
194164
allowances,
195165
scope,
196-
application,
197-
expire,
166+
applicationAddress,
167+
expiresAt,
198168
)
199169
if err != nil {
200170
return fmt.Errorf("failed to sign challenge: %w", err)
@@ -274,7 +244,7 @@ func (c *Client) Authenticate() error {
274244
}
275245
}
276246

277-
func (c *Client) GetAssets() ([]Asset, error) {
247+
func (c *Client) GetAssets() ([]rpc.Asset, error) {
278248
if err := c.EnsureConnected(); err != nil {
279249
return nil, err
280250
}
@@ -297,7 +267,7 @@ func (c *Client) GetAssets() ([]Asset, error) {
297267
return assets, nil
298268
}
299269

300-
func (c *Client) GetFaucetBalance(tokenSymbol string) (*Balance, error) {
270+
func (c *Client) GetFaucetBalance(tokenSymbol string) (*rpc.LedgerBalance, error) {
301271
if err := c.EnsureConnected(); err != nil {
302272
return nil, err
303273
}
@@ -320,13 +290,13 @@ func (c *Client) GetFaucetBalance(tokenSymbol string) (*Balance, error) {
320290
return balance, nil
321291
}
322292

323-
func (c *Client) Transfer(destination, asset string, amount decimal.Decimal) (*TransferResult, error) {
324-
transferData := TransferRequest{
293+
func (c *Client) Transfer(destination, asset string, amount decimal.Decimal) (*rpc.TransferResponse, error) {
294+
transferData := rpc.TransferRequest{
325295
Destination: destination,
326-
Allocations: []Allocation{
296+
Allocations: []rpc.TransferAllocation{
327297
{
328-
Asset: asset,
329-
Amount: amount,
298+
AssetSymbol: asset,
299+
Amount: amount,
330300
},
331301
},
332302
}
@@ -338,7 +308,7 @@ func (c *Client) Transfer(destination, asset string, amount decimal.Decimal) (*T
338308
return nil, fmt.Errorf("transfer failed: %w", err)
339309
}
340310

341-
logger.Info("Transfer completed successfully", "destination", destination)
311+
logger.Infof("Transfer completed successfully, destination: %s", destination)
342312

343313
// Parse the response data
344314
result, err := c.parseTransferResult(response.Data, destination, asset, amount)
@@ -494,13 +464,13 @@ func (c *Client) signMessage(data interface{}) (string, error) {
494464

495465
// Parsing helper methods
496466

497-
func (c *Client) parseAssets(data map[string]interface{}) ([]Asset, error) {
467+
func (c *Client) parseAssets(data map[string]interface{}) ([]rpc.Asset, error) {
498468
assetsInterface, ok := data["assets"].([]interface{})
499469
if !ok {
500470
return nil, fmt.Errorf("invalid assets response format")
501471
}
502472

503-
var assets []Asset
473+
var assets []rpc.Asset
504474
for _, assetInterface := range assetsInterface {
505475
assetData, ok := assetInterface.(map[string]interface{})
506476
if !ok {
@@ -512,18 +482,18 @@ func (c *Client) parseAssets(data map[string]interface{}) ([]Asset, error) {
512482
decimals, _ := assetData["decimals"].(float64)
513483
chainID, _ := assetData["chain_id"].(float64)
514484

515-
assets = append(assets, Asset{
485+
assets = append(assets, rpc.Asset{
516486
Token: token,
517-
ChainID: int(chainID),
487+
ChainID: uint32(chainID),
518488
Symbol: symbol,
519-
Decimals: int(decimals),
489+
Decimals: uint8(decimals),
520490
})
521491
}
522492

523493
return assets, nil
524494
}
525495

526-
func (c *Client) parseTokenBalance(data map[string]interface{}, tokenSymbol string) (*Balance, error) {
496+
func (c *Client) parseTokenBalance(data map[string]interface{}, tokenSymbol string) (*rpc.LedgerBalance, error) {
527497
balancesInterface, ok := data["ledger_balances"].([]interface{})
528498
if !ok {
529499
return nil, fmt.Errorf("invalid ledger balances response format")
@@ -550,49 +520,43 @@ func (c *Client) parseTokenBalance(data map[string]interface{}, tokenSymbol stri
550520
return nil, fmt.Errorf("failed to parse balance amount: %w", err)
551521
}
552522

553-
return &Balance{
523+
return &rpc.LedgerBalance{
554524
Asset: asset,
555525
Amount: amount,
556526
}, nil
557527
}
558528

559-
return &Balance{
529+
return &rpc.LedgerBalance{
560530
Asset: tokenSymbol,
561531
Amount: decimal.Zero,
562532
}, nil
563533
}
564534

565-
func (c *Client) parseTransferResult(data map[string]interface{}, destination, asset string, amount decimal.Decimal) (*TransferResult, error) {
535+
func (c *Client) parseTransferResult(data map[string]interface{}, destination, asset string, amount decimal.Decimal) (*rpc.TransferResponse, error) {
536+
// Parse the response data into RPC TransferResponse
537+
var response rpc.TransferResponse
538+
566539
transactionsInterface, ok := data["transactions"].([]interface{})
567540
if !ok {
568541
return nil, fmt.Errorf("invalid transfer response format")
569542
}
570543

571-
// Use the first transaction for the result
572-
if len(transactionsInterface) == 0 {
573-
return &TransferResult{
574-
TransactionID: "",
575-
Amount: amount,
576-
Asset: asset,
577-
Destination: destination,
578-
Status: "completed",
579-
}, nil
580-
}
544+
// Parse transactions
545+
for _, txInterface := range transactionsInterface {
546+
txDataRaw, err := json.Marshal(txInterface)
547+
if err != nil {
548+
continue
549+
}
581550

582-
txData, ok := transactionsInterface[0].(map[string]interface{})
583-
if !ok {
584-
return nil, fmt.Errorf("invalid transaction data format")
585-
}
551+
var tx rpc.LedgerTransaction
552+
if err := json.Unmarshal(txDataRaw, &tx); err != nil {
553+
continue
554+
}
586555

587-
txID, _ := txData["id"].(string)
556+
response.Transactions = append(response.Transactions, tx)
557+
}
588558

589-
return &TransferResult{
590-
TransactionID: txID,
591-
Amount: amount,
592-
Asset: asset,
593-
Destination: destination,
594-
Status: "completed",
595-
}, nil
559+
return &response, nil
596560
}
597561

598562
func (c *Client) Close() error {
@@ -685,6 +649,10 @@ func (c *Client) EnsureOperational() error {
685649
return nil
686650
}
687651

688-
func (c *Client) GetAddress() common.Address {
652+
func (c *Client) GetOwnerAddress() common.Address {
653+
return c.ownerAddress
654+
}
655+
656+
func (c *Client) GetSessionKeyAddress() common.Address {
689657
return c.signerAddress
690658
}

server/internal/clearnode/client_validation_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ func TestNewClientValidation(t *testing.T) {
3232

3333
// Verify addresses are different
3434
assert.NotEqual(t, client.ownerAddress, client.signerAddress)
35-
36-
// Verify GetAddress returns signer address
37-
assert.Equal(t, client.signerAddress, client.GetAddress())
35+
36+
// Verify GetAddress returns sessionKey address
37+
assert.Equal(t, client.signerAddress, client.GetSessionKeyAddress())
3838
})
3939

4040
t.Run("should handle 0x prefixed keys correctly", func(t *testing.T) {
@@ -60,4 +60,4 @@ func TestNewClientValidation(t *testing.T) {
6060
require.Error(t, err)
6161
assert.Contains(t, err.Error(), "owner and signer private keys must be different for security reasons")
6262
})
63-
}
63+
}

0 commit comments

Comments
 (0)