Skip to content

Commit e5a2e4c

Browse files
committed
Switch to RPC chain ID validation
1 parent 35826aa commit e5a2e4c

3 files changed

Lines changed: 105 additions & 26 deletions

File tree

internal/config/add-alias.go

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -56,16 +56,20 @@ func addAlias(
5656
_ []string,
5757
globalFlags command.GlobalFlags,
5858
_ output.Logger,
59-
_ flowkit.Services,
59+
flowServices flowkit.Services,
6060
state *flowkit.State,
6161
) (command.Result, error) {
62-
raw, flagsProvided, err := flagsToAliasData(addAliasFlags)
62+
raw, flagsProvided, err := flagsToAliasData(addAliasFlags, state)
6363
if err != nil {
6464
return nil, err
6565
}
6666

6767
if !flagsProvided {
6868
raw = prompt.NewAliasPrompt()
69+
err = validateAliasData(raw, state)
70+
if err != nil {
71+
return nil, err
72+
}
6973
}
7074

7175
contract, err := state.Contracts().ByName(raw.Contract)
@@ -90,7 +94,21 @@ func addAlias(
9094
}, nil
9195
}
9296

93-
func flagsToAliasData(flags flagsAddAlias) (*prompt.AliasData, bool, error) {
97+
func validateAliasData(data *prompt.AliasData, state *flowkit.State) error {
98+
address := flow.HexToAddress(data.Address)
99+
if address == flow.EmptyAddress {
100+
return fmt.Errorf("invalid address")
101+
}
102+
103+
network, err := state.Networks().ByName(data.Network)
104+
if err != nil {
105+
return fmt.Errorf("network %s not found in configuration", data.Network)
106+
}
107+
108+
return util.ValidateAddressForNetwork(address, network)
109+
}
110+
111+
func flagsToAliasData(flags flagsAddAlias, state *flowkit.State) (*prompt.AliasData, bool, error) {
94112
if flags.Contract == "" && flags.Network == "" && flags.Address == "" {
95113
return nil, false, nil
96114
}
@@ -107,14 +125,16 @@ func flagsToAliasData(flags flagsAddAlias) (*prompt.AliasData, bool, error) {
107125
return nil, true, fmt.Errorf("address must be provided")
108126
}
109127

110-
// Validate address is valid for the specified network
111-
if !util.IsAddressValidForNetwork(address, flags.Network) {
112-
return nil, true, fmt.Errorf("address %s is not valid for network %s", flags.Address, flags.Network)
113-
}
114-
115-
return &prompt.AliasData{
128+
data := &prompt.AliasData{
116129
Contract: flags.Contract,
117130
Network: flags.Network,
118131
Address: flags.Address,
119-
}, true, nil
132+
}
133+
134+
err := validateAliasData(data, state)
135+
if err != nil {
136+
return nil, true, err
137+
}
138+
139+
return data, true, nil
120140
}

internal/config/add-alias_test.go

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -206,13 +206,18 @@ func Test_AddAlias(t *testing.T) {
206206

207207
func Test_FlagsToAliasData(t *testing.T) {
208208
t.Run("Success with all flags", func(t *testing.T) {
209+
_, state, _ := util.TestMocks(t)
210+
211+
// Add testnet network to state
212+
state.Networks().AddOrUpdate(config.TestnetNetwork)
213+
209214
flags := flagsAddAlias{
210215
Contract: "TestContract",
211216
Network: "testnet",
212217
Address: "0x9a0766d93b6608b7",
213218
}
214219

215-
data, flagsProvided, err := flagsToAliasData(flags)
220+
data, flagsProvided, err := flagsToAliasData(flags, state)
216221

217222
require.NoError(t, err)
218223
assert.True(t, flagsProvided)
@@ -222,132 +227,158 @@ func Test_FlagsToAliasData(t *testing.T) {
222227
})
223228

224229
t.Run("No flags provided", func(t *testing.T) {
230+
_, state, _ := util.TestMocks(t)
225231
flags := flagsAddAlias{}
226232

227-
data, flagsProvided, err := flagsToAliasData(flags)
233+
data, flagsProvided, err := flagsToAliasData(flags, state)
228234

229235
require.NoError(t, err)
230236
assert.False(t, flagsProvided)
231237
assert.Nil(t, data)
232238
})
233239

234240
t.Run("Fail missing contract name", func(t *testing.T) {
241+
_, state, _ := util.TestMocks(t)
235242
flags := flagsAddAlias{
236243
Network: "testnet",
237244
Address: "0x9a0766d93b6608b7",
238245
}
239246

240-
data, flagsProvided, err := flagsToAliasData(flags)
247+
data, flagsProvided, err := flagsToAliasData(flags, state)
241248

242249
assert.Nil(t, data)
243250
assert.True(t, flagsProvided)
244251
assert.EqualError(t, err, "contract name must be provided")
245252
})
246253

247254
t.Run("Fail missing network", func(t *testing.T) {
255+
_, state, _ := util.TestMocks(t)
248256
flags := flagsAddAlias{
249257
Contract: "TestContract",
250258
Address: "0x9a0766d93b6608b7",
251259
}
252260

253-
data, flagsProvided, err := flagsToAliasData(flags)
261+
data, flagsProvided, err := flagsToAliasData(flags, state)
254262

255263
assert.Nil(t, data)
256264
assert.True(t, flagsProvided)
257265
assert.EqualError(t, err, "network name must be provided")
258266
})
259267

260268
t.Run("Fail missing address", func(t *testing.T) {
269+
_, state, _ := util.TestMocks(t)
261270
flags := flagsAddAlias{
262271
Contract: "TestContract",
263272
Network: "testnet",
264273
}
265274

266-
data, flagsProvided, err := flagsToAliasData(flags)
275+
data, flagsProvided, err := flagsToAliasData(flags, state)
267276

268277
assert.Nil(t, data)
269278
assert.True(t, flagsProvided)
270279
assert.EqualError(t, err, "address must be provided")
271280
})
272281

273282
t.Run("Fail invalid address", func(t *testing.T) {
283+
_, state, _ := util.TestMocks(t)
274284
flags := flagsAddAlias{
275285
Contract: "TestContract",
276286
Network: "testnet",
277287
Address: "invalid-address",
278288
}
279289

280-
data, flagsProvided, err := flagsToAliasData(flags)
290+
data, flagsProvided, err := flagsToAliasData(flags, state)
281291

282292
assert.Nil(t, data)
283293
assert.True(t, flagsProvided)
284294
assert.EqualError(t, err, "invalid address")
285295
})
286296

287297
t.Run("Fail empty address", func(t *testing.T) {
298+
_, state, _ := util.TestMocks(t)
288299
flags := flagsAddAlias{
289300
Contract: "TestContract",
290301
Network: "testnet",
291302
Address: "0x0000000000000000",
292303
}
293304

294-
data, flagsProvided, err := flagsToAliasData(flags)
305+
data, flagsProvided, err := flagsToAliasData(flags, state)
295306

296307
assert.Nil(t, data)
297308
assert.True(t, flagsProvided)
298309
assert.EqualError(t, err, "invalid address")
299310
})
300311

301312
t.Run("Success with address without 0x prefix", func(t *testing.T) {
313+
_, state, _ := util.TestMocks(t)
314+
315+
// Add mainnet network to state
316+
state.Networks().AddOrUpdate(config.MainnetNetwork)
317+
302318
flags := flagsAddAlias{
303319
Contract: "TestContract",
304320
Network: "mainnet",
305321
Address: "1d7e57aa55817448",
306322
}
307323

308-
data, flagsProvided, err := flagsToAliasData(flags)
324+
data, flagsProvided, err := flagsToAliasData(flags, state)
309325

310326
require.NoError(t, err)
311327
assert.True(t, flagsProvided)
312328
assert.Equal(t, "1d7e57aa55817448", data.Address)
313329
})
314330

315331
t.Run("Fail testnet address used for mainnet", func(t *testing.T) {
332+
_, state, _ := util.TestMocks(t)
333+
334+
// Add mainnet network to state
335+
state.Networks().AddOrUpdate(config.MainnetNetwork)
336+
316337
flags := flagsAddAlias{
317338
Contract: "TestContract",
318339
Network: "mainnet",
319340
Address: "0x9a0766d93b6608b7", // Testnet address
320341
}
321342

322-
data, flagsProvided, err := flagsToAliasData(flags)
343+
data, flagsProvided, err := flagsToAliasData(flags, state)
323344

324345
assert.Nil(t, data)
325346
assert.True(t, flagsProvided)
326347
assert.ErrorContains(t, err, "not valid for network mainnet")
327348
})
328349

329350
t.Run("Fail mainnet address used for testnet", func(t *testing.T) {
351+
_, state, _ := util.TestMocks(t)
352+
353+
// Add testnet network to state
354+
state.Networks().AddOrUpdate(config.TestnetNetwork)
355+
330356
flags := flagsAddAlias{
331357
Contract: "TestContract",
332358
Network: "testnet",
333359
Address: "0xf233dcee88fe0abe", // Mainnet address
334360
}
335361

336-
data, flagsProvided, err := flagsToAliasData(flags)
362+
data, flagsProvided, err := flagsToAliasData(flags, state)
337363

338364
assert.Nil(t, data)
339365
assert.True(t, flagsProvided)
340366
assert.ErrorContains(t, err, "not valid for network testnet")
341367
})
342368

343369
t.Run("Fail emulator address used for testnet", func(t *testing.T) {
370+
_, state, _ := util.TestMocks(t)
371+
372+
// Add testnet network to state
373+
state.Networks().AddOrUpdate(config.TestnetNetwork)
374+
344375
flags := flagsAddAlias{
345376
Contract: "TestContract",
346377
Network: "testnet",
347378
Address: "0xf8d6e0586b0a20c7", // Emulator address
348379
}
349380

350-
data, flagsProvided, err := flagsToAliasData(flags)
381+
data, flagsProvided, err := flagsToAliasData(flags, state)
351382

352383
assert.Nil(t, data)
353384
assert.True(t, flagsProvided)

internal/util/util.go

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,12 @@ import (
3232
"time"
3333

3434
"github.com/onflow/flow-go-sdk"
35+
"github.com/onflow/flow-go-sdk/access/grpc"
3536
"github.com/onflow/flow-go-sdk/crypto"
3637
"github.com/onflow/flow-go/fvm/systemcontracts"
3738
flowGo "github.com/onflow/flow-go/model/flow"
3839
flowaccess "github.com/onflow/flow/protobuf/go/flow/access"
39-
"google.golang.org/grpc"
40+
grpcOpts "google.golang.org/grpc"
4041
"google.golang.org/grpc/credentials/insecure"
4142

4243
emulatorUtils "github.com/onflow/flow-emulator/utils"
@@ -52,7 +53,8 @@ func Exit(code int, msg string) {
5253
os.Exit(code)
5354
}
5455

55-
// IsAddressValidForNetwork checks if an address is valid for a specific network
56+
// IsAddressValidForNetwork checks if an address is valid for a specific network based on address format
57+
// This is used for address introspection only - use ValidateAddressForNetwork for actual validation
5658
func IsAddressValidForNetwork(address flow.Address, networkName string) bool {
5759
switch networkName {
5860
case "mainnet":
@@ -62,11 +64,37 @@ func IsAddressValidForNetwork(address flow.Address, networkName string) bool {
6264
case "emulator", "testing":
6365
return address.IsValid(flow.Emulator)
6466
default:
65-
// For custom networks, assume they use the same validation as emulator
6667
return address.IsValid(flow.Emulator)
6768
}
6869
}
6970

71+
// ValidateAddressForNetwork validates that an address is valid for the specified network
72+
// by querying the access node to get the actual chain ID
73+
func ValidateAddressForNetwork(address flow.Address, network *config.Network) error {
74+
// Create a grpc client to query the network
75+
client, err := grpc.NewBaseClient(network.Host, grpcOpts.WithTransportCredentials(insecure.NewCredentials()))
76+
if err != nil {
77+
return fmt.Errorf("failed to connect to access node: %w", err)
78+
}
79+
defer client.Close()
80+
81+
// Get the chain ID from the access node
82+
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
83+
defer cancel()
84+
85+
params, err := client.GetNetworkParameters(ctx)
86+
if err != nil {
87+
return fmt.Errorf("failed to get chain ID from access node: %w", err)
88+
}
89+
90+
// Validate the address against the chain ID returned from the access node
91+
if !address.IsValid(params.ChainID) {
92+
return fmt.Errorf("address %s is not valid for network %s (chain ID: %s)", address, network.Name, params.ChainID)
93+
}
94+
95+
return nil
96+
}
97+
7098
// entryExists checks if an entry already exists in the content
7199
func entryExists(content, entry string) bool {
72100
lines := strings.SplitSeq(strings.TrimSpace(content), "\n")
@@ -250,9 +278,9 @@ func GetChainIDFromHost(host string) (flowGo.ChainID, error) {
250278
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
251279
defer cancel()
252280

253-
conn, err := grpc.NewClient(
281+
conn, err := grpcOpts.NewClient(
254282
host,
255-
grpc.WithTransportCredentials(insecure.NewCredentials()),
283+
grpcOpts.WithTransportCredentials(insecure.NewCredentials()),
256284
emulatorUtils.DefaultGRPCRetryInterceptor(),
257285
)
258286
if err != nil {

0 commit comments

Comments
 (0)