Skip to content

Commit 651cc67

Browse files
committed
multi: persist full mac root key in sql actions db
When migrating the actions store from kvdb to sql, we will update the existing actions to include the full mac root key, instead of just the last 4 bytes (currently called `MacaroonIdentifier`). In order to do so, we change the sql implementation of the `actions` store to persist the full mac root key, instead of just the last 4 bytes. As no production data in the sql actions store exists for users yet, it's fine for us to change this without having to address old sql actions which only stored the last 4 bytes. Note though that since old actions stored in the kvdb implementation only have the last 4 bytes of the mac root key persisted, we will only ever persist the last 4 byte of the mac root key ID for kvdb actions. When the actions are later read back from the kvdb store, the first 4 bytes of the mac root key ID will be padded with zeroes to make up the full 8 bytes. As no call site currently utilizes the full 8 bytes of the mac root key ID, this is okay for now. When we later deprecate and remove the kvdb implementation, we can then update the rest of `litd` to also use the full mac root key ID.
1 parent 580ce70 commit 651cc67

File tree

6 files changed

+84
-8
lines changed

6 files changed

+84
-8
lines changed

firewall/request_logger.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"sync"
88

99
"github.com/lightninglabs/lightning-terminal/firewalldb"
10+
litmac "github.com/lightninglabs/lightning-terminal/macaroons"
1011
mid "github.com/lightninglabs/lightning-terminal/rpcmiddleware"
1112
"github.com/lightninglabs/lightning-terminal/session"
1213
"github.com/lightningnetwork/lnd/fn"
@@ -182,21 +183,33 @@ func (r *RequestLogger) Intercept(ctx context.Context,
182183
func (r *RequestLogger) addNewAction(ctx context.Context, ri *RequestInfo,
183184
withPayloadData bool) error {
184185

185-
var macaroonID fn.Option[[4]byte]
186+
var (
187+
rootKeyID fn.Option[uint64]
188+
macaroonID fn.Option[[4]byte]
189+
)
190+
186191
if ri.Macaroon != nil {
187192
var err error
188-
macID, err := session.IDFromMacaroon(ri.Macaroon)
193+
194+
fullRootKeyID, err := litmac.RootKeyIDFromMacaroon(
195+
ri.Macaroon,
196+
)
189197
if err != nil {
190-
return fmt.Errorf("could not extract ID from macaroon")
198+
return fmt.Errorf("could not extract root key ID from "+
199+
"macaroon: %w", err)
191200
}
192201

202+
macID := session.IDFromMacRootKeyID(fullRootKeyID)
203+
204+
rootKeyID = fn.Some(fullRootKeyID)
193205
macaroonID = fn.Some([4]byte(macID))
194206
}
195207

196208
actionReq := &firewalldb.AddActionReq{
197209
SessionID: ri.SessionID,
198210
AccountID: ri.AccountID,
199211
MacaroonIdentifier: macaroonID,
212+
MacaroonRootKeyID: rootKeyID,
200213
RPCMethod: ri.URI,
201214
}
202215

firewalldb/actions.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,15 @@ type AddActionReq struct {
3939
// If no macaroon was used for the action, then this will not be set.
4040
MacaroonIdentifier fn.Option[[4]byte]
4141

42+
// MacaroonRootKeyID is the uint64 / full 8 bytes of the root key ID of
43+
// the macaroon used to perform the action.
44+
// If no macaroon was used for the action, then this will not be set.
45+
//
46+
// NOTE: for our BoltDB impl, only the lower 32 bits / last 4 bytes of
47+
// this uint64 are stored. When read back, the upper 32 bits / first 4
48+
// bytes are zeroed.
49+
MacaroonRootKeyID fn.Option[uint64]
50+
4251
// SessionID holds the optional session ID of the session that this
4352
// action was performed with.
4453
//

firewalldb/actions_kvdb.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -596,7 +596,13 @@ func DeserializeAction(r io.Reader, sessionID session.ID) (*Action, error) {
596596
return nil, err
597597
}
598598

599+
// Since the kvdb only persists 4 bytes for the macaroon root key ID, we
600+
// first cast it to a uint32, and then to a uint64, effectively padding
601+
// the first 4 bytes with zeroes.
602+
rootKeyID := uint64(binary.BigEndian.Uint32(sessionID[:]))
603+
599604
action.MacaroonIdentifier = fn.Some([4]byte(sessionID))
605+
action.MacaroonRootKeyID = fn.Some(rootKeyID)
600606
action.SessionID = fn.Some(sessionID)
601607
action.ActorName = string(actor)
602608
action.FeatureName = string(featureName)

firewalldb/actions_sql.go

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package firewalldb
33
import (
44
"context"
55
"database/sql"
6+
"encoding/binary"
67
"errors"
78
"fmt"
89
"math"
@@ -140,8 +141,11 @@ func (s *SQLDB) AddAction(ctx context.Context,
140141
}
141142

142143
var macID []byte
143-
req.MacaroonIdentifier.WhenSome(func(id [4]byte) {
144-
macID = id[:]
144+
req.MacaroonRootKeyID.WhenSome(func(rootKeyID uint64) {
145+
rootKeyBytes := make([]byte, 8)
146+
binary.BigEndian.PutUint64(rootKeyBytes[:], rootKeyID)
147+
148+
macID = rootKeyBytes
145149
})
146150

147151
id, err := db.InsertAction(ctx, sqlc.InsertActionParams{
@@ -393,14 +397,24 @@ func unmarshalAction(ctx context.Context, db SQLActionQueries,
393397
legacyAcctID = fn.Some(acctID)
394398
}
395399

400+
// Note that we export the full 8 byte macaroon root key ID in the sql
401+
// actions DB, while the kvdb version persists and exports stored the
402+
// last 4 bytes only.
396403
var macID fn.Option[[4]byte]
397-
if len(dbAction.MacaroonIdentifier) > 0 {
398-
macID = fn.Some([4]byte(dbAction.MacaroonIdentifier))
404+
var macRootKeyID fn.Option[uint64]
405+
if len(dbAction.MacaroonIdentifier) >= 4 {
406+
dbMacID := dbAction.MacaroonIdentifier
407+
macID = fn.Some([4]byte(dbMacID[len(dbMacID)-4:]))
408+
409+
if len(dbAction.MacaroonIdentifier) >= 8 {
410+
macRootKeyID = fn.Some(binary.BigEndian.Uint64(dbMacID))
411+
}
399412
}
400413

401414
return &Action{
402415
AddActionReq: AddActionReq{
403416
MacaroonIdentifier: macID,
417+
MacaroonRootKeyID: macRootKeyID,
404418
AccountID: legacyAcctID,
405419
SessionID: legacySessID,
406420
ActorName: dbAction.ActorName.String,

firewalldb/actions_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"time"
88

99
"github.com/lightninglabs/lightning-terminal/accounts"
10+
litmac "github.com/lightninglabs/lightning-terminal/macaroons"
1011
"github.com/lightninglabs/lightning-terminal/session"
1112
"github.com/lightningnetwork/lnd/clock"
1213
"github.com/lightningnetwork/lnd/fn"
@@ -60,10 +61,13 @@ func TestActionStorage(t *testing.T) {
6061
acct1, err := accountsDB.NewAccount(ctx, 0, time.Time{}, "foo")
6162
require.NoError(t, err)
6263

64+
sess1RootKeyID := litmac.NewSuperMacaroonRootKeyID(sess1.ID)
65+
6366
action1Req := &AddActionReq{
6467
SessionID: fn.Some(sess1.ID),
6568
AccountID: fn.Some(acct1.ID),
6669
MacaroonIdentifier: fn.Some([4]byte(sess1.ID)),
70+
MacaroonRootKeyID: fn.Some(sess1RootKeyID),
6771
ActorName: "Autopilot",
6872
FeatureName: "auto-fees",
6973
Trigger: "fee too low",
@@ -79,9 +83,12 @@ func TestActionStorage(t *testing.T) {
7983
State: ActionStateDone,
8084
}
8185

86+
sess2RootKeyID := litmac.NewSuperMacaroonRootKeyID(sess2.ID)
87+
8288
action2Req := &AddActionReq{
8389
SessionID: fn.Some(sess2.ID),
8490
MacaroonIdentifier: fn.Some([4]byte(sess2.ID)),
91+
MacaroonRootKeyID: fn.Some(sess2RootKeyID),
8592
ActorName: "Autopilot",
8693
FeatureName: "rebalancer",
8794
Trigger: "channels not balanced",
@@ -213,8 +220,11 @@ func TestListActions(t *testing.T) {
213220
addAction := func(sessionID [4]byte) {
214221
actionIds++
215222

223+
sessRootKeyID := litmac.NewSuperMacaroonRootKeyID(sessionID)
224+
216225
actionReq := &AddActionReq{
217226
MacaroonIdentifier: fn.Some(sessionID),
227+
MacaroonRootKeyID: fn.Some(sessRootKeyID),
218228
ActorName: "Autopilot",
219229
FeatureName: fmt.Sprintf("%d", actionIds),
220230
Trigger: "fee too low",
@@ -424,9 +434,12 @@ func TestListGroupActions(t *testing.T) {
424434
)
425435
require.NoError(t, err)
426436

437+
sess1RootKeyID := litmac.NewSuperMacaroonRootKeyID(sess1.ID)
438+
427439
action1Req := &AddActionReq{
428440
SessionID: fn.Some(sess1.ID),
429441
MacaroonIdentifier: fn.Some([4]byte(sess1.ID)),
442+
MacaroonRootKeyID: fn.Some(sess1RootKeyID),
430443
ActorName: "Autopilot",
431444
FeatureName: "auto-fees",
432445
Trigger: "fee too low",
@@ -442,9 +455,12 @@ func TestListGroupActions(t *testing.T) {
442455
State: ActionStateDone,
443456
}
444457

458+
sess2RootKeyID := litmac.NewSuperMacaroonRootKeyID(sess2.ID)
459+
445460
action2Req := &AddActionReq{
446461
SessionID: fn.Some(sess2.ID),
447462
MacaroonIdentifier: fn.Some([4]byte(sess2.ID)),
463+
MacaroonRootKeyID: fn.Some(sess2RootKeyID),
448464
ActorName: "Autopilot",
449465
FeatureName: "rebalancer",
450466
Trigger: "channels not balanced",

firewalldb/test_kvdb.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
package firewalldb
44

55
import (
6+
"encoding/binary"
67
"testing"
78

89
"github.com/lightninglabs/lightning-terminal/session"
910
"github.com/lightningnetwork/lnd/clock"
11+
"github.com/lightningnetwork/lnd/fn"
1012
"github.com/stretchr/testify/require"
1113
)
1214

@@ -59,7 +61,23 @@ func assertEqualActions(t *testing.T, expected, got *Action) {
5961
// Accounts are not explicitly linked in our bbolt DB implementation.
6062
actualAccountID := got.AccountID
6163
got.AccountID = expected.AccountID
62-
require.Equal(t, expected, got)
6364

65+
// As the kvdb implementation only stores the last 4 bytes Macaroon Root
66+
// Key ID, we pad it with 4 zero bytes when comparing.
67+
expectedMacRootKey := expected.MacaroonRootKeyID
68+
69+
expectedMacRootKey.WhenSome(func(rootID uint64) {
70+
// Remove the 4 byte prefix of the actual Macaroon Root Key ID.
71+
sessID := session.IDFromMacRootKeyID(rootID)
72+
73+
// Recreate the full 8 byte Macaroon Root Key ID (represented as
74+
// a uint64) by padding the first 4 bytes with zeroes.
75+
expected.MacaroonRootKeyID = fn.Some(
76+
uint64(binary.BigEndian.Uint32(sessID[:])),
77+
)
78+
})
79+
80+
require.Equal(t, expected, got)
6481
got.AccountID = actualAccountID
82+
expected.MacaroonRootKeyID = expectedMacRootKey
6583
}

0 commit comments

Comments
 (0)