Skip to content

Commit

Permalink
Redefine single-variant discriminated union types
Browse files Browse the repository at this point in the history
A lot of types in DotNetLightning are defined as discriminated unions
with a single constructor with the same name as the type. eg:

    type NodeId = | NodeId PubKey

This commit redefines these types as:

    type NodeId(id: PubKey) = ...

The main motivation for this is that having types and constructors with
the same name breaks the json serialization library on older mono
versions. It can also lead to confusing error messages. Besides that,
it's also somewhat pointless to define a type as a discriminated union
but then only give it one variant.
  • Loading branch information
canndrew committed Aug 18, 2020
1 parent cae6b93 commit 6234c40
Show file tree
Hide file tree
Showing 15 changed files with 83 additions and 85 deletions.
6 changes: 3 additions & 3 deletions src/DotNetLightning.Core/Channel/ChannelError.fs
Original file line number Diff line number Diff line change
Expand Up @@ -253,9 +253,9 @@ module private ValidationHelper =
/// Helpers to create channel error
[<AutoOpen>]
module internal ChannelError =
let feeRateMismatch (FeeRatePerKw remote, FeeRatePerKw local) =
let remote = float remote
let local = float local
let feeRateMismatch (remote: FeeRatePerKw, local: FeeRatePerKw) =
let remote = float remote.Value
let local = float local.Value
abs (2.0 * (remote - local) / (remote + local))

let inline feeDeltaTooHigh msg (actualDelta, maxAccepted) =
Expand Down
4 changes: 2 additions & 2 deletions src/DotNetLightning.Core/Channel/ChannelHelpers.fs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ open NBitcoin
/// cousin of `ChannelHelpers` module which only includes very primitive function.
module internal ChannelConstantHelpers =
let deriveOurDustLimitSatoshis (feeEstimator: IFeeEstimator): Money =
let (FeeRatePerKw atOpenBackGroundFee) = feeEstimator.GetEstSatPer1000Weight(ConfirmationTarget.Background)
(Money.Satoshis((uint64 atOpenBackGroundFee) * B_OUTPUT_PLUS_SPENDING_INPUT_WEIGHT / 1000UL), Money.Satoshis(546UL))
let atOpenBackGroundFee = feeEstimator.GetEstSatPer1000Weight(ConfirmationTarget.Background)
(Money.Satoshis((uint64 atOpenBackGroundFee.Value) * B_OUTPUT_PLUS_SPENDING_INPUT_WEIGHT / 1000UL), Money.Satoshis(546UL))
|> Money.Max

let getOurChannelReserve (channelValue: Money) =
Expand Down
10 changes: 5 additions & 5 deletions src/DotNetLightning.Core/Channel/ChannelValidation.fs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ module internal ChannelHelpers =
[| theirFundingPubKey; ourFundingKey |]
PayToMultiSigTemplate.Instance.GenerateScriptPubKey(2, pks)

let getFundingScriptCoin (ck: ChannelPubKeys) (theirFundingPubKey: PubKey) (TxId fundingTxId) (TxOutIndex fundingOutputIndex) (fundingSatoshis): ScriptCoin =
let getFundingScriptCoin (ck: ChannelPubKeys) (theirFundingPubKey: PubKey) (fundingTxId: TxId) (fundingOutputIndex: TxOutIndex) (fundingSatoshis): ScriptCoin =
let redeem = getFundingRedeemScript ck theirFundingPubKey
Coin(fundingTxId, uint32 fundingOutputIndex, fundingSatoshis, redeem.WitHash.ScriptPubKey)
Coin(fundingTxId.Value, uint32 fundingOutputIndex.Value, fundingSatoshis, redeem.WitHash.ScriptPubKey)
|> fun c -> ScriptCoin(c, redeem)

let private makeFlags (isNode1: bool, enable: bool) =
Expand Down Expand Up @@ -52,14 +52,14 @@ module internal ChannelHelpers =
}

/// gets the fee we'd want to charge for adding an HTLC output to this channel
let internal getOurFeeBaseMSat (feeEstimator: IFeeEstimator) (FeeRatePerKw feeRatePerKw) (isFunder: bool) =
let internal getOurFeeBaseMSat (feeEstimator: IFeeEstimator) (feeRatePerKw: FeeRatePerKw) (isFunder: bool) =
// for lack of a better metric, we calculate waht it would cost to consolidate the new HTLC
// output value back into a transaction with the regular channel output:

