From 05d05ac5ee4605f0d305841067746999a4ce2576 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 21 Aug 2017 22:28:24 -0700 Subject: [PATCH] lnwire: introduce new lnwire.MilliSatoshi type This commit adds a new type to the lnwire package: MilliSatoshi. A milli-satoshi is simply 1/1000th of a satoshi, and will be used for all internal accounting when sending payments, calculating fees, updating commitment state, etc. Two helper methods are added: ToBTC(), and ToSatoshis() to make manipulation of the values easy. --- lnwire/lnwire.go | 28 +++++++++++++++++ lnwire/msat.go | 43 +++++++++++++++++++++++++ lnwire/msat_test.go | 77 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 148 insertions(+) create mode 100644 lnwire/msat.go create mode 100644 lnwire/msat_test.go diff --git a/lnwire/lnwire.go b/lnwire/lnwire.go index 4475e41a..579d667b 100644 --- a/lnwire/lnwire.go +++ b/lnwire/lnwire.go @@ -61,6 +61,12 @@ func writeElement(w io.Writer, element interface{}) error { if _, err := w.Write(b[:]); err != nil { return err } + case MilliSatoshi: + var b [8]byte + binary.BigEndian.PutUint64(b[:], uint64(e)) + if _, err := w.Write(b[:]); err != nil { + return err + } case btcutil.Amount: var b [8]byte binary.BigEndian.PutUint64(b[:], uint64(e)) @@ -80,6 +86,10 @@ func writeElement(w io.Writer, element interface{}) error { return err } case *btcec.PublicKey: + if e == nil { + fmt.Errorf("cannot write nil pubkey") + } + var b [33]byte serializedPubkey := e.SerializeCompressed() copy(b[:], serializedPubkey) @@ -101,6 +111,10 @@ func writeElement(w io.Writer, element interface{}) error { } } case *btcec.Signature: + if e == nil { + return fmt.Errorf("cannot write nil signature") + } + var b [64]byte err := serializeSigToWire(&b, e) if err != nil { @@ -166,6 +180,10 @@ func writeElement(w io.Writer, element interface{}) error { return err } case *FeatureVector: + if e == nil { + return fmt.Errorf("cannot write nil feature vector") + } + if err := e.Encode(w); err != nil { return err } @@ -229,6 +247,10 @@ func writeElement(w io.Writer, element interface{}) error { } case *net.TCPAddr: + if e == nil { + return fmt.Errorf("cannot write nil TCPAddr") + } + if e.IP.To4() != nil { var descriptor [1]byte descriptor[0] = uint8(tcp4Addr) @@ -347,6 +369,12 @@ func readElement(r io.Reader, element interface{}) error { return err } *e = binary.BigEndian.Uint64(b[:]) + case *MilliSatoshi: + var b [8]byte + if _, err := io.ReadFull(r, b[:]); err != nil { + return err + } + *e = MilliSatoshi(int64(binary.BigEndian.Uint64(b[:]))) case *btcutil.Amount: var b [8]byte if _, err := io.ReadFull(r, b[:]); err != nil { diff --git a/lnwire/msat.go b/lnwire/msat.go new file mode 100644 index 00000000..8636e7e5 --- /dev/null +++ b/lnwire/msat.go @@ -0,0 +1,43 @@ +package lnwire + +import ( + "fmt" + + "github.com/roasbeef/btcutil" +) + +// mSatScale is a value that's used to scale satoshis to milli-satoshis, and +// the other way around. +const mSatScale int64 = 1000 + +// MilliSatoshi are the native unit of the Lightning Network. A milli-satoshi +// is simply 1/1000th of a satoshi. There are 100 milli-satoshis in a single +// satoshi. Within the network, all HTLC payments are denominated in +// milli-satoshis. As milli-satoshis aren't deliverable on the native +// blockchain, before settling to broadcasting, the values are rounded down to +// the nearest satoshi. +type MilliSatoshi int64 + +// NewMSatFromSatoshis creates a new MilliSatoshi instance from a target amount +// of satoshis. +func NewMSatFromSatoshis(sat btcutil.Amount) MilliSatoshi { + return MilliSatoshi(int64(sat) * mSatScale) +} + +// ToBTC converts the target MilliSatoshi amount to its corresponding value +// when expressed in BTC. +func (m MilliSatoshi) ToBTC() float64 { + sat := m.ToSatoshis() + return sat.ToBTC() +} + +// ToSatoshis converts the target MilliSatoshi amount to satoshis. Simply, this +// sheds a factor of 1000 from the mSAT amount in order to convert it to SAT. +func (m MilliSatoshi) ToSatoshis() btcutil.Amount { + return btcutil.Amount(int64(m) / mSatScale) +} + +// String returns the string representation of the mSAT amount. +func (m MilliSatoshi) String() string { + return fmt.Sprintf("%v mSAT", int64(m)) +} diff --git a/lnwire/msat_test.go b/lnwire/msat_test.go new file mode 100644 index 00000000..305d4b11 --- /dev/null +++ b/lnwire/msat_test.go @@ -0,0 +1,77 @@ +package lnwire + +import ( + "testing" + + "github.com/roasbeef/btcutil" +) + +func TestMilliSatoshiConversion(t *testing.T) { + t.Parallel() + + testCases := []struct { + mSatAmount MilliSatoshi + + satAmount btcutil.Amount + btcAmount float64 + }{ + { + mSatAmount: 0, + satAmount: 0, + btcAmount: 0, + }, + { + mSatAmount: 10, + satAmount: 0, + btcAmount: 0, + }, + { + mSatAmount: 999, + satAmount: 0, + btcAmount: 0, + }, + { + mSatAmount: 1000, + satAmount: 1, + btcAmount: 1e-8, + }, + { + mSatAmount: 10000, + satAmount: 10, + btcAmount: 0.00000010, + }, + { + mSatAmount: 100000000000, + satAmount: 100000000, + btcAmount: 1, + }, + { + mSatAmount: 2500000000000, + satAmount: 2500000000, + btcAmount: 25, + }, + { + mSatAmount: 5000000000000, + satAmount: 5000000000, + btcAmount: 50, + }, + { + mSatAmount: 21 * 1e6 * 1e8 * 1e3, + satAmount: 21 * 1e6 * 1e8, + btcAmount: 21 * 1e6, + }, + } + + for i, test := range testCases { + if test.mSatAmount.ToSatoshis() != test.satAmount { + t.Fatalf("test #%v: wrong sat amount, expected %v "+ + "got %v", i, int64(test.satAmount), + int64(test.mSatAmount.ToSatoshis())) + } + if test.mSatAmount.ToBTC() != test.btcAmount { + t.Fatalf("test #%v: wrong btc amount, expected %v "+ + "got %v", i, test.btcAmount, + test.mSatAmount.ToBTC()) + } + } +}