Skip to content

Commit 35ecb9c

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 a3f9962 commit 35ecb9c

File tree

6 files changed

+93
-9
lines changed

6 files changed

+93
-9
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: 13 additions & 2 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,6 +397,13 @@ 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]
397408
if len(dbAction.MacaroonIdentifier) > 0 {
398409
macID = fn.Some([4]byte(dbAction.MacaroonIdentifier))

firewalldb/actions_test.go

Lines changed: 41 additions & 4 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,17 @@ func TestActionStorage(t *testing.T) {
6061
acct1, err := accountsDB.NewAccount(ctx, 0, time.Time{}, "foo")
6162
require.NoError(t, err)
6263

64+
sess1MacaroonIdentifier := [4]byte(sess1.ID)
65+
sess1MacRootKeyIDBytes := make([]byte, 8)
66+
copy(sess1MacRootKeyIDBytes[4:], sess1MacaroonIdentifier[:])
67+
68+
sess1RootKeyID := binary.BigEndian.Uint64(sess1MacRootKeyIDBytes[:])
69+
6370
action1Req := &AddActionReq{
6471
SessionID: fn.Some(sess1.ID),
6572
AccountID: fn.Some(acct1.ID),
66-
MacaroonIdentifier: fn.Some([4]byte(sess1.ID)),
73+
MacaroonIdentifier: fn.Some(sess1MacaroonIdentifier),
74+
MacaroonRootKeyID: fn.Some(sess1RootKeyID),
6775
ActorName: "Autopilot",
6876
FeatureName: "auto-fees",
6977
Trigger: "fee too low",
@@ -79,9 +87,16 @@ func TestActionStorage(t *testing.T) {
7987
State: ActionStateDone,
8088
}
8189

90+
sess2MacaroonIdentifier := [4]byte(sess2.ID)
91+
sess2MacRootKeyIDBytes := make([]byte, 8)
92+
copy(sess2MacRootKeyIDBytes[4:], sess2MacaroonIdentifier[:])
93+
94+
sess2RootKeyID := binary.BigEndian.Uint64(sess2MacRootKeyIDBytes[:])
95+
8296
action2Req := &AddActionReq{
8397
SessionID: fn.Some(sess2.ID),
84-
MacaroonIdentifier: fn.Some([4]byte(sess2.ID)),
98+
MacaroonIdentifier: fn.Some(sess2MacaroonIdentifier),
99+
MacaroonRootKeyID: fn.Some(sess2RootKeyID),
85100
ActorName: "Autopilot",
86101
FeatureName: "rebalancer",
87102
Trigger: "channels not balanced",
@@ -213,8 +228,16 @@ func TestListActions(t *testing.T) {
213228
addAction := func(sessionID [4]byte) {
214229
actionIds++
215230

231+
sessMacRootKeyIDBytes := make([]byte, 8)
232+
copy(sessMacRootKeyIDBytes[4:], sessionID[:])
233+
234+
sessRootKeyID := binary.BigEndian.Uint64(
235+
sessMacRootKeyIDBytes[:],
236+
)
237+
216238
actionReq := &AddActionReq{
217239
MacaroonIdentifier: fn.Some(sessionID),
240+
MacaroonRootKeyID: fn.Some(sessRootKeyID),
218241
ActorName: "Autopilot",
219242
FeatureName: fmt.Sprintf("%d", actionIds),
220243
Trigger: "fee too low",
@@ -424,9 +447,16 @@ func TestListGroupActions(t *testing.T) {
424447
)
425448
require.NoError(t, err)
426449

450+
sess1MacaroonIdentifier := [4]byte(sess1.ID)
451+
sess1MacRootKeyIDBytes := make([]byte, 8)
452+
copy(sess1MacRootKeyIDBytes[4:], sess1MacaroonIdentifier[:])
453+
454+
sess1RootKeyID := binary.BigEndian.Uint64(sess1MacRootKeyIDBytes[:])
455+
427456
action1Req := &AddActionReq{
428457
SessionID: fn.Some(sess1.ID),
429-
MacaroonIdentifier: fn.Some([4]byte(sess1.ID)),
458+
MacaroonIdentifier: fn.Some(sess1MacaroonIdentifier),
459+
MacaroonRootKeyID: fn.Some(sess1RootKeyID),
430460
ActorName: "Autopilot",
431461
FeatureName: "auto-fees",
432462
Trigger: "fee too low",
@@ -442,9 +472,16 @@ func TestListGroupActions(t *testing.T) {
442472
State: ActionStateDone,
443473
}
444474

475+
sess2MacaroonIdentifier := [4]byte(sess2.ID)
476+
sess2MacRootKeyIDBytes := make([]byte, 8)
477+
copy(sess2MacRootKeyIDBytes[4:], sess2MacaroonIdentifier[:])
478+
479+
sess2RootKeyID := binary.BigEndian.Uint64(sess2MacRootKeyIDBytes[:])
480+
445481
action2Req := &AddActionReq{
446482
SessionID: fn.Some(sess2.ID),
447-
MacaroonIdentifier: fn.Some([4]byte(sess2.ID)),
483+
MacaroonIdentifier: fn.Some(sess2MacaroonIdentifier),
484+
MacaroonRootKeyID: fn.Some(sess2RootKeyID),
448485
ActorName: "Autopilot",
449486
FeatureName: "rebalancer",
450487
Trigger: "channels not balanced",

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)