// the fee cost of the HTLC-success/HTLC-Timout transaction
let mutable res = uint64 feeRatePerKw * (max (ChannelConstants.HTLC_TIMEOUT_TX_WEIGHT) (ChannelConstants.HTLC_TIMEOUT_TX_WEIGHT)) |> fun r -> r / 1000UL
let mutable res = uint64 feeRatePerKw.Value * (max (ChannelConstants.HTLC_TIMEOUT_TX_WEIGHT) (ChannelConstants.HTLC_TIMEOUT_TX_WEIGHT)) |> fun r -> r / 1000UL
if (isFunder) then
res <- res + uint64 feeRatePerKw * COMMITMENT_TX_WEIGHT_PER_HTLC / 1000UL
res <- res + uint64 feeRatePerKw.Value * COMMITMENT_TX_WEIGHT_PER_HTLC / 1000UL

//+ the marginal cost of an input which spends the HTLC-Success/HTLC-Timeout output:
res <-
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ type MacaroonIdentifier =
| 0us ->
if (b.Length <> 2 + 32 + 32) then e else
{
PaymentHash = PaymentHash.PaymentHash(uint256(b.[2..33], false))
PaymentHash = PaymentHash(uint256(b.[2..33], false))
TokenId = uint256(b.[34..], false)
}
|> V0
Expand Down
8 changes: 4 additions & 4 deletions src/DotNetLightning.Core/Payment/PaymentRequest.fs
Original file line number Diff line number Diff line change
Expand Up @@ -258,11 +258,11 @@ type TaggedField =
| DescriptionHashTaggedField h ->
let dBase32 = h.ToBytes(false) |> Helpers.convert8BitsTo5
this.WriteField(writer, dBase32)
| NodeIdTaggedField(NodeId pk) ->
let dBase32 = pk.ToBytes() |> Helpers.convert8BitsTo5
| NodeIdTaggedField(nodeId) ->
let dBase32 = nodeId.Value.ToBytes() |> Helpers.convert8BitsTo5
this.WriteField(writer, dBase32)
| MinFinalCltvExpiryTaggedField (BlockHeightOffset32 c) ->
let dBase32 = c |> uint64 |> Helpers.uint64ToBase32
| MinFinalCltvExpiryTaggedField (blockHeightOffset32) ->
let dBase32 = blockHeightOffset32.Value |> uint64 |> Helpers.uint64ToBase32
this.WriteField(writer, dBase32)
| ExpiryTaggedField x ->
let dBase32 = ((x.ToUnixTimeSeconds() |> uint64) - timestamp) |> Helpers.uint64ToBase32
Expand Down
6 changes: 3 additions & 3 deletions src/DotNetLightning.Core/Peer/PeerChannelEncryptor.fs
Original file line number Diff line number Diff line change
Expand Up @@ -279,11 +279,11 @@ module PeerChannelEncryptor =


