Skip to content

Commit c56f4fa

Browse files
holimanligi
andauthored
cmd/clef: add newaccount command (#20782)
* cmd/clef: add newaccount command * cmd/clef: document clef_New, update API versioning * Update cmd/clef/intapi_changelog.md Co-Authored-By: ligi <[email protected]> * Update signer/core/uiapi.go Co-Authored-By: ligi <[email protected]> Co-authored-by: ligi <[email protected]>
1 parent 8f05cfa commit c56f4fa

File tree

4 files changed

+78
-5
lines changed

4 files changed

+78
-5
lines changed

cmd/clef/intapi_changelog.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,17 @@ TL;DR: Given a version number MAJOR.MINOR.PATCH, increment the:
1010

1111
Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format.
1212

13+
### 7.0.1
14+
15+
Added `clef_New` to the internal API calleable from a UI.
16+
17+
> `New` creates a new password protected Account. The private key is protected with
18+
> the given password. Users are responsible to backup the private key that is stored
19+
> in the keystore location that was specified when this API was created.
20+
> This method is the same as New on the external API, the difference being that
21+
> this implementation does not ask for confirmation, since it's initiated by
22+
> the user
23+
1324
### 7.0.0
1425

1526
- The `message` field was renamed to `messages` in all data signing request methods to better reflect that it's a list, not a value.

cmd/clef/main.go

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,21 @@ The setpw command stores a password for a given address (keyfile).
187187
Description: `
188188
The delpw command removes a password for a given address (keyfile).
189189
`}
190+
newAccountCommand = cli.Command{
191+
Action: utils.MigrateFlags(newAccount),
192+
Name: "newaccount",
193+
Usage: "Create a new account",
194+
ArgsUsage: "",
195+
Flags: []cli.Flag{
196+
logLevelFlag,
197+
keystoreFlag,
198+
utils.LightKDFFlag,
199+
},
200+
Description: `
201+
The newaccount command creates a new keystore-backed account. It is a convenience-method
202+
which can be used in lieu of an external UI.`,
203+
}
204+
190205
gendocCommand = cli.Command{
191206
Action: GenDoc,
192207
Name: "gendoc",
@@ -222,7 +237,12 @@ func init() {
222237
advancedMode,
223238
}
224239
app.Action = signer
225-
app.Commands = []cli.Command{initCommand, attestCommand, setCredentialCommand, delCredentialCommand, gendocCommand}
240+
app.Commands = []cli.Command{initCommand,
241+
attestCommand,
242+
setCredentialCommand,
243+
delCredentialCommand,
244+
newAccountCommand,
245+
gendocCommand}
226246
cli.CommandHelpTemplate = utils.OriginCommandHelpTemplate
227247
}
228248

@@ -382,6 +402,31 @@ func removeCredential(ctx *cli.Context) error {
382402
return nil
383403
}
384404

405+
func newAccount(c *cli.Context) error {
406+
if err := initialize(c); err != nil {
407+
return err
408+
}
409+
// The newaccount is meant for users using the CLI, since 'real' external
410+
// UIs can use the UI-api instead. So we'll just use the native CLI UI here.
411+
var (
412+
ui = core.NewCommandlineUI()
413+
pwStorage storage.Storage = &storage.NoStorage{}
414+
ksLoc = c.GlobalString(keystoreFlag.Name)
415+
lightKdf = c.GlobalBool(utils.LightKDFFlag.Name)
416+
)
417+
log.Info("Starting clef", "keystore", ksLoc, "light-kdf", lightKdf)
418+
am := core.StartClefAccountManager(ksLoc, true, lightKdf, "")
419+
// This gives is us access to the external API
420+
apiImpl := core.NewSignerAPI(am, 0, true, ui, nil, false, pwStorage)
421+
// This gives us access to the internal API
422+
internalApi := core.NewUIServerAPI(apiImpl)
423+
addr, err := internalApi.New(context.Background())
424+
if err == nil {
425+
fmt.Printf("Generated account %v\n", addr.String())
426+
}
427+
return err
428+
}
429+
385430
func initialize(c *cli.Context) error {
386431
// Set up the logger to print everything
387432
logOutput := os.Stdout
@@ -457,7 +502,6 @@ func signer(c *cli.Context) error {
457502
api core.ExternalAPI
458503
pwStorage storage.Storage = &storage.NoStorage{}
459504
)
460-
461505
configDir := c.GlobalString(configdirFlag.Name)
462506
if stretchedKey, err := readMasterKey(c, ui); err != nil {
463507
log.Warn("Failed to open master, rules disabled", "err", err)

signer/core/api.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ const (
4343
// ExternalAPIVersion -- see extapi_changelog.md
4444
ExternalAPIVersion = "6.0.0"
4545
// InternalAPIVersion -- see intapi_changelog.md
46-
InternalAPIVersion = "7.0.0"
46+
InternalAPIVersion = "7.0.1"
4747
)
4848

4949
// ExternalAPI defines the external API through which signing requests are made.
@@ -395,16 +395,24 @@ func (api *SignerAPI) List(ctx context.Context) ([]common.Address, error) {
395395
// the given password. Users are responsible to backup the private key that is stored
396396
// in the keystore location thas was specified when this API was created.
397397
func (api *SignerAPI) New(ctx context.Context) (common.Address, error) {
398-
be := api.am.Backends(keystore.KeyStoreType)
399-
if len(be) == 0 {
398+
if be := api.am.Backends(keystore.KeyStoreType); len(be) == 0 {
400399
return common.Address{}, errors.New("password based accounts not supported")
401400
}
402401
if resp, err := api.UI.ApproveNewAccount(&NewAccountRequest{MetadataFromContext(ctx)}); err != nil {
403402
return common.Address{}, err
404403
} else if !resp.Approved {
405404
return common.Address{}, ErrRequestDenied
406405
}
406+
return api.newAccount()
407+
}
407408

409+
// newAccount is the internal method to create a new account. It should be used
410+
// _after_ user-approval has been obtained
411+
func (api *SignerAPI) newAccount() (common.Address, error) {
412+
be := api.am.Backends(keystore.KeyStoreType)
413+
if len(be) == 0 {
414+
return common.Address{}, errors.New("password based accounts not supported")
415+
}
408416
// Three retries to get a valid password
409417
for i := 0; i < 3; i++ {
410418
resp, err := api.UI.OnInputRequired(UserInputRequest{

signer/core/uiapi.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,16 @@ func (api *UIServerAPI) Import(ctx context.Context, keyJSON json.RawMessage, old
195195
return be[0].(*keystore.KeyStore).Import(keyJSON, oldPassphrase, newPassphrase)
196196
}
197197

198+
// New creates a new password protected Account. The private key is protected with
199+
// the given password. Users are responsible to backup the private key that is stored
200+
// in the keystore location that was specified when this API was created.
201+
// This method is the same as New on the external API, the difference being that
202+
// this implementation does not ask for confirmation, since it's initiated by
203+
// the user
204+
func (api *UIServerAPI) New(ctx context.Context) (common.Address, error) {
205+
return api.extApi.newAccount()
206+
}
207+
198208
// Other methods to be added, not yet implemented are:
199209
// - Ruleset interaction: add rules, attest rulefiles
200210
// - Store metadata about accounts, e.g. naming of accounts

0 commit comments

Comments
 (0)