mirror of
https://github.com/aljazceru/lspd.git
synced 2026-02-21 22:14:20 +01:00
return opening_fee_params on channel info call
This commit is contained in:
3
basetypes/time.go
Normal file
3
basetypes/time.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package basetypes
|
||||
|
||||
var TIME_FORMAT string = "2006-01-02T15:04:05.999Z"
|
||||
@@ -66,6 +66,22 @@ type NodeConfig struct {
|
||||
// The channel can be closed if not used this duration in seconds.
|
||||
MaxInactiveDuration uint64 `json:"maxInactiveDuration,string"`
|
||||
|
||||
// The validity duration of an opening params promise.
|
||||
FeeValidityDuration uint64 `json:"feeValidityDuration,string"`
|
||||
|
||||
// Maximum number of blocks that the client is allowed to set its
|
||||
// `to_self_delay` parameter.
|
||||
MaxClientToSelfDelay uint64 `json:"maxClientToSelfDelay,string"`
|
||||
|
||||
// Multiplication factor to calculate the minimum fee for a JIT channel open.
|
||||
// The resulting fee after multiplying sat/vbyte by the multiplication factor
|
||||
// is denominated in millisat.
|
||||
// e.g. if you expect to publish 500 bytes onchain with the given sat/vbyte
|
||||
// fee rate, and take a margin of 20%, the fee multiplication factor should
|
||||
// be 500 * 1.2 * 1000 = 600000. With 20 sat/vbyte, the resulting minimum fee
|
||||
// would be 600000 * 20 = 12000000msat = 12000sat.
|
||||
FeeMultiplicationFactor uint64 `json:"feeMultiplicationFactor,string"`
|
||||
|
||||
// Set this field to connect to an LND node.
|
||||
Lnd *LndConfig `json:"lnd,omitempty"`
|
||||
|
||||
|
||||
3
main.go
3
main.go
@@ -117,7 +117,8 @@ func main() {
|
||||
|
||||
address := os.Getenv("LISTEN_ADDRESS")
|
||||
certMagicDomain := os.Getenv("CERTMAGIC_DOMAIN")
|
||||
s, err := NewGrpcServer(nodes, address, certMagicDomain, interceptStore)
|
||||
cachedEstimator := chain.NewCachedFeeEstimator(feeEstimator)
|
||||
s, err := NewGrpcServer(nodes, address, certMagicDomain, interceptStore, feeStrategy, cachedEstimator)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to initialize grpc server: %v", err)
|
||||
}
|
||||
|
||||
114
server.go
114
server.go
@@ -2,15 +2,20 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"math"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/breez/lspd/basetypes"
|
||||
"github.com/breez/lspd/btceclegacy"
|
||||
"github.com/breez/lspd/chain"
|
||||
"github.com/breez/lspd/cln"
|
||||
"github.com/breez/lspd/config"
|
||||
"github.com/breez/lspd/interceptor"
|
||||
@@ -27,12 +32,15 @@ import (
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/caddyserver/certmagic"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
)
|
||||
|
||||
var TIME_FORMAT string = "2006-01-02T15:04:05.999Z"
|
||||
|
||||
type server struct {
|
||||
lspdrpc.ChannelOpenerServer
|
||||
address string
|
||||
@@ -41,6 +49,8 @@ type server struct {
|
||||
s *grpc.Server
|
||||
nodes map[string]*node
|
||||
store interceptor.InterceptStore
|
||||
feeStrategy chain.FeeStrategy
|
||||
feeEstimator chain.FeeEstimator
|
||||
}
|
||||
|
||||
type node struct {
|
||||
@@ -59,6 +69,11 @@ func (s *server) ChannelInformation(ctx context.Context, in *lspdrpc.ChannelInfo
|
||||
return nil, err
|
||||
}
|
||||
|
||||
params, err := s.createOpeningParams(ctx, node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &lspdrpc.ChannelInformationReply{
|
||||
Name: node.nodeConfig.Name,
|
||||
Pubkey: node.nodeConfig.NodePubkey,
|
||||
@@ -73,9 +88,104 @@ func (s *server) ChannelInformation(ctx context.Context, in *lspdrpc.ChannelInfo
|
||||
ChannelMinimumFeeMsat: int64(node.nodeConfig.ChannelMinimumFeeMsat),
|
||||
LspPubkey: node.publicKey.SerializeCompressed(), // TODO: Is the publicKey different from the ecies public key?
|
||||
MaxInactiveDuration: int64(node.nodeConfig.MaxInactiveDuration),
|
||||
OpeningFeeParamsMenu: []*lspdrpc.OpeningFeeParams{params},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *server) createOpeningParams(
|
||||
ctx context.Context,
|
||||
node *node,
|
||||
) (*lspdrpc.OpeningFeeParams, error) {
|
||||
// Get a fee estimate.
|
||||
estimate, err := s.feeEstimator.EstimateFeeRate(ctx, s.feeStrategy)
|
||||
if err != nil {
|
||||
log.Printf("Failed to get fee estimate: %v", err)
|
||||
return nil, fmt.Errorf("failed to get fee estimate")
|
||||
}
|
||||
|
||||
// Multiply the fee estiimate by the configured multiplication factor.
|
||||
minFeeMsat := estimate.SatPerVByte *
|
||||
float64(node.nodeConfig.FeeMultiplicationFactor)
|
||||
|
||||
// Make sure the fee is not lower than the minimum fee.
|
||||
minFeeMsat = math.Max(minFeeMsat, float64(node.nodeConfig.ChannelMinimumFeeMsat))
|
||||
|
||||
validUntil := time.Now().UTC().Add(
|
||||
time.Second * time.Duration(node.nodeConfig.FeeValidityDuration),
|
||||
)
|
||||
params := &lspdrpc.OpeningFeeParams{
|
||||
MinMsat: uint64(minFeeMsat),
|
||||
// Proportional is ppm, so divide by 100.
|
||||
Proportional: uint32(node.nodeConfig.ChannelFeePermyriad / 100),
|
||||
ValidUntil: validUntil.Format(basetypes.TIME_FORMAT),
|
||||
// MaxInactiveDuration is in seconds, so divide by 600 for blocks.
|
||||
MaxIdleTime: uint32(node.nodeConfig.MaxInactiveDuration / 600),
|
||||
MaxClientToSelfDelay: uint32(node.nodeConfig.MaxClientToSelfDelay),
|
||||
}
|
||||
|
||||
promise, err := createPromise(node, params)
|
||||
if err != nil {
|
||||
log.Printf("Failed to create promise: %v", err)
|
||||
}
|
||||
|
||||
params.Promise = *promise
|
||||
return params, nil
|
||||
}
|
||||
|
||||
func createPromise(node *node, params *lspdrpc.OpeningFeeParams) (*string, error) {
|
||||
|
||||
// First hash all the values in the params in a fixed order.
|
||||
items := []interface{}{
|
||||
params.MinMsat,
|
||||
params.Proportional,
|
||||
params.ValidUntil,
|
||||
params.MaxIdleTime,
|
||||
params.MaxClientToSelfDelay,
|
||||
}
|
||||
blob, err := json.Marshal(items)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hash := sha256.Sum256(blob)
|
||||
|
||||
// Sign the hash with the private key of the LSP id.
|
||||
sig, err := ecdsa.SignCompact(node.privateKey, hash[:], true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// The promise is the hex encoded hash of the signature.
|
||||
result := sha256.Sum256(sig)
|
||||
promise := hex.EncodeToString(result[:])
|
||||
return &promise, nil
|
||||
}
|
||||
|
||||
func validateOpeningFeeParams(node *node, params *lspdrpc.OpeningFeeParams) bool {
|
||||
if params == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
promise, err := createPromise(node, params)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if *promise != params.Promise {
|
||||
return false
|
||||
}
|
||||
|
||||
t, err := time.Parse(basetypes.TIME_FORMAT, params.ValidUntil)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if time.Now().UTC().After(t) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *server) RegisterPayment(ctx context.Context, in *lspdrpc.RegisterPaymentRequest) (*lspdrpc.RegisterPaymentReply, error) {
|
||||
node, err := getNode(ctx)
|
||||
if err != nil {
|
||||
@@ -270,6 +380,8 @@ func NewGrpcServer(
|
||||
address string,
|
||||
certmagicDomain string,
|
||||
store interceptor.InterceptStore,
|
||||
feeStrategy chain.FeeStrategy,
|
||||
feeEstimator chain.FeeEstimator,
|
||||
) (*server, error) {
|
||||
if len(configs) == 0 {
|
||||
return nil, fmt.Errorf("no nodes supplied")
|
||||
@@ -329,6 +441,8 @@ func NewGrpcServer(
|
||||
certmagicDomain: certmagicDomain,
|
||||
nodes: nodes,
|
||||
store: store,
|
||||
feeStrategy: feeStrategy,
|
||||
feeEstimator: feeEstimator,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user