Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 86 additions & 5 deletions src/main/scala/fr/acinq/bitcoin/BtcAmount.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package fr.acinq.bitcoin

sealed trait BtcAmount
sealed trait BtcAmount {
def toMilliSatoshi: MilliSatoshi
def toSatoshi: Satoshi = millisatoshi2satoshi(toMilliSatoshi)
def toMilliBtc: MilliBtc = satoshi2millibtc(toSatoshi)
def toBtc: Btc = millibtc2btc(toMilliBtc)
}

case class Satoshi(amount: Long) extends BtcAmount {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a bit unrelated but I think the amount field should be made private here, and has to be accessed with toLong. It's what we did for the UInt64 class and I find it cleaner.

Suggested change
case class Satoshi(amount: Long) extends BtcAmount {
case class Satoshi(private val amount: Long) extends BtcAmount {

// @formatter:off
Expand All @@ -9,12 +14,31 @@ case class Satoshi(amount: Long) extends BtcAmount {
def -(other: Satoshi) = Satoshi(amount - other.amount)
def *(m: Long) = Satoshi(amount * m)
def /(d: Long) = Satoshi(amount / d)
def compare(other: Satoshi): Int = if (amount == other.amount) 0 else if (amount < other.amount) -1 else 1

def compare(other: BtcAmount): Int = this.toMilliSatoshi.compare(other.toMilliSatoshi)

def <= (that: MilliSatoshi): Boolean = compare(that) <= 0
def <= (that: Satoshi): Boolean = compare(that) <= 0
def <= (that: MilliBtc): Boolean = compare(that) <= 0
def <= (that: Btc): Boolean = compare(that) <= 0

def >= (that: MilliSatoshi): Boolean = compare(that) >= 0
def >= (that: Satoshi): Boolean = compare(that) >= 0
def >= (that: MilliBtc): Boolean = compare(that) >= 0
def >= (that: Btc): Boolean = compare(that) >= 0

def < (that: MilliSatoshi): Boolean = compare(that) < 0
def < (that: Satoshi): Boolean = compare(that) < 0
def < (that: MilliBtc): Boolean = compare(that) < 0
def < (that: Btc): Boolean = compare(that) < 0

def > (that: MilliSatoshi): Boolean = compare(that) > 0
def > (that: Satoshi): Boolean = compare(that) > 0
def > (that: MilliBtc): Boolean = compare(that) > 0
def > (that: Btc): Boolean = compare(that) > 0

def unary_-() = Satoshi(-amount)
def toMilliSatoshi: MilliSatoshi = satoshi2millisatoshi(this)
// @formatter:on
}

Expand All @@ -24,12 +48,31 @@ case class MilliBtc(amount: BigDecimal) extends BtcAmount {
def -(other: MilliBtc) = MilliBtc(amount - other.amount)
def *(m: Long) = MilliBtc(amount * m)
def /(d: Long) = MilliBtc(amount / d)
def compare(other: MilliBtc): Int = if (amount == other.amount) 0 else if (amount < other.amount) -1 else 1

def compare(other: BtcAmount): Int = this.toMilliSatoshi.compare(other.toMilliSatoshi)

def <= (that: MilliSatoshi): Boolean = compare(that) <= 0
def <= (that: Satoshi): Boolean = compare(that) <= 0
def <= (that: MilliBtc): Boolean = compare(that) <= 0
def <= (that: Btc): Boolean = compare(that) <= 0

def >= (that: MilliSatoshi): Boolean = compare(that) >= 0
def >= (that: Satoshi): Boolean = compare(that) >= 0
def >= (that: MilliBtc): Boolean = compare(that) >= 0
def >= (that: Btc): Boolean = compare(that) >= 0

def < (that: MilliSatoshi): Boolean = compare(that) < 0
def < (that: Satoshi): Boolean = compare(that) < 0
def < (that: MilliBtc): Boolean = compare(that) < 0
def < (that: Btc): Boolean = compare(that) < 0

def > (that: MilliSatoshi): Boolean = compare(that) > 0
def > (that: Satoshi): Boolean = compare(that) > 0
def > (that: MilliBtc): Boolean = compare(that) > 0
def > (that: Btc): Boolean = compare(that) > 0

def unary_-() = MilliBtc(-amount)
def toMilliSatoshi: MilliSatoshi = millibtc2millisatoshi(this)
// @formatter:on
}

Expand All @@ -41,12 +84,31 @@ case class Btc(amount: BigDecimal) extends BtcAmount {
def -(other: Btc) = Btc(amount - other.amount)
def *(m: Long) = Btc(amount * m)
def /(d: Long) = Btc(amount / d)
def compare(other: Btc): Int = if (amount == other.amount) 0 else if (amount < other.amount) -1 else 1

def compare(other: BtcAmount): Int = this.toMilliSatoshi.compare(other.toMilliSatoshi)

def <= (that: MilliSatoshi): Boolean = compare(that) <= 0
def <= (that: Satoshi): Boolean = compare(that) <= 0
def <= (that: MilliBtc): Boolean = compare(that) <= 0
def <= (that: Btc): Boolean = compare(that) <= 0

def >= (that: MilliSatoshi): Boolean = compare(that) >= 0
def >= (that: Satoshi): Boolean = compare(that) >= 0
def >= (that: MilliBtc): Boolean = compare(that) >= 0
def >= (that: Btc): Boolean = compare(that) >= 0

def < (that: MilliSatoshi): Boolean = compare(that) < 0
def < (that: Satoshi): Boolean = compare(that) < 0
def < (that: MilliBtc): Boolean = compare(that) < 0
def < (that: Btc): Boolean = compare(that) < 0

def > (that: MilliSatoshi): Boolean = compare(that) > 0
def > (that: Satoshi): Boolean = compare(that) > 0
def > (that: MilliBtc): Boolean = compare(that) > 0
def > (that: Btc): Boolean = compare(that) > 0

def unary_-() = Btc(-amount)
def toMilliSatoshi: MilliSatoshi = btc2millisatoshi(this)
// @formatter:on
}

Expand All @@ -57,12 +119,31 @@ case class MilliSatoshi(amount: Long) extends BtcAmount {
def -(other: MilliSatoshi) = MilliSatoshi(amount - other.amount)
def *(m: Long) = MilliSatoshi(amount * m)
def /(d: Long) = MilliSatoshi(amount / d)
def compare(other: MilliSatoshi): Int = if (amount == other.amount) 0 else if (amount < other.amount) -1 else 1

def compare(other: BtcAmount): Int = if (amount == other.toMilliSatoshi.amount) 0 else if (amount < other.toMilliSatoshi.amount) -1 else 1

def <= (that: MilliSatoshi): Boolean = compare(that) <= 0
def <= (that: Satoshi): Boolean = compare(that) <= 0
def <= (that: MilliBtc): Boolean = compare(that) <= 0
def <= (that: Btc): Boolean = compare(that) <= 0

def >= (that: MilliSatoshi): Boolean = compare(that) >= 0
def >= (that: Satoshi): Boolean = compare(that) >= 0
def >= (that: MilliBtc): Boolean = compare(that) >= 0
def >= (that: Btc): Boolean = compare(that) >= 0

def < (that: MilliSatoshi): Boolean = compare(that) < 0
def < (that: Satoshi): Boolean = compare(that) < 0
def < (that: MilliBtc): Boolean = compare(that) < 0
def < (that: Btc): Boolean = compare(that) < 0

def > (that: MilliSatoshi): Boolean = compare(that) > 0
def > (that: Satoshi): Boolean = compare(that) > 0
def > (that: MilliBtc): Boolean = compare(that) > 0
def > (that: Btc): Boolean = compare(that) > 0

def unary_-() = MilliSatoshi(-amount)
def toMilliSatoshi: MilliSatoshi = this
// @formatter:on
}

16 changes: 16 additions & 0 deletions src/test/scala/fr/acinq/bitcoin/BtcAmountSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,22 @@ class BtcAmountSpec extends FunSuite {
assert(MilliSatoshi(32) > MilliSatoshi(31))
}

test("lossless comparisons") {
assert(Satoshi(2) < MilliSatoshi(2999))
assert(Satoshi(3) > MilliSatoshi(2999))
assert(!(Satoshi(2) >= MilliSatoshi(2999)))
assert(!(Satoshi(3) < MilliSatoshi(2999)))
assert(MilliSatoshi(2999) >= Satoshi(2))
assert(!(MilliSatoshi(2999) < Satoshi(2)))
assert(MilliSatoshi(2001).toBtc >= Satoshi(2).toBtc)
assert(!(MilliSatoshi(2001).toBtc < Satoshi(2).toBtc))
assert(Satoshi(2000001).compare(MilliSatoshi(2000001000)) == 0)
assert(Satoshi(2000001) > MilliSatoshi(2000000001))
assert(MilliBtc(20) < Satoshi(2000001))
assert(MilliBtc(20) < MilliSatoshi(2000000001))
assert(Satoshi(2000001) > MilliBtc(20))
}

test("negate amount") {
assert(Satoshi(-20) == -Satoshi(20))
}
Expand Down