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.
This commit is contained in:
Olaoluwa Osuntokun 2017-08-21 22:28:24 -07:00
parent 50d521ba8c
commit 05d05ac5ee
No known key found for this signature in database
GPG Key ID: 9CC5B105D03521A2
3 changed files with 148 additions and 0 deletions

@ -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 {

43
lnwire/msat.go Normal file

@ -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))
}

77
lnwire/msat_test.go Normal file

@ -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())
}
}
}