Skip to content

Commit 87cf67e

Browse files
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 we do not update the kvdb implementation, and the full macaroon root key will be ignored by the kvdb store even if set. Therefore, the rest of `litd` will still have to just expect the last 4 bytes of the mac root key when accessing an `Action`'s MacaroonIdentifier. Therefore, we we currently never expose the rest of the mac root key outside of the sql actions store. Once the kvdb store has been fully deprecated and removed, we can then update the rest of `litd` to also use the full mac root key, and change the `Action` struct's field to reflect this.
1 parent 4f7c6fc commit 87cf67e

File tree

6 files changed

+82
-7
lines changed

6 files changed

+82
-7
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+
litd_macaroons "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 := litd_macaroons.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: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ 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+
MacaroonRootKeyID fn.Option[uint64]
46+
4247
// SessionID holds the optional session ID of the session that this
4348
// action was performed with.
4449
//

firewalldb/actions_sql.go

Lines changed: 16 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,9 +397,17 @@ func unmarshalAction(ctx context.Context, db SQLActionQueries,
393397
legacyAcctID = fn.Some(acctID)
394398
}
395399

400+
// While we store the full 8 byte macaroon root key ID in the sql
401+
// actions DB, the kvdb version only stored the last 4 bytes. So
402+
// we'll only return that here to maintain compatibility with any
403+
// existing callers.
404+
//
405+
// TODO(viktor): Remove this when we no longer need to be compatible
406+
// with the kvdb version.
396407
var macID fn.Option[[4]byte]
397-
if len(dbAction.MacaroonIdentifier) > 0 {
398-
macID = fn.Some([4]byte(dbAction.MacaroonIdentifier))
408+
if len(dbAction.MacaroonIdentifier) >= 4 {
409+
dbMacID := dbAction.MacaroonIdentifier
410+
macID = fn.Some([4]byte(dbMacID[len(dbMacID)-4:]))
399411
}
400412

401413
return &Action{

firewalldb/actions_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package firewalldb
22

33
import (
44
"context"
5+
"encoding/binary"
56
"fmt"
67
"testing"
78
"time"
@@ -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 := macIDToRootKeyID(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 := macIDToRootKeyID(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 := macIDToRootKeyID(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 := macIDToRootKeyID(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 := macIDToRootKeyID(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",
@@ -501,3 +517,14 @@ func TestListGroupActions(t *testing.T) {
501517
assertEqualActions(t, action2, al[0])
502518
assertEqualActions(t, action1, al[1])
503519
}
520+
521+
// macIDToRootKeyID is a helper function for tests that converts a 4 byte
522+
// macaroon ID to a full macaroon root key ID by padding it to 8 bytes.
523+
// Note that the first 4 bytes of the returned root key ID will be zeros,
524+
// followed by the 4 bytes passed in.
525+
func macIDToRootKeyID(macID [4]byte) uint64 {
526+
rootKeyIDBytes := make([]byte, 8)
527+
copy(rootKeyIDBytes[4:], macID[:])
528+
529+
return binary.BigEndian.Uint64(rootKeyIDBytes)
530+
}

firewalldb/test_kvdb.go

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

88
"github.com/lightninglabs/lightning-terminal/session"
99
"github.com/lightningnetwork/lnd/clock"
10+
"github.com/lightningnetwork/lnd/fn"
1011
"github.com/stretchr/testify/require"
1112
)
1213

@@ -59,7 +60,14 @@ func assertEqualActions(t *testing.T, expected, got *Action) {
5960
// Accounts are not explicitly linked in our bbolt DB implementation.
6061
actualAccountID := got.AccountID
6162
got.AccountID = expected.AccountID
63+
64+
// As the kvdb implementation doesn't store the Macaroon Root Key ID,
65+
// we clear the expected value before comparison, and restore it after.
66+
expectedMacRootKey := expected.MacaroonRootKeyID
67+
expected.MacaroonRootKeyID = fn.None[uint64]()
68+
6269
require.Equal(t, expected, got)
6370

6471
got.AccountID = actualAccountID
72+
expected.MacaroonRootKeyID = expectedMacRootKey
6573
}

firewalldb/test_sql.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"github.com/lightninglabs/lightning-terminal/db"
1111
"github.com/lightninglabs/lightning-terminal/session"
1212
"github.com/lightningnetwork/lnd/clock"
13+
"github.com/lightningnetwork/lnd/fn"
1314
"github.com/stretchr/testify/require"
1415
)
1516

@@ -46,11 +47,20 @@ func assertEqualActions(t *testing.T, expected, got *Action) {
4647
expected.AttemptedAt = time.Time{}
4748
got.AttemptedAt = time.Time{}
4849

50+
// As the kvdb implementation doesn't store the Macaroon Root Key ID,
51+
// we don't yet expose this for the sql version, until the kvdb version
52+
// has been deprecated and removed. Therefore, we ignore this field in
53+
// our comparison here, and clear the expected value before comparison,
54+
// and restore it after.
55+
expectedMacRootKey := expected.MacaroonRootKeyID
56+
expected.MacaroonRootKeyID = fn.None[uint64]()
57+
4958
require.Equal(t, expected, got)
5059
require.Equal(t, expectedAttemptedAt.Unix(), actualAttemptedAt.Unix())
5160

5261
expected.AttemptedAt = expectedAttemptedAt
5362
got.AttemptedAt = actualAttemptedAt
63+
expected.MacaroonRootKeyID = expectedMacRootKey
5464
}
5565

5666
// createStore is a helper function that creates a new SQLDB and ensure that

0 commit comments

Comments
 (0)