diff --git a/graph/db/graph_test.go b/graph/db/graph_test.go index 02e96dfc9..5aec3c2e4 100644 --- a/graph/db/graph_test.go +++ b/graph/db/graph_test.go @@ -77,7 +77,7 @@ func createLightningNode(priv *btcec.PrivateKey) *models.LightningNode { n := &models.LightningNode{ HaveNodeAnnouncement: true, AuthSigBytes: testSig.Serialize(), - LastUpdate: time.Unix(nextUpdateTime(), 0), + LastUpdate: nextUpdateTime(), Color: color.RGBA{1, 2, 3, 0}, Alias: "kek" + hex.EncodeToString(pub), Features: testFeatures, @@ -784,7 +784,7 @@ func createChannelEdge(node1, node2 *models.LightningNode, edge1 := &models.ChannelEdgePolicy{ SigBytes: testSig.Serialize(), ChannelID: chanID, - LastUpdate: time.Unix(433453, 0), + LastUpdate: nextUpdateTime(), MessageFlags: 1, ChannelFlags: 0, TimeLockDelta: 99, @@ -798,7 +798,7 @@ func createChannelEdge(node1, node2 *models.LightningNode, edge2 := &models.ChannelEdgePolicy{ SigBytes: testSig.Serialize(), ChannelID: chanID, - LastUpdate: time.Unix(124234, 0), + LastUpdate: nextUpdateTime(), MessageFlags: 1, ChannelFlags: 1, TimeLockDelta: 99, @@ -908,6 +908,72 @@ func TestEdgeInfoUpdates(t *testing.T) { assertEdgeInfoEqual(t, dbEdgeInfo, edgeInfo) } +// TestEdgePolicyCRUD tests basic CRUD operations for edge policies. +func TestEdgePolicyCRUD(t *testing.T) { + t.Parallel() + ctx := context.Background() + + graph := MakeTestGraph(t) + + node1 := createTestVertex(t) + node2 := createTestVertex(t) + + // Create an edge. Don't add it to the DB yet. + edgeInfo, edge1, edge2 := createChannelEdge(node1, node2) + + updateAndAssertPolicies := func() { + // Make copies of the policies before calling UpdateEdgePolicy + // to avoid any data race's that can occur during async calls + // that UpdateEdgePolicy may trigger. + edge1 := copyEdgePolicy(edge1) + edge2 := copyEdgePolicy(edge2) + + edge1.LastUpdate = nextUpdateTime() + edge2.LastUpdate = nextUpdateTime() + + require.NoError(t, graph.UpdateEdgePolicy(ctx, edge1)) + require.NoError(t, graph.UpdateEdgePolicy(ctx, edge2)) + + // Use the ForEachChannel method to fetch the policies and + // assert that the deserialized policies match the original + // ones. + err := graph.ForEachChannel(func(info *models.ChannelEdgeInfo, + policy1 *models.ChannelEdgePolicy, + policy2 *models.ChannelEdgePolicy) error { + + require.NoError(t, compareEdgePolicies(edge1, policy1)) + require.NoError(t, compareEdgePolicies(edge2, policy2)) + + return nil + }) + require.NoError(t, err) + } + + // Make sure inserting the policy at this point, before the edge info + // is added, will fail. + require.ErrorIs(t, graph.UpdateEdgePolicy(ctx, edge1), ErrEdgeNotFound) + + // Now add the edge. + require.NoError(t, graph.AddChannelEdge(ctx, edgeInfo)) + + updateAndAssertPolicies() + + // Update one of the edges to have no extra opaque data. + edge1.ExtraOpaqueData = nil + + updateAndAssertPolicies() + + // Update one of the edges to have ChannelFlags include a bit unknown + // to us. + edge1.ChannelFlags |= 1 << 6 + + // Update the other edge to have MessageFlags include a bit unknown to + // us. + edge2.MessageFlags |= 1 << 4 + + updateAndAssertPolicies() +} + func assertNodeInCache(t *testing.T, g *ChannelGraph, n *models.LightningNode, expectedFeatures *lnwire.FeatureVector) { @@ -3455,13 +3521,13 @@ var ( updateTimeMu sync.Mutex ) -func nextUpdateTime() int64 { +func nextUpdateTime() time.Time { updateTimeMu.Lock() defer updateTimeMu.Unlock() updateTime++ - return updateTime + return time.Unix(updateTime, 0) } // TestNodeIsPublic ensures that we properly detect nodes that are seen as @@ -3506,7 +3572,7 @@ func TestNodeIsPublic(t *testing.T) { graphs := []*ChannelGraph{aliceGraph, bobGraph, carolGraph} for _, graph := range graphs { for _, node := range nodes { - node.LastUpdate = time.Unix(nextUpdateTime(), 0) + node.LastUpdate = nextUpdateTime() err := graph.AddLightningNode(ctx, node) require.NoError(t, err) } @@ -3621,7 +3687,7 @@ func TestDisabledChannelIDs(t *testing.T) { // Adding a new channel edge to the graph. edgeInfo, edge1, edge2 := createChannelEdge(node1, node2) - node2.LastUpdate = time.Unix(nextUpdateTime(), 0) + node2.LastUpdate = nextUpdateTime() if err := graph.AddLightningNode(ctx, node2); err != nil { t.Fatalf("unable to add node: %v", err) } @@ -4114,7 +4180,7 @@ func TestBatchedUpdateEdgePolicy(t *testing.T) { // Make sure inserting the policy at this point, before the edge info // is added, will fail. - require.Error(t, ErrEdgeNotFound, graph.UpdateEdgePolicy(ctx, edge1)) + require.ErrorIs(t, graph.UpdateEdgePolicy(ctx, edge1), ErrEdgeNotFound) // Add the edge info. require.NoError(t, graph.AddChannelEdge(ctx, edgeInfo)) diff --git a/graph/db/sql_store.go b/graph/db/sql_store.go index 5ff8b7a1d..8f3a48b85 100644 --- a/graph/db/sql_store.go +++ b/graph/db/sql_store.go @@ -1271,7 +1271,7 @@ func (s *SQLStore) ForEachChannelCacheable(cb func(*models.CachedEdgeInfo, var pol1, pol2 *models.CachedEdgePolicy if dbPol1 != nil { policy1, err := buildChanPolicy( - *dbPol1, edge.ChannelID, nil, node2, true, + *dbPol1, edge.ChannelID, nil, node2, ) if err != nil { return err @@ -1281,7 +1281,7 @@ func (s *SQLStore) ForEachChannelCacheable(cb func(*models.CachedEdgeInfo, } if dbPol2 != nil { policy2, err := buildChanPolicy( - *dbPol2, edge.ChannelID, nil, node1, false, + *dbPol2, edge.ChannelID, nil, node1, ) if err != nil { return err @@ -2900,7 +2900,7 @@ func forEachNodeDirectedChannel(ctx context.Context, db SQLQueries, var p1, p2 *models.CachedEdgePolicy if dbPol1 != nil { policy1, err := buildChanPolicy( - *dbPol1, edge.ChannelID, nil, node2, true, + *dbPol1, edge.ChannelID, nil, node2, ) if err != nil { return err @@ -2910,7 +2910,7 @@ func forEachNodeDirectedChannel(ctx context.Context, db SQLQueries, } if dbPol2 != nil { policy2, err := buildChanPolicy( - *dbPol2, edge.ChannelID, nil, node1, false, + *dbPol2, edge.ChannelID, nil, node1, ) if err != nil { return err @@ -3138,6 +3138,8 @@ func updateChanEdgePolicy(ctx context.Context, tx SQLQueries, Valid: edge.MessageFlags.HasMaxHtlc(), Int64: int64(edge.MaxHTLC), }, + MessageFlags: sqldb.SQLInt16(edge.MessageFlags), + ChannelFlags: sqldb.SQLInt16(edge.ChannelFlags), InboundBaseFeeMsat: inboundBase, InboundFeeRateMilliMsat: inboundRate, Signature: edge.SigBytes, @@ -4135,7 +4137,7 @@ func getAndBuildChanPolicies(ctx context.Context, db SQLQueries, var pol1, pol2 *models.ChannelEdgePolicy if dbPol1 != nil { pol1, err = buildChanPolicy( - *dbPol1, channelID, dbPol1Extras, node2, true, + *dbPol1, channelID, dbPol1Extras, node2, ) if err != nil { return nil, nil, err @@ -4143,7 +4145,7 @@ func getAndBuildChanPolicies(ctx context.Context, db SQLQueries, } if dbPol2 != nil { pol2, err = buildChanPolicy( - *dbPol2, channelID, dbPol2Extras, node1, false, + *dbPol2, channelID, dbPol2Extras, node1, ) if err != nil { return nil, nil, err @@ -4156,8 +4158,8 @@ func getAndBuildChanPolicies(ctx context.Context, db SQLQueries, // buildChanPolicy builds a models.ChannelEdgePolicy instance from the // provided sqlc.ChannelPolicy and other required information. func buildChanPolicy(dbPolicy sqlc.ChannelPolicy, channelID uint64, - extras map[uint64][]byte, toNode route.Vertex, - isNode1 bool) (*models.ChannelEdgePolicy, error) { + extras map[uint64][]byte, + toNode route.Vertex) (*models.ChannelEdgePolicy, error) { recs, err := lnwire.CustomRecords(extras).Serialize() if err != nil { @@ -4165,19 +4167,6 @@ func buildChanPolicy(dbPolicy sqlc.ChannelPolicy, channelID uint64, "fields: %w", err) } - var msgFlags lnwire.ChanUpdateMsgFlags - if dbPolicy.MaxHtlcMsat.Valid { - msgFlags |= lnwire.ChanUpdateRequiredMaxHtlc - } - - var chanFlags lnwire.ChanUpdateChanFlags - if !isNode1 { - chanFlags |= lnwire.ChanUpdateDirection - } - if dbPolicy.Disabled.Bool { - chanFlags |= lnwire.ChanUpdateDisabled - } - var inboundFee fn.Option[lnwire.Fee] if dbPolicy.InboundFeeRateMilliMsat.Valid || dbPolicy.InboundBaseFeeMsat.Valid { @@ -4194,8 +4183,12 @@ func buildChanPolicy(dbPolicy sqlc.ChannelPolicy, channelID uint64, LastUpdate: time.Unix( dbPolicy.LastUpdate.Int64, 0, ), - MessageFlags: msgFlags, - ChannelFlags: chanFlags, + MessageFlags: sqldb.ExtractSqlInt16[lnwire.ChanUpdateMsgFlags]( + dbPolicy.MessageFlags, + ), + ChannelFlags: sqldb.ExtractSqlInt16[lnwire.ChanUpdateChanFlags]( + dbPolicy.ChannelFlags, + ), TimeLockDelta: uint16(dbPolicy.Timelock), MinHTLC: lnwire.MilliSatoshi( dbPolicy.MinHtlcMsat, @@ -4259,6 +4252,8 @@ func extractChannelPolicies(row any) (*sqlc.ChannelPolicy, *sqlc.ChannelPolicy, InboundBaseFeeMsat: r.Policy1InboundBaseFeeMsat, InboundFeeRateMilliMsat: r.Policy1InboundFeeRateMilliMsat, Disabled: r.Policy1Disabled, + MessageFlags: r.Policy1MessageFlags, + ChannelFlags: r.Policy1ChannelFlags, Signature: r.Policy1Signature, } } @@ -4277,6 +4272,8 @@ func extractChannelPolicies(row any) (*sqlc.ChannelPolicy, *sqlc.ChannelPolicy, InboundBaseFeeMsat: r.Policy2InboundBaseFeeMsat, InboundFeeRateMilliMsat: r.Policy2InboundFeeRateMilliMsat, Disabled: r.Policy2Disabled, + MessageFlags: r.Policy2MessageFlags, + ChannelFlags: r.Policy2ChannelFlags, Signature: r.Policy2Signature, } } @@ -4299,6 +4296,8 @@ func extractChannelPolicies(row any) (*sqlc.ChannelPolicy, *sqlc.ChannelPolicy, InboundBaseFeeMsat: r.Policy1InboundBaseFeeMsat, InboundFeeRateMilliMsat: r.Policy1InboundFeeRateMilliMsat, Disabled: r.Policy1Disabled, + MessageFlags: r.Policy1MessageFlags, + ChannelFlags: r.Policy1ChannelFlags, Signature: r.Policy1Signature, } } @@ -4317,6 +4316,8 @@ func extractChannelPolicies(row any) (*sqlc.ChannelPolicy, *sqlc.ChannelPolicy, InboundBaseFeeMsat: r.Policy2InboundBaseFeeMsat, InboundFeeRateMilliMsat: r.Policy2InboundFeeRateMilliMsat, Disabled: r.Policy2Disabled, + MessageFlags: r.Policy2MessageFlags, + ChannelFlags: r.Policy2ChannelFlags, Signature: r.Policy2Signature, } } @@ -4339,6 +4340,8 @@ func extractChannelPolicies(row any) (*sqlc.ChannelPolicy, *sqlc.ChannelPolicy, InboundBaseFeeMsat: r.Policy1InboundBaseFeeMsat, InboundFeeRateMilliMsat: r.Policy1InboundFeeRateMilliMsat, Disabled: r.Policy1Disabled, + MessageFlags: r.Policy1MessageFlags, + ChannelFlags: r.Policy1ChannelFlags, Signature: r.Policy1Signature, } } @@ -4357,6 +4360,8 @@ func extractChannelPolicies(row any) (*sqlc.ChannelPolicy, *sqlc.ChannelPolicy, InboundBaseFeeMsat: r.Policy2InboundBaseFeeMsat, InboundFeeRateMilliMsat: r.Policy2InboundFeeRateMilliMsat, Disabled: r.Policy2Disabled, + MessageFlags: r.Policy2MessageFlags, + ChannelFlags: r.Policy2ChannelFlags, Signature: r.Policy2Signature, } } @@ -4379,6 +4384,8 @@ func extractChannelPolicies(row any) (*sqlc.ChannelPolicy, *sqlc.ChannelPolicy, InboundBaseFeeMsat: r.Policy1InboundBaseFeeMsat, InboundFeeRateMilliMsat: r.Policy1InboundFeeRateMilliMsat, Disabled: r.Policy1Disabled, + MessageFlags: r.Policy1MessageFlags, + ChannelFlags: r.Policy1ChannelFlags, Signature: r.Policy1Signature, } } @@ -4397,6 +4404,8 @@ func extractChannelPolicies(row any) (*sqlc.ChannelPolicy, *sqlc.ChannelPolicy, InboundBaseFeeMsat: r.Policy2InboundBaseFeeMsat, InboundFeeRateMilliMsat: r.Policy2InboundFeeRateMilliMsat, Disabled: r.Policy2Disabled, + MessageFlags: r.Policy2MessageFlags, + ChannelFlags: r.Policy2ChannelFlags, Signature: r.Policy2Signature, } } @@ -4419,6 +4428,8 @@ func extractChannelPolicies(row any) (*sqlc.ChannelPolicy, *sqlc.ChannelPolicy, InboundBaseFeeMsat: r.Policy1InboundBaseFeeMsat, InboundFeeRateMilliMsat: r.Policy1InboundFeeRateMilliMsat, Disabled: r.Policy1Disabled, + MessageFlags: r.Policy1MessageFlags, + ChannelFlags: r.Policy1ChannelFlags, Signature: r.Policy1Signature, } } @@ -4437,6 +4448,8 @@ func extractChannelPolicies(row any) (*sqlc.ChannelPolicy, *sqlc.ChannelPolicy, InboundBaseFeeMsat: r.Policy2InboundBaseFeeMsat, InboundFeeRateMilliMsat: r.Policy2InboundFeeRateMilliMsat, Disabled: r.Policy2Disabled, + MessageFlags: r.Policy2MessageFlags, + ChannelFlags: r.Policy2ChannelFlags, Signature: r.Policy2Signature, } } diff --git a/graph/db/test_postgres.go b/graph/db/test_postgres.go index 11144eb3b..8067756e2 100644 --- a/graph/db/test_postgres.go +++ b/graph/db/test_postgres.go @@ -12,8 +12,7 @@ import ( ) // NewTestDB is a helper function that creates a SQLStore backed by a postgres -// database for testing. At the moment, it embeds a KVStore but once the -// SQLStore fully implements the V1Store interface, the KVStore will be removed. +// database for testing. func NewTestDB(t testing.TB) V1Store { pgFixture := sqldb.NewTestPgFixture( t, sqldb.DefaultPostgresFixtureLifetime, diff --git a/graph/db/test_sqlite.go b/graph/db/test_sqlite.go index f9cb5a46d..76b59c84b 100644 --- a/graph/db/test_sqlite.go +++ b/graph/db/test_sqlite.go @@ -12,8 +12,7 @@ import ( ) // NewTestDB is a helper function that creates a SQLStore backed by a sqlite -// database for testing. At the moment, it embeds a KVStore but once the -// SQLStore fully implements the V1Store interface, the KVStore will be removed. +// database for testing. func NewTestDB(t testing.TB) V1Store { db := sqldb.NewTestSqliteDB(t).BaseDB diff --git a/sqldb/sqlc/graph.sql.go b/sqldb/sqlc/graph.sql.go index 0387f612e..639696d0f 100644 --- a/sqldb/sqlc/graph.sql.go +++ b/sqldb/sqlc/graph.sql.go @@ -377,6 +377,8 @@ SELECT cp1.disabled AS policy_1_disabled, cp1.inbound_base_fee_msat AS policy1_inbound_base_fee_msat, cp1.inbound_fee_rate_milli_msat AS policy1_inbound_fee_rate_milli_msat, + cp1.message_flags AS policy_1_message_flags, + cp1.channel_flags AS policy_1_channel_flags, cp1.signature AS policy_1_signature, -- Node 2 policy @@ -392,6 +394,8 @@ SELECT cp2.disabled AS policy_2_disabled, cp2.inbound_base_fee_msat AS policy2_inbound_base_fee_msat, cp2.inbound_fee_rate_milli_msat AS policy2_inbound_fee_rate_milli_msat, + cp2.message_flags AS policy_2_message_flags, + cp2.channel_flags AS policy_2_channel_flags, cp2.signature AS policy_2_signature FROM channels c JOIN nodes n1 ON c.node_id_1 = n1.id @@ -424,6 +428,8 @@ type GetChannelByOutpointWithPoliciesRow struct { Policy1Disabled sql.NullBool Policy1InboundBaseFeeMsat sql.NullInt64 Policy1InboundFeeRateMilliMsat sql.NullInt64 + Policy1MessageFlags sql.NullInt16 + Policy1ChannelFlags sql.NullInt16 Policy1Signature []byte Policy2ID sql.NullInt64 Policy2NodeID sql.NullInt64 @@ -437,6 +443,8 @@ type GetChannelByOutpointWithPoliciesRow struct { Policy2Disabled sql.NullBool Policy2InboundBaseFeeMsat sql.NullInt64 Policy2InboundFeeRateMilliMsat sql.NullInt64 + Policy2MessageFlags sql.NullInt16 + Policy2ChannelFlags sql.NullInt16 Policy2Signature []byte } @@ -471,6 +479,8 @@ func (q *Queries) GetChannelByOutpointWithPolicies(ctx context.Context, arg GetC &i.Policy1Disabled, &i.Policy1InboundBaseFeeMsat, &i.Policy1InboundFeeRateMilliMsat, + &i.Policy1MessageFlags, + &i.Policy1ChannelFlags, &i.Policy1Signature, &i.Policy2ID, &i.Policy2NodeID, @@ -484,6 +494,8 @@ func (q *Queries) GetChannelByOutpointWithPolicies(ctx context.Context, arg GetC &i.Policy2Disabled, &i.Policy2InboundBaseFeeMsat, &i.Policy2InboundFeeRateMilliMsat, + &i.Policy2MessageFlags, + &i.Policy2ChannelFlags, &i.Policy2Signature, ) return i, err @@ -539,6 +551,8 @@ SELECT cp1.disabled AS policy1_disabled, cp1.inbound_base_fee_msat AS policy1_inbound_base_fee_msat, cp1.inbound_fee_rate_milli_msat AS policy1_inbound_fee_rate_milli_msat, + cp1.message_flags AS policy1_message_flags, + cp1.channel_flags AS policy1_channel_flags, cp1.signature AS policy1_signature, -- Policy 2 @@ -554,6 +568,8 @@ SELECT cp2.disabled AS policy2_disabled, cp2.inbound_base_fee_msat AS policy2_inbound_base_fee_msat, cp2.inbound_fee_rate_milli_msat AS policy2_inbound_fee_rate_milli_msat, + cp2.message_flags AS policy_2_message_flags, + cp2.channel_flags AS policy_2_channel_flags, cp2.signature AS policy2_signature FROM channels c @@ -588,6 +604,8 @@ type GetChannelBySCIDWithPoliciesRow struct { Policy1Disabled sql.NullBool Policy1InboundBaseFeeMsat sql.NullInt64 Policy1InboundFeeRateMilliMsat sql.NullInt64 + Policy1MessageFlags sql.NullInt16 + Policy1ChannelFlags sql.NullInt16 Policy1Signature []byte Policy2ID sql.NullInt64 Policy2NodeID sql.NullInt64 @@ -601,6 +619,8 @@ type GetChannelBySCIDWithPoliciesRow struct { Policy2Disabled sql.NullBool Policy2InboundBaseFeeMsat sql.NullInt64 Policy2InboundFeeRateMilliMsat sql.NullInt64 + Policy2MessageFlags sql.NullInt16 + Policy2ChannelFlags sql.NullInt16 Policy2Signature []byte } @@ -647,6 +667,8 @@ func (q *Queries) GetChannelBySCIDWithPolicies(ctx context.Context, arg GetChann &i.Policy1Disabled, &i.Policy1InboundBaseFeeMsat, &i.Policy1InboundFeeRateMilliMsat, + &i.Policy1MessageFlags, + &i.Policy1ChannelFlags, &i.Policy1Signature, &i.Policy2ID, &i.Policy2NodeID, @@ -660,6 +682,8 @@ func (q *Queries) GetChannelBySCIDWithPolicies(ctx context.Context, arg GetChann &i.Policy2Disabled, &i.Policy2InboundBaseFeeMsat, &i.Policy2InboundFeeRateMilliMsat, + &i.Policy2MessageFlags, + &i.Policy2ChannelFlags, &i.Policy2Signature, ) return i, err @@ -725,7 +749,7 @@ func (q *Queries) GetChannelFeaturesAndExtras(ctx context.Context, channelID int } const getChannelPolicyByChannelAndNode = `-- name: GetChannelPolicyByChannelAndNode :one -SELECT id, version, channel_id, node_id, timelock, fee_ppm, base_fee_msat, min_htlc_msat, max_htlc_msat, last_update, disabled, inbound_base_fee_msat, inbound_fee_rate_milli_msat, signature +SELECT id, version, channel_id, node_id, timelock, fee_ppm, base_fee_msat, min_htlc_msat, max_htlc_msat, last_update, disabled, inbound_base_fee_msat, inbound_fee_rate_milli_msat, message_flags, channel_flags, signature FROM channel_policies WHERE channel_id = $1 AND node_id = $2 @@ -755,6 +779,8 @@ func (q *Queries) GetChannelPolicyByChannelAndNode(ctx context.Context, arg GetC &i.Disabled, &i.InboundBaseFeeMsat, &i.InboundFeeRateMilliMsat, + &i.MessageFlags, + &i.ChannelFlags, &i.Signature, ) return i, err @@ -834,6 +860,8 @@ SELECT cp1.disabled AS policy1_disabled, cp1.inbound_base_fee_msat AS policy1_inbound_base_fee_msat, cp1.inbound_fee_rate_milli_msat AS policy1_inbound_fee_rate_milli_msat, + cp1.message_flags AS policy1_message_flags, + cp1.channel_flags AS policy1_channel_flags, cp1.signature AS policy1_signature, -- Policy 2 (node_id_2) @@ -849,6 +877,8 @@ SELECT cp2.disabled AS policy2_disabled, cp2.inbound_base_fee_msat AS policy2_inbound_base_fee_msat, cp2.inbound_fee_rate_milli_msat AS policy2_inbound_fee_rate_milli_msat, + cp2.message_flags AS policy2_message_flags, + cp2.channel_flags AS policy2_channel_flags, cp2.signature AS policy2_signature FROM channels c @@ -894,6 +924,8 @@ type GetChannelsByPolicyLastUpdateRangeRow struct { Policy1Disabled sql.NullBool Policy1InboundBaseFeeMsat sql.NullInt64 Policy1InboundFeeRateMilliMsat sql.NullInt64 + Policy1MessageFlags sql.NullInt16 + Policy1ChannelFlags sql.NullInt16 Policy1Signature []byte Policy2ID sql.NullInt64 Policy2NodeID sql.NullInt64 @@ -907,6 +939,8 @@ type GetChannelsByPolicyLastUpdateRangeRow struct { Policy2Disabled sql.NullBool Policy2InboundBaseFeeMsat sql.NullInt64 Policy2InboundFeeRateMilliMsat sql.NullInt64 + Policy2MessageFlags sql.NullInt16 + Policy2ChannelFlags sql.NullInt16 Policy2Signature []byte } @@ -959,6 +993,8 @@ func (q *Queries) GetChannelsByPolicyLastUpdateRange(ctx context.Context, arg Ge &i.Policy1Disabled, &i.Policy1InboundBaseFeeMsat, &i.Policy1InboundFeeRateMilliMsat, + &i.Policy1MessageFlags, + &i.Policy1ChannelFlags, &i.Policy1Signature, &i.Policy2ID, &i.Policy2NodeID, @@ -972,6 +1008,8 @@ func (q *Queries) GetChannelsByPolicyLastUpdateRange(ctx context.Context, arg Ge &i.Policy2Disabled, &i.Policy2InboundBaseFeeMsat, &i.Policy2InboundFeeRateMilliMsat, + &i.Policy2MessageFlags, + &i.Policy2ChannelFlags, &i.Policy2Signature, ); err != nil { return nil, err @@ -1710,6 +1748,8 @@ SELECT c.id, c.version, c.scid, c.node_id_1, c.node_id_2, c.outpoint, c.capacity cp1.disabled AS policy1_disabled, cp1.inbound_base_fee_msat AS policy1_inbound_base_fee_msat, cp1.inbound_fee_rate_milli_msat AS policy1_inbound_fee_rate_milli_msat, + cp1.message_flags AS policy1_message_flags, + cp1.channel_flags AS policy1_channel_flags, cp1.signature AS policy1_signature, -- Policy 2 @@ -1725,6 +1765,8 @@ SELECT c.id, c.version, c.scid, c.node_id_1, c.node_id_2, c.outpoint, c.capacity cp2.disabled AS policy2_disabled, cp2.inbound_base_fee_msat AS policy2_inbound_base_fee_msat, cp2.inbound_fee_rate_milli_msat AS policy2_inbound_fee_rate_milli_msat, + cp2.message_flags AS policy2_message_flags, + cp2.channel_flags AS policy2_channel_flags, cp2.signature AS policy2_signature FROM channels c @@ -1759,6 +1801,8 @@ type ListChannelsByNodeIDRow struct { Policy1Disabled sql.NullBool Policy1InboundBaseFeeMsat sql.NullInt64 Policy1InboundFeeRateMilliMsat sql.NullInt64 + Policy1MessageFlags sql.NullInt16 + Policy1ChannelFlags sql.NullInt16 Policy1Signature []byte Policy2ID sql.NullInt64 Policy2NodeID sql.NullInt64 @@ -1772,6 +1816,8 @@ type ListChannelsByNodeIDRow struct { Policy2Disabled sql.NullBool Policy2InboundBaseFeeMsat sql.NullInt64 Policy2InboundFeeRateMilliMsat sql.NullInt64 + Policy2MessageFlags sql.NullInt16 + Policy2ChannelFlags sql.NullInt16 Policy2Signature []byte } @@ -1812,6 +1858,8 @@ func (q *Queries) ListChannelsByNodeID(ctx context.Context, arg ListChannelsByNo &i.Policy1Disabled, &i.Policy1InboundBaseFeeMsat, &i.Policy1InboundFeeRateMilliMsat, + &i.Policy1MessageFlags, + &i.Policy1ChannelFlags, &i.Policy1Signature, &i.Policy2ID, &i.Policy2NodeID, @@ -1825,6 +1873,8 @@ func (q *Queries) ListChannelsByNodeID(ctx context.Context, arg ListChannelsByNo &i.Policy2Disabled, &i.Policy2InboundBaseFeeMsat, &i.Policy2InboundFeeRateMilliMsat, + &i.Policy2MessageFlags, + &i.Policy2ChannelFlags, &i.Policy2Signature, ); err != nil { return nil, err @@ -1910,6 +1960,8 @@ SELECT cp1.disabled AS policy_1_disabled, cp1.inbound_base_fee_msat AS policy1_inbound_base_fee_msat, cp1.inbound_fee_rate_milli_msat AS policy1_inbound_fee_rate_milli_msat, + cp1.message_flags AS policy1_message_flags, + cp1.channel_flags AS policy1_channel_flags, cp1.signature AS policy_1_signature, -- Node 2 policy @@ -1925,6 +1977,8 @@ SELECT cp2.disabled AS policy_2_disabled, cp2.inbound_base_fee_msat AS policy2_inbound_base_fee_msat, cp2.inbound_fee_rate_milli_msat AS policy2_inbound_fee_rate_milli_msat, + cp2.message_flags AS policy2_message_flags, + cp2.channel_flags AS policy2_channel_flags, cp2.signature AS policy_2_signature FROM channels c @@ -1961,6 +2015,8 @@ type ListChannelsWithPoliciesPaginatedRow struct { Policy1Disabled sql.NullBool Policy1InboundBaseFeeMsat sql.NullInt64 Policy1InboundFeeRateMilliMsat sql.NullInt64 + Policy1MessageFlags sql.NullInt16 + Policy1ChannelFlags sql.NullInt16 Policy1Signature []byte Policy2ID sql.NullInt64 Policy2NodeID sql.NullInt64 @@ -1974,6 +2030,8 @@ type ListChannelsWithPoliciesPaginatedRow struct { Policy2Disabled sql.NullBool Policy2InboundBaseFeeMsat sql.NullInt64 Policy2InboundFeeRateMilliMsat sql.NullInt64 + Policy2MessageFlags sql.NullInt16 + Policy2ChannelFlags sql.NullInt16 Policy2Signature []byte } @@ -2014,6 +2072,8 @@ func (q *Queries) ListChannelsWithPoliciesPaginated(ctx context.Context, arg Lis &i.Policy1Disabled, &i.Policy1InboundBaseFeeMsat, &i.Policy1InboundFeeRateMilliMsat, + &i.Policy1MessageFlags, + &i.Policy1ChannelFlags, &i.Policy1Signature, &i.Policy2ID, &i.Policy2NodeID, @@ -2027,6 +2087,8 @@ func (q *Queries) ListChannelsWithPoliciesPaginated(ctx context.Context, arg Lis &i.Policy2Disabled, &i.Policy2InboundBaseFeeMsat, &i.Policy2InboundFeeRateMilliMsat, + &i.Policy2MessageFlags, + &i.Policy2ChannelFlags, &i.Policy2Signature, ); err != nil { return nil, err @@ -2139,9 +2201,10 @@ INSERT INTO channel_policies ( version, channel_id, node_id, timelock, fee_ppm, base_fee_msat, min_htlc_msat, last_update, disabled, max_htlc_msat, inbound_base_fee_msat, - inbound_fee_rate_milli_msat, signature + inbound_fee_rate_milli_msat, message_flags, channel_flags, + signature ) VALUES ( - $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13 + $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15 ) ON CONFLICT (channel_id, node_id, version) -- Update the following fields if a conflict occurs on channel_id, @@ -2156,6 +2219,8 @@ ON CONFLICT (channel_id, node_id, version) max_htlc_msat = EXCLUDED.max_htlc_msat, inbound_base_fee_msat = EXCLUDED.inbound_base_fee_msat, inbound_fee_rate_milli_msat = EXCLUDED.inbound_fee_rate_milli_msat, + message_flags = EXCLUDED.message_flags, + channel_flags = EXCLUDED.channel_flags, signature = EXCLUDED.signature WHERE EXCLUDED.last_update > channel_policies.last_update RETURNING id @@ -2174,6 +2239,8 @@ type UpsertEdgePolicyParams struct { MaxHtlcMsat sql.NullInt64 InboundBaseFeeMsat sql.NullInt64 InboundFeeRateMilliMsat sql.NullInt64 + MessageFlags sql.NullInt16 + ChannelFlags sql.NullInt16 Signature []byte } @@ -2191,6 +2258,8 @@ func (q *Queries) UpsertEdgePolicy(ctx context.Context, arg UpsertEdgePolicyPara arg.MaxHtlcMsat, arg.InboundBaseFeeMsat, arg.InboundFeeRateMilliMsat, + arg.MessageFlags, + arg.ChannelFlags, arg.Signature, ) var id int64 diff --git a/sqldb/sqlc/migrations/000007_graph.up.sql b/sqldb/sqlc/migrations/000007_graph.up.sql index c56478ec5..2217aee9e 100644 --- a/sqldb/sqlc/migrations/000007_graph.up.sql +++ b/sqldb/sqlc/migrations/000007_graph.up.sql @@ -228,7 +228,7 @@ CREATE TABLE IF NOT EXISTS channel_policies ( -- The DB ID of the node that created the policy update. node_id BIGINT NOT NULL REFERENCES nodes(id) ON DELETE CASCADE, - -- The number of blocks that the node will subtrace from the expiry + -- The number of blocks that the node will subtract from the expiry -- of an incoming HTLC. timelock INTEGER NOT NULL, @@ -266,6 +266,25 @@ CREATE TABLE IF NOT EXISTS channel_policies ( -- charge for incoming HTLCs. inbound_fee_rate_milli_msat BIGINT, + -- A bitfield used to provide extra details about the message. This is + -- nullable since for later protocol versions, this might not be + -- present. Even though we explicitly store some details from the + -- message_flags in other ways (for example, max_htlc_msat being not + -- null means that bit 0 of the message_flags is set), we still store + -- the full message_flags field so that we can reconstruct the original + -- announcement if needed, even if the bitfield contains bits that we + -- don't use or understand. + message_flags SMALLINT CHECK (message_flags >= 0 AND message_flags <= 255), + + -- A bitfield used to provide extra details about the update. This is + -- nullable since for later protocol versions, this might not be present. + -- Even though we explicitly store some details from the channel_flags in + -- other ways (for example, the disabled field's value is derived directly + -- from the channel_flags), we still store the full bitfield so that we + -- can reconstruct the original announcement if needed, even if the + -- bitfield contains bits that we don't use or understand. + channel_flags SMALLINT CHECK (channel_flags >= 0 AND channel_flags <= 255), + -- The signature of the channel update announcement. signature BLOB ); diff --git a/sqldb/sqlc/models.go b/sqldb/sqlc/models.go index 887870b1c..ca43bed1f 100644 --- a/sqldb/sqlc/models.go +++ b/sqldb/sqlc/models.go @@ -69,6 +69,8 @@ type ChannelPolicy struct { Disabled sql.NullBool InboundBaseFeeMsat sql.NullInt64 InboundFeeRateMilliMsat sql.NullInt64 + MessageFlags sql.NullInt16 + ChannelFlags sql.NullInt16 Signature []byte } diff --git a/sqldb/sqlc/queries/graph.sql b/sqldb/sqlc/queries/graph.sql index 681f80def..dfa32ef82 100644 --- a/sqldb/sqlc/queries/graph.sql +++ b/sqldb/sqlc/queries/graph.sql @@ -296,6 +296,8 @@ SELECT cp1.disabled AS policy1_disabled, cp1.inbound_base_fee_msat AS policy1_inbound_base_fee_msat, cp1.inbound_fee_rate_milli_msat AS policy1_inbound_fee_rate_milli_msat, + cp1.message_flags AS policy1_message_flags, + cp1.channel_flags AS policy1_channel_flags, cp1.signature AS policy1_signature, -- Policy 2 (node_id_2) @@ -311,6 +313,8 @@ SELECT cp2.disabled AS policy2_disabled, cp2.inbound_base_fee_msat AS policy2_inbound_base_fee_msat, cp2.inbound_fee_rate_milli_msat AS policy2_inbound_fee_rate_milli_msat, + cp2.message_flags AS policy2_message_flags, + cp2.channel_flags AS policy2_channel_flags, cp2.signature AS policy2_signature FROM channels c @@ -353,6 +357,8 @@ SELECT cp1.disabled AS policy_1_disabled, cp1.inbound_base_fee_msat AS policy1_inbound_base_fee_msat, cp1.inbound_fee_rate_milli_msat AS policy1_inbound_fee_rate_milli_msat, + cp1.message_flags AS policy_1_message_flags, + cp1.channel_flags AS policy_1_channel_flags, cp1.signature AS policy_1_signature, -- Node 2 policy @@ -368,6 +374,8 @@ SELECT cp2.disabled AS policy_2_disabled, cp2.inbound_base_fee_msat AS policy2_inbound_base_fee_msat, cp2.inbound_fee_rate_milli_msat AS policy2_inbound_fee_rate_milli_msat, + cp2.message_flags AS policy_2_message_flags, + cp2.channel_flags AS policy_2_channel_flags, cp2.signature AS policy_2_signature FROM channels c JOIN nodes n1 ON c.node_id_1 = n1.id @@ -406,6 +414,8 @@ SELECT sqlc.embed(c), cp1.disabled AS policy1_disabled, cp1.inbound_base_fee_msat AS policy1_inbound_base_fee_msat, cp1.inbound_fee_rate_milli_msat AS policy1_inbound_fee_rate_milli_msat, + cp1.message_flags AS policy1_message_flags, + cp1.channel_flags AS policy1_channel_flags, cp1.signature AS policy1_signature, -- Policy 2 @@ -421,6 +431,8 @@ SELECT sqlc.embed(c), cp2.disabled AS policy2_disabled, cp2.inbound_base_fee_msat AS policy2_inbound_base_fee_msat, cp2.inbound_fee_rate_milli_msat AS policy2_inbound_fee_rate_milli_msat, + cp2.message_flags AS policy2_message_flags, + cp2.channel_flags AS policy2_channel_flags, cp2.signature AS policy2_signature FROM channels c @@ -468,6 +480,8 @@ SELECT cp1.disabled AS policy_1_disabled, cp1.inbound_base_fee_msat AS policy1_inbound_base_fee_msat, cp1.inbound_fee_rate_milli_msat AS policy1_inbound_fee_rate_milli_msat, + cp1.message_flags AS policy1_message_flags, + cp1.channel_flags AS policy1_channel_flags, cp1.signature AS policy_1_signature, -- Node 2 policy @@ -483,6 +497,8 @@ SELECT cp2.disabled AS policy_2_disabled, cp2.inbound_base_fee_msat AS policy2_inbound_base_fee_msat, cp2.inbound_fee_rate_milli_msat AS policy2_inbound_fee_rate_milli_msat, + cp2.message_flags AS policy2_message_flags, + cp2.channel_flags AS policy2_channel_flags, cp2.signature AS policy_2_signature FROM channels c @@ -532,9 +548,10 @@ INSERT INTO channel_policies ( version, channel_id, node_id, timelock, fee_ppm, base_fee_msat, min_htlc_msat, last_update, disabled, max_htlc_msat, inbound_base_fee_msat, - inbound_fee_rate_milli_msat, signature + inbound_fee_rate_milli_msat, message_flags, channel_flags, + signature ) VALUES ( - $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13 + $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15 ) ON CONFLICT (channel_id, node_id, version) -- Update the following fields if a conflict occurs on channel_id, @@ -549,6 +566,8 @@ ON CONFLICT (channel_id, node_id, version) max_htlc_msat = EXCLUDED.max_htlc_msat, inbound_base_fee_msat = EXCLUDED.inbound_base_fee_msat, inbound_fee_rate_milli_msat = EXCLUDED.inbound_fee_rate_milli_msat, + message_flags = EXCLUDED.message_flags, + channel_flags = EXCLUDED.channel_flags, signature = EXCLUDED.signature WHERE EXCLUDED.last_update > channel_policies.last_update RETURNING id; @@ -579,6 +598,8 @@ SELECT cp1.disabled AS policy1_disabled, cp1.inbound_base_fee_msat AS policy1_inbound_base_fee_msat, cp1.inbound_fee_rate_milli_msat AS policy1_inbound_fee_rate_milli_msat, + cp1.message_flags AS policy1_message_flags, + cp1.channel_flags AS policy1_channel_flags, cp1.signature AS policy1_signature, -- Policy 2 @@ -594,6 +615,8 @@ SELECT cp2.disabled AS policy2_disabled, cp2.inbound_base_fee_msat AS policy2_inbound_base_fee_msat, cp2.inbound_fee_rate_milli_msat AS policy2_inbound_fee_rate_milli_msat, + cp2.message_flags AS policy_2_message_flags, + cp2.channel_flags AS policy_2_channel_flags, cp2.signature AS policy2_signature FROM channels c diff --git a/sqldb/sqlutils.go b/sqldb/sqlutils.go index 1bf9164e2..8745fe2f7 100644 --- a/sqldb/sqlutils.go +++ b/sqldb/sqlutils.go @@ -11,6 +11,18 @@ import ( // reset function ExecTx calls. var NoOpReset = func() {} +// SQLInt16 turns a numerical integer type into the NullInt16 that sql/sqlc +// uses when an integer field can be permitted to be NULL. +// +// We use this constraints.Integer constraint here which maps to all signed and +// unsigned integer types. +func SQLInt16[T constraints.Integer](num T) sql.NullInt16 { + return sql.NullInt16{ + Int16: int16(num), + Valid: true, + } +} + // SQLInt32 turns a numerical integer type into the NullInt32 that sql/sqlc // uses when an integer field can be permitted to be NULL. // @@ -56,3 +68,10 @@ func SQLTime(t time.Time) sql.NullTime { Valid: true, } } + +// ExtractSqlInt16 turns a NullInt16 into a numerical type. This can be useful +// when reading directly from the database, as this function handles extracting +// the inner value from the "option"-like struct. +func ExtractSqlInt16[T constraints.Integer](num sql.NullInt16) T { + return T(num.Int16) +}