module PeerChannelEncryptor =
let newOutBound (NodeId theirNodeId, ourNodeSecret: Key) =
let hashInput = Array.concat[| NOISE_H; theirNodeId.ToBytes()|]
let newOutBound (theirNodeId: NodeId, ourNodeSecret: Key) =
let hashInput = Array.concat[| NOISE_H; theirNodeId.Value.ToBytes()|]
let h = uint256(Hashes.SHA256(hashInput))
{
TheirNodeId = Some(NodeId theirNodeId)
TheirNodeId = Some(theirNodeId)
NoiseState = InProgress {
State = PreActOne
DirectionalState = OutBound ({ IE = ourNodeSecret })
Expand Down
2 changes: 1 addition & 1 deletion src/DotNetLightning.Core/Serialize/Msgs/Msgs.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1370,7 +1370,7 @@ type ErrorMsg =
this.Data <- ls.ReadWithLen()
member this.Serialize(ls) =
match this.ChannelId with
| SpecificChannel (ChannelId id) -> ls.Write(id.ToBytes())
| SpecificChannel id -> ls.Write(id.Value.ToBytes())
| All -> ls.Write(Array.zeroCreate 32)
ls.WriteWithLen(this.Data)

Expand Down
4 changes: 2 additions & 2 deletions src/DotNetLightning.Core/Transactions/Scripts.fs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ module Scripts =
let multiSigOfM_2 (sort) (pks) =
PayToMultiSigTemplate.Instance.GenerateScriptPubKey(2, sort, pks)

let toLocalDelayed (revocationPubKey: PubKey) (BlockHeightOffset16 toSelfDelay) (localDelayedPaymentPubkey: PubKey): Script =
let toLocalDelayed (revocationPubKey: PubKey) (toSelfDelay: BlockHeightOffset16) (localDelayedPaymentPubkey: PubKey): Script =
let opList = ResizeArray<Op>()
opList.Add(!> OpcodeType.OP_IF)
opList.Add(Op.GetPushOp(revocationPubKey.ToBytes()))
opList.Add(!> OpcodeType.OP_ELSE)
opList.Add(Op.GetPushOp(int64 toSelfDelay))
opList.Add(Op.GetPushOp(int64 toSelfDelay.Value))
opList.Add(!> OpcodeType.OP_CHECKSEQUENCEVERIFY)
opList.Add(!> OpcodeType.OP_DROP)
opList.Add(Op.GetPushOp(localDelayedPaymentPubkey.ToBytes()))
Expand Down
7 changes: 3 additions & 4 deletions src/DotNetLightning.Core/Transactions/Transactions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -176,10 +176,9 @@ type ClosingTx = ClosingTx of PSBT


/// Tx already verified and it can be published anytime
type FinalizedTx =
FinalizedTx of Transaction
with
member this.Value = let (FinalizedTx v) = this in v
[<Struct>]
type FinalizedTx(tx: Transaction) =
member this.Value = tx

type InputInfo = {
OutPoint: OutPoint
Expand Down
6 changes: 3 additions & 3 deletions src/DotNetLightning.Core/Utils/ChannelId.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ namespace DotNetLightning.Utils

open NBitcoin

type ChannelId = | ChannelId of uint256 with
member x.Value = let (ChannelId v) = x in v

[<Struct>]
type ChannelId(id: uint256) =
member x.Value = id
static member Zero = uint256.Zero |> ChannelId

30 changes: 13 additions & 17 deletions src/DotNetLightning.Core/Utils/LNMoney.fs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ type LNMoneyUnit =
/// (i.e. We might want to support this package in BTCPayServer.Lightning)
/// refs: https://github.com/btcpayserver/BTCPayServer.Lightning/blob/f65a883a63bf607176a3b7b0baa94527ac592f5e/src/BTCPayServer.Lightning.Common/LightMoney.cs
[<Struct>]
type LNMoney = | LNMoney of int64 with
type LNMoney(millisatoshis: int64) =

static member private BitcoinStyle =
NumberStyles.AllowLeadingWhite ||| NumberStyles.AllowTrailingWhite |||
Expand Down Expand Up @@ -80,17 +80,19 @@ type LNMoney = | LNMoney of int64 with
| true, v -> v
| _ -> raise (FormatException("Impossible to parse the string in a bitcoin amount"))

member this.MilliSatoshi = millisatoshis
member this.Satoshi = this.MilliSatoshi / 1000L
member this.BTC = this.MilliSatoshi / (int64 LNMoneyUnit.BTC)
member this.Value = this.MilliSatoshi
member this.ToMoney() = this.Satoshi |> Money

// -------- Arithmetic operations
static member (+) (LNMoney a, LNMoney b) = LNMoney(a + b)
static member (-) (LNMoney a, LNMoney b) = LNMoney(a - b)
static member (*) (LNMoney a, LNMoney b) = LNMoney(a * b)
static member (/) (LNMoney a, LNMoney b) = LNMoney(a / b)
static member inline (/) (LNMoney a, b) = LNMoney(a / (int64 b))
static member inline (+) (LNMoney a, b) = LNMoney(a + (int64 b))
static member inline (-) (LNMoney a, b) = LNMoney(a - (int64 b))
static member inline (*) (LNMoney a, b) = LNMoney(a * (int64 b))
static member Max(LNMoney a, LNMoney b) = if a >= b then LNMoney a else LNMoney b
static member Min(LNMoney a, LNMoney b) = if a <= b then LNMoney a else LNMoney b
static member (+) (a: LNMoney, b: LNMoney) = LNMoney(a.MilliSatoshi + b.MilliSatoshi)
static member (-) (a: LNMoney, b: LNMoney) = LNMoney(a.MilliSatoshi - b.MilliSatoshi)
static member inline (/) (a: LNMoney, b) = LNMoney(a.MilliSatoshi / (int64 b))
static member inline (*) (a: LNMoney, b) = LNMoney(a.MilliSatoshi * (int64 b))
static member Max(a: LNMoney, b: LNMoney) = if a.MilliSatoshi >= b.MilliSatoshi then a else b
static member Min(a: LNMoney, b: LNMoney) = if a.MilliSatoshi <= b.MilliSatoshi then a else b

static member MaxValue =
let maxSatoshis = 21000000UL * (uint64 Money.COIN)
Expand All @@ -102,12 +104,6 @@ type LNMoney = | LNMoney of int64 with
member this.Abs() =
if this < LNMoney.Zero then LNMoney(-this.Value) else this

member this.MilliSatoshi = let (LNMoney v) = this in v
member this.Satoshi = this.MilliSatoshi / 1000L
member this.BTC = this.MilliSatoshi / (int64 LNMoneyUnit.BTC)
member this.Value = this.MilliSatoshi
member this.ToMoney() = this.Satoshi |> Money

member this.Split(parts: int): LNMoney seq =
if parts <= 0 then
raise (ArgumentOutOfRangeException("parts"))
Expand Down
73 changes: 38 additions & 35 deletions src/DotNetLightning.Core/Utils/Primitives.fs
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ module Primitives =

/// Absolute block height
[<Struct>]
type BlockHeight = | BlockHeight of uint32 with
type BlockHeight(blockHeight: uint32) =
static member Zero = 0u |> BlockHeight
static member One = 1u |> BlockHeight
member x.Value = let (BlockHeight v) = x in v
member x.Value = blockHeight
member x.AsOffset() =
x.Value |> Checked.uint16 |> BlockHeightOffset16

Expand All @@ -58,8 +58,8 @@ module Primitives =
/// 16bit relative block height used for `OP_CSV` locks,
/// Since OP_CSV allow only block number of 0 ~ 65535, it is safe
/// to restrict into the range smaller than BlockHeight
and [<Struct>] BlockHeightOffset16 = | BlockHeightOffset16 of uint16 with
member x.Value = let (BlockHeightOffset16 v) = x in v
and [<Struct>] BlockHeightOffset16(offset: uint16) =
member x.Value = offset

static member ofBlockHeightOffset32(bho32: BlockHeightOffset32) =
BlockHeightOffset16 (uint16 bho32.Value)
Expand All @@ -77,8 +77,8 @@ module Primitives =
///
/// 32bit relative block height. For `OP_CSV` locks, BlockHeightOffset16
/// should be used instead.
and [<Struct>] BlockHeightOffset32 = | BlockHeightOffset32 of uint32 with
member x.Value = let (BlockHeightOffset32 v) = x in v
and [<Struct>] BlockHeightOffset32(offset: uint32) =
member x.Value = offset

static member ofBlockHeightOffset16(bho16: BlockHeightOffset16) =
BlockHeightOffset32 (uint32 bho16.Value)
Expand All @@ -96,9 +96,9 @@ module Primitives =
/// 1. It is equatable
/// 2. Some Convenience methods for serialization
/// 3. Custom `ToString`
[<CustomEquality;CustomComparison;StructuredFormatDisplay("{AsString}")>]
type LNECDSASignature = LNECDSASignature of ECDSASignature | Empty with
member x.Value = match x with LNECDSASignature s -> s | Empty -> failwith "Unreachable!"
[<Struct;CustomEquality;CustomComparison;StructuredFormatDisplay("{AsString}")>]
type LNECDSASignature(signature: ECDSASignature) =
member x.Value = signature
override this.GetHashCode() = hash this.Value
override this.Equals(obj: obj) =
match obj with
Expand Down Expand Up @@ -159,8 +159,9 @@ module Primitives =
static member op_Implicit (ec: ECDSASignature) =
ec |> LNECDSASignature

type PaymentHash = | PaymentHash of uint256 with
member x.Value = let (PaymentHash v) = x in v
[<Struct>]
type PaymentHash(paymentHash: uint256) =
member x.Value = paymentHash
member x.ToBytes(?lEndian) =
let e = defaultArg lEndian false
x.Value.ToBytes(e)
Expand Down Expand Up @@ -206,12 +207,12 @@ module Primitives =
let (|PaymentPreimage|) x =
match x with
| PaymentPreimage x -> x

type ConnectionId = ConnectionId of Guid
[<CustomEquality;CustomComparison>]
type PeerId = PeerId of EndPoint
with
member this.Value = let (PeerId ep) = this in ep

[<Struct;CustomEquality;CustomComparison>]
type PeerId(endPoint: EndPoint) =
member this.Value = endPoint

override this.GetHashCode() = this.Value.GetHashCode()
member this.Equals(o: PeerId) =
Expand All @@ -231,9 +232,9 @@ module Primitives =
| :? PeerId as p -> this.CompareTo(p)
| _ -> -1

[<CustomEquality;CustomComparison>]
type ComparablePubKey = ComparablePubKey of PubKey with
member x.Value = let (ComparablePubKey v) = x in v
[<Struct;CustomEquality;CustomComparison>]
type ComparablePubKey(pubKey: PubKey) =
member x.Value = pubKey
interface IComparable with
override this.CompareTo(other) =
match other with
Expand All @@ -247,9 +248,9 @@ module Primitives =
static member op_Implicit (pk: PubKey) =
pk |> ComparablePubKey

[<CustomEquality;CustomComparison>]
type NodeId = | NodeId of PubKey with
member x.Value = let (NodeId v) = x in v
[<Struct;CustomEquality;CustomComparison>]
type NodeId(id: PubKey) =
member x.Value = id
interface IComparable with
override this.CompareTo(other) =
match other with
Expand All @@ -264,9 +265,9 @@ module Primitives =

/// Small wrapper for NBitcoin's OutPoint type
/// So that it supports comparison and equality constraints
[<CustomComparison;CustomEquality>]
type LNOutPoint = LNOutPoint of OutPoint with
member x.Value = let (LNOutPoint v) = x in v
[<Struct;CustomComparison;CustomEquality>]
type LNOutPoint(outPoint: OutPoint) =
member x.Value = outPoint

member this.CompareTo(other: LNOutPoint) =
if this.Value.Hash > other.Value.Hash then
Expand Down Expand Up @@ -302,8 +303,9 @@ module Primitives =
member this.Equals(other) = this.Equals(other)

/// feerate per kilo weight
type FeeRatePerKw = | FeeRatePerKw of uint32 with
member x.Value = let (FeeRatePerKw v) = x in v
[<Struct>]
type FeeRatePerKw(feeRatePerKw: uint32) =
member x.Value = feeRatePerKw
static member FromFee(fee: Money, weight: uint64) =
(((uint64 fee.Satoshi) * weight) / 1000UL)
|> uint32
Expand Down Expand Up @@ -333,23 +335,24 @@ module Primitives =
static member (*) (a: FeeRatePerKw, b: uint32) =
(a.Value * b) |> FeeRatePerKw
/// Block Hash
type BlockId = | BlockId of uint256 with
member x.Value = let (BlockId v) = x in v
[<Struct>]
type BlockId(id: uint256) =
member x.Value = id

[<Struct>]
type HTLCId = | HTLCId of uint64 with
type HTLCId(id: uint64) =
static member Zero = HTLCId(0UL)
member x.Value = let (HTLCId v) = x in v
member x.Value = id

static member (+) (a: HTLCId, b: uint64) = (a.Value + b) |> HTLCId

[<Struct>]
type TxOutIndex = | TxOutIndex of uint16 with
member x.Value = let (TxOutIndex v) = x in v
type TxOutIndex(index: uint16) =
member x.Value = index

[<Struct>]
type TxIndexInBlock = | TxIndexInBlock of uint32 with
member x.Value = let (TxIndexInBlock v) = x in v
type TxIndexInBlock(index: uint32) =
member x.Value = index


[<Struct;StructuredFormatDisplay("{AsString}")>]
Expand Down
6 changes: 3 additions & 3 deletions src/DotNetLightning.Core/Utils/TxId.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ namespace DotNetLightning.Utils

open NBitcoin

[<StructuralComparison;StructuralEquality>]
type TxId = | TxId of uint256 with
member x.Value = let (TxId v) = x in v
[<Struct;StructuralComparison;StructuralEquality>]
type TxId(id: uint256) =
member x.Value = id
static member Zero = uint256.Zero |> TxId

2 changes: 1 addition & 1 deletion tests/DotNetLightning.Core.Tests/Generators/Payments.fs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ open PrimitiveGenerators


let private macaroonIdV1Gen =
(uint256Gen |> Gen.map(PaymentHash.PaymentHash), uint256Gen)
(uint256Gen |> Gen.map(PaymentHash), uint256Gen)
||> Gen.map2(fun p u -> { MacaroonIdentifierV0.PaymentHash = p
TokenId = u })
|> Gen.map(MacaroonIdentifier.V0)
Expand Down
2 changes: 1 addition & 1 deletion tests/DotNetLightning.Core.Tests/PaymentTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ let unitTest =
testCase "PaymentSecret can get correct PaymentHash by its .Hash field" <| fun _ ->
let p = Primitives.PaymentPreimage.Create(hex.DecodeData "60ba77a7f0174a3dd0f4fc8c1b28cda6aa9fab0e87c87e936af40b34cca40883")
let h = p.Hash
let expectedHash = PaymentHash.PaymentHash (uint256.Parse "b1d9fa54b693576947e3b78eaf8a2595b5b6288b283c7c75f51ee0fe5bb82cba")
let expectedHash = PaymentHash (uint256.Parse "b1d9fa54b693576947e3b78eaf8a2595b5b6288b283c7c75f51ee0fe5bb82cba")
Expect.equal expectedHash h ""
()
]

0 comments on commit 6234c40

Please sign in to comment.