-
Notifications
You must be signed in to change notification settings - Fork 3.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(x/accounts)!: make address generation more robust and add predictable address creation #22776
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,13 +19,13 @@ func TestGenesis(t *testing.T) { | |
// we init two accounts of the same type | ||
|
||
// we set counter to 10 | ||
_, addr1, err := k.Init(ctx, testAccountType, []byte("sender"), &types.Empty{}, nil) | ||
_, addr1, err := k.Init(ctx, testAccountType, []byte("sender"), &types.Empty{}, nil, nil) | ||
require.NoError(t, err) | ||
_, err = k.Execute(ctx, addr1, []byte("sender"), &types.UInt64Value{Value: 10}, nil) | ||
require.NoError(t, err) | ||
|
||
// we set counter to 20 | ||
_, addr2, err := k.Init(ctx, testAccountType, []byte("sender"), &types.Empty{}, nil) | ||
_, addr2, err := k.Init(ctx, testAccountType, []byte("sender"), &types.Empty{}, nil, nil) | ||
Comment on lines
+22
to
+28
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add test cases for address seed persistence The genesis test should verify that address seeds are correctly persisted and restored during export/import cycles. Consider adding test cases that:
|
||
require.NoError(t, err) | ||
_, err = k.Execute(ctx, addr2, []byte("sender"), &types.UInt64Value{Value: 20}, nil) | ||
require.NoError(t, err) | ||
|
@@ -62,7 +62,7 @@ func TestGenesis(t *testing.T) { | |
require.Equal(t, &types.UInt64Value{Value: 20}, resp) | ||
|
||
// check initted on genesis account | ||
addr3, err := k.makeAddress(2) | ||
addr3, err := k.makeAddress([]byte("sender-2"), 2, nil) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Codebase verification Add test cases for address generation with non-nil seeds Based on the implementation in
Add test cases to verify the deterministic address creation with non-nil seeds to ensure both code paths work as expected. Example locations that need attention:
🔗 Analysis chainVerify address generation with non-nil seed. The test only covers address generation with nil seed. Consider adding test cases with non-nil seeds to verify deterministic address creation. Let me check the implementation of Let me try to find the implementation using ripgrep instead. Let me check all test files to see if there are any tests using non-nil addressSeed. Let me try one more search with a different approach. 🏁 Scripts executedThe following scripts were executed for the analysis: Script: #!/bin/bash
# Check if there are any tests covering non-nil address seeds
rg -l "makeAddress.*nil" | while read -r file; do
echo "=== $file ==="
rg "makeAddress.*[^n][^i][^l]" "$file" || echo "No non-nil seed tests found"
done
Length of output: 252 Script: #!/bin/bash
# Find the implementation of makeAddress function
ast-grep --pattern 'func $_ makeAddress($$$) {
$$$
}'
Length of output: 57 Script: #!/bin/bash
# Find the implementation of makeAddress function
rg -A 10 "func.*makeAddress"
Length of output: 733 Script: #!/bin/bash
# Search for makeAddress calls in test files
fd -e test.go --exec rg -B2 -A2 "makeAddress.*\(" {} \;
Length of output: 60 Script: #!/bin/bash
# Search for all makeAddress calls in the codebase
rg -B2 -A2 "makeAddress\("
Length of output: 1070 |
||
require.NoError(t, err) | ||
resp, err = k.Query(ctx, addr3, &types.DoubleValue{}) | ||
require.NoError(t, err) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -148,14 +148,15 @@ func (k Keeper) Init( | |
creator []byte, | ||
initRequest transaction.Msg, | ||
funds sdk.Coins, | ||
addressSeed []byte, | ||
) (transaction.Msg, []byte, error) { | ||
// get the next account number | ||
num, err := k.AccountNumber.Next(ctx) | ||
Comment on lines
+151
to
154
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Update function comment to include the new The comment for the |
||
if err != nil { | ||
return nil, nil, err | ||
} | ||
// create address | ||
accountAddr, err := k.makeAddress(num) | ||
accountAddr, err := k.makeAddress(creator, num, addressSeed) | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
|
@@ -180,7 +181,7 @@ func (k Keeper) initFromMsg(ctx context.Context, initMsg *v1.MsgInit) (transacti | |
} | ||
|
||
// run account creation logic | ||
return k.Init(ctx, initMsg.AccountType, creator, msg, initMsg.Funds) | ||
return k.Init(ctx, initMsg.AccountType, creator, msg, initMsg.Funds, initMsg.AddressSeed) | ||
} | ||
|
||
// init initializes the account, given the type, the creator the newly created account number, its address and the | ||
|
@@ -199,8 +200,17 @@ func (k Keeper) init( | |
return nil, fmt.Errorf("%w: not found %s", errAccountTypeNotFound, accountType) | ||
} | ||
|
||
// check if account exists | ||
alreadyExists, err := k.AccountsByType.Has(ctx, accountAddr) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if alreadyExists { | ||
return nil, ErrAccountAlreadyExists | ||
} | ||
|
||
// send funds, if provided | ||
err := k.maybeSendFunds(ctx, creator, accountAddr, funds) | ||
err = k.maybeSendFunds(ctx, creator, accountAddr, funds) | ||
if err != nil { | ||
return nil, fmt.Errorf("unable to transfer funds: %w", err) | ||
} | ||
|
@@ -307,9 +317,26 @@ func (k Keeper) getImplementation(ctx context.Context, addr []byte) (implementat | |
return impl, nil | ||
} | ||
|
||
func (k Keeper) makeAddress(accNum uint64) ([]byte, error) { | ||
// TODO: better address scheme, ref: https://github.com/cosmos/cosmos-sdk/issues/17516 | ||
addr := sha256.Sum256(append([]byte("x/accounts"), binary.BigEndian.AppendUint64(nil, accNum)...)) | ||
// makeAddress creates an address for the given account. | ||
// It uses the creator address to ensure address squatting cannot happen, for example | ||
// assuming creator sends funds to a new account X nobody can front-run that address instantiation | ||
// unless the creator itself sends the tx. | ||
// AddressSeed can be used to create predictable addresses, security guarantees of the above are retained. | ||
// If address seed is not provided, the address is created using the creator and account number. | ||
func (k Keeper) makeAddress(creator []byte, accNum uint64, addressSeed []byte) ([]byte, error) { | ||
// in case an address seed is provided, we use it to create the address. | ||
var seed []byte | ||
if len(addressSeed) > 0 { | ||
seed = append(creator, addressSeed...) | ||
} else { | ||
// otherwise we use the creator and account number to create the address. | ||
seed = append(creator, binary.BigEndian.AppendUint64(nil, accNum)...) | ||
} | ||
|
||
moduleAndSeed := append([]byte(ModuleName), seed...) | ||
|
||
addr := sha256.Sum256(moduleAndSeed) | ||
|
||
return addr[:], nil | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove redundant nil parameter.
The Init method call includes two nil parameters, which appears to be redundant. One nil parameter should be sufficient for the address_seed.