@@ -10,6 +10,7 @@ import (
1010 "strings"
1111 "unicode"
1212
13+ "github.com/lightningnetwork/lnd/fn"
1314 "github.com/lightningnetwork/lnd/lncfg"
1415 "github.com/lightningnetwork/lnd/lnrpc"
1516 "github.com/lightningnetwork/lnd/macaroons"
3839 Usage : "the condition of the custom caveat to add, can be " +
3940 "empty if custom caveat doesn't need a value" ,
4041 }
42+ bakeFromRootKeyFlag = cli.StringFlag {
43+ Name : "root_key" ,
44+ Usage : "if the root key is known, it can be passed directly " +
45+ "as a hex encoded string, turning the command into " +
46+ "an offline operation" ,
47+ }
4148)
4249
4350var bakeMacaroonCommand = cli.Command {
@@ -48,7 +55,7 @@ var bakeMacaroonCommand = cli.Command{
4855 ArgsUsage : "[--save_to=] [--timeout=] [--ip_address=] " +
4956 "[--custom_caveat_name= [--custom_caveat_condition=]] " +
5057 "[--root_key_id=] [--allow_external_permissions] " +
51- "permissions..." ,
58+ "[--root_key=] permissions..." ,
5259 Description : `
5360 Bake a new macaroon that grants the provided permissions and
5461 optionally adds restrictions (timeout, IP address) to it.
@@ -74,6 +81,12 @@ var bakeMacaroonCommand = cli.Command{
7481
7582 To get a list of all available URIs and permissions, use the
7683 "lncli listpermissions" command.
84+
85+ If the root key is known (for example because "lncli create" was used
86+ with a custom --mac_root_key value), it can be passed directly as a
87+ hex encoded string using the --root_key flag. This turns the command
88+ into an offline operation and the macaroon will be created without
89+ calling into the server's RPC endpoint.
7790 ` ,
7891 Flags : []cli.Flag {
7992 cli.StringFlag {
@@ -95,14 +108,13 @@ var bakeMacaroonCommand = cli.Command{
95108 Usage : "whether permissions lnd is not familiar with " +
96109 "are allowed" ,
97110 },
111+ bakeFromRootKeyFlag ,
98112 },
99113 Action : actionDecorator (bakeMacaroon ),
100114}
101115
102116func bakeMacaroon (ctx * cli.Context ) error {
103117 ctxc := getContext ()
104- client , cleanUp := getClient (ctx )
105- defer cleanUp ()
106118
107119 // Show command help if no arguments.
108120 if ctx .NArg () == 0 {
@@ -154,36 +166,66 @@ func bakeMacaroon(ctx *cli.Context) error {
154166 )
155167 }
156168
157- // Now we have gathered all the input we need and can do the actual
158- // RPC call.
159- req := & lnrpc.BakeMacaroonRequest {
160- Permissions : parsedPermissions ,
161- RootKeyId : rootKeyID ,
162- AllowExternalPermissions : ctx .Bool ("allow_external_permissions" ),
163- }
164- resp , err := client .BakeMacaroon (ctxc , req )
165- if err != nil {
166- return err
167- }
169+ var rawMacaroon * macaroon.Macaroon
170+ switch {
171+ case ctx .IsSet (bakeFromRootKeyFlag .Name ):
172+ macRootKey , err := hex .DecodeString (
173+ ctx .String (bakeFromRootKeyFlag .Name ),
174+ )
175+ if err != nil {
176+ return fmt .Errorf ("unable to parse macaroon root key: " +
177+ "%w" , err )
178+ }
168179
169- // Now we should have gotten a valid macaroon. Unmarshal it so we can
170- // add first-party caveats (if necessary) to it.
171- macBytes , err := hex .DecodeString (resp .Macaroon )
172- if err != nil {
173- return err
174- }
175- unmarshalMac := & macaroon.Macaroon {}
176- if err = unmarshalMac .UnmarshalBinary (macBytes ); err != nil {
177- return err
180+ ops := fn .Map (func (p * lnrpc.MacaroonPermission ) bakery.Op {
181+ return bakery.Op {
182+ Entity : p .Entity ,
183+ Action : p .Action ,
184+ }
185+ }, parsedPermissions )
186+
187+ rawMacaroon , err = macaroons .BakeFromRootKey (macRootKey , ops )
188+ if err != nil {
189+ return fmt .Errorf ("unable to bake macaroon: %w" , err )
190+ }
191+
192+ default :
193+ client , cleanUp := getClient (ctx )
194+ defer cleanUp ()
195+
196+ // Now we have gathered all the input we need and can do the
197+ // actual RPC call.
198+ req := & lnrpc.BakeMacaroonRequest {
199+ Permissions : parsedPermissions ,
200+ RootKeyId : rootKeyID ,
201+ AllowExternalPermissions : ctx .Bool (
202+ "allow_external_permissions" ,
203+ ),
204+ }
205+ resp , err := client .BakeMacaroon (ctxc , req )
206+ if err != nil {
207+ return err
208+ }
209+
210+ // Now we should have gotten a valid macaroon. Unmarshal it so
211+ // we can add first-party caveats (if necessary) to it.
212+ macBytes , err := hex .DecodeString (resp .Macaroon )
213+ if err != nil {
214+ return err
215+ }
216+ rawMacaroon = & macaroon.Macaroon {}
217+ if err = rawMacaroon .UnmarshalBinary (macBytes ); err != nil {
218+ return err
219+ }
178220 }
179221
180222 // Now apply the desired constraints to the macaroon. This will always
181223 // create a new macaroon object, even if no constraints are added.
182- constrainedMac , err := applyMacaroonConstraints (ctx , unmarshalMac )
224+ constrainedMac , err := applyMacaroonConstraints (ctx , rawMacaroon )
183225 if err != nil {
184226 return err
185227 }
186- macBytes , err = constrainedMac .MarshalBinary ()
228+ macBytes , err : = constrainedMac .MarshalBinary ()
187229 if err != nil {
188230 return err
189231 }
0 commit comments