Update scalafmt-core to 3.8.1 (#5501)

* Update scalafmt-core to 3.8.1

* Update .scalafmt.conf settings to be factory default settings

* Fix typo

* scalafmt

* Empty commit to re-run CI

* Revert some scalafmt back to original scalafmt.conf

---------

Co-authored-by: Chris Stewart <stewart.chris1234@gmail.com>
This commit is contained in:
Scala Steward 2024-04-21 02:55:49 +02:00 committed by GitHub
parent 9442dba217
commit afddf73c48
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1090 changed files with 46186 additions and 30144 deletions

View File

@ -1,14 +1,6 @@
version = "2.7.5"
version = "3.8.1"
# See Documentation at https://scalameta.org/scalafmt/#Configuration
trailingCommas = never
maxColumn = 80
lineEndings = preserve
docstrings = ScalaDoc
continuationIndent {
callSite = 2
defnSite = 4
}
runner.dialect=scala213
align = some
align {
openParenDefnSite = false
@ -19,29 +11,3 @@ danglingParentheses {
callSite = false
defnSite = false
}
newlines {
alwaysBeforeTopLevelStatements = true
sometimesBeforeColonInMethodReturnType = false
alwaysBeforeCurlyBraceLambdaParams = false
}
assumeStandardLibraryStripMargin = true
rewrite.rules = [
SortModifiers,
RedundantParens,
SortImports
]
binPack.literalArgumentLists = true
project {
excludeFilters = [
.bloop,
.metals,
target
]
}
# Consider Rewrite Rules

View File

@ -49,16 +49,20 @@ class DLCStatusTest extends BitcoinSJvmTest {
assert(status.state == DLCState.Offered)
assert(read[DLCStatus](write(status)) == status)
assert(read[DLCStatus](
write(status.asInstanceOf[DLCStatus])(Picklers.dlcStatusW)) == status)
assert(
read[DLCStatus](
write(status.asInstanceOf[DLCStatus])(Picklers.dlcStatusW)
) == status
)
}
}
it must "have json symmetry in DLCStatus.Accepted" in {
forAllParallel(NumberGenerator.bool,
forAllParallel(
NumberGenerator.bool,
TLVGen.dlcOfferTLV,
NumberGenerator.bytevector) {
case (isInit, offerTLV, contractId) =>
NumberGenerator.bytevector
) { case (isInit, offerTLV, contractId) =>
val offer = DLCOffer.fromTLV(offerTLV)
val totalCollateral = offer.contractInfo.totalCollateral
@ -66,9 +70,13 @@ class DLCStatusTest extends BitcoinSJvmTest {
// random testnet address
val payoutAddress =
Some(
PayoutAddress(BitcoinAddress.fromString(
"tb1q4ps6c9ewa7uca5v39fakykq9q6hpgjkxje8gve"),
true))
PayoutAddress(
BitcoinAddress.fromString(
"tb1q4ps6c9ewa7uca5v39fakykq9q6hpgjkxje8gve"
),
true
)
)
val contact = Option.empty[String]
@ -90,17 +98,21 @@ class DLCStatusTest extends BitcoinSJvmTest {
assert(status.state == DLCState.Accepted)
assert(read[DLCStatus](write(status)) == status)
assert(read[DLCStatus](
write(status.asInstanceOf[DLCStatus])(Picklers.dlcStatusW)) == status)
assert(
read[DLCStatus](
write(status.asInstanceOf[DLCStatus])(Picklers.dlcStatusW)
) == status
)
}
}
it must "have json symmetry in DLCStatus.Signed" in {
forAllParallel(NumberGenerator.bool,
forAllParallel(
NumberGenerator.bool,
TLVGen.dlcOfferTLV,
NumberGenerator.bytevector,
CryptoGenerators.doubleSha256DigestBE) {
case (isInit, offerTLV, contractId, txId) =>
CryptoGenerators.doubleSha256DigestBE
) { case (isInit, offerTLV, contractId, txId) =>
val offer = DLCOffer.fromTLV(offerTLV)
val totalCollateral = offer.contractInfo.totalCollateral
@ -128,17 +140,21 @@ class DLCStatusTest extends BitcoinSJvmTest {
assert(status.state == DLCState.Signed)
assert(read[DLCStatus](write(status)) == status)
assert(read[DLCStatus](
write(status.asInstanceOf[DLCStatus])(Picklers.dlcStatusW)) == status)
assert(
read[DLCStatus](
write(status.asInstanceOf[DLCStatus])(Picklers.dlcStatusW)
) == status
)
}
}
it must "have json symmetry in DLCStatus.Broadcasted" in {
forAllParallel(NumberGenerator.bool,
forAllParallel(
NumberGenerator.bool,
TLVGen.dlcOfferTLV,
NumberGenerator.bytevector,
CryptoGenerators.doubleSha256DigestBE) {
case (isInit, offerTLV, contractId, fundingTxId) =>
CryptoGenerators.doubleSha256DigestBE
) { case (isInit, offerTLV, contractId, fundingTxId) =>
val offer = DLCOffer.fromTLV(offerTLV)
val totalCollateral = offer.contractInfo.totalCollateral
@ -166,17 +182,21 @@ class DLCStatusTest extends BitcoinSJvmTest {
assert(status.state == DLCState.Broadcasted)
assert(read[DLCStatus](write(status)) == status)
assert(read[DLCStatus](
write(status.asInstanceOf[DLCStatus])(Picklers.dlcStatusW)) == status)
assert(
read[DLCStatus](
write(status.asInstanceOf[DLCStatus])(Picklers.dlcStatusW)
) == status
)
}
}
it must "have json symmetry in DLCStatus.Confirmed" in {
forAllParallel(NumberGenerator.bool,
forAllParallel(
NumberGenerator.bool,
TLVGen.dlcOfferTLV,
NumberGenerator.bytevector,
CryptoGenerators.doubleSha256DigestBE) {
case (isInit, offerTLV, contractId, fundingTxId) =>
CryptoGenerators.doubleSha256DigestBE
) { case (isInit, offerTLV, contractId, fundingTxId) =>
val offer = DLCOffer.fromTLV(offerTLV)
val totalCollateral = offer.contractInfo.totalCollateral
@ -204,8 +224,11 @@ class DLCStatusTest extends BitcoinSJvmTest {
assert(status.state == DLCState.Confirmed)
assert(read[DLCStatus](write(status)) == status)
assert(read[DLCStatus](
write(status.asInstanceOf[DLCStatus])(Picklers.dlcStatusW)) == status)
assert(
read[DLCStatus](
write(status.asInstanceOf[DLCStatus])(Picklers.dlcStatusW)
) == status
)
}
}
@ -260,7 +283,9 @@ class DLCStatusTest extends BitcoinSJvmTest {
assert(read[DLCStatus](write(status)) == status)
assert(
read[DLCStatus](
write(status.asInstanceOf[DLCStatus])(Picklers.dlcStatusW)) == status)
write(status.asInstanceOf[DLCStatus])(Picklers.dlcStatusW)
) == status
)
}
}
@ -317,7 +342,9 @@ class DLCStatusTest extends BitcoinSJvmTest {
assert(read[DLCStatus](write(status)) == status)
assert(
read[DLCStatus](
write(status.asInstanceOf[DLCStatus])(Picklers.dlcStatusW)) == status)
write(status.asInstanceOf[DLCStatus])(Picklers.dlcStatusW)
) == status
)
}
}
@ -368,7 +395,9 @@ class DLCStatusTest extends BitcoinSJvmTest {
assert(read[DLCStatus](write(status)) == status)
assert(
read[DLCStatus](
write(status.asInstanceOf[DLCStatus])(Picklers.dlcStatusW)) == status)
write(status.asInstanceOf[DLCStatus])(Picklers.dlcStatusW)
) == status
)
}
}
}

View File

@ -13,7 +13,8 @@ class SerializedPSBTTest extends BitcoinSUnitTest {
it must "correctly decode a psbt" in {
val psbt = PSBT.fromBase64(
"cHNidP9Fq2AoiZroZUZZ7/Fl0n4dcF8zKWfpD3QzRcAm1QTxUQzzGnHjM5xU+xYUvYSPokH86tLWHVVmhrOQE2d//fPeu6Px6r0LW2DbYkBubiYldhPvO50/3M0wHtHncJ6w/UmdpFVMt/z1iQfbH9U4bq6iLS930BnOiRlc0KX8DQmDnKFdTdiyceBPOmWKSJT+cR1RIQabSiKY6plO4jkSbZ2yFGMBAP3dAgIAAAABG2Z29d/AhiGmmjjrgO9p0LwwYozRbr404YcnQQ+LTQMEAAAAAAAAAAADGQVGPusCAAD9vwFjVyEDkIsYrb9OJzC2oJUtRDntXxhNo7cZeByTHPkuJeCnAmshA4B9R4qr45hWk4fD3F+0um+wJVmP8kFRyXWflgEYKi61IQMLERF8Yx7x85yRmZyD7888+GENPbV2xACyxsAbEJoZpCEDLfkiP2NbzOWOTrM5qIiGFWLDrAYEoYj+6B/p474XWbQhAuNDwvhwWxJHuG/S7CS/bsk/wr8pR9LzCm8eEEqoQ58NIQPlPMn3Y69Fovcn4cexpU9r1whI0yWZ1OwvYHlsngyAyCECyGo6/HPABsWFUwjKuE+mG57duFkwbk17rug85rd0MCohA8ObOSpT1WPHeKKjc3MwDtFM8xr+LRlrDETYazNJhawyIQNqTHMWXX5dNDAuY71BaXfFF8yoHEaQDnDyNudqgfuMQSEC410iVARHWVmvKpcQUFJJx/H41G2/9sJ5jvRnyYIXwQIhA4APXyr35MW/4PPuMRtYg+caUGFqk+12x9wW+JFxHqnwIQMiS1b6HfL93efBgysG0PrlMPwA7eKMSFexdnOGHhOQP1yuZwXdzsmnALF1dqkUCzczWAtdSewwYE3SNlcLhnxJInGIrGjelDmdMwkAABYAFO26WaDgWxcVeSe4lfoysDTitaKJp/r3t2YNAAC4Y1IhA6WEMWgMOWjUk1FZvxYDTRvyVZKDWeCKsBvgJruAngCOIQKeg5DPhM1ghA26JAP1gvhyZ7K/Z2ttavmOd7AyFv2OyyEDjgniAeDz5qiX/5tyDxMJcyMN4yRoYXZwfFgT9x6iKqghAxmojRQWODhnuwBaEtxJHK9AlJXUkPjIOs4o08raE5zfVK5nBF6KYiOxdSEDvbxvoF42LUei1lcblCHsUzeXo7Wr1zKxw5agIRZVWqysaAAAAAAAAQMEggAAAAAAA6jMPk3fqYXY2pAV+YsGMMB2CAwXDBpwMObcmgEz8M0NO07ZYF60Sf8jj0zms7HOPzU3tBtRMROIXzyZreEJOfmqBIZKk6tg78xgjIW8mR+WuQAA")
"cHNidP9Fq2AoiZroZUZZ7/Fl0n4dcF8zKWfpD3QzRcAm1QTxUQzzGnHjM5xU+xYUvYSPokH86tLWHVVmhrOQE2d//fPeu6Px6r0LW2DbYkBubiYldhPvO50/3M0wHtHncJ6w/UmdpFVMt/z1iQfbH9U4bq6iLS930BnOiRlc0KX8DQmDnKFdTdiyceBPOmWKSJT+cR1RIQabSiKY6plO4jkSbZ2yFGMBAP3dAgIAAAABG2Z29d/AhiGmmjjrgO9p0LwwYozRbr404YcnQQ+LTQMEAAAAAAAAAAADGQVGPusCAAD9vwFjVyEDkIsYrb9OJzC2oJUtRDntXxhNo7cZeByTHPkuJeCnAmshA4B9R4qr45hWk4fD3F+0um+wJVmP8kFRyXWflgEYKi61IQMLERF8Yx7x85yRmZyD7888+GENPbV2xACyxsAbEJoZpCEDLfkiP2NbzOWOTrM5qIiGFWLDrAYEoYj+6B/p474XWbQhAuNDwvhwWxJHuG/S7CS/bsk/wr8pR9LzCm8eEEqoQ58NIQPlPMn3Y69Fovcn4cexpU9r1whI0yWZ1OwvYHlsngyAyCECyGo6/HPABsWFUwjKuE+mG57duFkwbk17rug85rd0MCohA8ObOSpT1WPHeKKjc3MwDtFM8xr+LRlrDETYazNJhawyIQNqTHMWXX5dNDAuY71BaXfFF8yoHEaQDnDyNudqgfuMQSEC410iVARHWVmvKpcQUFJJx/H41G2/9sJ5jvRnyYIXwQIhA4APXyr35MW/4PPuMRtYg+caUGFqk+12x9wW+JFxHqnwIQMiS1b6HfL93efBgysG0PrlMPwA7eKMSFexdnOGHhOQP1yuZwXdzsmnALF1dqkUCzczWAtdSewwYE3SNlcLhnxJInGIrGjelDmdMwkAABYAFO26WaDgWxcVeSe4lfoysDTitaKJp/r3t2YNAAC4Y1IhA6WEMWgMOWjUk1FZvxYDTRvyVZKDWeCKsBvgJruAngCOIQKeg5DPhM1ghA26JAP1gvhyZ7K/Z2ttavmOd7AyFv2OyyEDjgniAeDz5qiX/5tyDxMJcyMN4yRoYXZwfFgT9x6iKqghAxmojRQWODhnuwBaEtxJHK9AlJXUkPjIOs4o08raE5zfVK5nBF6KYiOxdSEDvbxvoF42LUei1lcblCHsUzeXo7Wr1zKxw5agIRZVWqysaAAAAAAAAQMEggAAAAAAA6jMPk3fqYXY2pAV+YsGMMB2CAwXDBpwMObcmgEz8M0NO07ZYF60Sf8jj0zms7HOPzU3tBtRMROIXzyZreEJOfmqBIZKk6tg78xgjIW8mR+WuQAA"
)
val tx = psbt.transaction
val decoded = SerializedPSBT.decodePSBT(psbt)
@ -21,10 +22,13 @@ class SerializedPSBTTest extends BitcoinSUnitTest {
assert(decoded.global.tx == SerializedTransaction.decodeRawTransaction(tx))
assert(decoded.global.version == UInt32.zero)
assert(
decoded.global.unknowns == Vector(GlobalPSBTRecord.Unknown(
decoded.global.unknowns == Vector(
GlobalPSBTRecord.Unknown(
hex"ab6028899ae8654659eff165d27e1d705f332967e90f743345c026d504f1510cf31a71e3339c54fb1614bd848fa241fcead2d61d556686b39013677ffdf3debba3f1eabd0b",
hex"60db62406e6e26257613ef3b9d3fdccd301ed1e7709eb0fd499da4554cb7fcf58907db1fd5386eaea22d2f77d019ce89195cd0a5fc0d09839ca15d4dd8b271e04f3a658a4894fe711d5121069b4a2298ea994ee239126d9db21463"
)))
)
)
)
assert(decoded.inputs.size == 1)
assert(decoded.inputs.head.bip32Paths.isEmpty)
@ -39,7 +43,8 @@ class SerializedPSBTTest extends BitcoinSUnitTest {
assert(decoded.inputs.head.unknowns.isEmpty)
assert(
decoded.inputs.head.sigHashType
.contains(HashType.sigHashNoneAnyoneCanPay))
.contains(HashType.sigHashNoneAnyoneCanPay)
)
assert(decoded.outputs.size == 3)
assert(decoded.outputs.head.bip32Paths.isEmpty)
@ -50,10 +55,13 @@ class SerializedPSBTTest extends BitcoinSUnitTest {
assert(decoded.outputs(1).redeemScript.isEmpty)
assert(decoded.outputs(1).witScript.isEmpty)
assert(
decoded.outputs(1).unknowns == Vector(OutputPSBTRecord.Unknown(
decoded.outputs(1).unknowns == Vector(
OutputPSBTRecord.Unknown(
hex"a8cc3e",
hex"dfa985d8da9015f98b0630c076080c170c1a7030e6dc9a0133f0cd0d3b4ed9605eb449ff238f4ce6b3b1ce3f3537b41b513113885f3c99ade10939f9aa04864a93ab60efcc608c85bc991f96b9"
)))
)
)
)
assert(decoded.outputs.last.bip32Paths.isEmpty)
assert(decoded.outputs.last.redeemScript.isEmpty)
assert(decoded.outputs.last.witScript.isEmpty)
@ -61,7 +69,8 @@ class SerializedPSBTTest extends BitcoinSUnitTest {
it must "correctly decode a finalized psbt" in {
val psbt = PSBT.fromBase64(
"cHNidP8BAP1FAQIAAAABGYuEj8rH1dQ8DYG/wIJoOmA07cMG3qUA2joduT39u3QBAAAAAEhEAAAGZi6ggGgAAAAqBOhp5BGydSEDT4d2r4kKjBDT7N3xtlQRvQOoZMDnvl9kcu+Yz0N+zOWsB9sJYW0AAAAmaiSqIantmOy8chX8u/gjrWjoJwzItqupL01TN2d15WGchnbpGlW08zmpugUAAAcEuQUHI7J1tyGqaVkFAABJZCEDd0QjA5VNPMrHshsJ5ToPa6aXjJClUKFubu0Z3qUgGCmsZyEDCXN1QPwcYCN+5PBjESbL9eI3/Fd2SW/L1kOKOJ6lAsqsaMnuN1dBTAAAFlMU75XVn2xj3vMdoWxBKP6yYswBfsdaZnFQHAAAACZqJKohqe2Q60JHII6xx+YrJFTmibrNi7e8pICEfsbYEMYNKhOtMwAAAAAAAQCKAgAAAAAC7ekoBUtTAABQYyED6l4Vptz8do0rhD+X5Xlmyl5Wwi/fM/EwJmYE/LE4XWhnBfcyAqcAsnUhA/spF0dsJg2ZiBFYnA80MM2Pxou90wok6B8hA44T/vJlaKy4YEecR1gAAB4CSESydXapFHyp4KCUiJddycf+SqnAZI21/aJFiKwAAAAAAQdqRzBEAiBMdpIM1wDaIgn1BJarrd27uzx9kcTixUzQMFGbb0KTsQIgBxNIy+cxhoe1Bnxy/X3+ankfNtC4ydjJcMHlxV0MJqOAIQJUjewAnrbEt88DGQklYoDSLGlNS5Z2C6ukMaVGy+p6EgAAAAAAAAA=")
"cHNidP8BAP1FAQIAAAABGYuEj8rH1dQ8DYG/wIJoOmA07cMG3qUA2joduT39u3QBAAAAAEhEAAAGZi6ggGgAAAAqBOhp5BGydSEDT4d2r4kKjBDT7N3xtlQRvQOoZMDnvl9kcu+Yz0N+zOWsB9sJYW0AAAAmaiSqIantmOy8chX8u/gjrWjoJwzItqupL01TN2d15WGchnbpGlW08zmpugUAAAcEuQUHI7J1tyGqaVkFAABJZCEDd0QjA5VNPMrHshsJ5ToPa6aXjJClUKFubu0Z3qUgGCmsZyEDCXN1QPwcYCN+5PBjESbL9eI3/Fd2SW/L1kOKOJ6lAsqsaMnuN1dBTAAAFlMU75XVn2xj3vMdoWxBKP6yYswBfsdaZnFQHAAAACZqJKohqe2Q60JHII6xx+YrJFTmibrNi7e8pICEfsbYEMYNKhOtMwAAAAAAAQCKAgAAAAAC7ekoBUtTAABQYyED6l4Vptz8do0rhD+X5Xlmyl5Wwi/fM/EwJmYE/LE4XWhnBfcyAqcAsnUhA/spF0dsJg2ZiBFYnA80MM2Pxou90wok6B8hA44T/vJlaKy4YEecR1gAAB4CSESydXapFHyp4KCUiJddycf+SqnAZI21/aJFiKwAAAAAAQdqRzBEAiBMdpIM1wDaIgn1BJarrd27uzx9kcTixUzQMFGbb0KTsQIgBxNIy+cxhoe1Bnxy/X3+ankfNtC4ydjJcMHlxV0MJqOAIQJUjewAnrbEt88DGQklYoDSLGlNS5Z2C6ukMaVGy+p6EgAAAAAAAAA="
)
val tx = psbt.transaction
val decoded = SerializedPSBT.decodePSBT(psbt)

View File

@ -16,7 +16,8 @@ class AppServerCliJsonTest extends BitcoinSUnitTest {
assert(parsed1.isSuccess)
assert(parsed1.get.walletNameOpt == Some(walletName))
assert(
parsed1.get.passwordOpt.map(_.toStringSensitive) == Some(aesPassword))
parsed1.get.passwordOpt.map(_.toStringSensitive) == Some(aesPassword)
)
assert(parsed1.get.bip39PasswordOpt == Some(bip39Password))
val arr2 = ujson.Arr(walletName, aesPassword, ujson.Null)
@ -24,7 +25,8 @@ class AppServerCliJsonTest extends BitcoinSUnitTest {
assert(parsed2.isSuccess)
assert(parsed2.get.walletNameOpt == Some(walletName))
assert(
parsed2.get.passwordOpt.map(_.toStringSensitive) == Some(aesPassword))
parsed2.get.passwordOpt.map(_.toStringSensitive) == Some(aesPassword)
)
}
}

View File

@ -51,7 +51,8 @@ class DLCAcceptJsonSerializerTest extends BitcoinSUnitTest {
it must "have serialization symmetry for a accept json message" in {
val accept = upickle.default.read[DLCAcceptTLV](testString)(
Picklers.dlcAcceptTLVPickler)
Picklers.dlcAcceptTLVPickler
)
val json: String =
upickle.default.write(accept)(Picklers.dlcAcceptTLVPickler)
assert(json == testString.replaceAll("\\s", ""))

View File

@ -11,7 +11,8 @@ class SpendingInfoDbSerializerTest extends BitcoinSUnitTest {
it must "be symmetrical" in {
val original = TransactionTestUtil.spendingInfoDb
val json = upickle.default.writeJs[SpendingInfoDb](original)(
Picklers.spendingInfoDbPickler)
Picklers.spendingInfoDbPickler
)
val parsed = upickle.default.read(json)(Picklers.spendingInfoDbPickler)

View File

@ -13,38 +13,36 @@ import org.bitcoins.core.compat.JavaConverters._
import scala.util.Properties
import scala.util.matching.Regex
/** Everything needed to configure functionality
* of bitcoin-s applications is found in here.
/** Everything needed to configure functionality of bitcoin-s applications is
* found in here.
*
* @see [[https://github.com/bitcoin-s/bitcoin-s-core/blob/master/doc/configuration.md `configuration.md`]]
* @see
* [[https://github.com/bitcoin-s/bitcoin-s-core/blob/master/doc/configuration.md `configuration.md`]]
* for more information.
*/
abstract class AppConfig extends StartStopAsync[Unit] with BitcoinSLogger {
/** Starts this project.
* After this future resolves, all operations should be
/** Starts this project. After this future resolves, all operations should be
* able to be performed correctly.
*
* Starting may include creating database tables,
* making directories or files needed later or
* something else entirely.
* Starting may include creating database tables, making directories or files
* needed later or something else entirely.
*/
override def start(): Future[Unit] = {
Future.unit
}
/** Sub members of AppConfig should override this type with
* the type of themselves, ensuring `withOverrides` return
* the correct type
/** Sub members of AppConfig should override this type with the type of
* themselves, ensuring `withOverrides` return the correct type
*/
protected[bitcoins] type ConfigType <: AppConfig
/** Constructor to make a new instance of this config type */
protected[bitcoins] def newConfigOfType(
configOverrides: Vector[Config]): ConfigType
configOverrides: Vector[Config]
): ConfigType
/** List of user-provided configs that should
* override defaults
/** List of user-provided configs that should override defaults
*/
protected[bitcoins] def configOverrides: Vector[Config]
@ -52,14 +50,11 @@ abstract class AppConfig extends StartStopAsync[Unit] with BitcoinSLogger {
withOverrides(Vector(configOverrides))
}
/** This method returns a new `AppConfig`, where every
* key under `bitcoin-s` overrides the configuration
* picked up by other means (the `reference.conf`
* provided by bitcoin-s and the `application.conf`
* provided by the user). If you pass in configs with
* overlapping keys (e.g. several configs with the key
* `bitcoin-s.network`), the latter config overrides the
* first.
/** This method returns a new `AppConfig`, where every key under `bitcoin-s`
* overrides the configuration picked up by other means (the `reference.conf`
* provided by bitcoin-s and the `application.conf` provided by the user). If
* you pass in configs with overlapping keys (e.g. several configs with the
* key `bitcoin-s.network`), the latter config overrides the first.
*/
def withOverrides(configOverrides: Vector[Config]): ConfigType = {
val numOverrides = configOverrides.length
@ -180,7 +175,8 @@ object AppConfig extends BitcoinSLogger {
def getBaseConfig(
baseDatadir: Path,
configFileName: String,
configOverrides: Vector[Config]): Config = {
configOverrides: Vector[Config]
): Config = {
val configOptions =
ConfigParseOptions
.defaults()
@ -195,7 +191,8 @@ object AppConfig extends BitcoinSLogger {
val withDatadir =
ConfigFactory.parseString(
s"bitcoin-s.datadir = ${safePathToString(baseDatadir)}")
s"bitcoin-s.datadir = ${safePathToString(baseDatadir)}"
)
withDatadir.withFallback(config)
}
@ -237,8 +234,8 @@ object AppConfig extends BitcoinSLogger {
/** The default data directory
*
* TODO: use different directories on Windows and Mac,
* should probably mimic what Bitcoin Core does
* TODO: use different directories on Windows and Mac, should probably mimic
* what Bitcoin Core does
*/
private[bitcoins] lazy val DEFAULT_BITCOIN_S_DATADIR: Path =
Paths.get(Properties.userHome, ".bitcoin-s")
@ -246,9 +243,8 @@ object AppConfig extends BitcoinSLogger {
private[bitcoins] lazy val DEFAULT_BITCOIN_S_CONF_FILE: String =
"bitcoin-s.conf"
/** Matches the default data directory location
* with a network appended,
* both with and without a trailing `/`
/** Matches the default data directory location with a network appended, both
* with and without a trailing `/`
*/
private lazy val defaultDatadirRegex: Regex = {
// Fix for windows
@ -257,8 +253,8 @@ object AppConfig extends BitcoinSLogger {
(home + "/.bitcoin-s/(testnet3|mainnet|regtest)/?$").r
}
/** Throws if the encountered datadir is the default one. Useful
* in tests, to make sure you don't blow up important data.
/** Throws if the encountered datadir is the default one. Useful in tests, to
* make sure you don't blow up important data.
*/
private[bitcoins] def throwIfDefaultDatadir(config: AppConfig): Unit = {
val datadirStr = config.datadir.toString()

View File

@ -5,7 +5,10 @@ import com.typesafe.config.{Config, ConfigFactory}
import java.nio.file.{Path, Paths}
import scala.concurrent.ExecutionContext
/** @tparam I - the implicit argument. This is usually an execution context or an actor system */
/** @tparam I
* \- the implicit argument. This is usually an execution context or an actor
* system
*/
trait AppConfigFactoryBase[C <: AppConfig, I] {
def moduleName: String
@ -18,13 +21,15 @@ trait AppConfigFactoryBase[C <: AppConfig, I] {
fromConfig(ConfigFactory.load())
}
def fromDefaultDatadir(confs: Vector[Config] = Vector.empty)(implicit
i: I): C = {
def fromDefaultDatadir(
confs: Vector[Config] = Vector.empty
)(implicit i: I): C = {
fromDatadir(AppConfig.DEFAULT_BITCOIN_S_DATADIR, confs)
}
def fromDatadir(datadir: Path, confs: Vector[Config] = Vector.empty)(implicit
i: I): C
i: I
): C
}
trait AppConfigFactory[C <: AppConfig]

View File

@ -12,15 +12,18 @@ object FileUtil extends BitcoinSLogger {
/** Zips the [[directory]] into a zip file and then stores it at [[target]]
*
* @see https://www.quickprogrammingtips.com/java/how-to-zip-a-folder-in-java.html
* @see
* https://www.quickprogrammingtips.com/java/how-to-zip-a-folder-in-java.html
*/
def zipDirectory(
source: Path,
target: Path,
fileNameFilter: Vector[Regex] = Vector.empty): Path = {
fileNameFilter: Vector[Regex] = Vector.empty
): Path = {
require(
!Files.exists(target),
s"Cannot overwrite existing target directory=${target.toAbsolutePath}")
s"Cannot overwrite existing target directory=${target.toAbsolutePath}"
)
// create directories for target if they DNE
Files.createDirectories(target.getParent)
@ -33,16 +36,18 @@ object FileUtil extends BitcoinSLogger {
@throws[IOException]
override def visitFile(
file: Path,
attrs: BasicFileAttributes): FileVisitResult = {
attrs: BasicFileAttributes
): FileVisitResult = {
if (
fileNameFilter.exists(reg =>
file.toAbsolutePath.toString.matches(reg.regex))
fileNameFilter
.exists(reg => file.toAbsolutePath.toString.matches(reg.regex))
) {
logger.info(s"Skipping ${file.toAbsolutePath} for zip")
FileVisitResult.CONTINUE
} else {
logger.info(
s"Zipping file=${file.toAbsolutePath} to ${target.toAbsolutePath}")
s"Zipping file=${file.toAbsolutePath} to ${target.toAbsolutePath}"
)
zos.putNextEntry(new ZipEntry(source.relativize(file).toString))
Files.copy(file, zos)
zos.closeEntry()
@ -63,24 +68,27 @@ object FileUtil extends BitcoinSLogger {
def copyDirectory(
source: Path,
target: Path,
fileNameFilter: Vector[Regex] = Vector.empty): Path = {
fileNameFilter: Vector[Regex] = Vector.empty
): Path = {
Files.walkFileTree(
source,
new SimpleFileVisitor[Path]() {
@throws[IOException]
override def visitFile(
file: Path,
attrs: BasicFileAttributes): FileVisitResult = {
attrs: BasicFileAttributes
): FileVisitResult = {
if (
fileNameFilter.exists(reg =>
file.toAbsolutePath.toString.matches(reg.regex))
fileNameFilter
.exists(reg => file.toAbsolutePath.toString.matches(reg.regex))
) {
logger.info(s"Skipping ${file.toAbsolutePath} for copy")
FileVisitResult.CONTINUE
} else {
val targetPath = target.resolve(source.relativize(file))
logger.info(
s"Copying file=${file.toAbsolutePath} to ${targetPath.toAbsolutePath}")
s"Copying file=${file.toAbsolutePath} to ${targetPath.toAbsolutePath}"
)
Files.createDirectories(targetPath.getParent)
Files.copy(file, targetPath)
logger.info(s"Done copying file=${file.toAbsolutePath}")
@ -100,14 +108,16 @@ object FileUtil extends BitcoinSLogger {
new SimpleFileVisitor[Path] {
override def visitFile(
file: Path,
attrs: BasicFileAttributes): FileVisitResult = {
attrs: BasicFileAttributes
): FileVisitResult = {
Files.delete(file)
FileVisitResult.CONTINUE
}
override def postVisitDirectory(
dir: Path,
exc: IOException): FileVisitResult = {
exc: IOException
): FileVisitResult = {
Files.delete(dir)
FileVisitResult.CONTINUE
}

View File

@ -12,7 +12,8 @@ case class BitcoinSServerInfo(
blockHash: DoubleSha256DigestBE,
torStarted: Boolean,
syncing: Boolean,
isInitialBlockDownload: Boolean) {
isInitialBlockDownload: Boolean
) {
lazy val toJson: Value = {
Obj(
@ -38,11 +39,13 @@ object BitcoinSServerInfo {
val sync = obj(PicklerKeys.syncKey).bool
val isIBD = obj(PicklerKeys.isInitialBlockDownload).bool
BitcoinSServerInfo(network = network,
BitcoinSServerInfo(
network = network,
blockHeight = height,
blockHash = blockHash,
torStarted = torStarted,
syncing = sync,
isInitialBlockDownload = isIBD)
isInitialBlockDownload = isIBD
)
}
}

View File

@ -14,7 +14,8 @@ import scodec.bits.ByteVector
case class SerializedPSBT(
global: SerializedPSBTGlobalMap,
inputs: Vector[SerializedPSBTInputMap],
outputs: Vector[SerializedPSBTOutputMap]) {
outputs: Vector[SerializedPSBTOutputMap]
) {
val toJson: JsValue = Json.toJson(this)
}
@ -22,7 +23,8 @@ case class SerializedPSBTGlobalMap(
tx: SerializedTransaction,
version: UInt32,
xpubs: Option[Vector[ExtPublicKey]],
unknowns: Vector[GlobalPSBTRecord.Unknown])
unknowns: Vector[GlobalPSBTRecord.Unknown]
)
case class SerializedPSBTInputMap(
nonWitnessUtxo: Option[SerializedTransaction],
@ -35,13 +37,15 @@ case class SerializedPSBTInputMap(
finalizedScriptSig: Option[Vector[ScriptToken]],
finalizedScriptWitness: Option[SerializedTransactionWitness],
proofOfReservesCommitment: Option[ByteVector],
unknowns: Vector[InputPSBTRecord.Unknown])
unknowns: Vector[InputPSBTRecord.Unknown]
)
case class SerializedPSBTOutputMap(
redeemScript: Option[Vector[ScriptToken]],
witScript: Option[Vector[ScriptToken]],
bip32Paths: Option[Vector[OutputPSBTRecord.BIP32DerivationPath]],
unknowns: Vector[OutputPSBTRecord.Unknown])
unknowns: Vector[OutputPSBTRecord.Unknown]
)
object SerializedPSBT {
@ -57,7 +61,8 @@ object SerializedPSBT {
def decodeInputMap(
input: InputPSBTMap,
index: Int): SerializedPSBTInputMap = {
index: Int
): SerializedPSBTInputMap = {
val prevTxOpt = input.nonWitnessOrUnknownUTXOOpt.map(_.transactionSpent)
val nonWitnessUtxo = prevTxOpt.map(decodeRawTransaction)
val witnessUtxo = input.witnessUTXOOpt.map(rec =>
@ -80,7 +85,8 @@ object SerializedPSBT {
val unknowns = input.getRecords(PSBTInputKeyId.UnknownKeyId)
SerializedPSBTInputMap(nonWitnessUtxo,
SerializedPSBTInputMap(
nonWitnessUtxo,
witnessUtxo,
sigsOpt,
hashType,
@ -90,7 +96,8 @@ object SerializedPSBT {
finalizedScriptSig,
finalizedWitScript,
porCommit,
unknowns)
unknowns
)
}
def decodeOutputMap(output: OutputPSBTMap): SerializedPSBTOutputMap = {

View File

@ -26,7 +26,8 @@ case class SerializedTransaction(
weight: Long,
locktime: UInt32,
vin: Vector[SerializedTransactionInput],
vout: Vector[SerializedTransactionOutput]) {
vout: Vector[SerializedTransactionOutput]
) {
val toJson: JsValue = Json.toJson(this)
}
@ -45,7 +46,8 @@ case class SerializedTransactionWitness(
script: Option[Vector[ScriptToken]],
pubKey: Option[ECPublicKeyBytes],
signature: Option[ECDigitalSignature],
stack: Option[Vector[ByteVector]])
stack: Option[Vector[ByteVector]]
)
case class SerializedTransactionOutput(
value: BigDecimal,
@ -65,35 +67,43 @@ object SerializedTransaction {
}
def decodeRawTransactionWitness(
witness: ScriptWitness): Option[SerializedTransactionWitness] = {
witness: ScriptWitness
): Option[SerializedTransactionWitness] = {
witness match {
case EmptyScriptWitness => None
case p2wpkh: P2WPKHWitnessV0 =>
Some(
SerializedTransactionWitness(hex = p2wpkh.hex,
SerializedTransactionWitness(
hex = p2wpkh.hex,
scriptType = Some("P2WPKH"),
script = None,
pubKey = Some(p2wpkh.pubKey),
signature = Some(p2wpkh.signature),
stack = None))
stack = None
)
)
case p2wsh: P2WSHWitnessV0 =>
Some(
SerializedTransactionWitness(hex = p2wsh.hex,
SerializedTransactionWitness(
hex = p2wsh.hex,
scriptType = Some("P2WSH"),
script =
Some(p2wsh.redeemScript.asm.toVector),
script = Some(p2wsh.redeemScript.asm.toVector),
pubKey = None,
signature = None,
stack = Some(p2wsh.stack.toVector.tail)))
stack = Some(p2wsh.stack.toVector.tail)
)
)
case taprootWitness: TaprootWitness =>
throw new UnsupportedOperationException(
s"Taproot not supported, got=$taprootWitness")
s"Taproot not supported, got=$taprootWitness"
)
}
}
def decodeTransactionInput(
input: TransactionInput,
witnessOpt: Option[ScriptWitness]): SerializedTransactionInput = {
witnessOpt: Option[ScriptWitness]
): SerializedTransactionInput = {
val decodedWitnessOpt = witnessOpt.flatMap(decodeRawTransactionWitness)
SerializedTransactionInput(
@ -108,11 +118,14 @@ object SerializedTransaction {
def decodeTransactionOutput(
output: TransactionOutput,
index: Int): SerializedTransactionOutput = {
SerializedTransactionOutput(value = output.value.toBigDecimal,
index: Int
): SerializedTransactionOutput = {
SerializedTransactionOutput(
value = output.value.toBigDecimal,
n = UInt32(index),
scriptPubKey = output.scriptPubKey.asm.toVector,
hex = output.hex)
hex = output.hex
)
}
def decodeRawTransaction(tx: Transaction): SerializedTransaction = {
@ -135,7 +148,8 @@ object SerializedTransaction {
case wtx: WitnessTransaction => Some(wtx.wTxIdBE)
}
SerializedTransaction(txid = tx.txIdBE,
SerializedTransaction(
txid = tx.txIdBE,
wtxid = wtxIdOpt,
version = tx.version,
size = tx.byteSize,
@ -143,6 +157,7 @@ object SerializedTransaction {
weight = tx.weight,
locktime = tx.lockTime,
vin = inputs,
vout = outputs)
vout = outputs
)
}
}

View File

@ -24,8 +24,8 @@ case class DumpTxOutSetResult(
coins_written: Int,
base_hash: DoubleSha256DigestBE,
base_height: Int,
path: Path)
extends BlockchainResult
path: Path
) extends BlockchainResult
case class GetBlockResult(
hash: DoubleSha256DigestBE,
@ -45,8 +45,8 @@ case class GetBlockResult(
difficulty: BigDecimal,
chainwork: String,
previousblockhash: Option[DoubleSha256DigestBE],
nextblockhash: Option[DoubleSha256DigestBE])
extends BlockchainResult
nextblockhash: Option[DoubleSha256DigestBE]
) extends BlockchainResult
abstract trait GetBlockWithTransactionsResult extends BlockchainResult {
def hash: DoubleSha256DigestBE
@ -87,8 +87,8 @@ case class GetBlockWithTransactionsResultV22(
difficulty: BigDecimal,
chainwork: String,
previousblockhash: Option[DoubleSha256DigestBE],
nextblockhash: Option[DoubleSha256DigestBE])
extends GetBlockWithTransactionsResult
nextblockhash: Option[DoubleSha256DigestBE]
) extends GetBlockWithTransactionsResult
sealed trait GetBlockChainInfoResult extends BlockchainResult {
def chain: NetworkParameters
@ -121,8 +121,8 @@ case class GetBlockChainInfoResultPreV19(
pruneheight: Option[Int],
softforks: Vector[SoftforkPreV19],
bip9_softforks: Map[String, Bip9SoftforkPreV19],
warnings: String)
extends GetBlockChainInfoResult
warnings: String
) extends GetBlockChainInfoResult
case class GetBlockChainInfoResultPostV19(
chain: NetworkParameters,
@ -138,8 +138,8 @@ case class GetBlockChainInfoResultPostV19(
pruned: Boolean,
pruneheight: Option[Int],
softforks: Map[String, SoftforkPostV19],
warnings: String)
extends GetBlockChainInfoResult
warnings: String
) extends GetBlockChainInfoResult
// adds time field removes softforks field
case class GetBlockChainInfoResultPostV23(
@ -156,30 +156,30 @@ case class GetBlockChainInfoResultPostV23(
size_on_disk: Long,
pruned: Boolean,
pruneheight: Option[Int],
warnings: String)
extends GetBlockChainInfoResult
warnings: String
) extends GetBlockChainInfoResult
case class SoftforkPreV19(
id: String,
version: Int,
enforce: Option[Map[String, SoftforkProgressPreV19]],
reject: SoftforkProgressPreV19)
extends BlockchainResult
reject: SoftforkProgressPreV19
) extends BlockchainResult
case class SoftforkProgressPreV19(
status: Option[Boolean],
found: Option[Int],
required: Option[Int],
window: Option[Int])
extends BlockchainResult
window: Option[Int]
) extends BlockchainResult
case class Bip9SoftforkPreV19(
status: String,
bit: Option[Int],
startTime: Int,
timeout: BigInt,
since: Int)
extends BlockchainResult
since: Int
) extends BlockchainResult
sealed trait SoftforkPostV19 extends BlockchainResult
@ -194,8 +194,8 @@ case class Bip9SoftforkDetails(
bit: Option[Int],
start_time: Int,
timeout: BigInt,
since: Int)
extends BlockchainResult
since: Int
) extends BlockchainResult
case class GetBlockHeaderResult(
hash: DoubleSha256DigestBE,
@ -211,8 +211,8 @@ case class GetBlockHeaderResult(
difficulty: BigDecimal,
chainwork: String,
previousblockhash: Option[DoubleSha256DigestBE],
nextblockhash: Option[DoubleSha256DigestBE])
extends BlockchainResult {
nextblockhash: Option[DoubleSha256DigestBE]
) extends BlockchainResult {
lazy val blockHeaderDb: BlockHeaderDb = {
val bytes = ByteVector.fromValidHex(chainwork).dropWhile(_ == 0x00).toArray
@ -231,12 +231,14 @@ case class GetBlockHeaderResult(
previousblockhash.get
}
}
BlockHeader(version = Int32(version),
BlockHeader(
version = Int32(version),
previousBlockHash = prevHash.flip,
merkleRootHash = merkleroot.flip,
time = time,
nBits = bits,
nonce = nonce)
nonce = nonce
)
}
}
@ -244,8 +246,8 @@ case class ChainTip(
height: Int,
hash: DoubleSha256DigestBE,
branchlen: Int,
status: String)
extends BlockchainResult
status: String
) extends BlockchainResult
case class GetChainTxStatsResult(
time: UInt32,
@ -254,8 +256,8 @@ case class GetChainTxStatsResult(
window_final_block_height: Option[Int],
window_tx_count: Option[Int],
window_interval: Option[UInt32],
txrate: Option[BigDecimal])
extends BlockchainResult
txrate: Option[BigDecimal]
) extends BlockchainResult
sealed trait GetMemPoolResult extends BlockchainResult {
def size: Int
@ -284,8 +286,8 @@ case class GetMemPoolResultPreV19(
ancestorfees: Option[Bitcoins],
wtxid: DoubleSha256DigestBE,
fees: FeeInfo,
depends: Vector[DoubleSha256DigestBE])
extends GetMemPoolResult
depends: Vector[DoubleSha256DigestBE]
) extends GetMemPoolResult
case class GetMemPoolResultPostV19(
vsize: Int,
@ -301,8 +303,8 @@ case class GetMemPoolResultPostV19(
ancestorfees: Option[Bitcoins],
wtxid: DoubleSha256DigestBE,
fees: FeeInfo,
depends: Vector[DoubleSha256DigestBE])
extends GetMemPoolResult {
depends: Vector[DoubleSha256DigestBE]
) extends GetMemPoolResult {
override def size: Int = vsize
}
@ -317,8 +319,8 @@ case class GetMemPoolResultPostV23(
ancestorsize: Int,
wtxid: DoubleSha256DigestBE,
fees: FeeInfo,
depends: Vector[DoubleSha256DigestBE])
extends GetMemPoolResult {
depends: Vector[DoubleSha256DigestBE]
) extends GetMemPoolResult {
override def size: Int = vsize
}
@ -356,8 +358,8 @@ case class GetMemPoolEntryResultPreV19(
ancestorfees: BitcoinFeeUnit,
wtxid: DoubleSha256DigestBE,
fees: FeeInfo,
depends: Option[Vector[DoubleSha256DigestBE]])
extends GetMemPoolEntryResult
depends: Option[Vector[DoubleSha256DigestBE]]
) extends GetMemPoolEntryResult
case class GetMemPoolEntryResultPostV19(
vsize: Int,
@ -374,8 +376,8 @@ case class GetMemPoolEntryResultPostV19(
ancestorfees: BitcoinFeeUnit,
wtxid: DoubleSha256DigestBE,
fees: FeeInfo,
depends: Option[Vector[DoubleSha256DigestBE]])
extends GetMemPoolEntryResult {
depends: Option[Vector[DoubleSha256DigestBE]]
) extends GetMemPoolEntryResult {
override def size: Int = vsize
}
@ -390,8 +392,8 @@ case class GetMemPoolEntryResultPostV23(
ancestorsize: Int,
wtxid: DoubleSha256DigestBE,
fees: FeeInfo,
depends: Option[Vector[DoubleSha256DigestBE]])
extends GetMemPoolEntryResult {
depends: Option[Vector[DoubleSha256DigestBE]]
) extends GetMemPoolEntryResult {
override def size: Int = vsize
}
@ -401,8 +403,8 @@ case class GetMemPoolInfoResult(
usage: Int,
maxmempool: Int,
mempoolminfee: BitcoinFeeUnit,
minrelaytxfee: Bitcoins)
extends BlockchainResult
minrelaytxfee: Bitcoins
) extends BlockchainResult
sealed abstract trait GetTxOutResult extends BlockchainResult {
def bestblock: DoubleSha256DigestBE
@ -417,8 +419,8 @@ case class GetTxOutResultV22(
confirmations: Int,
value: Bitcoins,
scriptPubKey: RpcScriptPubKeyPostV22,
coinbase: Boolean)
extends GetTxOutResult
coinbase: Boolean
) extends GetTxOutResult
case class GetTxOutSetInfoResult(
height: Int,
@ -428,17 +430,18 @@ case class GetTxOutSetInfoResult(
bogosize: Int,
hash_serialized_2: DoubleSha256DigestBE,
disk_size: Int,
total_amount: Bitcoins)
extends BlockchainResult
total_amount: Bitcoins
) extends BlockchainResult
case class GetBlockFilterResult(
filter: GolombFilter,
header: DoubleSha256DigestBE)
extends BlockchainResult {
header: DoubleSha256DigestBE
) extends BlockchainResult {
def filterDb(
height: Int,
blockHashBE: DoubleSha256DigestBE): CompactFilterDb = {
blockHashBE: DoubleSha256DigestBE
): CompactFilterDb = {
CompactFilterDbHelper.fromGolombFilter(filter, blockHashBE, height)
}
}
@ -446,7 +449,8 @@ case class GetBlockFilterResult(
case class GetTxSpendingPrevOutResult(
txid: DoubleSha256DigestBE,
vout: Int,
spendingtxid: Option[DoubleSha256DigestBE]) {
spendingtxid: Option[DoubleSha256DigestBE]
) {
def outpoint: TransactionOutPoint = TransactionOutPoint(txid, UInt32(vout))
}

View File

@ -13,8 +13,8 @@ sealed abstract class NetworkResult
case class Node(
addednode: URI,
connected: Option[Boolean],
addresses: Option[Vector[NodeAddress]])
extends NetworkResult
addresses: Option[Vector[NodeAddress]]
) extends NetworkResult
case class NodeAddress(address: URI, connected: String) extends NetworkResult
@ -22,8 +22,8 @@ case class GetNetTotalsResult(
totalbytesrecv: Int,
totalbytessent: Int,
timemillis: UInt64,
uploadtarget: NetTarget)
extends NetworkResult
uploadtarget: NetTarget
) extends NetworkResult
case class NetTarget(
timeframe: UInt32,
@ -31,8 +31,8 @@ case class NetTarget(
target_reached: Boolean,
serve_historical_blocks: Boolean,
bytes_left_in_cycle: Int,
time_left_in_cycle: UInt32)
extends NetworkResult
time_left_in_cycle: UInt32
) extends NetworkResult
trait GetNetworkInfoResult extends NetworkResult {
def version: Int
@ -65,8 +65,8 @@ case class GetNetworkInfoResultPreV21(
relayfee: Bitcoins,
incrementalfee: Bitcoins,
localaddresses: Vector[NetworkAddress],
warnings: String)
extends GetNetworkInfoResult
warnings: String
) extends GetNetworkInfoResult
case class GetNetworkInfoResultPostV21(
version: Int,
@ -84,16 +84,16 @@ case class GetNetworkInfoResultPostV21(
relayfee: Bitcoins,
incrementalfee: Bitcoins,
localaddresses: Vector[NetworkAddress],
warnings: String)
extends GetNetworkInfoResult
warnings: String
) extends GetNetworkInfoResult
case class Network(
name: String,
limited: Boolean,
reachable: Boolean,
proxy: String,
proxy_randomize_credentials: Boolean)
extends NetworkResult
proxy_randomize_credentials: Boolean
) extends NetworkResult
case class NetworkAddress(address: String, port: Int, score: Int)
extends NetworkResult
@ -127,8 +127,8 @@ case class PeerPostV21(
inflight: Vector[Int],
bytessent_per_msg: Map[String, Int],
bytesrecv_per_msg: Map[String, Int],
minfeefilter: Option[SatoshisPerKiloByte])
extends Peer {
minfeefilter: Option[SatoshisPerKiloByte]
) extends Peer {
override val addnode: Boolean = connection_type == "manual"
}
@ -148,8 +148,8 @@ case class PeerV22(
minfeefilter: Option[SatoshisPerKiloByte],
bip152_hb_to: Boolean,
bip152_hb_from: Boolean,
permissions: Vector[String])
extends Peer {
permissions: Vector[String]
) extends Peer {
override val addnode: Boolean = connection_type == "manual"
}
@ -186,8 +186,8 @@ case class PeerNetworkInfoPreV21(
timeoffset: Int,
pingtime: Option[BigDecimal],
minping: Option[BigDecimal],
pingwait: Option[BigDecimal])
extends PeerNetworkInfo
pingwait: Option[BigDecimal]
) extends PeerNetworkInfo
case class PeerNetworkInfoPostV21(
addr: URI,
@ -208,8 +208,8 @@ case class PeerNetworkInfoPostV21(
timeoffset: Int,
pingtime: Option[BigDecimal],
minping: Option[BigDecimal],
pingwait: Option[BigDecimal])
extends PeerNetworkInfo
pingwait: Option[BigDecimal]
) extends PeerNetworkInfo
trait NodeBan extends NetworkResult {
def address: URI
@ -221,8 +221,8 @@ case class NodeBanPreV20(
address: URI,
banned_until: UInt32,
ban_created: UInt32,
ban_reason: String)
extends NodeBan
ban_reason: String
) extends NodeBan
case class NodeBanPostV22(
address: URI,

View File

@ -40,8 +40,8 @@ case class GetBlockTemplateResult(
weightlimit: Int,
curtime: UInt32,
bits: String, // What should this be?
height: Int)
extends OtherResult
height: Int
) extends OtherResult
case class BlockTransaction(
data: Transaction,
@ -51,8 +51,8 @@ case class BlockTransaction(
fee: Satoshis,
sigops: Int,
weight: Int,
required: Option[Boolean])
extends OtherResult
required: Option[Boolean]
) extends OtherResult
case class GetMiningInfoResult(
blocks: Int,
@ -62,8 +62,8 @@ case class GetMiningInfoResult(
networkhashps: BigDecimal,
pooledtx: Int,
chain: String,
warnings: String)
extends OtherResult
warnings: String
) extends OtherResult
case class GetMemoryInfoResult(locked: MemoryManager) extends OtherResult
@ -75,14 +75,12 @@ case class MemoryManager(
total: Int,
locked: Int,
chunks_used: Int,
chunks_free: Int)
extends OtherResult
chunks_free: Int
) extends OtherResult
/** @note This is defined as a trait
* and not just a raw case class
* (as is done in other RPC return
* values) in order to make it possible
* to deprecate fields.
/** @note
* This is defined as a trait and not just a raw case class (as is done in
* other RPC return values) in order to make it possible to deprecate fields.
*/
trait ValidateAddressResult {
@ -149,14 +147,14 @@ case class ValidateAddressResultImpl(
hdmasterkeyid: Option[Sha256Hash160Digest],
ischange: Option[Boolean],
solvable: Option[Boolean],
desc: Option[String])
extends ValidateAddressResult
desc: Option[String]
) extends ValidateAddressResult
case class EstimateSmartFeeResult(
feerate: Option[BitcoinFeeUnit],
errors: Option[Vector[String]],
blocks: Int)
extends OtherResult
blocks: Int
) extends OtherResult
case class TestMempoolAcceptResult(
txid: DoubleSha256DigestBE,
@ -164,17 +162,12 @@ case class TestMempoolAcceptResult(
rejectReason: Option[String]
)
/** sealed trait TestMempoolAcceptResult {
* def txid: DoubleSha256DigestBE
* def allowed: Boolean
* def rejectReason: Option[String]
* }
/** sealed trait TestMempoolAcceptResult { def txid: DoubleSha256DigestBE def
* allowed: Boolean def rejectReason: Option[String] }
*
* case class TestMempoolAcceptResultPreV22(
* txid: DoubleSha256DigestBE,
* allowed: Boolean,
* rejectReason: Option[String]
* ) extends TestMempoolAcceptResult
* case class TestMempoolAcceptResultPreV22( txid: DoubleSha256DigestBE,
* allowed: Boolean, rejectReason: Option[String] ) extends
* TestMempoolAcceptResult
*/
case class FeeInfoTwo(

View File

@ -33,8 +33,8 @@ case class RpcTransactionV22(
locktime: UInt32,
vin: Vector[TransactionInput],
vout: Vector[RpcTransactionOutputV22],
hex: Option[Transaction])
extends RpcTransaction
hex: Option[Transaction]
) extends RpcTransaction
sealed trait RpcTransactionOutput extends RawTransactionResult {
def value: Bitcoins
@ -45,14 +45,14 @@ sealed trait RpcTransactionOutput extends RawTransactionResult {
case class RpcTransactionOutputPreV22(
value: Bitcoins,
n: Int,
scriptPubKey: RpcScriptPubKeyPreV22)
extends RpcTransactionOutput
scriptPubKey: RpcScriptPubKeyPreV22
) extends RpcTransactionOutput
case class RpcTransactionOutputV22(
value: Bitcoins,
n: Int,
scriptPubKey: RpcScriptPubKeyPostV22)
extends RpcTransactionOutput
scriptPubKey: RpcScriptPubKeyPostV22
) extends RpcTransactionOutput
sealed trait RpcScriptPubKey extends RawTransactionResult {
def asm: String
@ -66,16 +66,16 @@ case class RpcScriptPubKeyPreV22(
hex: String,
reqSigs: Option[Int],
scriptType: ScriptType,
addresses: Option[Vector[BitcoinAddress]])
extends RpcScriptPubKey
addresses: Option[Vector[BitcoinAddress]]
) extends RpcScriptPubKey
case class RpcScriptPubKeyPostV22(
asm: String,
hex: String,
scriptType: ScriptType,
addresses: Option[Vector[BitcoinAddress]],
address: Option[BitcoinAddress])
extends RpcScriptPubKey
address: Option[BitcoinAddress]
) extends RpcScriptPubKey
sealed trait DecodeScriptResult extends RawTransactionResult {
def asm: String
@ -86,14 +86,14 @@ sealed trait DecodeScriptResult extends RawTransactionResult {
case class DecodeScriptResultV22(
asm: String,
typeOfScript: Option[ScriptType],
p2sh: P2SHAddress)
extends DecodeScriptResult
p2sh: P2SHAddress
) extends DecodeScriptResult
case class FundRawTransactionResult(
hex: Transaction,
fee: Bitcoins,
changepos: Int)
extends RawTransactionResult
changepos: Int
) extends RawTransactionResult
case class SignRawTransactionWithWalletResult(
hex: Transaction,
@ -131,8 +131,8 @@ case class GetRawTransactionResultV22(
blockhash: Option[DoubleSha256DigestBE],
confirmations: Option[Int],
time: Option[UInt32],
blocktime: Option[UInt32])
extends GetRawTransactionResult
blocktime: Option[UInt32]
) extends GetRawTransactionResult
case class GetRawTransactionVin(
txid: Option[DoubleSha256DigestBE],
@ -148,16 +148,16 @@ case class GetRawTransactionScriptSig(asm: String, hex: ScriptSignature)
case class SignRawTransactionResult(
hex: Transaction,
complete: Boolean,
errors: Option[Vector[SignRawTransactionError]])
extends RawTransactionResult
errors: Option[Vector[SignRawTransactionError]]
) extends RawTransactionResult
case class SignRawTransactionError(
txid: DoubleSha256DigestBE,
vout: Int,
scriptSig: ScriptPubKey,
sequence: UInt32,
error: String)
extends RawTransactionResult
error: String
) extends RawTransactionResult
final case class GetRpcInfoResult(
active_commands: Vector[RpcCommands]

View File

@ -43,7 +43,8 @@ object RpcOpts {
lockUnspents: Boolean = false,
reverseChangeKey: Boolean = true,
feeRate: Option[Bitcoins] = None,
subtractFeeFromOutputs: Option[Vector[Int]])
subtractFeeFromOutputs: Option[Vector[Int]]
)
sealed abstract class FeeEstimationMode
@ -75,8 +76,9 @@ object RpcOpts {
}
}
implicit val fundRawTransactionOptionsWrites: Writes[
FundRawTransactionOptions] = Json.writes[FundRawTransactionOptions]
implicit val fundRawTransactionOptionsWrites
: Writes[FundRawTransactionOptions] =
Json.writes[FundRawTransactionOptions]
case class SignRawTransactionOutputParameter(
txid: DoubleSha256DigestBE,
@ -84,10 +86,11 @@ object RpcOpts {
scriptPubKey: ScriptPubKey,
redeemScript: Option[ScriptPubKey] = None,
witnessScript: Option[WitnessScriptPubKey] = None,
amount: Option[Bitcoins] = None)
amount: Option[Bitcoins] = None
)
implicit val signRawTransactionOutputParameterWrites: Writes[
SignRawTransactionOutputParameter] =
implicit val signRawTransactionOutputParameterWrites
: Writes[SignRawTransactionOutputParameter] =
Json.writes[SignRawTransactionOutputParameter]
object SignRawTransactionOutputParameter {
@ -97,7 +100,8 @@ object RpcOpts {
scriptPubKey: ScriptPubKey,
redeemScript: Option[ScriptPubKey] = None,
witnessScript: Option[WitnessScriptPubKey] = None,
amount: Option[Bitcoins] = None): SignRawTransactionOutputParameter = {
amount: Option[Bitcoins] = None
): SignRawTransactionOutputParameter = {
SignRawTransactionOutputParameter(
txid = transactionInput.previousOutput.txIdBE,
vout = transactionInput.previousOutput.vout.toInt,
@ -117,7 +121,8 @@ object RpcOpts {
keys: Option[Vector[ECPrivateKeyBytes]] = None,
internal: Option[Boolean] = None,
watchonly: Option[Boolean] = None,
label: Option[String] = None)
label: Option[String] = None
)
case class ImportMultiAddress(address: BitcoinAddress)
@ -140,7 +145,8 @@ object RpcOpts {
object LockUnspentOutputParameter {
def fromOutPoint(
outPoint: TransactionOutPoint): LockUnspentOutputParameter = {
outPoint: TransactionOutPoint
): LockUnspentOutputParameter = {
LockUnspentOutputParameter(outPoint.txIdBE, outPoint.vout.toInt)
}
@ -216,7 +222,9 @@ object RpcOpts {
override def fromString(string: String): AddressType = {
fromStringOpt(string).getOrElse(
throw new IllegalArgumentException(
s"Could not find AddressType for string: $string"))
s"Could not find AddressType for string: $string"
)
)
}
}
@ -236,7 +244,8 @@ object RpcOpts {
case class BlockTemplateRequest(
mode: String,
capabilities: Vector[String],
rules: Vector[String])
rules: Vector[String]
)
implicit val blockTemplateRequest: Writes[BlockTemplateRequest] =
Json.writes[BlockTemplateRequest]

View File

@ -35,8 +35,8 @@ final case class DecodePsbtResultV22(
unknown: Map[String, String],
inputs: Vector[RpcPsbtInputV22],
outputs: Vector[RpcPsbtOutput],
fee: Option[Bitcoins])
extends DecodePsbtResult
fee: Option[Bitcoins]
) extends DecodePsbtResult
sealed abstract class RpcPsbtInput extends RpcPsbtResult {
def nonWitnessUtxo: Option[RpcTransaction]

View File

@ -30,15 +30,16 @@ case class MultiSigResultPostV20(
address: BitcoinAddress,
redeemScript: ScriptPubKey,
descriptor: String,
warnings: Option[String]) //available in v23
warnings: Option[String]
) //available in v23
extends MultiSigResult
case class BumpFeeResult(
txid: DoubleSha256DigestBE,
origfee: Bitcoins,
fee: Bitcoins, // TODO: Should be BitcoinFeeUnit
errors: Vector[String])
extends WalletResult
errors: Vector[String]
) extends WalletResult
case class GetTransactionResult(
amount: Bitcoins,
@ -56,14 +57,14 @@ case class GetTransactionResult(
comment: Option[String],
to: Option[String],
details: Vector[TransactionDetails],
hex: Transaction)
extends WalletResult
hex: Transaction
) extends WalletResult
case class SetWalletFlagResult(
flag_name: String,
flag_state: Boolean,
warnings: Option[String])
extends WalletResult
warnings: Option[String]
) extends WalletResult
case class GetBalancesResult(mine: BalanceInfo, watchonly: Option[BalanceInfo])
extends WalletResult
@ -71,7 +72,8 @@ case class GetBalancesResult(mine: BalanceInfo, watchonly: Option[BalanceInfo])
case class BalanceInfo(
trusted: Bitcoins,
untrusted_pending: Bitcoins,
immature: Bitcoins)
immature: Bitcoins
)
case class TransactionDetails(
involvesWatchonly: Option[Boolean],
@ -81,8 +83,8 @@ case class TransactionDetails(
amount: Bitcoins,
vout: Int,
fee: Option[Bitcoins],
abandoned: Option[Boolean])
extends WalletResult
abandoned: Option[Boolean]
) extends WalletResult
sealed trait GetWalletInfoResult extends WalletResult {
def walletname: String
@ -114,8 +116,8 @@ case class GetWalletInfoResultPostV22(
hdmasterkeyid: Option[Sha256Hash160Digest],
unlocked_until: Option[Int],
private_keys_enabled: Boolean,
descriptors: Boolean)
extends GetWalletInfoResult
descriptors: Boolean
) extends GetWalletInfoResult
case class ImportMultiResult(success: Boolean, error: Option[ImportMultiError])
extends WalletResult
@ -125,15 +127,15 @@ case class ImportMultiError(code: Int, message: String) extends WalletResult
case class RpcAddress(
address: BitcoinAddress,
balance: Bitcoins,
account: Option[String])
extends WalletResult
account: Option[String]
) extends WalletResult
case class RpcAccount(
involvesWatchonly: Boolean,
account: String,
amount: Bitcoins,
confirmations: Int)
extends WalletResult
confirmations: Int
) extends WalletResult
case class LoadWalletResult(name: String, warning: String) extends WalletResult
case class RescanBlockChainResult(start_height: Int, stop_height: Int)
@ -146,28 +148,28 @@ case class ReceivedAddress(
amount: Bitcoins,
confirmations: Int,
label: String,
txids: Vector[DoubleSha256DigestBE])
extends WalletResult
txids: Vector[DoubleSha256DigestBE]
) extends WalletResult
case class ReceivedAccount(
involvesWatchonly: Option[Boolean],
account: String,
amount: Bitcoins,
confirmations: Int,
lable: Option[String])
extends WalletResult
lable: Option[String]
) extends WalletResult
case class ReceivedLabel(
involvesWatchonly: Option[Boolean],
amount: Bitcoins,
confirmations: Int,
label: String)
extends WalletResult
label: String
) extends WalletResult
case class ListSinceBlockResult(
transactions: Vector[Payment],
lastblock: DoubleSha256DigestBE)
extends WalletResult
lastblock: DoubleSha256DigestBE
) extends WalletResult
case class Payment(
involvesWatchonly: Option[Boolean],
@ -188,8 +190,8 @@ case class Payment(
timereceived: UInt32,
bip125_replaceable: String,
comment: Option[String],
to: Option[String])
extends WalletResult
to: Option[String]
) extends WalletResult
case class ListTransactionsResult(
account: Option[String],
@ -214,8 +216,8 @@ case class ListTransactionsResult(
to: Option[String],
otheraccount: Option[String],
bip125_replaceable: String,
abandoned: Option[Boolean])
extends WalletResult
abandoned: Option[Boolean]
) extends WalletResult
case class UnspentOutput(
txid: DoubleSha256DigestBE,
@ -228,8 +230,8 @@ case class UnspentOutput(
confirmations: Int,
spendable: Boolean,
solvable: Boolean,
reused: Option[Boolean])
extends WalletResult
reused: Option[Boolean]
) extends WalletResult
sealed trait AddressInfoResult extends WalletResult {
def address: BitcoinAddress
@ -274,8 +276,8 @@ case class AddressInfoResultPreV18(
hdkeypath: Option[BIP32Path],
hdseedid: Option[RipeMd160Digest],
hdmasterkeyid: Option[RipeMd160Digest],
labels: Vector[LabelResult])
extends AddressInfoResult
labels: Vector[LabelResult]
) extends AddressInfoResult
// The split into two case classes is to deal with the 22 param limit for case classes
case class AddressInfoResultPostV18(
@ -297,8 +299,8 @@ case class AddressInfoResultPostV18(
hdkeypath: Option[BIP32Path],
hdseedid: Option[RipeMd160Digest],
hdmasterfingerprint: Option[String],
labels: Vector[LabelResult])
extends AddressInfoResult {
labels: Vector[LabelResult]
) extends AddressInfoResult {
override def ismine: Boolean = isProps.ismine
def solvable: Boolean = isProps.solvable
override def iswatchonly: Boolean = isProps.iswatchonly
@ -315,7 +317,8 @@ object AddressInfoResultPostV18 {
iswatchonly: Boolean,
isscript: Boolean,
iswitness: Boolean,
iscompressed: Option[Boolean])
iscompressed: Option[Boolean]
)
case class AddressInfoResultPostV18WithoutIsProps(
address: BitcoinAddress,
@ -335,11 +338,13 @@ object AddressInfoResultPostV18 {
hdkeypath: Option[BIP32Path],
hdseedid: Option[RipeMd160Digest],
hdmasterfingerprint: Option[String],
labels: Vector[LabelResult])
labels: Vector[LabelResult]
)
def apply(
info: AddressInfoResultPostV18WithoutIsProps,
isProps: AddressInfoIsProps): AddressInfoResultPostV18 = {
isProps: AddressInfoIsProps
): AddressInfoResultPostV18 = {
AddressInfoResultPostV18(
address = info.address,
scriptPubKey = info.scriptPubKey,
@ -401,7 +406,8 @@ object AddressInfoResultPostV21 {
iswatchonly: Boolean,
isscript: Boolean,
iswitness: Boolean,
iscompressed: Option[Boolean])
iscompressed: Option[Boolean]
)
case class AddressInfoResultPostV21WithoutIsProps(
address: BitcoinAddress,
@ -420,11 +426,13 @@ object AddressInfoResultPostV21 {
hdkeypath: Option[BIP32Path],
hdseedid: Option[RipeMd160Digest],
hdmasterfingerprint: Option[String],
labels: Vector[String])
labels: Vector[String]
)
def apply(
info: AddressInfoResultPostV21WithoutIsProps,
isProps: AddressInfoIsProps): AddressInfoResultPostV21 = {
isProps: AddressInfoIsProps
): AddressInfoResultPostV21 = {
AddressInfoResultPostV21(
address = info.address,
scriptPubKey = info.scriptPubKey,
@ -474,8 +482,8 @@ case class EmbeddedResult(
witness_program: Option[String],
pubkey: ECPublicKey,
address: BitcoinAddress,
scriptPubKey: ScriptPubKey)
extends WalletResult
scriptPubKey: ScriptPubKey
) extends WalletResult
case class LabelResult(name: String, purpose: LabelPurpose) extends WalletResult
@ -494,5 +502,5 @@ final case class CreateWalletResult(
case class ImportDescriptorResult(
success: Boolean,
warnings: Option[Vector[String]])
extends WalletResult
warnings: Option[Vector[String]]
) extends WalletResult

View File

@ -18,7 +18,8 @@ object ContractDescriptorParser {
def parseCmdLine(
value: ujson.Value,
announcementTLV: OracleAnnouncementTLV): ContractDescriptorTLV = {
announcementTLV: OracleAnnouncementTLV
): ContractDescriptorTLV = {
value match {
case obj: Obj =>
upickle.default
@ -33,19 +34,24 @@ object ContractDescriptorParser {
}
val payoutCurve = DLCPayoutCurve
.fromPoints(payoutPoints,
serializationVersion = DLCSerializationVersion.Beta)
.fromPoints(
payoutPoints,
serializationVersion = DLCSerializationVersion.Beta
)
.toTLV
val numDigits = announcementTLV.eventTLV.eventDescriptor
.asInstanceOf[DigitDecompositionEventDescriptorV0TLV]
.numDigits
.toInt
ContractDescriptorV1TLV(numDigits,
ContractDescriptorV1TLV(
numDigits,
payoutCurve,
RoundingIntervalsV0TLV.noRounding)
RoundingIntervalsV0TLV.noRounding
)
case fail @ (_: Num | _: Bool | Null | _: Str) =>
sys.error(
s"Cannot parse contract descriptor from $fail, expected json object or array")
s"Cannot parse contract descriptor from $fail, expected json object or array"
)
}
}
}

View File

@ -94,8 +94,8 @@ object CLightningJsonModels {
case class ListFundsResult(
outputs: Vector[Output],
channels: Vector[ChannelFunds])
extends CLightningJsonModel
channels: Vector[ChannelFunds]
) extends CLightningJsonModel
case class Channel(
source: NodeId,
@ -159,8 +159,8 @@ object CLightningJsonModels {
connected: Boolean,
features: ByteVector,
netaddr: Vector[String],
channels: Vector[CLightningPeerChannel])
extends CLightningJsonModel
channels: Vector[CLightningPeerChannel]
) extends CLightningJsonModel
case class CLightningPeers(peers: Vector[CLightningPeer])
extends CLightningJsonModel
@ -228,8 +228,8 @@ object CLightningJsonModels {
) extends CLightningJsonModel
case class CLightningListInvoicesResult(
invoices: Vector[CLightningLookupInvoiceResult])
extends CLightningJsonModel
invoices: Vector[CLightningLookupInvoiceResult]
) extends CLightningJsonModel
case class CLightningPsbtResult(signed_psbt: PSBT) extends CLightningJsonModel

View File

@ -33,22 +33,27 @@ case class GetInfoResult(
network: BitcoinNetwork,
blockHeight: Long,
publicAddresses: Seq[InetSocketAddress],
instanceId: UUID)
instanceId: UUID
)
case class PeerInfo(
nodeId: NodeId,
state: PeerState,
address: Option[String],
channels: Int)
channels: Int
)
case class ChannelCommandResult(
results: scala.collection.Map[
Either[ShortChannelId, FundedChannelId],
results: scala.collection.Map[Either[
ShortChannelId,
FundedChannelId
],
State]
)
case class UpdateRelayFeeResult(
results: Map[Either[ShortChannelId, FundedChannelId], UpdateRelayFee])
results: Map[Either[ShortChannelId, FundedChannelId], UpdateRelayFee]
)
sealed trait UpdateRelayFee
@ -57,8 +62,8 @@ object UpdateRelayFee {
case class OK(
channelId: ChannelId,
feeBaseMsat: MilliSatoshis,
feeProportionalMillionths: Long)
extends UpdateRelayFee
feeProportionalMillionths: Long
) extends UpdateRelayFee
case class Error(message: String) extends UpdateRelayFee
}
@ -84,12 +89,10 @@ object ChannelCommandResult extends StringFactory[State] {
}
}
/** This is the data model returned by the RPC call
* `channels nodeId`. The content of the objects
* being returne differ based on whatever state
* the channel is in. The member of this abstract
* class are in eveyr channel state, whereas other
* channel states may have extra information.
/** This is the data model returned by the RPC call `channels nodeId`. The
* content of the objects being returne differ based on whatever state the
* channel is in. The member of this abstract class are in eveyr channel state,
* whereas other channel states may have extra information.
*/
sealed abstract class ChannelInfo {
def nodeId: NodeId
@ -111,8 +114,8 @@ case class BaseChannelInfo(
state: ChannelState
) extends ChannelInfo
/** This represents the case where the channel is
* in state `NORMAL` (i.e. an open channel)
/** This represents the case where the channel is in state `NORMAL` (i.e. an
* open channel)
*/
case class OpenChannelInfo(
nodeId: NodeId,
@ -129,7 +132,8 @@ case class UnknownFeature(bitIndex: Int)
case class Features(
activated: Set[ActivatedFeature],
unknown: Set[UnknownFeature])
unknown: Set[UnknownFeature]
)
case class NodeInfo(
signature: ECDigitalSignature,
@ -138,7 +142,8 @@ case class NodeInfo(
nodeId: NodeId,
rgbColor: String,
alias: String,
addresses: Vector[InetSocketAddress])
addresses: Vector[InetSocketAddress]
)
case class ChannelDesc(shortChannelId: ShortChannelId, a: NodeId, b: NodeId)
@ -189,7 +194,8 @@ case class RealChannelId(status: String, realScid: ShortChannelId)
case class ShortIds(
real: RealChannelId,
localAlias: String,
remoteAlias: String)
remoteAlias: String
)
case class UsableBalancesResult(
remoteNodeId: NodeId,
@ -255,7 +261,8 @@ case class ChannelUpdate(
htlcMinimumMsat: MilliSatoshis,
feeProportionalMillionths: FeeProportionalMillionths,
htlcMaximumMsat: Option[MilliSatoshis],
feeBaseMsat: MilliSatoshis)
feeBaseMsat: MilliSatoshis
)
case class ChannelResult(
nodeId: NodeId,
@ -263,7 +270,8 @@ case class ChannelResult(
state: ChannelState,
feeBaseMsat: Option[MilliSatoshis],
feeProportionalMillionths: Option[FeeProportionalMillionths],
data: JsObject) {
data: JsObject
) {
lazy val shortChannelId: Option[ShortChannelId] =
(data \ "shortIds" \ "real" \ "realScid").validate[ShortChannelId].asOpt
@ -278,7 +286,8 @@ case class InvoiceResult(
serialized: String,
description: String,
paymentHash: Sha256Digest,
expiry: FiniteDuration)
expiry: FiniteDuration
)
case class PaymentId(value: UUID) {
override def toString: String = value.toString
@ -297,7 +306,8 @@ case class PaymentRequest(
expiry: FiniteDuration, // seconds
minFinalCltvExpiry: Int,
amount: Option[MilliSatoshis],
features: Features)
features: Features
)
sealed trait PaymentType
@ -330,14 +340,16 @@ case class OutgoingPayment(
recipientNodeId: NodeId,
createdAt: Instant, // milliseconds
paymentRequest: Option[PaymentRequest],
status: OutgoingPaymentStatus)
status: OutgoingPaymentStatus
)
case class IncomingPayment(
paymentRequest: PaymentRequest,
paymentPreimage: PaymentPreimage,
paymentType: PaymentType,
createdAt: Instant, // milliseconds
status: IncomingPaymentStatus)
status: IncomingPaymentStatus
)
sealed trait IncomingPaymentStatus
@ -373,7 +385,8 @@ object OutgoingPaymentStatus {
case class PaymentFailure(
failureType: PaymentFailure.Type,
failureMessage: String,
failedRoute: Seq[Hop])
failedRoute: Seq[Hop]
)
object PaymentFailure {
sealed trait Type
@ -385,7 +398,8 @@ object PaymentFailure {
case class Hop(
nodeId: NodeId,
nextNodeId: NodeId,
shortChannelId: Option[ShortChannelId])
shortChannelId: Option[ShortChannelId]
)
sealed trait WebSocketEvent
@ -456,4 +470,5 @@ case class WalletTransaction(
blockHash: DoubleSha256DigestBE,
confirmations: Long,
txid: DoubleSha256DigestBE,
timestamp: Long)
timestamp: Long
)

View File

@ -16,8 +16,8 @@ case class AddInvoiceResult(
rHash: PaymentHashTag,
invoice: LnInvoice,
addIndex: UInt64,
paymentAddr: ByteVector)
extends LndModel
paymentAddr: ByteVector
) extends LndModel
case class UTXOResult(
address: BitcoinAddress,

View File

@ -14,7 +14,9 @@ import ujson.Value
import java.net.InetSocketAddress
/** The event type being sent over the websocket. An example is [[WalletWsType.NewAddress]] */
/** The event type being sent over the websocket. An example is
* [[WalletWsType.NewAddress]]
*/
sealed trait WsType
object WsType extends StringFactory[WsType] {
@ -46,7 +48,8 @@ object WalletWsType extends StringFactory[WalletWsType] {
case object FeeRateChange extends WalletWsType
private val all =
Vector(TxProcessed,
Vector(
TxProcessed,
TxBroadcast,
ReservedUtxos,
NewAddress,
@ -54,7 +57,8 @@ object WalletWsType extends StringFactory[WalletWsType] {
DLCOfferAdd,
DLCOfferRemove,
RescanComplete,
FeeRateChange)
FeeRateChange
)
override def fromStringOpt(string: String): Option[WalletWsType] = {
all.find(_.toString.toLowerCase() == string.toLowerCase)
@ -140,9 +144,9 @@ object DLCNodeWsType extends StringFactory[DLCNodeWsType] {
}
}
/** A notification that we send over the websocket.
* The type of the notification is indicated by [[WsType]].
* An example is [[org.bitcoins.commons.jsonmodels.ws.WalletNotification.NewAddressNotification]]
/** A notification that we send over the websocket. The type of the notification
* is indicated by [[WsType]]. An example is
* [[org.bitcoins.commons.jsonmodels.ws.WalletNotification.NewAddressNotification]]
* This sends a notification that the wallet generated a new address
*/
sealed trait WsNotification[T] {
@ -263,13 +267,14 @@ object ChainNotification {
}
case class CompactFilterHeaderProcessedNotification(
payload: CompactFilterHeaderDb)
extends ChainNotification[CompactFilterHeaderDb] {
payload: CompactFilterHeaderDb
) extends ChainNotification[CompactFilterHeaderDb] {
override val `type`: ChainWsType = ChainWsType.CompactFilterHeaderProcessed
override val json: ujson.Value = {
upickle.default.writeJs(this)(
WsPicklers.compactFilterHeaderProcessedPickler)
WsPicklers.compactFilterHeaderProcessedPickler
)
}
}
@ -311,7 +316,8 @@ object DLCNodeNotification {
override def `type`: DLCNodeWsType = DLCNodeWsType.DLCConnectionInitiated
override def json: Value = upickle.default.writeJs(this)(
WsPicklers.dlcNodeConnectionInitiatedPickler)
WsPicklers.dlcNodeConnectionInitiatedPickler
)
}
case class DLCNodeConnectionEstablished(payload: InetSocketAddress)
@ -319,7 +325,8 @@ object DLCNodeNotification {
override def `type`: DLCNodeWsType = DLCNodeWsType.DLCConnectionEstablished
override def json: Value = upickle.default.writeJs(this)(
WsPicklers.dlcNodeConnectionEstablishedPickler)
WsPicklers.dlcNodeConnectionEstablishedPickler
)
}
case class DLCNodeConnectionFailed(payload: InetSocketAddress)

View File

@ -51,21 +51,21 @@ import scala.util.{Failure, Success, Try}
object JsonReaders {
/** Tries to prase the provided JSON into a map with keys of
* type `K` and values of type `V`
/** Tries to prase the provided JSON into a map with keys of type `K` and
* values of type `V`
*/
def mapReads[K, V](js: JsValue)(implicit
readsK: Reads[K],
readsV: Reads[V]): JsResult[Map[K, V]] = {
def mapReads[K, V](
js: JsValue
)(implicit readsK: Reads[K], readsV: Reads[V]): JsResult[Map[K, V]] = {
js.validate[JsObject].flatMap { jsObj =>
val jsResults: scala.collection.Seq[(JsResult[K], JsResult[V])] =
jsObj.fields.map { case (key, value) =>
JsString(key).validate[K] -> value.validate[V]
}
val allErrors: scala.collection.Seq[(
JsPath,
scala.collection.Seq[JsonValidationError])] =
val allErrors: scala.collection.Seq[
(JsPath, scala.collection.Seq[JsonValidationError])
] =
jsResults.collect {
case (JsError(keyErrors), _) => keyErrors
case (_, JsError(valueErrors)) => valueErrors
@ -91,16 +91,20 @@ object JsonReaders {
override def reads(json: JsValue): JsResult[ZonedDateTime] =
SerializerUtil.processJsNumberBigInt[ZonedDateTime](bigInt =>
ZonedDateTime.ofInstant(Instant.ofEpochSecond(bigInt.toLong),
ZoneOffset.UTC))(json)
ZonedDateTime.ofInstant(
Instant.ofEpochSecond(bigInt.toLong),
ZoneOffset.UTC
))(json)
}
implicit object LocalDateTimeReads extends Reads[LocalDateTime] {
override def reads(json: JsValue): JsResult[LocalDateTime] =
SerializerUtil.processJsNumberBigInt[LocalDateTime](bigInt =>
LocalDateTime.ofInstant(Instant.ofEpochSecond(bigInt.toLong),
ZoneId.systemDefault()))(json)
LocalDateTime.ofInstant(
Instant.ofEpochSecond(bigInt.toLong),
ZoneId.systemDefault()
))(json)
}
implicit object BigIntReads extends Reads[BigInt] {
@ -119,21 +123,24 @@ object JsonReaders {
override def reads(json: JsValue): JsResult[RipeMd160Digest] =
SerializerUtil.processJsString[RipeMd160Digest](RipeMd160Digest.fromHex)(
json)
json
)
}
implicit object RipeMd160DigestBEReads extends Reads[RipeMd160DigestBE] {
override def reads(json: JsValue): JsResult[RipeMd160DigestBE] =
SerializerUtil.processJsString[RipeMd160DigestBE](
RipeMd160DigestBE.fromHex)(json)
RipeMd160DigestBE.fromHex
)(json)
}
implicit object DoubleSha256DigestReads extends Reads[DoubleSha256Digest] {
override def reads(json: JsValue): JsResult[DoubleSha256Digest] =
SerializerUtil.processJsString[DoubleSha256Digest](
DoubleSha256Digest.fromHex)(json)
DoubleSha256Digest.fromHex
)(json)
}
implicit object DoubleSha256DigestBEReads
@ -141,7 +148,8 @@ object JsonReaders {
override def reads(json: JsValue): JsResult[DoubleSha256DigestBE] =
SerializerUtil.processJsString[DoubleSha256DigestBE](
DoubleSha256DigestBE.fromHex)(json)
DoubleSha256DigestBE.fromHex
)(json)
}
implicit object BitcoinsReads extends Reads[Bitcoins] {
@ -161,7 +169,8 @@ object JsonReaders {
override def reads(json: JsValue): JsResult[Satoshis] =
SerializerUtil.processJsNumber[Satoshis](num => Satoshis(num.toBigInt))(
json)
json
)
}
implicit object BlockHeaderReads extends Reads[BlockHeader] {
@ -266,8 +275,10 @@ object JsonReaders {
case JsNumber(num) if num != 0 =>
SerializerUtil.buildErrorMsg("Expected witness_version 0", num)
case err =>
SerializerUtil.buildErrorMsg("Expected numerical witness_version",
err)
SerializerUtil.buildErrorMsg(
"Expected numerical witness_version",
err
)
}
}
@ -316,21 +327,24 @@ object JsonReaders {
override def reads(json: JsValue): JsResult[ScriptPubKey] =
SerializerUtil.processJsString[ScriptPubKey](ScriptPubKey.fromAsmHex)(
json)
json
)
}
implicit object ScriptWitnessReads extends Reads[ScriptWitness] {
override def reads(json: JsValue): JsResult[ScriptWitness] =
SerializerUtil.processJsStringOpt[ScriptWitness](
ScriptWitness.fromHexOpt)(json)
ScriptWitness.fromHexOpt
)(json)
}
implicit object DescriptorReads extends Reads[Descriptor] {
override def reads(json: JsValue): JsResult[Descriptor] = {
SerializerUtil.processJsStringOpt[Descriptor](Descriptor.fromStringOpt)(
json)
json
)
}
}
@ -344,7 +358,8 @@ object JsonReaders {
override def reads(json: JsValue): JsResult[Sha256Hash160Digest] =
SerializerUtil.processJsString[Sha256Hash160Digest](
Sha256Hash160Digest.fromHex)(json)
Sha256Hash160Digest.fromHex
)(json)
}
implicit object ECPublicKeyReads extends Reads[ECPublicKey] {
@ -357,14 +372,16 @@ object JsonReaders {
override def reads(json: JsValue): JsResult[ECPublicKeyBytes] =
SerializerUtil.processJsString[ECPublicKeyBytes](
ECPublicKeyBytes.fromHex)(json)
ECPublicKeyBytes.fromHex
)(json)
}
implicit object SchnorrPublicKeyReads extends Reads[SchnorrPublicKey] {
override def reads(json: JsValue): JsResult[SchnorrPublicKey] =
SerializerUtil.processJsString[SchnorrPublicKey](
SchnorrPublicKey.fromHex)(json)
SchnorrPublicKey.fromHex
)(json)
}
implicit object SchnorrNonceReads extends Reads[SchnorrNonce] {
@ -378,7 +395,8 @@ object JsonReaders {
override def reads(json: JsValue): JsResult[SchnorrDigitalSignature] =
SerializerUtil.processJsString[SchnorrDigitalSignature](
SchnorrDigitalSignature.fromHex)(json)
SchnorrDigitalSignature.fromHex
)(json)
}
implicit object FieldElementReads extends Reads[FieldElement] {
@ -423,7 +441,8 @@ object JsonReaders {
override def reads(json: JsValue): JsResult[ScriptSignature] =
SerializerUtil.processJsString[ScriptSignature](
ScriptSignature.fromAsmHex)(json)
ScriptSignature.fromAsmHex
)(json)
}
implicit object TransactionInputReads extends Reads[TransactionInput] {
@ -433,7 +452,8 @@ object JsonReaders {
(json \ "coinbase").validate[String] match {
case s: JsSuccess[String] =>
JsSuccess(
CoinbaseInput(ScriptSignature.fromAsmHex(s.value), sequence))
CoinbaseInput(ScriptSignature.fromAsmHex(s.value), sequence)
)
case _ =>
(json \ "txid").validate[DoubleSha256DigestBE].flatMap { txid =>
(json \ "vout").validate[UInt32].flatMap { vout =>
@ -441,9 +461,12 @@ object JsonReaders {
.validate[ScriptSignature]
.flatMap { scriptSig =>
JsSuccess(
TransactionInput(TransactionOutPoint(txid.flip, vout),
TransactionInput(
TransactionOutPoint(txid.flip, vout),
scriptSig,
sequence))
sequence
)
)
}
}
}
@ -510,7 +533,8 @@ object JsonReaders {
case Some(JsNumber(n)) => JsSuccess(Bitcoins(n))
case Some(
err @ (JsNull | _: JsBoolean | _: JsString | _: JsArray |
_: JsObject)) =>
_: JsObject)
) =>
SerializerUtil.buildJsErrorMsg("jsnumber", err)
case None => JsError("error.expected.balance")
}
@ -605,9 +629,11 @@ object JsonReaders {
pubkey <- (json \ "pubkey").validate[ECPublicKey]
masterFingerprint <- (json \ "master_fingerprint").validate[String]
path <- (json \ "path").validate[String]
} yield PsbtBIP32Deriv(pubkey = pubkey,
} yield PsbtBIP32Deriv(
pubkey = pubkey,
masterFingerprint = masterFingerprint,
path = path)
path = path
)
}
implicit object RpcPsbtScriptReads extends Reads[RpcPsbtScript] {
@ -618,19 +644,24 @@ object JsonReaders {
hex <- (json \ "hex").validate[ScriptPubKey]
scriptType <- (json \ "type").validateOpt[ScriptType]
address <- (json \ "address").validateOpt[BitcoinAddress]
} yield RpcPsbtScript(asm = asm,
} yield RpcPsbtScript(
asm = asm,
hex = hex,
scriptType = scriptType,
address = address)
address = address
)
}
implicit object MapPubKeySignatureReads
extends Reads[Map[ECPublicKey, ECDigitalSignature]] {
override def reads(
json: JsValue): JsResult[Map[ECPublicKey, ECDigitalSignature]] =
JsonReaders.mapReads(json)(implicitly[Reads[ECPublicKey]],
implicitly[Reads[ECDigitalSignature]])
json: JsValue
): JsResult[Map[ECPublicKey, ECDigitalSignature]] =
JsonReaders.mapReads(json)(
implicitly[Reads[ECPublicKey]],
implicitly[Reads[ECDigitalSignature]]
)
}
implicit object RpcPsbtInputV22Reads extends Reads[RpcPsbtInputV22] {
@ -740,7 +771,8 @@ object JsonReaders {
case Failure(err) =>
SerializerUtil.buildJsErrorMsg(
s"Unexpected Service Identifier: $err",
json)
json
)
}
case err @ (JsNull | _: JsBoolean | _: JsNumber | _: JsArray |
_: JsObject) =>
@ -759,7 +791,8 @@ object JsonReaders {
case Failure(err) =>
SerializerUtil.buildJsErrorMsg(
s"Unexpected Service Identifier: $err",
json)
json
)
}
case err @ (JsNull | _: JsBoolean | _: JsNumber | _: JsArray |
_: JsObject) =>
@ -778,7 +811,8 @@ object JsonReaders {
case Failure(err) =>
SerializerUtil.buildJsErrorMsg(
s"Unexpected Service Identifier: $err",
json)
json
)
}
case err @ (JsNull | _: JsBoolean | _: JsNumber | _: JsArray |
_: JsObject) =>
@ -786,10 +820,11 @@ object JsonReaders {
}
}
implicit val feeProportionalMillionthsReads: Reads[
FeeProportionalMillionths] = Reads { js =>
implicit val feeProportionalMillionthsReads
: Reads[FeeProportionalMillionths] = Reads { js =>
SerializerUtil.processJsNumberBigInt(FeeProportionalMillionths.fromBigInt)(
js)
js
)
}
implicit val channelStateReads: Reads[ChannelState] = {
@ -845,7 +880,8 @@ object JsonReaders {
implicit val lnHrpReads: Reads[LnHumanReadablePart] = {
Reads { jsValue =>
SerializerUtil.processJsStringOpt(LnHumanReadablePart.fromStringOpt(_))(
jsValue)
jsValue
)
}
}
@ -861,7 +897,8 @@ object JsonReaders {
addr.split(":") match {
case Array(host, portStr) =>
val port = Try(portStr.toInt).getOrElse(
throw new RuntimeException(s"Invalid port number `$portStr`"))
throw new RuntimeException(s"Invalid port number `$portStr`")
)
InetSocketAddress.createUnresolved(host, port.toInt)
case _ => throw new RuntimeException(s"Invalid inet address `$addr`")
}
@ -922,7 +959,8 @@ object JsonReaders {
implicit val shortChannelIdReads: Reads[ShortChannelId] = {
Reads { jsValue =>
SerializerUtil.processJsString(ShortChannelId.fromHumanReadableString)(
jsValue)
jsValue
)
}
}
@ -943,13 +981,15 @@ object JsonReaders {
rgbColor <- (jsValue \ "rgbColor").validate[String]
alias <- (jsValue \ "alias").validate[String]
addresses <- (jsValue \ "addresses").validate[Vector[InetSocketAddress]]
} yield NodeInfo(signature,
} yield NodeInfo(
signature,
features,
timestamp,
nodeId,
rgbColor,
alias,
addresses)
addresses
)
}
}
@ -986,13 +1026,15 @@ object JsonReaders {
description <- (jsValue \ "description").validate[String]
paymentHash <- (jsValue \ "paymentHash").validate[Sha256Digest]
expiry <- (jsValue \ "expiry").validate[Long]
} yield InvoiceResult(prefix,
} yield InvoiceResult(
prefix,
timestamp,
nodeId,
serialized,
description,
paymentHash,
expiry.seconds)
expiry.seconds
)
}
}
@ -1010,12 +1052,14 @@ object JsonReaders {
(jsValue \ "data" \ "commitments" \ "localCommit" \ "spec" \ "toLocal")
.validate[MilliSatoshis]
} yield OpenChannelInfo(nodeId = nodeId,
} yield OpenChannelInfo(
nodeId = nodeId,
shortChannelId = shortChannelId,
channelId = channelId,
localMsat = localMsat,
remoteMsat = remoteMsat,
state = state)
state = state
)
}
implicit val baseChannelInfoReads: Reads[BaseChannelInfo] = Reads { jsValue =>
@ -1030,11 +1074,13 @@ object JsonReaders {
(jsValue \ "data" \ "commitments" \ "localCommit" \ "spec" \ "toLocal")
.validate[MilliSatoshis]
} yield BaseChannelInfo(nodeId = nodeId,
} yield BaseChannelInfo(
nodeId = nodeId,
channelId = channelId,
localMsat = localMsat,
remoteMsat = remoteMsat,
state = state)
state = state
)
}
implicit val channelInfoReads: Reads[ChannelInfo] = Reads { jsValue =>
@ -1078,12 +1124,14 @@ object JsonReaders {
val result = Try(
UpdateRelayFee.OK(
channelId = FundedChannelId.fromHex(
(x._2 \ "channelId").validate[String].get),
(x._2 \ "channelId").validate[String].get
),
feeBaseMsat =
(x._2 \ "cmd" \ "feeBase").validate[MilliSatoshis].get,
feeProportionalMillionths =
(x._2 \ "cmd" \ "feeProportionalMillionths").validate[Long].get
)) match {
)
) match {
case Success(ok) => ok
case Failure(_) => UpdateRelayFee.Error(x._2.toString())
}
@ -1182,10 +1230,12 @@ object JsonReaders {
route <- (js \ "route").validate[Seq[Hop]]
completed <- (js \ "completedAt" \ "unix")
.validate[Instant](instantReadsMilliseconds)
} yield OutgoingPaymentStatus.Succeeded(paymentPreimage = preimage,
} yield OutgoingPaymentStatus.Succeeded(
paymentPreimage = preimage,
feesPaid = feesPaid,
route = route,
completedAt = completed)
completedAt = completed
)
}
implicit val paymentFailureTypeReads: Reads[PaymentFailure.Type] = Reads {
@ -1247,7 +1297,8 @@ object JsonReaders {
amount <- (js \ "amount").validateOpt[MilliSatoshis]
minFinalCltvExpiry <- (js \ "minFinalCltvExpiry").validate[Int]
features <- (js \ "features").validate[Features]
} yield PaymentRequest(prefix,
} yield PaymentRequest(
prefix,
timestamp,
nodeId,
serialized,
@ -1257,7 +1308,8 @@ object JsonReaders {
expiry,
minFinalCltvExpiry,
amount,
features)
features
)
}
implicit val paymentSucceededReads: Reads[OutgoingPayment] = Reads { js =>
@ -1274,7 +1326,8 @@ object JsonReaders {
.validate[Instant](instantReadsMilliseconds)
paymentRequest <- (js \ "paymentRequest").validateOpt[PaymentRequest]
status <- (js \ "status").validate[OutgoingPaymentStatus]
} yield OutgoingPayment(id,
} yield OutgoingPayment(
id,
parentId,
externalId,
paymentHash,
@ -1284,7 +1337,8 @@ object JsonReaders {
recipientNodeId,
createdAt,
paymentRequest,
status)
status
)
}
implicit val receivedPaymentResultReads: Reads[IncomingPayment] = Reads {
@ -1296,11 +1350,13 @@ object JsonReaders {
createdAt <- (js \ "createdAt" \ "unix")
.validate[Instant](instantReadsMilliseconds)
status <- (js \ "status").validate[IncomingPaymentStatus]
} yield IncomingPayment(paymentRequest,
} yield IncomingPayment(
paymentRequest,
paymentPreimage,
paymentType,
createdAt,
status)
status
)
}
implicit val channelResultReads: Reads[ChannelResult] = Reads { js =>
@ -1314,12 +1370,14 @@ object JsonReaders {
(js \ "data" \ "channelUpdate" \ "feeProportionalMillionths")
.validateOpt[FeeProportionalMillionths]
data <- (js \ "data").validate[JsObject]
} yield ChannelResult(nodeId = nodeId,
} yield ChannelResult(
nodeId = nodeId,
state = state,
channelId = channelId,
feeBaseMsat = feeBaseMsat,
feeProportionalMillionths = feeProportional,
data = data)
data = data
)
}
implicit val lnInvoiceReads: Reads[LnInvoice] =
@ -1370,12 +1428,14 @@ object JsonReaders {
toChannelId <- (js \ "toChannelId").validate[FundedChannelId]
timestamp <- (js \ "timestamp" \ "unix")
.validate[Instant](instantReadsMilliseconds)
} yield RelayedPayment(amountIn,
} yield RelayedPayment(
amountIn,
amountOut,
paymentHash,
fromChannelId,
toChannelId,
timestamp)
timestamp
)
}
implicit val auditResultReads: Reads[AuditResult] = Json.reads[AuditResult]
@ -1388,12 +1448,14 @@ object JsonReaders {
txType <- (js \ "txType").validate[String]
timestamp <- (js \ "timestamp" \ "unix")
.validate[Instant](instantReadsMilliseconds)
} yield NetworkFeesResult(remoteNodeId,
} yield NetworkFeesResult(
remoteNodeId,
channelId,
txId,
fee,
txType,
timestamp)
timestamp
)
}
implicit val channelStatsDirectionReads: Reads[ChannelStats.Direction] =
@ -1417,28 +1479,32 @@ object JsonReaders {
toChannelId <- (js \ "toChannelId").validate[FundedChannelId]
timestamp <- (js \ "timestamp" \ "unix")
.validate[Instant](instantReadsMilliseconds)
} yield WebSocketEvent.PaymentRelayed(amountIn,
} yield WebSocketEvent.PaymentRelayed(
amountIn,
amountOut,
paymentHash,
fromChannelId,
toChannelId,
timestamp)
timestamp
)
}
implicit val paymentReceivedEventPartReads: Reads[
WebSocketEvent.PaymentReceived.Part] = Reads { js =>
implicit val paymentReceivedEventPartReads
: Reads[WebSocketEvent.PaymentReceived.Part] = Reads { js =>
for {
amount <- (js \ "amount").validate[MilliSatoshis]
fromChannelId <- (js \ "fromChannelId").validate[FundedChannelId]
timestamp <- (js \ "timestamp" \ "unix")
.validate[Instant](instantReadsMilliseconds)
} yield WebSocketEvent.PaymentReceived.Part(amount,
} yield WebSocketEvent.PaymentReceived.Part(
amount,
fromChannelId,
timestamp)
timestamp
)
}
implicit val paymentReceivedEventReads: Reads[
WebSocketEvent.PaymentReceived] = Reads { js =>
implicit val paymentReceivedEventReads
: Reads[WebSocketEvent.PaymentReceived] = Reads { js =>
for {
paymentHash <- (js \ "paymentHash").validate[Sha256Digest]
parts <- (js \ "parts")
@ -1454,14 +1520,16 @@ object JsonReaders {
failures <- (js \ "failures").validate[Vector[JsObject]]
timestamp <- (js \ "timestamp" \ "unix")
.validate[Instant](instantReadsMilliseconds)
} yield WebSocketEvent.PaymentFailed(id,
} yield WebSocketEvent.PaymentFailed(
id,
paymentHash,
failures.map(_.toString()),
timestamp)
timestamp
)
}
implicit val paymentSentEventPartReads: Reads[
WebSocketEvent.PaymentSent.Part] = Reads { js =>
implicit val paymentSentEventPartReads
: Reads[WebSocketEvent.PaymentSent.Part] = Reads { js =>
for {
id <- (js \ "id").validate[PaymentId]
amount <- (js \ "amount").validate[MilliSatoshis]
@ -1469,11 +1537,13 @@ object JsonReaders {
toChannelId <- (js \ "toChannelId").validate[FundedChannelId]
timestamp <- (js \ "timestamp" \ "unix")
.validate[Instant](instantReadsMilliseconds)
} yield WebSocketEvent.PaymentSent.Part(id,
} yield WebSocketEvent.PaymentSent.Part(
id,
amount,
feesPaid,
toChannelId,
timestamp)
timestamp
)
}
implicit val paymentSentEventReads: Reads[WebSocketEvent.PaymentSent] =
@ -1484,22 +1554,26 @@ object JsonReaders {
paymentPreimage <- (js \ "paymentPreimage").validate[PaymentPreimage]
parts <- (js \ "parts")
.validate[Vector[WebSocketEvent.PaymentSent.Part]]
} yield WebSocketEvent.PaymentSent(id,
} yield WebSocketEvent.PaymentSent(
id,
paymentHash,
paymentPreimage,
parts)
parts
)
}
implicit val paymentSettlingOnchainEventReads: Reads[
WebSocketEvent.PaymentSettlingOnchain] = Reads { js =>
implicit val paymentSettlingOnchainEventReads
: Reads[WebSocketEvent.PaymentSettlingOnchain] = Reads { js =>
for {
amount <- (js \ "amount").validate[MilliSatoshis]
paymentHash <- (js \ "paymentHash").validate[Sha256Digest]
timestamp <- (js \ "timestamp" \ "unix")
.validate[Instant](instantReadsMilliseconds)
} yield WebSocketEvent.PaymentSettlingOnchain(amount,
} yield WebSocketEvent.PaymentSettlingOnchain(
amount,
paymentHash,
timestamp)
timestamp
)
}
implicit val webSocketEventReads: Reads[WebSocketEvent] =
@ -1549,7 +1623,8 @@ object JsonReaders {
implicit val connectionDirectionReads: Reads[ConnectionDirection] = {
Reads { jsValue =>
SerializerUtil.processJsStringOpt(ConnectionDirection.fromStringOpt)(
jsValue)
jsValue
)
}
}
@ -1568,7 +1643,8 @@ object JsonReaders {
implicit val closedChannelTypeReads: Reads[ClosedChannelType] = {
Reads { jsValue =>
SerializerUtil.processJsStringOpt(ClosedChannelType.fromStringOpt)(
jsValue)
jsValue
)
}
}

View File

@ -136,8 +136,8 @@ object JsonSerializers {
.readNullable[Vector[BitcoinAddress]] and
(__ \ "address").readNullable[BitcoinAddress])(RpcScriptPubKeyPostV22)
implicit val rpcTransactionOutputPreV22Reads: Reads[
RpcTransactionOutputPreV22] =
implicit val rpcTransactionOutputPreV22Reads
: Reads[RpcTransactionOutputPreV22] =
Json.reads[RpcTransactionOutputPreV22]
implicit val rpcTransactionOutputV22Reads: Reads[RpcTransactionOutputV22] =
@ -154,18 +154,19 @@ object JsonSerializers {
implicit val fundRawTransactionResultReads: Reads[FundRawTransactionResult] =
Json.reads[FundRawTransactionResult]
implicit val signRawTransactionWithWalletResultReads: Reads[
SignRawTransactionWithWalletResult] =
implicit val signRawTransactionWithWalletResultReads
: Reads[SignRawTransactionWithWalletResult] =
Json.reads[SignRawTransactionWithWalletResult]
implicit val getRawTransactionScriptSigReads: Reads[
GetRawTransactionScriptSig] = Json.reads[GetRawTransactionScriptSig]
implicit val getRawTransactionScriptSigReads
: Reads[GetRawTransactionScriptSig] =
Json.reads[GetRawTransactionScriptSig]
implicit val getRawTransactionVinReads: Reads[GetRawTransactionVin] =
Json.reads[GetRawTransactionVin]
implicit val getRawTransactionResultV22Reads: Reads[
GetRawTransactionResultV22] =
implicit val getRawTransactionResultV22Reads
: Reads[GetRawTransactionResultV22] =
Json.reads[GetRawTransactionResultV22]
implicit val signRawTransactionErrorReads: Reads[SignRawTransactionError] =
@ -258,8 +259,8 @@ object JsonSerializers {
implicit val getBlockResultReads: Reads[GetBlockResult] =
Json.reads[GetBlockResult]
implicit val getBlockWithTransactionsResultV22Reads: Reads[
GetBlockWithTransactionsResultV22] =
implicit val getBlockWithTransactionsResultV22Reads
: Reads[GetBlockWithTransactionsResultV22] =
Json.reads[GetBlockWithTransactionsResultV22]
implicit val softforkProgressPreV19Reads: Reads[SoftforkProgressPreV19] =
@ -271,8 +272,8 @@ object JsonSerializers {
implicit val bip9SoftforkPreV19Reads: Reads[Bip9SoftforkPreV19] =
Json.reads[Bip9SoftforkPreV19]
implicit val getBlockChainInfoResultPreV19Reads: Reads[
GetBlockChainInfoResultPreV19] =
implicit val getBlockChainInfoResultPreV19Reads
: Reads[GetBlockChainInfoResultPreV19] =
Json.reads[GetBlockChainInfoResultPreV19]
implicit val bip9SoftforkDetailsReads: Reads[Bip9SoftforkDetails] =
@ -286,11 +287,13 @@ object JsonSerializers {
}
}
implicit val getBlockChainInfoResultPostV19Reads: Reads[
GetBlockChainInfoResultPostV19] = Json.reads[GetBlockChainInfoResultPostV19]
implicit val getBlockChainInfoResultPostV19Reads
: Reads[GetBlockChainInfoResultPostV19] =
Json.reads[GetBlockChainInfoResultPostV19]
implicit val getBlockChainInfoResultPostV23Reads: Reads[
GetBlockChainInfoResultPostV23] = Json.reads[GetBlockChainInfoResultPostV23]
implicit val getBlockChainInfoResultPostV23Reads
: Reads[GetBlockChainInfoResultPostV23] =
Json.reads[GetBlockChainInfoResultPostV23]
implicit val blockHeaderFormattedReads: Reads[GetBlockHeaderResult] =
Json.reads[GetBlockHeaderResult]
@ -311,16 +314,16 @@ object JsonSerializers {
implicit val getMemPoolResultPostV23Reads: Reads[GetMemPoolResultPostV23] =
Json.reads[GetMemPoolResultPostV23]
implicit val getMemPoolEntryResultPreV19Reads: Reads[
GetMemPoolEntryResultPreV19] =
implicit val getMemPoolEntryResultPreV19Reads
: Reads[GetMemPoolEntryResultPreV19] =
Json.reads[GetMemPoolEntryResultPreV19]
implicit val getMemPoolEntryResultPostV19Reads: Reads[
GetMemPoolEntryResultPostV19] =
implicit val getMemPoolEntryResultPostV19Reads
: Reads[GetMemPoolEntryResultPostV19] =
Json.reads[GetMemPoolEntryResultPostV19]
implicit val getMemPoolEntryResultPostV23Reads: Reads[
GetMemPoolEntryResultPostV23] =
implicit val getMemPoolEntryResultPostV23Reads
: Reads[GetMemPoolEntryResultPostV23] =
Json.reads[GetMemPoolEntryResultPostV23]
implicit val getMemPoolInfoResultReads: Reads[GetMemPoolInfoResult] =
@ -332,12 +335,12 @@ object JsonSerializers {
implicit val getTxOutSetInfoResultReads: Reads[GetTxOutSetInfoResult] =
Json.reads[GetTxOutSetInfoResult]
implicit val GetTxSpendingPrevOutResultReads: Reads[
GetTxSpendingPrevOutResult] =
implicit val GetTxSpendingPrevOutResultReads
: Reads[GetTxSpendingPrevOutResult] =
Json.reads[GetTxSpendingPrevOutResult]
implicit val SimulateRawTransactionResultReads: Reads[
SimulateRawTransactionResult] =
implicit val SimulateRawTransactionResultReads
: Reads[SimulateRawTransactionResult] =
Json.reads[SimulateRawTransactionResult]
implicit object Bip32PathFormats extends Format[BIP32Path] {
@ -383,8 +386,8 @@ object JsonSerializers {
(__ \ "details").read[Vector[TransactionDetails]] and
(__ \ "hex").read[Transaction])(GetTransactionResult)
implicit val getWalletInfoResultReadsPostV22: Reads[
GetWalletInfoResultPostV22] =
implicit val getWalletInfoResultReadsPostV22
: Reads[GetWalletInfoResultPostV22] =
Json.reads[GetWalletInfoResultPostV22]
implicit val importMultiErrorReads: Reads[ImportMultiError] =
@ -530,8 +533,8 @@ object JsonSerializers {
implicit val addressInfoResultPreV18Reads: Reads[AddressInfoResultPreV18] =
Json.reads[AddressInfoResultPreV18]
implicit val addressInfoResultPostV18Reads: Reads[
AddressInfoResultPostV18] = {
implicit val addressInfoResultPostV18Reads
: Reads[AddressInfoResultPostV18] = {
Reads[AddressInfoResultPostV18] { json =>
for {
isProps <-
@ -539,7 +542,8 @@ object JsonSerializers {
infoWithoutProps <-
Json
.reads[
AddressInfoResultPostV18.AddressInfoResultPostV18WithoutIsProps]
AddressInfoResultPostV18.AddressInfoResultPostV18WithoutIsProps
]
.reads(json)
} yield {
AddressInfoResultPostV18(infoWithoutProps, isProps)
@ -547,8 +551,8 @@ object JsonSerializers {
}
}
implicit val addressInfoResultPostV21Reads: Reads[
AddressInfoResultPostV21] = {
implicit val addressInfoResultPostV21Reads
: Reads[AddressInfoResultPostV21] = {
Reads[AddressInfoResultPostV21] { json =>
for {
isProps <-
@ -556,7 +560,8 @@ object JsonSerializers {
infoWithoutProps <-
Json
.reads[
AddressInfoResultPostV21.AddressInfoResultPostV21WithoutIsProps]
AddressInfoResultPostV21.AddressInfoResultPostV21WithoutIsProps
]
.reads(json)
} yield {
AddressInfoResultPostV21(infoWithoutProps, isProps)
@ -591,8 +596,8 @@ object JsonSerializers {
implicit val psbtWitnessUtxoInputReads: Reads[PsbtWitnessUtxoInput] =
Json.reads[PsbtWitnessUtxoInput]
implicit val mapPubKeySignatureReads: Reads[
Map[ECPublicKey, ECDigitalSignature]] = MapPubKeySignatureReads
implicit val mapPubKeySignatureReads
: Reads[Map[ECPublicKey, ECDigitalSignature]] = MapPubKeySignatureReads
implicit val rpcPsbtInputV22Reads: Reads[RpcPsbtInputV22] =
RpcPsbtInputV22Reads
@ -609,8 +614,8 @@ object JsonSerializers {
implicit val analyzePsbtResultReads: Reads[AnalyzePsbtResult] =
Json.reads[AnalyzePsbtResult]
implicit val getNodeAddressesPostV22Reads: Reads[
GetNodeAddressesResultPostV22] =
implicit val getNodeAddressesPostV22Reads
: Reads[GetNodeAddressesResultPostV22] =
Reads[GetNodeAddressesResultPostV22] { js =>
for {
time <- (js \ "time").validate[Long].map(_.seconds)
@ -618,11 +623,13 @@ object JsonSerializers {
address <- (js \ "address").validate[URI]
port <- (js \ "port").validate[Int]
network <- (js \ "network").validate[String]
} yield GetNodeAddressesResultPostV22(time,
} yield GetNodeAddressesResultPostV22(
time,
services,
address,
port,
network)
network
)
}
implicit val rgetpcCommandsReads: Reads[RpcCommands] = Reads[RpcCommands] {
@ -648,8 +655,8 @@ object JsonSerializers {
implicit val getDescriptorInfoResultReads: Reads[GetDescriptorInfoResult] =
Json.reads[GetDescriptorInfoResult]
implicit val walletCreateFundedPsbtResultReads: Reads[
WalletCreateFundedPsbtResult] =
implicit val walletCreateFundedPsbtResultReads
: Reads[WalletCreateFundedPsbtResult] =
Json.reads[WalletCreateFundedPsbtResult]
implicit val scriptTypeReads: Reads[ScriptType] = ScriptTypeReads
@ -659,8 +666,9 @@ object JsonSerializers {
implicit val FeeInfoTwoReads: Reads[FeeInfoTwo] = Json.reads[FeeInfoTwo]
implicit val testMempoolAcceptResultReadsPostV22: Reads[
TestMempoolAcceptResultPostV22] = Json.reads[TestMempoolAcceptResultPostV22]
implicit val testMempoolAcceptResultReadsPostV22
: Reads[TestMempoolAcceptResultPostV22] =
Json.reads[TestMempoolAcceptResultPostV22]
implicit val indexInfoResultReads: Reads[IndexInfoResult] =
Json.reads[IndexInfoResult]
@ -719,12 +727,12 @@ object JsonSerializers {
implicit val cLightningInvoiceResultReads: Reads[CLightningInvoiceResult] =
Json.reads[CLightningInvoiceResult]
implicit val cLightningLookupInvoiceResultReads: Reads[
CLightningLookupInvoiceResult] =
implicit val cLightningLookupInvoiceResultReads
: Reads[CLightningLookupInvoiceResult] =
Json.reads[CLightningLookupInvoiceResult]
implicit val cLightningListInvoicesResultReads: Reads[
CLightningListInvoicesResult] =
implicit val cLightningListInvoicesResultReads
: Reads[CLightningListInvoicesResult] =
Json.reads[CLightningListInvoicesResult]
implicit val cLightningPayResultReads: Reads[CLightningPayResult] =
@ -745,23 +753,23 @@ object JsonSerializers {
implicit val cLightningWithdrawResultReads: Reads[WithdrawResult] =
Json.reads[WithdrawResult]
implicit val cLightningFundChannelStartResultReads: Reads[
FundChannelStartResult] =
implicit val cLightningFundChannelStartResultReads
: Reads[FundChannelStartResult] =
Json.reads[FundChannelStartResult]
implicit val cLightningFundChannelCompleteResultReads: Reads[
FundChannelCompleteResult] =
implicit val cLightningFundChannelCompleteResultReads
: Reads[FundChannelCompleteResult] =
Json.reads[FundChannelCompleteResult]
implicit val cLightningFundChannelCancelResultReads: Reads[
FundChannelCancelResult] =
implicit val cLightningFundChannelCancelResultReads
: Reads[FundChannelCancelResult] =
Json.reads[FundChannelCancelResult]
implicit val CLightningTransactionReads: Reads[CLightningTransaction] =
Json.reads[CLightningTransaction]
implicit val CLightningListTransactionsResultsReads: Reads[
ListTransactionsResults] =
implicit val CLightningListTransactionsResultsReads
: Reads[ListTransactionsResults] =
Json.reads[ListTransactionsResults]
implicit val SendCustomMessageResultReads: Reads[SendCustomMessageResult] =
@ -788,14 +796,17 @@ object JsonSerializers {
implicit val int32Writes: Writes[Int32] =
Writes[Int32](num => JsNumber(num.toLong))
implicit val serializedTransactionWitnessWrites: Writes[
SerializedTransactionWitness] = Json.writes[SerializedTransactionWitness]
implicit val serializedTransactionWitnessWrites
: Writes[SerializedTransactionWitness] =
Json.writes[SerializedTransactionWitness]
implicit val serializedTransactionInputWrites: Writes[
SerializedTransactionInput] = Json.writes[SerializedTransactionInput]
implicit val serializedTransactionInputWrites
: Writes[SerializedTransactionInput] =
Json.writes[SerializedTransactionInput]
implicit val serializedTransactionOutputWrites: Writes[
SerializedTransactionOutput] = Json.writes[SerializedTransactionOutput]
implicit val serializedTransactionOutputWrites
: Writes[SerializedTransactionOutput] =
Json.writes[SerializedTransactionOutput]
implicit val serializedTransactionWrites: Writes[SerializedTransaction] =
Json.writes[SerializedTransaction]
@ -822,46 +833,46 @@ object JsonSerializers {
Json.writes[SerializedPSBT]
// Map stuff
implicit def mapDoubleSha256DigestReadsPreV19: Reads[
Map[DoubleSha256Digest, GetMemPoolResultPreV19]] =
implicit def mapDoubleSha256DigestReadsPreV19
: Reads[Map[DoubleSha256Digest, GetMemPoolResultPreV19]] =
Reads.mapReads[DoubleSha256Digest, GetMemPoolResultPreV19](s =>
JsSuccess(DoubleSha256Digest.fromHex(s)))
implicit def mapDoubleSha256DigestReadsPostV19: Reads[
Map[DoubleSha256Digest, GetMemPoolResultPostV19]] =
implicit def mapDoubleSha256DigestReadsPostV19
: Reads[Map[DoubleSha256Digest, GetMemPoolResultPostV19]] =
Reads.mapReads[DoubleSha256Digest, GetMemPoolResultPostV19](s =>
JsSuccess(DoubleSha256Digest.fromHex(s)))
implicit def mapDoubleSha256DigestReadsPostV23: Reads[
Map[DoubleSha256Digest, GetMemPoolResultPostV23]] =
implicit def mapDoubleSha256DigestReadsPostV23
: Reads[Map[DoubleSha256Digest, GetMemPoolResultPostV23]] =
Reads.mapReads[DoubleSha256Digest, GetMemPoolResultPostV23](s =>
JsSuccess(DoubleSha256Digest.fromHex(s)))
implicit def mapDoubleSha256DigestBEReadsPreV19: Reads[
Map[DoubleSha256DigestBE, GetMemPoolResultPreV19]] =
implicit def mapDoubleSha256DigestBEReadsPreV19
: Reads[Map[DoubleSha256DigestBE, GetMemPoolResultPreV19]] =
Reads.mapReads[DoubleSha256DigestBE, GetMemPoolResultPreV19](s =>
JsSuccess(DoubleSha256DigestBE.fromHex(s)))
implicit def mapDoubleSha256DigestBEReadsPostV19: Reads[
Map[DoubleSha256DigestBE, GetMemPoolResultPostV19]] =
implicit def mapDoubleSha256DigestBEReadsPostV19
: Reads[Map[DoubleSha256DigestBE, GetMemPoolResultPostV19]] =
Reads.mapReads[DoubleSha256DigestBE, GetMemPoolResultPostV19](s =>
JsSuccess(DoubleSha256DigestBE.fromHex(s)))
implicit def mapDoubleSha256DigestBEReadsPostV23: Reads[
Map[DoubleSha256DigestBE, GetMemPoolResultPostV23]] =
implicit def mapDoubleSha256DigestBEReadsPostV23
: Reads[Map[DoubleSha256DigestBE, GetMemPoolResultPostV23]] =
Reads.mapReads[DoubleSha256DigestBE, GetMemPoolResultPostV23](s =>
JsSuccess(DoubleSha256DigestBE.fromHex(s)))
implicit def mapAddressesByLabelReads: Reads[
Map[BitcoinAddress, LabelResult]] =
implicit def mapAddressesByLabelReads
: Reads[Map[BitcoinAddress, LabelResult]] =
Reads.mapReads[BitcoinAddress, LabelResult](s =>
JsSuccess(BitcoinAddress.fromString(s)))
implicit def mapSatsPerKByteByIntReads: Reads[Map[Int, SatoshisPerKiloByte]] =
Reads.mapReads[Int, SatoshisPerKiloByte](s => JsSuccess(s.toInt))
implicit def mapBitcoinerLiveEstimateReads: Reads[
Map[Int, BitcoinerLiveEstimate]] =
implicit def mapBitcoinerLiveEstimateReads
: Reads[Map[Int, BitcoinerLiveEstimate]] =
Reads.mapReads[Int, BitcoinerLiveEstimate](s => JsSuccess(s.toInt))
implicit val outputMapWrites: Writes[Map[BitcoinAddress, Bitcoins]] =

View File

@ -40,7 +40,8 @@ object JsonWriters {
case _: SIGHASH_SINGLE_ANYONECANPAY => JsString("SINGLE|ANYONECANPAY")
case _: SIGHASH_ANYONECANPAY =>
throw new IllegalArgumentException(
"SIGHHASH_ANYONECANPAY is not supported by the bitcoind RPC interface")
"SIGHHASH_ANYONECANPAY is not supported by the bitcoind RPC interface"
)
}
}
@ -122,17 +123,22 @@ object JsonWriters {
override def writes(o: TransactionInput): JsValue =
JsObject(
Seq(("txid", JsString(o.previousOutput.txIdBE.hex)),
Seq(
("txid", JsString(o.previousOutput.txIdBE.hex)),
("vout", JsNumber(o.previousOutput.vout.toLong)),
("sequence", JsNumber(o.sequence.toLong))))
("sequence", JsNumber(o.sequence.toLong))
)
)
}
implicit object TransactionOutPointWrites
extends OWrites[TransactionOutPoint] {
override def writes(o: TransactionOutPoint): JsObject = {
Json.obj(PicklerKeys.txIdKey -> o.txIdBE.hex,
PicklerKeys.voutKey -> o.vout.toLong)
Json.obj(
PicklerKeys.txIdKey -> o.txIdBE.hex,
PicklerKeys.voutKey -> o.vout.toLong
)
}
}
@ -152,8 +158,9 @@ object JsonWriters {
override def writes(o: PSBT): JsValue = JsString(o.base64)
}
implicit def mapWrites[K, V](keyString: K => String)(implicit
vWrites: Writes[V]): Writes[Map[K, V]] =
implicit def mapWrites[K, V](
keyString: K => String
)(implicit vWrites: Writes[V]): Writes[Map[K, V]] =
new Writes[Map[K, V]] {
override def writes(o: Map[K, V]): JsValue =
@ -180,7 +187,8 @@ object JsonWriters {
implicit object LnInvoiceWrites extends Writes[LnInvoice] {
override def writes(invoice: LnInvoice): JsValue = JsString(
invoice.toString)
invoice.toString
)
}
implicit object WalletCreateFundedPsbtOptionsWrites
@ -195,7 +203,8 @@ object JsonWriters {
)
def addToMapIfDefined[T](key: String, opt: Option[T])(implicit
writes: Writes[T]): Unit =
writes: Writes[T]
): Unit =
opt.foreach(o => jsOpts += (key -> Json.toJson(o)))
addToMapIfDefined("changeAddress", opts.changeAddress)
@ -214,7 +223,8 @@ object JsonWriters {
override def writes(o: GlobalPSBTRecord.Unknown): JsValue =
JsObject(
Seq(("key", JsString(o.key.toHex)), ("value", JsString(o.value.toHex))))
Seq(("key", JsString(o.key.toHex)), ("value", JsString(o.value.toHex)))
)
}
implicit object InputPSBTRecordUnknownWrites
@ -222,7 +232,8 @@ object JsonWriters {
override def writes(o: InputPSBTRecord.Unknown): JsValue =
JsObject(
Seq(("key", JsString(o.key.toHex)), ("value", JsString(o.value.toHex))))
Seq(("key", JsString(o.key.toHex)), ("value", JsString(o.value.toHex)))
)
}
implicit object OutputPSBTRecordUnknownWrites
@ -230,7 +241,8 @@ object JsonWriters {
override def writes(o: OutputPSBTRecord.Unknown): JsValue =
JsObject(
Seq(("key", JsString(o.key.toHex)), ("value", JsString(o.value.toHex))))
Seq(("key", JsString(o.key.toHex)), ("value", JsString(o.value.toHex)))
)
}
implicit object PartialSignatureWrites
@ -238,8 +250,11 @@ object JsonWriters {
override def writes(o: InputPSBTRecord.PartialSignature): JsValue =
JsObject(
Seq(("pubkey", JsString(o.pubKey.hex)),
("signature", JsString(o.signature.hex))))
Seq(
("pubkey", JsString(o.pubKey.hex)),
("signature", JsString(o.signature.hex))
)
)
}
implicit object InputBIP32PathWrites
@ -247,9 +262,12 @@ object JsonWriters {
override def writes(o: InputPSBTRecord.BIP32DerivationPath): JsValue =
JsObject(
Seq(("pubkey", JsString(o.pubKey.hex)),
Seq(
("pubkey", JsString(o.pubKey.hex)),
("master_fingerprint", JsString(o.masterFingerprint.toHex)),
("path", JsString(o.path.toString))))
("path", JsString(o.path.toString))
)
)
}
implicit object OutputBIP32PathWrites
@ -257,8 +275,11 @@ object JsonWriters {
override def writes(o: OutputPSBTRecord.BIP32DerivationPath): JsValue =
JsObject(
Seq(("pubkey", JsString(o.pubKey.hex)),
Seq(
("pubkey", JsString(o.pubKey.hex)),
("master_fingerprint", JsString(o.masterFingerprint.toHex)),
("path", JsString(o.path.toString))))
("path", JsString(o.path.toString))
)
)
}
}

View File

@ -62,7 +62,8 @@ object Picklers {
str => {
val uri = new URI("tcp://" + str)
InetSocketAddress.createUnresolved(uri.getHost, uri.getPort)
})
}
)
implicit val byteVectorPickler: ReadWriter[ByteVector] =
readwriter[String].bimap(_.toHex, str => ByteVector.fromValidHex(str))
@ -80,14 +81,16 @@ object Picklers {
implicit val schnorrNoncePickler: ReadWriter[SchnorrNonce] =
readwriter[String].bimap(_.hex, SchnorrNonce.fromHex)
implicit val enumEventDescriptorPickler: ReadWriter[
EnumEventDescriptorV0TLV] =
implicit val enumEventDescriptorPickler
: ReadWriter[EnumEventDescriptorV0TLV] =
readwriter[String].bimap(_.hex, EnumEventDescriptorV0TLV.fromHex)
implicit val digitDecompEventDescriptorPickler: ReadWriter[
DigitDecompositionEventDescriptorV0TLV] =
readwriter[String].bimap(_.hex,
DigitDecompositionEventDescriptorV0TLV.fromHex)
implicit val digitDecompEventDescriptorPickler
: ReadWriter[DigitDecompositionEventDescriptorV0TLV] =
readwriter[String].bimap(
_.hex,
DigitDecompositionEventDescriptorV0TLV.fromHex
)
implicit val eventDescriptorPickler: ReadWriter[EventDescriptorTLV] =
readwriter[String].bimap(_.hex, EventDescriptorTLV.fromHex)
@ -116,8 +119,8 @@ object Picklers {
implicit val uInt32Pickler: ReadWriter[UInt32] =
readwriter[Long].bimap(_.toLong, long => UInt32(long))
implicit val satoshisPerVirtualBytePickler: ReadWriter[
SatoshisPerVirtualByte] =
implicit val satoshisPerVirtualBytePickler
: ReadWriter[SatoshisPerVirtualByte] =
readwriter[Long]
.bimap(_.toLong, long => SatoshisPerVirtualByte(Satoshis(long)))
@ -133,8 +136,8 @@ object Picklers {
implicit val contractInfoTLVPickler: ReadWriter[ContractInfoV0TLV] =
readwriter[String].bimap(_.hex, ContractInfoV0TLV.fromHex)
implicit val schnorrDigitalSignaturePickler: ReadWriter[
SchnorrDigitalSignature] =
implicit val schnorrDigitalSignaturePickler
: ReadWriter[SchnorrDigitalSignature] =
readwriter[String].bimap(_.hex, SchnorrDigitalSignature.fromHex)
implicit val partialSignaturePickler: ReadWriter[PartialSignature] =
@ -223,14 +226,16 @@ object Picklers {
upickle.default.read[TransactionOutput](obj(PicklerKeys.outputKey))
val hdPath = upickle.default.read[HDPath](obj(PicklerKeys.hdPathKey))
val redeemScript = upickle.default.read[Option[ScriptPubKey]](
obj(PicklerKeys.redeemScriptKey))
obj(PicklerKeys.redeemScriptKey)
)
val scriptWitness =
upickle.default.read[Option[ScriptWitness]](obj(PicklerKeys.witnessKey))
val state = upickle.default.read[TxoState](obj(PicklerKeys.stateKey))
val txId =
upickle.default.read[DoubleSha256DigestBE](obj(PicklerKeys.txIdKey))
val spendingTxId = upickle.default.read[Option[DoubleSha256DigestBE]](
obj(PicklerKeys.spendingTxIdKey))
obj(PicklerKeys.spendingTxIdKey)
)
SpendingInfoDb(
id = id,
outpoint = outpoint,
@ -293,7 +298,8 @@ object Picklers {
}
private def parseAdaptorSignatures(
arr: ujson.Arr): Vector[ECAdaptorSignature] = {
arr: ujson.Arr
): Vector[ECAdaptorSignature] = {
arr.value.toVector.map {
case obj: ujson.Obj =>
ECAdaptorSignature.fromHex(obj(PicklerKeys.signatureKey).str)
@ -303,19 +309,22 @@ object Picklers {
}
private def writeAdaptorSignatures(
sigs: Vector[ECAdaptorSignature]): Vector[ujson.Obj] = {
sigs: Vector[ECAdaptorSignature]
): Vector[ujson.Obj] = {
sigs.map { sig =>
ujson.Obj(PicklerKeys.signatureKey -> Str(sig.hex))
}
}
private def writeCetAdaptorSigs(
cetSignaturesTLV: CETSignaturesTLV): ujson.Obj = {
cetSignaturesTLV: CETSignaturesTLV
): ujson.Obj = {
cetSignaturesTLV match {
case v0: CETSignaturesV0TLV =>
val sigsVec = writeAdaptorSignatures(v0.sigs)
ujson.Obj(
PicklerKeys.ecdsaAdaptorSignaturesKey -> ujson.Arr.from(sigsVec))
PicklerKeys.ecdsaAdaptorSignaturesKey -> ujson.Arr.from(sigsVec)
)
}
}
@ -323,17 +332,20 @@ object Picklers {
val tempContractId =
Sha256Digest.fromHex(obj(PicklerKeys.tempContractIdKey).str)
val acceptCollateral = Satoshis(
obj(PicklerKeys.acceptCollateralKey).num.toLong)
obj(PicklerKeys.acceptCollateralKey).num.toLong
)
val fundingPubKey =
ECPublicKey.fromHex(obj(PicklerKeys.fundingPubKeyKey).str)
val payoutSpk = ScriptPubKey.fromAsmHex(obj(PicklerKeys.payoutSpkKey).str)
val payoutSerialId = parseU64(obj(PicklerKeys.payoutSerialIdKey).str)
val fundingInputs = parseFundingInputs(
obj(PicklerKeys.fundingInputsKey).arr)
obj(PicklerKeys.fundingInputsKey).arr
)
val changeSpk = ScriptPubKey.fromAsmHex(obj(PicklerKeys.changeSpkKey).str)
val changeSerialId = parseU64(obj(PicklerKeys.changeSerialIdKey).str)
val cetAdaptorSigs = parseCetAdaptorSignatures(
obj(PicklerKeys.cetAdaptorSignaturesKey).obj)
obj(PicklerKeys.cetAdaptorSignaturesKey).obj
)
val refundSignature =
ECDigitalSignature.fromHex(obj(PicklerKeys.refundSignatureKey).str)
val negotiationFields = {
@ -365,17 +377,21 @@ object Picklers {
Obj(
PicklerKeys.tempContractIdKey -> Str(accept.tempContractId.hex),
PicklerKeys.acceptCollateralKey -> Num(
accept.acceptCollateralSatoshis.toLong.toDouble),
accept.acceptCollateralSatoshis.toLong.toDouble
),
PicklerKeys.fundingPubKeyKey -> Str(accept.fundingPubKey.hex),
PicklerKeys.payoutSpkKey -> Str(accept.payoutSPK.asmHex),
PicklerKeys.payoutSerialIdKey -> Str(
accept.payoutSerialId.toBigInt.toString()),
accept.payoutSerialId.toBigInt.toString()
),
PicklerKeys.fundingInputsKey -> writeJs(accept.fundingInputs),
PicklerKeys.changeSpkKey -> Str(accept.changeSPK.asmHex),
PicklerKeys.changeSerialIdKey -> Str(
accept.changeSerialId.toBigInt.toString()),
accept.changeSerialId.toBigInt.toString()
),
PicklerKeys.cetAdaptorSignaturesKey -> writeCetAdaptorSigs(
accept.cetSignatures),
accept.cetSignatures
),
PicklerKeys.refundSignatureKey -> Str(accept.refundSignature.hex),
PicklerKeys.negotiationFieldsKey -> ujson.Null
)
@ -383,13 +399,15 @@ object Picklers {
private def parseFundingSignatures(obj: ujson.Obj): FundingSignaturesTLV = {
val fundingSignatures: Vector[ujson.Value] = obj(
PicklerKeys.fundingSignaturesKey).arr.toVector
PicklerKeys.fundingSignaturesKey
).arr.toVector
val witV0 = paresFundingSignaturesArr(fundingSignatures)
FundingSignaturesV0TLV(witV0)
}
private def paresFundingSignaturesArr(
arr: Vector[ujson.Value]): Vector[ScriptWitnessV0] = {
arr: Vector[ujson.Value]
): Vector[ScriptWitnessV0] = {
arr.map {
case obj: ujson.Obj =>
val witnessElementsArr = obj(PicklerKeys.witnessElementsKey).arr
@ -405,7 +423,8 @@ object Picklers {
}
private def writeFundingSignatures(
fundingSigs: FundingSignaturesTLV): ujson.Obj = {
fundingSigs: FundingSignaturesTLV
): ujson.Obj = {
val sigs: Vector[ujson.Obj] = fundingSigs match {
case v0: FundingSignaturesV0TLV =>
val witnessJson: Vector[Obj] = {
@ -424,11 +443,13 @@ object Picklers {
private def readSignTLV(obj: ujson.Obj): DLCSignTLV = {
val contractId = ByteVector.fromValidHex(obj(PicklerKeys.contractIdKey).str)
val adaptorSigs = parseCetAdaptorSignatures(
obj(PicklerKeys.cetAdaptorSignaturesKey).obj)
obj(PicklerKeys.cetAdaptorSignaturesKey).obj
)
val refundSignature =
ECDigitalSignature.fromHex(obj(PicklerKeys.refundSignatureKey).str)
val fundingSignatures = parseFundingSignatures(
obj(PicklerKeys.fundingSignaturesKey).obj)
obj(PicklerKeys.fundingSignaturesKey).obj
)
val signTLV =
DLCSignTLV(contractId, adaptorSigs, refundSignature, fundingSignatures)
@ -441,7 +462,8 @@ object Picklers {
ujson.Obj(
PicklerKeys.contractIdKey -> sign.contractId.toHex,
PicklerKeys.cetAdaptorSignaturesKey -> writeCetAdaptorSigs(
sign.cetSignatures),
sign.cetSignatures
),
PicklerKeys.refundSignatureKey -> ujson.Str(sign.refundSignature.hex),
PicklerKeys.fundingSignaturesKey ->
writeFundingSignatures(sign.fundingSignatures)
@ -456,8 +478,8 @@ object Picklers {
readwriter[ujson.Obj].bimap(writeSignTLV, readSignTLV)
}
implicit val lnMessageDLCAcceptTLVPickler: ReadWriter[
LnMessage[DLCAcceptTLV]] =
implicit val lnMessageDLCAcceptTLVPickler
: ReadWriter[LnMessage[DLCAcceptTLV]] =
readwriter[String].bimap(_.hex, LnMessageFactory(DLCAcceptTLV).fromHex)
implicit val lnMessageDLCSignTLVPickler: ReadWriter[LnMessage[DLCSignTLV]] =
@ -488,8 +510,8 @@ object Picklers {
implicit val addressLabelTagPickler: ReadWriter[AddressLabelTag] =
readwriter[String].bimap(_.name, AddressLabelTag)
implicit val lockUnspentOutputParameterPickler: ReadWriter[
LockUnspentOutputParameter] =
implicit val lockUnspentOutputParameterPickler
: ReadWriter[LockUnspentOutputParameter] =
readwriter[Value].bimap(_.toJson, LockUnspentOutputParameter.fromJson)
// can't make implicit because it will overlap with ones needed for cli
@ -501,8 +523,10 @@ object Picklers {
val descriptorJson = announcement.eventTLV.eventDescriptor match {
case EnumEventDescriptorV0TLV(outcomes) =>
Obj("outcomes" -> outcomes.map(Str(_)),
"hex" -> announcement.eventTLV.eventDescriptor.hex)
Obj(
"outcomes" -> outcomes.map(Str(_)),
"hex" -> announcement.eventTLV.eventDescriptor.hex
)
case numeric: NumericEventDescriptorTLV =>
Obj(
"base" -> Num(numeric.base.toLong.toDouble),
@ -516,10 +540,12 @@ object Picklers {
val maturityStr =
TimeUtil.iso8601ToString(Date.from(announcement.eventTLV.maturation))
val eventJson = Obj("nonces" -> noncesJson,
val eventJson = Obj(
"nonces" -> noncesJson,
"maturity" -> Str(maturityStr),
"descriptor" -> descriptorJson,
"eventId" -> Str(announcement.eventTLV.eventId))
"eventId" -> Str(announcement.eventTLV.eventId)
)
Obj(
"announcementSignature" -> Str(announcement.announcementSignature.hex),
@ -541,10 +567,12 @@ object Picklers {
val sigsJson = attestments.sigs.map(sig => Str(sig.hex))
val valuesJson = attestments.outcomes.map(Str(_))
Obj("eventId" -> Str(attestments.eventId),
Obj(
"eventId" -> Str(attestments.eventId),
"signatures" -> sigsJson,
"values" -> valuesJson,
"hex" -> attestments.hex)
"hex" -> attestments.hex
)
}
implicit val fundingInputV0Writer: Writer[FundingInputTLV] =
@ -591,15 +619,17 @@ object Picklers {
}
}
implicit val hyperbolaPayoutCurvePieceTLVWriter: Writer[
HyperbolaPayoutCurvePieceTLV] = {
implicit val hyperbolaPayoutCurvePieceTLVWriter
: Writer[HyperbolaPayoutCurvePieceTLV] = {
writer[Obj].comap { piece =>
Obj(
PicklerKeys.usePositivePiece -> Bool(piece.usePositivePiece),
PicklerKeys.translateOutcome -> Num(
piece.translateOutcome.toBigDecimal.toDouble),
piece.translateOutcome.toBigDecimal.toDouble
),
PicklerKeys.translatePayout -> Num(
piece.translatePayout.toBigDecimal.toDouble),
piece.translatePayout.toBigDecimal.toDouble
),
PicklerKeys.a -> Num(piece.a.toBigDecimal.toDouble),
PicklerKeys.b -> Num(piece.b.toBigDecimal.toDouble),
PicklerKeys.c -> Num(piece.c.toBigDecimal.toDouble),
@ -644,7 +674,8 @@ object Picklers {
val points: Vector[TLVPoint] = pointsArr.map {
case x @ (_: Arr | _: Num | Null | _: Bool | _: Str) =>
sys.error(
s"Cannot have $x when parsing payout curve points, expected json object")
s"Cannot have $x when parsing payout curve points, expected json object"
)
case obj: Obj =>
upickle.default.read[TLVPoint](obj)
}.toVector
@ -660,8 +691,10 @@ object Picklers {
import roundingIntervals._
val intervalsJs = intervalStarts.map { i =>
Obj("beginInterval" -> Num(i._1.toDouble),
"roundingMod" -> Num(i._2.toLong.toDouble))
Obj(
"beginInterval" -> Num(i._1.toDouble),
"roundingMod" -> Num(i._2.toLong.toDouble)
)
}
Obj("intervals" -> intervalsJs)
@ -686,10 +719,12 @@ object Picklers {
writer[Obj].comap { v1 =>
import v1._
Obj("numDigits" -> Num(numDigits.toDouble),
Obj(
"numDigits" -> Num(numDigits.toDouble),
"payoutFunction" -> writeJs(payoutFunction),
"roundingIntervals" -> writeJs(roundingIntervals),
"hex" -> v1.hex)
"hex" -> v1.hex
)
}
implicit val contractDescriptorWriter: Writer[ContractDescriptorTLV] =
@ -704,23 +739,29 @@ object Picklers {
writer[Obj].comap { oracleInfo =>
Obj(
"announcement" -> writeJs(oracleInfo.announcement)(
oracleAnnouncementTLVJsonWriter))
oracleAnnouncementTLVJsonWriter
)
)
}
implicit val oracleInfoV1TLVWriter: Writer[OracleInfoV1TLV] =
writer[Obj].comap { oracleInfo =>
import oracleInfo._
Obj("threshold" -> Num(threshold.toDouble),
Obj(
"threshold" -> Num(threshold.toDouble),
"announcements" -> oracles.map(o =>
writeJs(o)(oracleAnnouncementTLVJsonWriter)))
writeJs(o)(oracleAnnouncementTLVJsonWriter))
)
}
implicit val oracleParamsV0TLVWriter: Writer[OracleParamsV0TLV] =
writer[Obj].comap { params =>
import params._
Obj("maxErrorExp" -> Num(maxErrorExp.toDouble),
Obj(
"maxErrorExp" -> Num(maxErrorExp.toDouble),
"minFailExp" -> Num(minFailExp.toDouble),
"maximizeCoverage" -> Bool(maximizeCoverage))
"maximizeCoverage" -> Bool(maximizeCoverage)
)
}
implicit val oracleParamsTLVWriter: Writer[OracleParamsTLV] =
@ -731,10 +772,12 @@ object Picklers {
implicit val oracleInfoV2TLVWriter: Writer[OracleInfoV2TLV] =
writer[Obj].comap { oracleInfo =>
import oracleInfo._
Obj("threshold" -> Num(threshold.toDouble),
Obj(
"threshold" -> Num(threshold.toDouble),
"announcements" -> oracles.map(o =>
writeJs(o)(oracleAnnouncementTLVJsonWriter)),
"params" -> writeJs(params))
"params" -> writeJs(params)
)
}
implicit val oracleInfoTLVWriter: Writer[OracleInfoTLV] =
@ -762,15 +805,18 @@ object Picklers {
case (c, o) =>
val contractDescriptorJson = writeJs(c)
val oracleInfoJson = writeJs(o)
ujson.Obj(PicklerKeys.contractDescriptorKey -> contractDescriptorJson,
PicklerKeys.oracleInfoKey -> oracleInfoJson)
ujson.Obj(
PicklerKeys.contractDescriptorKey -> contractDescriptorJson,
PicklerKeys.oracleInfoKey -> oracleInfoJson
)
}
val arrayJson = ujson.Arr.from(arrayVec)
Obj(
PicklerKeys.totalCollateralKey -> Num(
contractInfo.totalCollateral.toLong.toDouble),
contractInfo.totalCollateral.toLong.toDouble
),
PicklerKeys.pairsKey -> arrayJson
)
}
@ -840,9 +886,11 @@ object Picklers {
PicklerKeys.tempContractIdKey -> Str(tempContractId.hex),
"contractInfo" -> Str(contractInfo.hex),
"contractMaturity" -> Num(
timeouts.contractMaturity.toUInt32.toLong.toDouble),
timeouts.contractMaturity.toUInt32.toLong.toDouble
),
"contractTimeout" -> Num(
timeouts.contractTimeout.toUInt32.toLong.toDouble),
timeouts.contractTimeout.toUInt32.toLong.toDouble
),
"feeRate" -> Num(feeRate.toLong.toDouble),
"totalCollateral" -> Num(totalCollateral.satoshis.toLong.toDouble),
"localCollateral" -> Num(localCollateral.satoshis.toLong.toDouble),
@ -852,8 +900,8 @@ object Picklers {
)
}
implicit val acceptedComputingAdaptorSigsW: Writer[
AcceptedComputingAdaptorSigs] = writer[Obj].comap { accepted =>
implicit val acceptedComputingAdaptorSigsW
: Writer[AcceptedComputingAdaptorSigs] = writer[Obj].comap { accepted =>
import accepted._
Obj(
"state" -> Str(statusString),
@ -864,9 +912,11 @@ object Picklers {
"contractId" -> Str(contractId.toHex),
"contractInfo" -> Str(contractInfo.hex),
"contractMaturity" -> Num(
timeouts.contractMaturity.toUInt32.toLong.toDouble),
timeouts.contractMaturity.toUInt32.toLong.toDouble
),
"contractTimeout" -> Num(
timeouts.contractTimeout.toUInt32.toLong.toDouble),
timeouts.contractTimeout.toUInt32.toLong.toDouble
),
"feeRate" -> Num(feeRate.toLong.toDouble),
"totalCollateral" -> Num(totalCollateral.satoshis.toLong.toDouble),
"localCollateral" -> Num(localCollateral.satoshis.toLong.toDouble),
@ -887,9 +937,11 @@ object Picklers {
"contractId" -> Str(contractId.toHex),
"contractInfo" -> Str(contractInfo.hex),
"contractMaturity" -> Num(
timeouts.contractMaturity.toUInt32.toLong.toDouble),
timeouts.contractMaturity.toUInt32.toLong.toDouble
),
"contractTimeout" -> Num(
timeouts.contractTimeout.toUInt32.toLong.toDouble),
timeouts.contractTimeout.toUInt32.toLong.toDouble
),
"feeRate" -> Num(feeRate.toLong.toDouble),
"totalCollateral" -> Num(totalCollateral.satoshis.toLong.toDouble),
"localCollateral" -> Num(localCollateral.satoshis.toLong.toDouble),
@ -911,9 +963,11 @@ object Picklers {
"contractId" -> Str(contractId.toHex),
"contractInfo" -> Str(contractInfo.hex),
"contractMaturity" -> Num(
timeouts.contractMaturity.toUInt32.toLong.toDouble),
timeouts.contractMaturity.toUInt32.toLong.toDouble
),
"contractTimeout" -> Num(
timeouts.contractTimeout.toUInt32.toLong.toDouble),
timeouts.contractTimeout.toUInt32.toLong.toDouble
),
"feeRate" -> Num(feeRate.toLong.toDouble),
"totalCollateral" -> Num(totalCollateral.satoshis.toLong.toDouble),
"localCollateral" -> Num(localCollateral.satoshis.toLong.toDouble),
@ -935,9 +989,11 @@ object Picklers {
"contractId" -> Str(contractId.toHex),
"contractInfo" -> Str(contractInfo.hex),
"contractMaturity" -> Num(
timeouts.contractMaturity.toUInt32.toLong.toDouble),
timeouts.contractMaturity.toUInt32.toLong.toDouble
),
"contractTimeout" -> Num(
timeouts.contractTimeout.toUInt32.toLong.toDouble),
timeouts.contractTimeout.toUInt32.toLong.toDouble
),
"feeRate" -> Num(feeRate.toLong.toDouble),
"totalCollateral" -> Num(totalCollateral.satoshis.toLong.toDouble),
"localCollateral" -> Num(localCollateral.satoshis.toLong.toDouble),
@ -960,9 +1016,11 @@ object Picklers {
"contractId" -> Str(contractId.toHex),
"contractInfo" -> Str(contractInfo.hex),
"contractMaturity" -> Num(
timeouts.contractMaturity.toUInt32.toLong.toDouble),
timeouts.contractMaturity.toUInt32.toLong.toDouble
),
"contractTimeout" -> Num(
timeouts.contractTimeout.toUInt32.toLong.toDouble),
timeouts.contractTimeout.toUInt32.toLong.toDouble
),
"feeRate" -> Num(feeRate.toLong.toDouble),
"totalCollateral" -> Num(totalCollateral.satoshis.toLong.toDouble),
"localCollateral" -> Num(localCollateral.satoshis.toLong.toDouble),
@ -985,9 +1043,11 @@ object Picklers {
"contractId" -> Str(contractId.toHex),
"contractInfo" -> Str(contractInfo.hex),
"contractMaturity" -> Num(
timeouts.contractMaturity.toUInt32.toLong.toDouble),
timeouts.contractMaturity.toUInt32.toLong.toDouble
),
"contractTimeout" -> Num(
timeouts.contractTimeout.toUInt32.toLong.toDouble),
timeouts.contractTimeout.toUInt32.toLong.toDouble
),
"feeRate" -> Num(feeRate.toLong.toDouble),
"totalCollateral" -> Num(totalCollateral.satoshis.toLong.toDouble),
"localCollateral" -> Num(localCollateral.satoshis.toLong.toDouble),
@ -1004,11 +1064,15 @@ object Picklers {
import claimed._
val (oraclesJs, outcomesJs) = oracleOutcome match {
case EnumOracleOutcome(oracles, outcome) =>
(Arr.from(oracles.map(o => Str(o.announcement.hex))),
Str(outcome.outcome))
(
Arr.from(oracles.map(o => Str(o.announcement.hex))),
Str(outcome.outcome)
)
case numeric: NumericOracleOutcome =>
(Arr.from(numeric.oracles.map(_.announcement.hex)),
Arr.from(numeric.outcomes.map(o => Arr.from(o.digits))))
(
Arr.from(numeric.oracles.map(_.announcement.hex)),
Arr.from(numeric.outcomes.map(o => Arr.from(o.digits)))
)
}
Obj(
@ -1020,9 +1084,11 @@ object Picklers {
"contractId" -> Str(contractId.toHex),
"contractInfo" -> Str(contractInfo.hex),
"contractMaturity" -> Num(
timeouts.contractMaturity.toUInt32.toLong.toDouble),
timeouts.contractMaturity.toUInt32.toLong.toDouble
),
"contractTimeout" -> Num(
timeouts.contractTimeout.toUInt32.toLong.toDouble),
timeouts.contractTimeout.toUInt32.toLong.toDouble
),
"feeRate" -> Num(feeRate.toLong.toDouble),
"totalCollateral" -> Num(totalCollateral.satoshis.toLong.toDouble),
"localCollateral" -> Num(localCollateral.satoshis.toLong.toDouble),
@ -1034,7 +1100,8 @@ object Picklers {
"oracles" -> oraclesJs,
PicklerKeys.myPayout -> Num(claimed.myPayout.satoshis.toLong.toDouble),
counterPartyPayoutKey -> Num(
claimed.counterPartyPayout.satoshis.toLong.toDouble),
claimed.counterPartyPayout.satoshis.toLong.toDouble
),
PicklerKeys.pnl -> Num(claimed.pnl.satoshis.toLong.toDouble),
PicklerKeys.rateOfReturn -> Num(claimed.rateOfReturn.toDouble),
"payoutAddress" -> writeJs(payoutAddress),
@ -1047,11 +1114,15 @@ object Picklers {
import remoteClaimed._
val (oraclesJs, outcomesJs) = oracleOutcome match {
case EnumOracleOutcome(oracles, outcome) =>
(Arr.from(oracles.map(o => Str(o.announcement.hex))),
Str(outcome.outcome))
(
Arr.from(oracles.map(o => Str(o.announcement.hex))),
Str(outcome.outcome)
)
case numeric: NumericOracleOutcome =>
(Arr.from(numeric.oracles.map(_.announcement.hex)),
Arr.from(numeric.outcomes.map(o => Arr.from(o.digits))))
(
Arr.from(numeric.oracles.map(_.announcement.hex)),
Arr.from(numeric.outcomes.map(o => Arr.from(o.digits)))
)
}
Obj(
@ -1063,9 +1134,11 @@ object Picklers {
"contractId" -> Str(contractId.toHex),
"contractInfo" -> Str(contractInfo.hex),
"contractMaturity" -> Num(
timeouts.contractMaturity.toUInt32.toLong.toDouble),
timeouts.contractMaturity.toUInt32.toLong.toDouble
),
"contractTimeout" -> Num(
timeouts.contractTimeout.toUInt32.toLong.toDouble),
timeouts.contractTimeout.toUInt32.toLong.toDouble
),
"feeRate" -> Num(feeRate.toLong.toDouble),
"totalCollateral" -> Num(totalCollateral.satoshis.toLong.toDouble),
"localCollateral" -> Num(localCollateral.satoshis.toLong.toDouble),
@ -1076,9 +1149,11 @@ object Picklers {
"outcomes" -> outcomesJs,
"oracles" -> oraclesJs,
PicklerKeys.myPayout -> Num(
remoteClaimed.myPayout.satoshis.toLong.toDouble),
remoteClaimed.myPayout.satoshis.toLong.toDouble
),
counterPartyPayoutKey -> Num(
remoteClaimed.counterPartyPayout.satoshis.toLong.toDouble),
remoteClaimed.counterPartyPayout.satoshis.toLong.toDouble
),
PicklerKeys.pnl -> Num(remoteClaimed.pnl.satoshis.toLong.toDouble),
PicklerKeys.rateOfReturn -> Num(remoteClaimed.rateOfReturn.toDouble),
"payoutAddress" -> writeJs(payoutAddress),
@ -1097,9 +1172,11 @@ object Picklers {
"contractId" -> Str(contractId.toHex),
"contractInfo" -> Str(contractInfo.hex),
"contractMaturity" -> Num(
timeouts.contractMaturity.toUInt32.toLong.toDouble),
timeouts.contractMaturity.toUInt32.toLong.toDouble
),
"contractTimeout" -> Num(
timeouts.contractTimeout.toUInt32.toLong.toDouble),
timeouts.contractTimeout.toUInt32.toLong.toDouble
),
"feeRate" -> Num(feeRate.toLong.toDouble),
"totalCollateral" -> Num(totalCollateral.satoshis.toLong.toDouble),
"localCollateral" -> Num(localCollateral.satoshis.toLong.toDouble),
@ -1108,7 +1185,8 @@ object Picklers {
"closingTxId" -> Str(closingTxId.hex),
PicklerKeys.myPayout -> Num(refunded.myPayout.satoshis.toLong.toDouble),
counterPartyPayoutKey -> Num(
refunded.counterPartyPayout.satoshis.toLong.toDouble),
refunded.counterPartyPayout.satoshis.toLong.toDouble
),
PicklerKeys.pnl -> Num(refunded.pnl.satoshis.toLong.toDouble),
PicklerKeys.rateOfReturn -> Num(refunded.rateOfReturn.toDouble),
"payoutAddress" -> writeJs(payoutAddress),
@ -1161,11 +1239,13 @@ object Picklers {
val message = Try(obj("message").str).toOption
val receivedAt = Instant.ofEpochSecond(obj("receivedAt").num.toLong)
val offerTLV = DLCOfferTLV.fromHex(obj("offerTLV").str)
IncomingDLCOfferDb(hash = hash,
IncomingDLCOfferDb(
hash = hash,
peer = peer,
message = message,
receivedAt = receivedAt,
offerTLV = offerTLV)
offerTLV = offerTLV
)
}
implicit val dlcOfferRemoveR: Reader[Sha256Digest] =
@ -1202,8 +1282,10 @@ object Picklers {
lazy val payoutAddress: Option[PayoutAddress] = payoutAddressJs match {
case json: Obj =>
json("address").strOpt.map(a =>
PayoutAddress(BitcoinAddress.fromString(a),
json("isExternal").boolOpt.getOrElse(false)))
PayoutAddress(
BitcoinAddress.fromString(a),
json("isExternal").boolOpt.getOrElse(false)
))
case Null => None
case v: Value =>
throw new IllegalArgumentException(s"Unexpected payout address $v")
@ -1228,8 +1310,10 @@ object Picklers {
lazy val oracleOutcome = outcomes.head match {
case outcome: EnumOutcome =>
EnumOracleOutcome(oracles.asInstanceOf[Vector[EnumSingleOracleInfo]],
outcome)
EnumOracleOutcome(
oracles.asInstanceOf[Vector[EnumSingleOracleInfo]],
outcome
)
case UnsignedNumericOutcome(_) =>
val numericOutcomes =
outcomes.map(_.asInstanceOf[UnsignedNumericOutcome])
@ -1377,8 +1461,10 @@ object Picklers {
peerOpt
)
case DLCState.RemoteClaimed =>
require(oracleSigs.size == 1,
"Remote claimed should only have one oracle sig")
require(
oracleSigs.size == 1,
"Remote claimed should only have one oracle sig"
)
RemoteClaimed(
dlcId,
isInitiator,
@ -1425,13 +1511,17 @@ object Picklers {
writer[Obj].comap { walletAccounting: DLCWalletAccounting =>
Obj(
PicklerKeys.myCollateral -> Num(
walletAccounting.myCollateral.satoshis.toLong.toDouble),
walletAccounting.myCollateral.satoshis.toLong.toDouble
),
PicklerKeys.theirCollateral -> Num(
walletAccounting.theirCollateral.satoshis.toLong.toDouble),
walletAccounting.theirCollateral.satoshis.toLong.toDouble
),
PicklerKeys.myPayout -> Num(
walletAccounting.myPayout.satoshis.toLong.toDouble),
walletAccounting.myPayout.satoshis.toLong.toDouble
),
PicklerKeys.theirPayout -> Num(
walletAccounting.theirPayout.satoshis.toLong.toDouble),
walletAccounting.theirPayout.satoshis.toLong.toDouble
),
PicklerKeys.pnl -> Num(walletAccounting.pnl.satoshis.toLong.toDouble),
PicklerKeys.rateOfReturn -> Num(walletAccounting.rateOfReturn.toDouble)
)
@ -1441,7 +1531,8 @@ object Picklers {
implicit val mnemonicCodePickler: ReadWriter[MnemonicCode] =
readwriter[String].bimap(
_.words.mkString(" "),
str => MnemonicCode.fromWords(str.split(' ').toVector))
str => MnemonicCode.fromWords(str.split(' ').toVector)
)
implicit val extPrivateKeyPickler: ReadWriter[ExtPrivateKey] =
readwriter[String].bimap(ExtKey.toString, ExtPrivateKey.fromString)
@ -1559,13 +1650,16 @@ object Picklers {
}
private def writeCompactFilterDb(
compactFilterDb: CompactFilterDb): ujson.Obj = {
compactFilterDb: CompactFilterDb
): ujson.Obj = {
ujson.Obj(
PicklerKeys.hashKey -> ujson.Str(compactFilterDb.hashBE.hex),
PicklerKeys.filterTypeKey -> ujson.Str(
compactFilterDb.filterType.toString),
compactFilterDb.filterType.toString
),
PicklerKeys.compactFilterBytesKey -> ujson.Str(
compactFilterDb.bytes.toHex),
compactFilterDb.bytes.toHex
),
PicklerKeys.heightKey -> ujson.Num(compactFilterDb.height),
PicklerKeys.blockHashKey -> ujson.Str(compactFilterDb.blockHashBE.hex)
)
@ -1590,7 +1684,8 @@ object Picklers {
}
private def writeCompactFilterHeaderDb(
filterHeaderDb: CompactFilterHeaderDb): ujson.Obj = {
filterHeaderDb: CompactFilterHeaderDb
): ujson.Obj = {
ujson.Obj(
PicklerKeys.hashKey -> ujson.Str(filterHeaderDb.hashBE.hex),
PicklerKeys.filterHashKey -> ujson.Str(filterHeaderDb.filterHashBE.hex),
@ -1602,7 +1697,8 @@ object Picklers {
}
private def readCompactFilterHeaderDb(
obj: ujson.Obj): CompactFilterHeaderDb = {
obj: ujson.Obj
): CompactFilterHeaderDb = {
val hash = DoubleSha256DigestBE.fromHex(obj(PicklerKeys.hashKey).str)
val filterHash =
DoubleSha256DigestBE.fromHex(obj(PicklerKeys.filterHashKey).str)
@ -1611,11 +1707,13 @@ object Picklers {
val blockHash =
DoubleSha256DigestBE.fromHex(obj(PicklerKeys.blockHashKey).str)
val height = obj(PicklerKeys.heightKey).num
CompactFilterHeaderDb(hashBE = hash,
CompactFilterHeaderDb(
hashBE = hash,
filterHashBE = filterHash,
previousFilterHeaderBE = previousFilterHeader,
blockHashBE = blockHash,
height = height.toInt)
height = height.toInt
)
}
private def writeContactDb(contact: DLCContactDb): ujson.Obj = {

View File

@ -4,8 +4,9 @@ import play.api.libs.json._
sealed abstract class SerializerUtil {
def processJsNumberBigInt[T](numFunc: BigInt => T)(
json: JsValue): JsResult[T] =
def processJsNumberBigInt[T](
numFunc: BigInt => T
)(json: JsValue): JsResult[T] =
json match {
case JsNumber(nDecimal) =>
val nOpt = nDecimal.toBigIntExact
@ -54,8 +55,9 @@ sealed abstract class SerializerUtil {
SerializerUtil.buildJsErrorMsg("jsstring", err)
}
def processJsStringOpt[T](f: String => Option[T])(
jsValue: JsValue): JsResult[T] = {
def processJsStringOpt[T](
f: String => Option[T]
)(jsValue: JsValue): JsResult[T] = {
jsValue match {
case JsString(key) =>
val tOpt = f(key)

View File

@ -69,13 +69,15 @@ object WsPicklers {
}
private def writeChainNotification(
notification: ChainNotification[_]): ujson.Obj = {
notification: ChainNotification[_]
): ujson.Obj = {
val payloadJson: ujson.Value = notification match {
case BlockProcessedNotification(block) =>
upickle.default.writeJs(block)(Picklers.getBlockHeaderResultPickler)
case CompactFilterHeaderProcessedNotification(filterHeader) =>
upickle.default.writeJs(filterHeader)(
Picklers.compactFilterHeaderPickler)
Picklers.compactFilterHeaderPickler
)
case CompactFilterProcessedNotification(filter) =>
upickle.default.writeJs(filter)(Picklers.compactFilterDbPickler)
case SyncFlagChangedNotification(syncing) =>
@ -115,7 +117,8 @@ object WsPicklers {
}
private def writeWalletNotification(
notification: WalletNotification[_]): ujson.Obj = {
notification: WalletNotification[_]
): ujson.Obj = {
val payloadJson: ujson.Value = notification match {
case TxBroadcastNotification(tx) =>
upickle.default.writeJs(tx)(Picklers.transactionPickler)
@ -184,7 +187,8 @@ object WsPicklers {
}
private def writeTorNotification(
notification: TorNotification[_]): ujson.Obj = {
notification: TorNotification[_]
): ujson.Obj = {
val payloadJson = notification.`type` match {
case TorWsType.TorStarted =>
ujson.Null
@ -206,12 +210,15 @@ object WsPicklers {
}
private def writeDLCNodeNotification(
notification: DLCNodeNotification[_]): ujson.Obj = {
notification: DLCNodeNotification[_]
): ujson.Obj = {
def addr2str(address: InetSocketAddress) =
address.getHostName + ":" + address.getPort
def failure2obj(payload: (Sha256Digest, String)): ujson.Obj = {
ujson.Obj(PicklerKeys.idKey -> writeJs(payload._1.hex),
PicklerKeys.errorKey -> writeJs(payload._2))
ujson.Obj(
PicklerKeys.idKey -> writeJs(payload._1.hex),
PicklerKeys.errorKey -> writeJs(payload._2)
)
}
val payloadJson: ujson.Value = notification match {
case DLCNodeConnectionInitiated(address) =>
@ -238,13 +245,16 @@ object WsPicklers {
}
private def readDLCNodeNotification(
obj: ujson.Obj): DLCNodeNotification[_] = {
obj: ujson.Obj
): DLCNodeNotification[_] = {
val typeObj = read[DLCNodeWsType](obj(PicklerKeys.typeKey))
val payloadObj = obj(PicklerKeys.payloadKey)
def obj2failure(payload: ujson.Value): (Sha256Digest, String) = {
(Sha256Digest.fromHex(payload.obj(PicklerKeys.idKey).str),
payload.obj(PicklerKeys.errorKey).str)
(
Sha256Digest.fromHex(payload.obj(PicklerKeys.idKey).str),
payload.obj(PicklerKeys.errorKey).str
)
}
typeObj match {
@ -278,25 +288,29 @@ object WsPicklers {
implicit val newAddressPickler: ReadWriter[NewAddressNotification] = {
readwriter[ujson.Obj].bimap(
writeWalletNotification(_),
readWalletNotification(_).asInstanceOf[NewAddressNotification])
readWalletNotification(_).asInstanceOf[NewAddressNotification]
)
}
implicit val txProcessedPickler: ReadWriter[TxProcessedNotification] = {
readwriter[ujson.Obj].bimap(
writeWalletNotification(_),
readWalletNotification(_).asInstanceOf[TxProcessedNotification])
readWalletNotification(_).asInstanceOf[TxProcessedNotification]
)
}
implicit val txBroadcastPickler: ReadWriter[TxBroadcastNotification] = {
readwriter[ujson.Obj].bimap(
writeWalletNotification(_),
readWalletNotification(_).asInstanceOf[TxBroadcastNotification])
readWalletNotification(_).asInstanceOf[TxBroadcastNotification]
)
}
implicit val reservedUtxosPickler: ReadWriter[ReservedUtxosNotification] = {
readwriter[ujson.Obj].bimap(
writeWalletNotification(_),
readWalletNotification(_).asInstanceOf[ReservedUtxosNotification])
readWalletNotification(_).asInstanceOf[ReservedUtxosNotification]
)
}
implicit val rescanPickler: ReadWriter[RescanComplete] = {
@ -313,8 +327,8 @@ object WsPicklers {
)
}
implicit val dlcNodeNotificationPickler: ReadWriter[
DLCNodeNotification[_]] = {
implicit val dlcNodeNotificationPickler
: ReadWriter[DLCNodeNotification[_]] = {
readwriter[ujson.Obj]
.bimap(writeDLCNodeNotification, readDLCNodeNotification)
}
@ -334,8 +348,8 @@ object WsPicklers {
)
}
implicit val compactFilterHeaderProcessedPickler: ReadWriter[
CompactFilterHeaderProcessedNotification] = {
implicit val compactFilterHeaderProcessedPickler
: ReadWriter[CompactFilterHeaderProcessedNotification] = {
readwriter[ujson.Obj].bimap(
writeChainNotification(_),
readChainNotification(_)
@ -343,16 +357,16 @@ object WsPicklers {
)
}
implicit val compactFilterProcessedPickler: ReadWriter[
CompactFilterProcessedNotification] = {
implicit val compactFilterProcessedPickler
: ReadWriter[CompactFilterProcessedNotification] = {
readwriter[ujson.Obj].bimap(
writeChainNotification(_),
readChainNotification(_).asInstanceOf[CompactFilterProcessedNotification]
)
}
implicit val syncFlagChangedPickler: ReadWriter[
SyncFlagChangedNotification] = {
implicit val syncFlagChangedPickler
: ReadWriter[SyncFlagChangedNotification] = {
readwriter[ujson.Obj].bimap(
writeChainNotification(_),
readChainNotification(_).asInstanceOf[SyncFlagChangedNotification]
@ -362,83 +376,96 @@ object WsPicklers {
implicit val dlcStateChangePickler: ReadWriter[DLCStateChangeNotification] = {
readwriter[ujson.Obj].bimap(
writeWalletNotification(_),
readWalletNotification(_).asInstanceOf[DLCStateChangeNotification])
readWalletNotification(_).asInstanceOf[DLCStateChangeNotification]
)
}
implicit val dlcOfferAddPickler: ReadWriter[DLCOfferAddNotification] = {
readwriter[ujson.Obj].bimap(
writeWalletNotification(_),
readWalletNotification(_).asInstanceOf[DLCOfferAddNotification])
readWalletNotification(_).asInstanceOf[DLCOfferAddNotification]
)
}
implicit val dlcOfferRemovePickler: ReadWriter[DLCOfferRemoveNotification] = {
readwriter[ujson.Obj].bimap(
writeWalletNotification(_),
readWalletNotification(_).asInstanceOf[DLCOfferRemoveNotification])
readWalletNotification(_).asInstanceOf[DLCOfferRemoveNotification]
)
}
implicit val torStartedPickler: ReadWriter[
TorNotification.TorStartedNotification.type] = {
implicit val torStartedPickler
: ReadWriter[TorNotification.TorStartedNotification.type] = {
readwriter[ujson.Obj].bimap(
writeTorNotification(_),
readTorNotification(_)
.asInstanceOf[TorNotification.TorStartedNotification.type])
.asInstanceOf[TorNotification.TorStartedNotification.type]
)
}
implicit val dlcNodeConnectionInitiatedPickler: ReadWriter[
DLCNodeConnectionInitiated] = {
implicit val dlcNodeConnectionInitiatedPickler
: ReadWriter[DLCNodeConnectionInitiated] = {
readwriter[ujson.Obj].bimap(
writeDLCNodeNotification(_),
readDLCNodeNotification(_).asInstanceOf[DLCNodeConnectionInitiated])
readDLCNodeNotification(_).asInstanceOf[DLCNodeConnectionInitiated]
)
}
implicit val dlcNodeConnectionFailedPickler: ReadWriter[
DLCNodeConnectionFailed] = {
implicit val dlcNodeConnectionFailedPickler
: ReadWriter[DLCNodeConnectionFailed] = {
readwriter[ujson.Obj].bimap(
writeDLCNodeNotification(_),
readDLCNodeNotification(_).asInstanceOf[DLCNodeConnectionFailed])
readDLCNodeNotification(_).asInstanceOf[DLCNodeConnectionFailed]
)
}
implicit val dlcNodeConnectionEstablishedPickler: ReadWriter[
DLCNodeConnectionEstablished] = {
implicit val dlcNodeConnectionEstablishedPickler
: ReadWriter[DLCNodeConnectionEstablished] = {
readwriter[ujson.Obj].bimap(
writeDLCNodeNotification(_),
readDLCNodeNotification(_).asInstanceOf[DLCNodeConnectionEstablished])
readDLCNodeNotification(_).asInstanceOf[DLCNodeConnectionEstablished]
)
}
implicit val dlcAcceptSucceedPickler: ReadWriter[DLCAcceptSucceed] = {
readwriter[ujson.Obj].bimap(
writeDLCNodeNotification(_),
readDLCNodeNotification(_).asInstanceOf[DLCAcceptSucceed])
readDLCNodeNotification(_).asInstanceOf[DLCAcceptSucceed]
)
}
implicit val dlcAcceptFailedPickler: ReadWriter[DLCAcceptFailed] = {
readwriter[ujson.Obj].bimap(
writeDLCNodeNotification(_),
readDLCNodeNotification(_).asInstanceOf[DLCAcceptFailed])
readDLCNodeNotification(_).asInstanceOf[DLCAcceptFailed]
)
}
implicit val dlcSignSucceedPickler: ReadWriter[DLCSignSucceed] = {
readwriter[ujson.Obj].bimap(
writeDLCNodeNotification(_),
readDLCNodeNotification(_).asInstanceOf[DLCSignSucceed])
readDLCNodeNotification(_).asInstanceOf[DLCSignSucceed]
)
}
implicit val dlcSignFailedPickler: ReadWriter[DLCSignFailed] = {
readwriter[ujson.Obj].bimap(
writeDLCNodeNotification(_),
readDLCNodeNotification(_).asInstanceOf[DLCSignFailed])
readDLCNodeNotification(_).asInstanceOf[DLCSignFailed]
)
}
implicit val dlcOfferSendSucceedPickler: ReadWriter[DLCOfferSendSucceed] = {
readwriter[ujson.Obj].bimap(
writeDLCNodeNotification(_),
readDLCNodeNotification(_).asInstanceOf[DLCOfferSendSucceed])
readDLCNodeNotification(_).asInstanceOf[DLCOfferSendSucceed]
)
}
implicit val dlcOfferSendFailedPickler: ReadWriter[DLCOfferSendFailed] = {
readwriter[ujson.Obj].bimap(
writeDLCNodeNotification(_),
readDLCNodeNotification(_).asInstanceOf[DLCOfferSendFailed])
readDLCNodeNotification(_).asInstanceOf[DLCOfferSendFailed]
)
}
}

View File

@ -5,14 +5,15 @@ import org.bitcoins.commons.config.AppConfig
import java.nio.file.{Path, Paths}
/** Parses the correct datadir given the possible input sources for datadir config
* 1. The --datadir command line flag
* 2. Inferring the datadir based on the bitcoin network configured
* 3. ??? Anything else i'm forgetting ????
/** Parses the correct datadir given the possible input sources for datadir
* config
* 1. The --datadir command line flag 2. Inferring the datadir based on the
* bitcoin network configured 3. ??? Anything else i'm forgetting ????
*/
case class DatadirParser(
serverArgs: ServerArgParser,
customFinalDirOpt: Option[String]) {
customFinalDirOpt: Option[String]
) {
/** Sets the default data dir, overridden by the --datadir option */
private lazy val datadirPath: Path = serverArgs.datadirOpt match {
@ -22,7 +23,8 @@ case class DatadirParser(
lazy val datadirConfig: Config =
ConfigFactory.parseString(
s"bitcoin-s.datadir = ${AppConfig.safePathToString(datadirPath)}")
s"bitcoin-s.datadir = ${AppConfig.safePathToString(datadirPath)}"
)
lazy val networkConfig: Config = serverArgs.networkOpt match {
case Some(network) =>
@ -35,9 +37,11 @@ case class DatadirParser(
serverArgs.configOpt match {
case None =>
AppConfig
.getBaseConfig(datadirPath,
.getBaseConfig(
datadirPath,
AppConfig.DEFAULT_BITCOIN_S_CONF_FILE,
Vector(networkConfig))
Vector(networkConfig)
)
.withFallback(datadirConfig)
.resolve()
case Some(config) =>
@ -55,11 +59,8 @@ case class DatadirParser(
lazy val datadir: Path =
Paths.get(baseConfig.getString("bitcoin-s.datadir"))
/** Directory specific for current network or custom dir
* Examples are
* HOME/.bitcoin-s/mainnet
* HOME/.bitcoin-s/testnet3
* HOME/.bitcoin-s/oracle
/** Directory specific for current network or custom dir Examples are
* HOME/.bitcoin-s/mainnet HOME/.bitcoin-s/testnet3 HOME/.bitcoin-s/oracle
*/
def networkDir: Path =
DatadirUtil.getFinalDatadir(datadir, baseConfig, customFinalDirOpt)

View File

@ -30,15 +30,15 @@ object DatadirUtil {
}
}
/** Sets the final datadir for our applicatoin.
* We allow useres to pass in a --datadir command line
* flag that needs to be used instead of the [[datadir]]
* specified in bitcoin-s.conf
/** Sets the final datadir for our applicatoin. We allow useres to pass in a
* --datadir command line flag that needs to be used instead of the
* [[datadir]] specified in bitcoin-s.conf
*/
def getFinalDatadir(
datadir: Path,
baseConfig: Config,
customFinalDirOpt: Option[String] = None): Path = {
customFinalDirOpt: Option[String] = None
): Path = {
// $HOME is not set for windows, need to manually set it
if (Properties.isWin) {

View File

@ -46,8 +46,8 @@ trait NativeProcessFactory extends BitcoinSLogger {
/** Stops the binary by destroying the underlying operating system process
*
* If the client is a remote client (not started on the host operating system)
* this method is a no-op
* If the client is a remote client (not started on the host operating
* system) this method is a no-op
*/
def stopBinary(): Future[Unit] = FutureUtil.makeAsync { () =>
processOpt match {

View File

@ -7,8 +7,9 @@ import org.bitcoins.core.config._
import java.nio.file.{Path, Paths}
import scala.util.Properties
/** Parses arguments passed to the bitcoin-s app server as command line arguments
* This does NOT consider things that exist in reference.conf or application.conf files
/** Parses arguments passed to the bitcoin-s app server as command line
* arguments This does NOT consider things that exist in reference.conf or
* application.conf files
*/
case class ServerArgParser(commandLineArgs: Vector[String]) {
@ -91,9 +92,9 @@ case class ServerArgParser(commandLineArgs: Vector[String]) {
}
}
/** Converts the given command line args into a Config object.
* There is one exclusion to this, we cannot write the --conf
* flag to the config file as that is self referential
/** Converts the given command line args into a Config object. There is one
* exclusion to this, we cannot write the --conf flag to the config file as
* that is self referential
*/
def toConfig: Config = {
val rpcPortString = rpcPortOpt match {

View File

@ -18,7 +18,8 @@ object Cli extends App {
} catch {
case _: ConnectException =>
printerr(
"Connection refused! Check that the server is running and configured correctly.")
"Connection refused! Check that the server is running and configured correctly."
)
sys.exit(1)
}
}

View File

@ -103,8 +103,8 @@ object CliReaders {
EnumEventDescriptorV0TLV.fromHex
}
implicit val digitDecompEventDescriptorReads: Read[
DigitDecompositionEventDescriptorV0TLV] =
implicit val digitDecompEventDescriptorReads
: Read[DigitDecompositionEventDescriptorV0TLV] =
new Read[DigitDecompositionEventDescriptorV0TLV] {
override def arity: Int = 1
@ -124,7 +124,8 @@ object CliReaders {
override def arity: Int = 1
override def reads: String => ContractDescriptorTLV = { str =>
upickle.default.read[ContractDescriptorV0TLV](str)(
Picklers.contractDescriptorV0)
Picklers.contractDescriptorV0
)
}
}
}
@ -239,14 +240,16 @@ object CliReaders {
val reads: String => BlockStamp = {
case dateRe(year, month, day) =>
val time = ZonedDateTime.of(year.toInt,
val time = ZonedDateTime.of(
year.toInt,
month.toInt,
day.toInt,
0,
0,
0,
0,
ZoneId.of("UTC"))
ZoneId.of("UTC")
)
BlockTime(time)
case str => BlockStamp.fromString(str)
}
@ -310,8 +313,8 @@ object CliReaders {
val reads: String => Sha256DigestBE = Sha256DigestBE.fromHex
}
implicit val lockUnspentOutputParametersReads: Read[
Vector[LockUnspentOutputParameter]] =
implicit val lockUnspentOutputParametersReads
: Read[Vector[LockUnspentOutputParameter]] =
new Read[Vector[LockUnspentOutputParameter]] {
override val arity: Int = 1

File diff suppressed because it is too large Load Diff

View File

@ -74,7 +74,8 @@ class OracleRoutesSpec
Get() ~> route ~> check {
assert(contentType == `application/json`)
assert(
responseAs[String] == s"""{"result":"${key.hex}","error":null}""")
responseAs[String] == s"""{"result":"${key.hex}","error":null}"""
)
}
}
@ -90,7 +91,8 @@ class OracleRoutesSpec
Get() ~> route ~> check {
assert(contentType == `application/json`)
assert(
responseAs[String] == s"""{"result":"$testAddress","error":null}""")
responseAs[String] == s"""{"result":"$testAddress","error":null}"""
)
}
}
@ -105,7 +107,10 @@ class OracleRoutesSpec
Get() ~> route ~> check {
assert(contentType == `application/json`)
assert(
responseAs[String] == s"""{"result":["${dummyOracleEvent.eventName}"],"error":null}""")
responseAs[
String
] == s"""{"result":["${dummyOracleEvent.eventName}"],"error":null}"""
)
}
}
@ -117,7 +122,8 @@ class OracleRoutesSpec
.returning(Future.successful(Some(dummyOracleEvent)))
val route = oracleRoutes.handleCommand(
ServerCommand("getannouncement", Arr(eventName)))
ServerCommand("getannouncement", Arr(eventName))
)
val expected =
s"""
@ -160,15 +166,19 @@ class OracleRoutesSpec
val route =
oracleRoutes.handleCommand(
ServerCommand("createenumannouncement",
Arr(Str("id"),
Str("2021-02-04T00:00:00Z"),
Arr(Str("1"), Str("2")))))
ServerCommand(
"createenumannouncement",
Arr(Str("id"), Str("2021-02-04T00:00:00Z"), Arr(Str("1"), Str("2")))
)
)
Post() ~> route ~> check {
assert(contentType == `application/json`)
assert(responseAs[
String] == s"""{"result":"${OracleAnnouncementV0TLV.dummy.hex}","error":null}""")
assert(
responseAs[
String
] == s"""{"result":"${OracleAnnouncementV0TLV.dummy.hex}","error":null}"""
)
}
}
@ -182,118 +192,159 @@ class OracleRoutesSpec
oracleRoutes.handleCommand(
ServerCommand(
"createenumannouncement",
Arr(Str("id"), Str("2021-02-04"), Arr(Str("1"), Str("2")))))
Arr(Str("id"), Str("2021-02-04"), Arr(Str("1"), Str("2")))
)
)
Post() ~> route ~> check {
assert(contentType == `application/json`)
assert(responseAs[
String] == s"""{"result":"${OracleAnnouncementV0TLV.dummy.hex}","error":null}""")
assert(
responseAs[
String
] == s"""{"result":"${OracleAnnouncementV0TLV.dummy.hex}","error":null}"""
)
}
}
"create numeric announcement" in {
(mockOracleApi
.createNewDigitDecompAnnouncement(_: String,
.createNewDigitDecompAnnouncement(
_: String,
_: Instant,
_: UInt16,
_: Boolean,
_: Int,
_: String,
_: Int32))
.expects("id",
_: Int32
))
.expects(
"id",
Instant.ofEpochSecond(1612396800),
UInt16(2),
false,
17,
"units",
Int32.zero)
Int32.zero
)
.returning(Future.successful(OracleAnnouncementV0TLV.dummy))
val route =
oracleRoutes.handleCommand(
ServerCommand("createnumericannouncement",
Arr(Str("id"),
ServerCommand(
"createnumericannouncement",
Arr(
Str("id"),
Str("2021-02-04T00:00:00Z"),
Num(0),
Num(131000),
Str("units"),
Num(0))))
Num(0)
)
)
)
Post() ~> route ~> check {
assert(contentType == `application/json`)
assert(responseAs[
String] == s"""{"result":"${OracleAnnouncementV0TLV.dummy.hex}","error":null}""")
assert(
responseAs[
String
] == s"""{"result":"${OracleAnnouncementV0TLV.dummy.hex}","error":null}"""
)
}
}
"create numeric announcement with just date" in {
(mockOracleApi
.createNewDigitDecompAnnouncement(_: String,
.createNewDigitDecompAnnouncement(
_: String,
_: Instant,
_: UInt16,
_: Boolean,
_: Int,
_: String,
_: Int32))
.expects("id",
_: Int32
))
.expects(
"id",
Instant.ofEpochSecond(1612396800),
UInt16(2),
true,
17,
"units",
Int32.zero)
Int32.zero
)
.returning(Future.successful(OracleAnnouncementV0TLV.dummy))
val route =
oracleRoutes.handleCommand(
ServerCommand("createnumericannouncement",
Arr(Str("id"),
ServerCommand(
"createnumericannouncement",
Arr(
Str("id"),
Str("2021-02-04"),
Num(-1),
Num(131000),
Str("units"),
Num(0))))
Num(0)
)
)
)
Post() ~> route ~> check {
assert(contentType == `application/json`)
assert(responseAs[
String] == s"""{"result":"${OracleAnnouncementV0TLV.dummy.hex}","error":null}""")
assert(
responseAs[
String
] == s"""{"result":"${OracleAnnouncementV0TLV.dummy.hex}","error":null}"""
)
}
}
"create digit decomp announcement" in {
(mockOracleApi
.createNewDigitDecompAnnouncement(_: String,
.createNewDigitDecompAnnouncement(
_: String,
_: Instant,
_: UInt16,
_: Boolean,
_: Int,
_: String,
_: Int32))
.expects("id",
_: Int32
))
.expects(
"id",
Instant.ofEpochSecond(1612396800),
UInt16(2),
true,
17,
"units",
Int32.zero)
Int32.zero
)
.returning(Future.successful(OracleAnnouncementV0TLV.dummy))
val route =
oracleRoutes.handleCommand(
ServerCommand("createdigitdecompannouncement",
Arr(Str("id"),
ServerCommand(
"createdigitdecompannouncement",
Arr(
Str("id"),
Num(1612396800),
Num(2),
Bool(true),
Num(17),
Str("units"),
Num(0))))
Num(0)
)
)
)
Post() ~> route ~> check {
assert(contentType == `application/json`)
assert(responseAs[
String] == s"""{"result":"${OracleAnnouncementV0TLV.dummy.hex}","error":null}""")
assert(
responseAs[
String
] == s"""{"result":"${OracleAnnouncementV0TLV.dummy.hex}","error":null}"""
)
}
}
@ -305,12 +356,16 @@ class OracleRoutesSpec
val route =
oracleRoutes.handleCommand(
ServerCommand("signenum", Arr(Str("id"), Str("outcome"))))
ServerCommand("signenum", Arr(Str("id"), Str("outcome")))
)
Post() ~> route ~> check {
assert(contentType == `application/json`)
assert(responseAs[
String] == s"""{"result":"${dummyAttestmentTLV.hex}","error":null}""")
assert(
responseAs[
String
] == s"""{"result":"${dummyAttestmentTLV.hex}","error":null}"""
)
}
}
@ -322,12 +377,16 @@ class OracleRoutesSpec
val route =
oracleRoutes.handleCommand(
ServerCommand("signdigits", Arr(Str("id"), Num(123))))
ServerCommand("signdigits", Arr(Str("id"), Num(123)))
)
Post() ~> route ~> check {
assert(contentType == `application/json`)
assert(responseAs[
String] == s"""{"result":"${dummyAttestmentTLV.hex}","error":null}""")
assert(
responseAs[
String
] == s"""{"result":"${dummyAttestmentTLV.hex}","error":null}"""
)
}
}
@ -339,12 +398,16 @@ class OracleRoutesSpec
val route =
oracleRoutes.handleCommand(
ServerCommand("getsignatures", Arr(Str("id"))))
ServerCommand("getsignatures", Arr(Str("id")))
)
Post() ~> route ~> check {
assert(contentType == `application/json`)
assert(responseAs[
String] == s"""{"result":"${dummyAttestmentTLV.hex}","error":null}""")
assert(
responseAs[
String
] == s"""{"result":"${dummyAttestmentTLV.hex}","error":null}"""
)
}
}
@ -356,12 +419,14 @@ class OracleRoutesSpec
val route =
oracleRoutes.handleCommand(
ServerCommand("signmessage", Arr(Str("message"))))
ServerCommand("signmessage", Arr(Str("message")))
)
Post() ~> route ~> check {
assert(contentType == `application/json`)
assert(
responseAs[String] == s"""{"result":"${sig.hex}","error":null}""")
responseAs[String] == s"""{"result":"${sig.hex}","error":null}"""
)
}
}
@ -376,8 +441,11 @@ class OracleRoutesSpec
val route = oracleRoutes.handleCommand(cmd)
Post() ~> route ~> check {
assert(contentType == `application/json`)
assert(responseAs[
String] == s"""{"result":"${dummyOracleEvent.announcementTLV.hex}","error":null}""")
assert(
responseAs[
String
] == s"""{"result":"${dummyOracleEvent.announcementTLV.hex}","error":null}"""
)
}
}
@ -393,8 +461,11 @@ class OracleRoutesSpec
Post() ~> route ~> check {
assert(contentType == `application/json`)
assert(responseAs[
String] == s"""{"result":"${dummyOracleEvent.announcementTLV.hex}","error":null}""")
assert(
responseAs[
String
] == s"""{"result":"${dummyOracleEvent.announcementTLV.hex}","error":null}"""
)
}
}
@ -409,7 +480,8 @@ class OracleRoutesSpec
Post() ~> route ~> check {
assert(contentType == `application/json`)
assert(
responseAs[String] == s"""{"result":"oracle name","error":null}""")
responseAs[String] == s"""{"result":"oracle name","error":null}"""
)
}
}
@ -421,12 +493,14 @@ class OracleRoutesSpec
val route =
oracleRoutes.handleCommand(
ServerCommand("setoraclename", Arr(Str("oracle name"))))
ServerCommand("setoraclename", Arr(Str("oracle name")))
)
Post() ~> route ~> check {
assert(contentType == `application/json`)
assert(
responseAs[String] == s"""{"result":"oracle name","error":null}""")
responseAs[String] == s"""{"result":"oracle name","error":null}"""
)
}
}
}

View File

@ -19,8 +19,8 @@ import scala.util.{Failure, Success}
case class OracleRoutes(oracle: DLCOracleApi)(implicit
system: ActorSystem,
conf: DLCOracleAppConfig)
extends ServerRoute {
conf: DLCOracleAppConfig
) extends ServerRoute {
import system.dispatcher
override def handleCommand: PartialFunction[ServerCommand, StandardRoute] = {
@ -71,12 +71,15 @@ case class OracleRoutes(oracle: DLCOracleApi)(implicit
case Failure(exception) =>
reject(ValidationRejection("failure", Some(exception)))
case Success(
CreateNumericAnnouncement(eventName,
CreateNumericAnnouncement(
eventName,
maturationTime,
minValue,
maxValue,
unit,
precision)) =>
precision
)
) =>
complete {
val isSigned = minValue < 0
@ -84,13 +87,15 @@ case class OracleRoutes(oracle: DLCOracleApi)(implicit
Math.ceil(Math.log(maxValue.toDouble) / Math.log(2)).toInt
oracle
.createNewDigitDecompAnnouncement(eventName,
.createNewDigitDecompAnnouncement(
eventName,
maturationTime,
UInt16(2),
isSigned,
numDigits,
unit,
Int32(precision))
Int32(precision)
)
.map { announcementTLV =>
Server.httpSuccess(announcementTLV.hex)
}
@ -105,22 +110,27 @@ case class OracleRoutes(oracle: DLCOracleApi)(implicit
case Failure(exception) =>
reject(ValidationRejection("failure", Some(exception)))
case Success(
CreateDigitDecompAnnouncement(eventName,
CreateDigitDecompAnnouncement(
eventName,
maturationTime,
base,
isSigned,
numDigits,
unit,
precision)) =>
precision
)
) =>
complete {
oracle
.createNewDigitDecompAnnouncement(eventName,
.createNewDigitDecompAnnouncement(
eventName,
maturationTime,
UInt16(base),
isSigned,
numDigits,
unit,
Int32(precision))
Int32(precision)
)
.map { announcementTLV =>
Server.httpSuccess(announcementTLV.hex)
}
@ -179,9 +189,11 @@ case class OracleRoutes(oracle: DLCOracleApi)(implicit
"signingVersion" -> Str(event.signingVersion.toString),
"maturationTime" -> Str(event.maturationTime.toString),
"maturationTimeEpoch" -> Num(
event.maturationTime.getEpochSecond.toDouble),
event.maturationTime.getEpochSecond.toDouble
),
"announcementSignature" -> Str(
event.announcementSignature.hex),
event.announcementSignature.hex
),
"eventDescriptorTLV" -> Str(event.eventDescriptorTLV.hex),
"eventTLV" -> Str(event.eventTLV.hex),
"announcementTLV" -> Str(event.announcementTLV.hex),
@ -269,9 +281,11 @@ case class OracleRoutes(oracle: DLCOracleApi)(implicit
case Success(KeyManagerPassphraseChange(oldPassword, newPassword)) =>
complete {
val path = conf.seedPath
WalletStorage.changeAesPassword(path,
WalletStorage.changeAesPassword(
path,
Some(oldPassword),
Some(newPassword))
Some(newPassword)
)
Server.httpSuccess(ujson.Null)
}

View File

@ -13,8 +13,8 @@ import scala.concurrent.{Await, Future}
class OracleServerMain(override val serverArgParser: ServerArgParser)(implicit
override val system: ActorSystem,
conf: DLCOracleAppConfig)
extends BitcoinSServerRunner[Unit] {
conf: DLCOracleAppConfig
) extends BitcoinSServerRunner[Unit] {
override def start(): Future[Unit] = {
@ -31,21 +31,25 @@ class OracleServerMain(override val serverArgParser: ServerArgParser)(implicit
routes = Seq(OracleRoutes(oracle), commonRoutes).map(Future.successful)
server = serverArgParser.rpcPortOpt match {
case Some(rpcport) =>
Server(conf = conf,
Server(
conf = conf,
handlersF = routes,
rpcbindOpt = bindConfOpt,
rpcport = rpcport,
rpcPassword = conf.rpcPassword,
None,
Source.empty)
Source.empty
)
case None =>
Server(conf = conf,
Server(
conf = conf,
handlersF = routes,
rpcbindOpt = bindConfOpt,
rpcport = conf.rpcPort,
rpcPassword = conf.rpcPassword,
None,
Source.empty)
Source.empty
)
}
bindings <- server.start()
@ -88,14 +92,16 @@ object OracleServerMain extends BitcoinSAppScalaDaemon {
implicit lazy val conf: DLCOracleAppConfig =
DLCOracleAppConfig(datadirParser.datadir, Vector(datadirParser.baseConfig))(
system.dispatcher)
system.dispatcher
)
val m = new OracleServerMain(serverCmdLineArgs)
m.run()
sys.addShutdownHook {
logger.info(
s"@@@@@@@@@@@@@@@@@@@@@ Shutting down ${getClass.getSimpleName} @@@@@@@@@@@@@@@@@@@@@")
s"@@@@@@@@@@@@@@@@@@@@@ Shutting down ${getClass.getSimpleName} @@@@@@@@@@@@@@@@@@@@@"
)
Await.result(m.stop(), 10.seconds)
}
}

View File

@ -30,7 +30,9 @@ object SignMessage extends ServerJsonModels {
case other =>
Failure(
new IllegalArgumentException(
s"Bad number of arguments: ${other.length}. Expected: 1"))
s"Bad number of arguments: ${other.length}. Expected: 1"
)
)
}
}
}
@ -38,7 +40,8 @@ object SignMessage extends ServerJsonModels {
case class CreateAnnouncement(
label: String,
maturationTime: Instant,
outcomes: Vector[String])
outcomes: Vector[String]
)
object CreateAnnouncement extends ServerJsonModels {
@ -54,11 +57,14 @@ object CreateAnnouncement extends ServerJsonModels {
}
case Nil =>
Failure(
new IllegalArgumentException("Missing label and outcome arguments"))
new IllegalArgumentException("Missing label and outcome arguments")
)
case other =>
Failure(
new IllegalArgumentException(
s"Bad number of arguments: ${other.length}. Expected: 2"))
s"Bad number of arguments: ${other.length}. Expected: 2"
)
)
}
}
}
@ -69,7 +75,8 @@ case class CreateNumericAnnouncement(
minValue: Long,
maxValue: Long,
unit: String,
precision: Int)
precision: Int
)
object CreateNumericAnnouncement extends ServerJsonModels {
@ -84,20 +91,27 @@ object CreateNumericAnnouncement extends ServerJsonModels {
val unit = unitJs.str
val precision = precisionJs.num.toInt
CreateNumericAnnouncement(label,
CreateNumericAnnouncement(
label,
maturationTime,
minValue,
maxValue,
unit,
precision)
precision
)
}
case Nil =>
Failure(new IllegalArgumentException(
"Missing label, maturationTime, minValue, maxValue, units, and precision arguments"))
Failure(
new IllegalArgumentException(
"Missing label, maturationTime, minValue, maxValue, units, and precision arguments"
)
)
case other =>
Failure(
new IllegalArgumentException(
s"Bad number of arguments: ${other.length}. Expected: 6"))
s"Bad number of arguments: ${other.length}. Expected: 6"
)
)
}
}
}
@ -109,7 +123,8 @@ case class CreateDigitDecompAnnouncement(
isSigned: Boolean,
numDigits: Int,
unit: String,
precision: Int)
precision: Int
)
object CreateDigitDecompAnnouncement extends ServerJsonModels {
@ -126,21 +141,28 @@ object CreateDigitDecompAnnouncement extends ServerJsonModels {
val unit = unitJs.str
val precision = precisionJs.num.toInt
CreateDigitDecompAnnouncement(label,
CreateDigitDecompAnnouncement(
label,
maturationTime,
base,
isSigned,
numDigits,
unit,
precision)
precision
)
}
case Nil =>
Failure(new IllegalArgumentException(
"Missing label, maturationTime, base, isSigned, numDigits, units, and precision arguments"))
Failure(
new IllegalArgumentException(
"Missing label, maturationTime, base, isSigned, numDigits, units, and precision arguments"
)
)
case other =>
Failure(
new IllegalArgumentException(
s"Bad number of arguments: ${other.length}. Expected: 7"))
s"Bad number of arguments: ${other.length}. Expected: 7"
)
)
}
}
}
@ -160,11 +182,15 @@ object SignEnum extends ServerJsonModels {
case Nil =>
Failure(
new IllegalArgumentException(
"Missing oracle event tlv and outcome arguments"))
"Missing oracle event tlv and outcome arguments"
)
)
case other =>
Failure(
new IllegalArgumentException(
s"Bad number of arguments: ${other.length}. Expected: 2"))
s"Bad number of arguments: ${other.length}. Expected: 2"
)
)
}
}
}
@ -182,7 +208,8 @@ object SignDigits extends ServerJsonModels {
case str: Str => str.value.toDouble
case _: Value =>
throw new IllegalArgumentException(
s"Unable to parse $numJs as a number")
s"Unable to parse $numJs as a number"
)
}
SignDigits(nameJs.str, num.toLong)
@ -190,11 +217,15 @@ object SignDigits extends ServerJsonModels {
case Nil =>
Failure(
new IllegalArgumentException(
"Missing oracle event tlv and num arguments"))
"Missing oracle event tlv and num arguments"
)
)
case other =>
Failure(
new IllegalArgumentException(
s"Bad number of arguments: ${other.length}. Expected: 2"))
s"Bad number of arguments: ${other.length}. Expected: 2"
)
)
}
}
}
@ -204,8 +235,10 @@ case class GetAnnouncement(eventName: String)
object GetAnnouncement extends ServerJsonModels {
def fromJsArr(jsArr: ujson.Arr): Try[GetAnnouncement] = {
require(jsArr.arr.size == 1,
s"Bad number of arguments: ${jsArr.arr.size}. Expected: 1")
require(
jsArr.arr.size == 1,
s"Bad number of arguments: ${jsArr.arr.size}. Expected: 1"
)
Try {
GetAnnouncement(jsArr.arr.head.str)
}
@ -214,7 +247,8 @@ object GetAnnouncement extends ServerJsonModels {
case class KeyManagerPassphraseChange(
oldPassword: AesPassword,
newPassword: AesPassword)
newPassword: AesPassword
)
object KeyManagerPassphraseChange extends ServerJsonModels {
@ -230,11 +264,15 @@ object KeyManagerPassphraseChange extends ServerJsonModels {
case Nil =>
Failure(
new IllegalArgumentException(
"Missing old password and new password arguments"))
"Missing old password and new password arguments"
)
)
case other =>
Failure(
new IllegalArgumentException(
s"Bad number of arguments: ${other.length}. Expected: 2"))
s"Bad number of arguments: ${other.length}. Expected: 2"
)
)
}
}
}
@ -256,7 +294,9 @@ object KeyManagerPassphraseSet extends ServerJsonModels {
case other =>
Failure(
new IllegalArgumentException(
s"Bad number of arguments: ${other.length}. Expected: 1"))
s"Bad number of arguments: ${other.length}. Expected: 1"
)
)
}
}
}
@ -276,8 +316,11 @@ object DeleteAnnouncement
case Vector() =>
Failure(new IllegalArgumentException(s"Missing event name argument"))
case other =>
Failure(new IllegalArgumentException(
s"Bad number of arguments to deleteannouncement, got=${other.length} expected: 1"))
Failure(
new IllegalArgumentException(
s"Bad number of arguments to deleteannouncement, got=${other.length} expected: 1"
)
)
}
}
@ -303,7 +346,9 @@ object DeleteAttestation
case other =>
Failure(
new IllegalArgumentException(
s"Bad number of arguments: ${other.length}. Expected: 1"))
s"Bad number of arguments: ${other.length}. Expected: 1"
)
)
}
}
@ -341,7 +386,8 @@ trait ServerJsonModels {
LockUnspentOutputParameter.fromJson(js)
def jsToLockUnspentOutputParameters(
js: Value): Seq[LockUnspentOutputParameter] = {
js: Value
): Seq[LockUnspentOutputParameter] = {
js.arr.foldLeft(Seq.empty[LockUnspentOutputParameter])((seq, outPoint) =>
seq :+ jsToLockUnspentOutputParameter(outPoint))
}

View File

@ -16,15 +16,16 @@ import org.bitcoins.server.util.BitcoinSAppScalaDaemon
import java.time.Instant
import scala.concurrent.Future
/** Useful script for scanning bitcoind
* This file assumes you have pre-configured the connection
* between bitcoin-s and bitcoind inside of bitcoin-s.conf
* @see https://bitcoin-s.org/docs/config/configuration#example-configuration-file
/** Useful script for scanning bitcoind This file assumes you have
* pre-configured the connection between bitcoin-s and bitcoind inside of
* bitcoin-s.conf
* @see
* https://bitcoin-s.org/docs/config/configuration#example-configuration-file
*/
class ScanBitcoind()(implicit
override val system: ActorSystem,
rpcAppConfig: BitcoindRpcAppConfig)
extends BitcoinSRunner[Unit] {
rpcAppConfig: BitcoindRpcAppConfig
) extends BitcoinSRunner[Unit] {
override def start(): Future[Unit] = {
@ -50,13 +51,15 @@ class ScanBitcoind()(implicit
.map(_ => ())
}
/** Searches a given Source[Int] that represents block heights applying f to them and returning a Seq[T] with the results */
/** Searches a given Source[Int] that represents block heights applying f to
* them and returning a Seq[T] with the results
*/
def searchBlocks[T](
bitcoind: BitcoindRpcClient,
source: Source[Int, NotUsed],
f: Block => T,
numParallelism: Int = Runtime.getRuntime.availableProcessors()): Future[
Seq[T]] = {
numParallelism: Int = Runtime.getRuntime.availableProcessors()
): Future[Seq[T]] = {
source
.mapAsync(parallelism = numParallelism) { height =>
bitcoind
@ -66,7 +69,8 @@ class ScanBitcoind()(implicit
}
.mapAsync(numParallelism) { case (block, height) =>
logger.info(
s"Searching block at height=$height hashBE=${block.blockHeader.hashBE.hex}")
s"Searching block at height=$height hashBE=${block.blockHeader.hashBE.hex}"
)
FutureUtil.makeAsync { () =>
f(block)
}
@ -78,7 +82,8 @@ class ScanBitcoind()(implicit
def countSegwitTxs(
bitcoind: BitcoindRpcClient,
startHeight: Int,
endHeight: Int): Future[Unit] = {
endHeight: Int
): Future[Unit] = {
val startTime = System.currentTimeMillis()
val source: Source[Int, NotUsed] = Source(startHeight.to(endHeight))
@ -96,14 +101,16 @@ class ScanBitcoind()(implicit
count <- countF
endTime = System.currentTimeMillis()
_ = logger.info(
s"Count of segwit txs from height=${startHeight} to endHeight=${endHeight} is ${count}. It took ${endTime - startTime}ms ")
s"Count of segwit txs from height=${startHeight} to endHeight=${endHeight} is ${count}. It took ${endTime - startTime}ms "
)
} yield ()
}
def countTaprootTxsInBlocks(
endHeight: Int,
lastBlocks: Int,
bitcoind: BitcoindRpcClient): Future[Int] = {
bitcoind: BitcoindRpcClient
): Future[Int] = {
val startTime = System.currentTimeMillis()
val startHeight = endHeight - lastBlocks
val source: Source[Int, NotUsed] = Source(startHeight.to(endHeight))
@ -124,7 +131,8 @@ class ScanBitcoind()(implicit
count <- countF
endTime = System.currentTimeMillis()
_ = logger.info(
s"Count of taproot outputs from height=${startHeight} to endHeight=${endHeight} is ${count}. It took ${endTime - startTime}ms ")
s"Count of taproot outputs from height=${startHeight} to endHeight=${endHeight} is ${count}. It took ${endTime - startTime}ms "
)
} yield count
}
@ -135,12 +143,14 @@ class ScanBitcoind()(implicit
})
countF.foreach(c =>
logger.info(
s"Found $c mempool transactions with witness v1 outputs at ${Instant.now}"))
s"Found $c mempool transactions with witness v1 outputs at ${Instant.now}"
))
countF
}
def getMemPoolSource(
bitcoind: BitcoindRpcClient): Future[Source[Transaction, NotUsed]] = {
bitcoind: BitcoindRpcClient
): Future[Source[Transaction, NotUsed]] = {
val mempoolF = bitcoind.getRawMemPool
val sourceF: Future[Source[DoubleSha256DigestBE, NotUsed]] =
mempoolF.map(Source(_))

View File

@ -10,11 +10,13 @@ import org.bitcoins.server.util.BitcoinSAppScalaDaemon
import java.nio.file.Paths
import scala.concurrent.Future
/** This script zips your $HOME/.bitcoin-s/ directory to a specified path, excluding chaindb.sqlite */
/** This script zips your $HOME/.bitcoin-s/ directory to a specified path,
* excluding chaindb.sqlite
*/
class ZipDatadir(override val serverArgParser: ServerArgParser)(implicit
override val system: ActorSystem,
conf: BitcoinSAppConfig)
extends BitcoinSServerRunner[Unit] {
conf: BitcoinSAppConfig
) extends BitcoinSServerRunner[Unit] {
override def start(): Future[Unit] = {
@ -46,7 +48,8 @@ object Zip extends BitcoinSAppScalaDaemon {
implicit lazy val conf: BitcoinSAppConfig =
BitcoinSAppConfig(datadirParser.datadir, Vector(datadirParser.baseConfig))(
system)
system
)
new ZipDatadir(serverCmdLineArgs).run()
}

View File

@ -31,11 +31,13 @@ import upickle.default.{read, write, Reader, Writer}
import scala.collection.immutable.Seq
/** Automatic to and from JSON marshalling/unmarshalling using *upickle* protocol.
/** Automatic to and from JSON marshalling/unmarshalling using *upickle*
* protocol.
*/
object UpickleSupport extends UpickleSupport
/** Automatic to and from JSON marshalling/unmarshalling using *upickle* protocol.
/** Automatic to and from JSON marshalling/unmarshalling using *upickle*
* protocol.
*/
trait UpickleSupport {
@ -58,16 +60,20 @@ trait UpickleSupport {
/** HTTP entity => `A`
*
* @tparam A type to decode
* @return unmarshaller for `A`
* @tparam A
* type to decode
* @return
* unmarshaller for `A`
*/
implicit def unmarshaller[A: Reader]: FromEntityUnmarshaller[A] =
jsonStringUnmarshaller.map(read(_))
/** `A` => HTTP entity
*
* @tparam A type to encode
* @return marshaller for any `A` value
* @tparam A
* type to encode
* @return
* marshaller for any `A` value
*/
implicit def marshaller[A: Writer]: ToEntityMarshaller[A] =
jsonStringMarshaller.compose(write(_))

View File

@ -22,7 +22,8 @@ trait BitcoinSRunner[T] extends StartStopAsync[T] with BitcoinSLogger {
// System.setProperty("bitcoins.log.location", usedDir.toAbsolutePath.toString)
logger.info(
s"version=${EnvUtil.getVersion} jdkVersion=${EnvUtil.getJdkVersion}")
s"version=${EnvUtil.getVersion} jdkVersion=${EnvUtil.getJdkVersion}"
)
// logger.info(s"using directory ${usedDir.toAbsolutePath.toString}")
val runner: Future[T] = start()

View File

@ -8,16 +8,19 @@ import org.apache.pekko.http.scaladsl.server.Directives._
import org.apache.pekko.http.scaladsl.server.Route
/** Add CORS handling for accessing backend over localhost from modern browsers
* @see dzone.com/articles/handling-cors-in-akka-http
* @see
* dzone.com/articles/handling-cors-in-akka-http
*/
trait CORSHandler {
private val corsResponseHeaders = List(
`Access-Control-Allow-Origin`.*,
`Access-Control-Allow-Credentials`(true),
`Access-Control-Allow-Headers`("Authorization",
`Access-Control-Allow-Headers`(
"Authorization",
"Content-Type",
"X-Requested-With")
"X-Requested-With"
)
)
// this directive adds access control headers to normal responses
@ -29,7 +32,9 @@ trait CORSHandler {
private def preflightRequestHandler: Route = options {
complete(
HttpResponse(StatusCodes.OK).withHeaders(
`Access-Control-Allow-Methods`(OPTIONS, POST, PUT, GET, DELETE)))
`Access-Control-Allow-Methods`(OPTIONS, POST, PUT, GET, DELETE)
)
)
}
// Wrap the Route with this method to enable adding of CORS headers

View File

@ -51,7 +51,9 @@ object ZipDataDir {
case other =>
Failure(
new IllegalArgumentException(
s"Bad number of arguments: ${other.length}. Expected: 1"))
s"Bad number of arguments: ${other.length}. Expected: 1"
)
)
}
}
}

View File

@ -1,8 +1,7 @@
package org.bitcoins.server.routes
/** HTTP errors our server knows how to handle.
* These gets picked up by the exceptions handler
* in Main
/** HTTP errors our server knows how to handle. These gets picked up by the
* exceptions handler in Main
*/
sealed abstract class HttpError extends Error

View File

@ -52,7 +52,8 @@ case class Server(
rpcport: Int,
rpcPassword: String,
wsConfigOpt: Option[WsServerConfig],
wsSource: Source[WsNotification[_], NotUsed])(implicit system: ActorSystem)
wsSource: Source[WsNotification[_], NotUsed]
)(implicit system: ActorSystem)
extends HttpLogger {
import system.dispatcher
@ -61,8 +62,10 @@ case class Server(
if (rpchost == "localhost" || rpchost == "127.0.0.1") {
logger.warn(s"RPC password is not set (rpchost=$rpchost)")
} else {
require(rpcPassword.nonEmpty,
s"RPC password must be set (rpchost=$rpchost)")
require(
rpcPassword.nonEmpty,
s"RPC password must be set (rpchost=$rpchost)"
)
}
}
@ -81,7 +84,8 @@ case class Server(
complete {
Server.httpError(
"""Resource not found. Hint: all RPC calls are made against root ('/')""",
StatusCodes.BadRequest)
StatusCodes.BadRequest
)
}
}
.result()
@ -89,8 +93,11 @@ case class Server(
val exceptionHandler = ExceptionHandler {
case HttpError.MethodNotFound(method) =>
complete(
Server.httpError(s"'$method' is not a valid method",
StatusCodes.BadRequest))
Server.httpError(
s"'$method' is not a valid method",
StatusCodes.BadRequest
)
)
case err: Throwable =>
logger.error(s"Unhandled error in server:", err)
complete(Server.httpError(err.getMessage))
@ -173,7 +180,8 @@ case class Server(
}
DebuggingDirectives.logRequestResult(
("http-rpc-server", Logging.DebugLevel)) {
("http-rpc-server", Logging.DebugLevel)
) {
authenticatedRoute
}
}
@ -250,7 +258,8 @@ object Server extends BitcoinSLogger {
// TODO id parameter
case class Response(
result: Option[ujson.Value] = None,
error: Option[String] = None) {
error: Option[String] = None
) {
def toJsonMap: Map[String, ujson.Value] = {
Map(
@ -267,8 +276,9 @@ object Server extends BitcoinSLogger {
}
/** Creates a HTTP response with the given body as a JSON response */
def httpSuccess[T](body: T)(implicit
writer: up.Writer[T]): HttpEntity.Strict = {
def httpSuccess[T](
body: T
)(implicit writer: up.Writer[T]): HttpEntity.Strict = {
val response = Response(result = Some(up.writeJs(body)))
HttpEntity(
ContentTypes.`application/json`,
@ -276,8 +286,9 @@ object Server extends BitcoinSLogger {
)
}
def httpSuccessOption[T](bodyOpt: Option[T])(implicit
writer: up.Writer[T]): HttpEntity.Strict = {
def httpSuccessOption[T](
bodyOpt: Option[T]
)(implicit writer: up.Writer[T]): HttpEntity.Strict = {
val response = Response(result = bodyOpt.map(body => up.writeJs(body)))
HttpEntity(
ContentTypes.`application/json`,

View File

@ -2,13 +2,17 @@ package org.bitcoins.server.util
import scala.concurrent.Future
/** A trait used to indicated when different parts of [[BitcoinSAppConfig]] are started */
/** A trait used to indicated when different parts of [[BitcoinSAppConfig]] are
* started
*/
sealed trait AppConfigMarker
/** This class represents when BitcoinSAppConfig modules are started
* @param torStartedF this future is completed when all tor dependent modules are fully started
* @param torStartedF
* this future is completed when all tor dependent modules are fully started
* the reason this is needed is because tor startup time is so variable
* @see https://github.com/bitcoin-s/bitcoin-s/issues/4210
* @see
* https://github.com/bitcoin-s/bitcoin-s/issues/4210
*/
case class StartedBitcoinSAppConfig(torStartedF: Future[Unit])
extends AppConfigMarker

View File

@ -10,7 +10,9 @@ trait BitcoinSApp {
def commandLineArgs: Array[String]
/** Useful for projects like the oracle server to specify a custom directory inside of ~./bitcoin-s */
/** Useful for projects like the oracle server to specify a custom directory
* inside of ~./bitcoin-s
*/
def customFinalDirOpt: Option[String]
}

View File

@ -8,8 +8,8 @@ import scala.concurrent.{ExecutionContext, Future}
case class ServerBindings(
httpServer: Http.ServerBinding,
webSocketServerOpt: Option[Http.ServerBinding])
extends BitcoinSLogger {
webSocketServerOpt: Option[Http.ServerBinding]
) extends BitcoinSLogger {
private val terminateTimeout = 5.seconds

View File

@ -27,8 +27,10 @@ class BitcoinSServerMainBitcoindTest
config: BitcoinSAppConfig =>
val server = new BitcoinSServerMain(ServerArgParser.empty)(system, config)
val cliConfig = Config(rpcPortOpt = Some(config.rpcPort),
rpcPassword = config.rpcPassword)
val cliConfig = Config(
rpcPortOpt = Some(config.rpcPort),
rpcPassword = config.rpcPassword
)
for {
_ <- server.start()
@ -54,8 +56,10 @@ class BitcoinSServerMainBitcoindTest
val server = new BitcoinSServerMain(ServerArgParser.empty)(system, config)
val cliConfig = Config(rpcPortOpt = Some(config.rpcPort),
rpcPassword = config.rpcPassword)
val cliConfig = Config(
rpcPortOpt = Some(config.rpcPort),
rpcPassword = config.rpcPassword
)
val mnemonic =
MnemonicCode.fromEntropy(ECPrivateKey.freshPrivateKey.bytes.toBitVector)
@ -136,7 +140,8 @@ class BitcoinSServerMainBitcoindTest
assert(infoT.isFailure)
assert(
infoT.failed.get.getMessage
.contains("The supplied authentication is invalid"))
.contains("The supplied authentication is invalid")
)
}
failF

View File

@ -21,8 +21,10 @@ class BitcoinSServerMainBitcoindTorTest
config: BitcoinSAppConfig =>
val server = new BitcoinSServerMain(ServerArgParser.empty)(system, config)
val cliConfig = Config(rpcPortOpt = Some(config.rpcPort),
rpcPassword = config.rpcPassword)
val cliConfig = Config(
rpcPortOpt = Some(config.rpcPort),
rpcPassword = config.rpcPassword
)
for {
_ <- torF

View File

@ -30,7 +30,8 @@ class BitcoindRpcAppConfigTest extends BitcoinSAsyncTest {
assert(withOther.rpcPort == 5555)
val mainnetConf = ConfigFactory.parseString(
s"bitcoin-s.bitcoind-rpc.rpcport = ${MainNet.rpcPort}")
s"bitcoin-s.bitcoind-rpc.rpcport = ${MainNet.rpcPort}"
)
val mainnet = withOther.withOverrides(mainnetConf)
assert(mainnet.rpcPort == MainNet.rpcPort)
}

View File

@ -54,7 +54,8 @@ class CommonRoutesSpec
val route =
commonRoutes.handleCommand(
ServerCommand("zipdatadir", ujson.Arr(target.toString)))
ServerCommand("zipdatadir", ujson.Arr(target.toString))
)
Post() ~> route ~> check {
assert(contentType == ContentTypes.`application/json`)

View File

@ -34,7 +34,8 @@ class DLCRoutesSpec
// https://test.oracle.suredbits.com/announcement/8863cd80e1d37f668e27b84cbfed48541d671b4fed1462b86d547e7f13b5a9e4
val announcement = OracleAnnouncementTLV.fromHex(
"fdd824c3988fabec9820690f366271c9ceac00fbec1412075f9b319bb0db1f86460519dd9c61478949f2c00c35aeb8e53a1507616072cb802891e2c189a9fa65a0493de5d3b04a6d7b90c9c43c09ebe5250d583e1c3fc423219b26f6a02ec394a130000afdd8225f0001ae3e30df5a203ad10ee89a909df0c8ccea4836e94e0a5d34c3cdab758fcaee1460189600fdd8062400030e52657075626c6963616e5f77696e0c44656d6f637261745f77696e056f7468657210323032302d75732d656c656374696f6e")
"fdd824c3988fabec9820690f366271c9ceac00fbec1412075f9b319bb0db1f86460519dd9c61478949f2c00c35aeb8e53a1507616072cb802891e2c189a9fa65a0493de5d3b04a6d7b90c9c43c09ebe5250d583e1c3fc423219b26f6a02ec394a130000afdd8225f0001ae3e30df5a203ad10ee89a909df0c8ccea4836e94e0a5d34c3cdab758fcaee1460189600fdd8062400030e52657075626c6963616e5f77696e0c44656d6f637261745f77696e056f7468657210323032302d75732d656c656374696f6e"
)
// https://test.oracle.suredbits.com/announcement/362ae482860fc93bac5cbcca3f1f0e49b3c94eac92224a008bd81ef81292f43a
val numericAnnouncement = OracleAnnouncementTLV.fromHex(
@ -43,7 +44,8 @@ class DLCRoutesSpec
// https://test.oracle.suredbits.com/contract/enum/5fbc1d037bacd9ece32ff4b591143bce7fa1c22e0aec2fa8437cc336feb95138
val expectedContractInfo = ContractInfo.fromHex(
"fdd82efd01120000000005f5e100fda7103b030e52657075626c6963616e5f77696e00000000000000000c44656d6f637261745f77696e0000000005f5e100056f746865720000000003938700fda712c7fdd824c3988fabec9820690f366271c9ceac00fbec1412075f9b319bb0db1f86460519dd9c61478949f2c00c35aeb8e53a1507616072cb802891e2c189a9fa65a0493de5d3b04a6d7b90c9c43c09ebe5250d583e1c3fc423219b26f6a02ec394a130000afdd8225f0001ae3e30df5a203ad10ee89a909df0c8ccea4836e94e0a5d34c3cdab758fcaee1460189600fdd8062400030e52657075626c6963616e5f77696e0c44656d6f637261745f77696e056f7468657210323032302d75732d656c656374696f6e")
"fdd82efd01120000000005f5e100fda7103b030e52657075626c6963616e5f77696e00000000000000000c44656d6f637261745f77696e0000000005f5e100056f746865720000000003938700fda712c7fdd824c3988fabec9820690f366271c9ceac00fbec1412075f9b319bb0db1f86460519dd9c61478949f2c00c35aeb8e53a1507616072cb802891e2c189a9fa65a0493de5d3b04a6d7b90c9c43c09ebe5250d583e1c3fc423219b26f6a02ec394a130000afdd8225f0001ae3e30df5a203ad10ee89a909df0c8ccea4836e94e0a5d34c3cdab758fcaee1460189600fdd8062400030e52657075626c6963616e5f77696e0c44656d6f637261745f77696e056f7468657210323032302d75732d656c656374696f6e"
)
// https://test.oracle.suredbits.com/contract/numeric/d4d4df2892fb2cfd2e8f030f0e69a568e19668b5d355e7713f69853db09a4c33
val expectedNumericContractInfo = ContractInfo.fromHex(
@ -84,8 +86,11 @@ class DLCRoutesSpec
Post() ~> route ~> check {
assert(contentType == ContentTypes.`application/json`)
assert(responseAs[
String] == s"""{"result":"${expectedContractInfo.hex}","error":null}""")
assert(
responseAs[
String
] == s"""{"result":"${expectedContractInfo.hex}","error":null}"""
)
}
}
@ -147,8 +152,11 @@ class DLCRoutesSpec
Post() ~> route ~> check {
assert(contentType == ContentTypes.`application/json`)
assert(responseAs[
String] == s"""{"result":"${expectedNumericContractInfo.hex}","error":null}""")
assert(
responseAs[
String
] == s"""{"result":"${expectedNumericContractInfo.hex}","error":null}"""
)
}
}
@ -222,7 +230,8 @@ class DLCRoutesSpec
Post() ~> route ~> check {
assert(contentType == ContentTypes.`application/json`)
assert(
responseAs[String] == s"""{"result":{"dlcId":"0000000000000000000000000000000000000000000000000000000000000000","contactId":"i3bfhauurptgypgnolqmj7xxv7pcs5c6udtzthbgojc7eaxx6zbbjyad.onion:2862"},"error":null}""")
responseAs[String] == s"""{"result":{"dlcId":"0000000000000000000000000000000000000000000000000000000000000000","contactId":"i3bfhauurptgypgnolqmj7xxv7pcs5c6udtzthbgojc7eaxx6zbbjyad.onion:2862"},"error":null}"""
)
}
}

View File

@ -45,8 +45,10 @@ class DLCWalletBitcoindBackendLoaderTest extends WalletLoaderFixtures {
val setRescanF = for {
_ <- blocksF
walletConfig <- walletConfigF
descriptorDAO = WalletStateDescriptorDAO()(system.dispatcher,
walletConfig)
descriptorDAO = WalletStateDescriptorDAO()(
system.dispatcher,
walletConfig
)
set <- descriptorDAO.compareAndSetRescanning(false, true)
} yield assert(set)
@ -65,7 +67,8 @@ class DLCWalletBitcoindBackendLoaderTest extends WalletLoaderFixtures {
{ () =>
loadWallet2.isRescanning().map(isRescanning => isRescanning == false)
},
1.second)
1.second
)
} yield {
assert(loader.isRescanStateEmpty)
}

View File

@ -27,10 +27,12 @@ class ServerRunTest extends BitcoinSAsyncTest {
val datadir = config.chainConf.datadir
val invalidPort = -1
val args = Vector("--datadir",
val args = Vector(
"--datadir",
datadir.toAbsolutePath.toString,
"--rpcport",
invalidPort.toString)
invalidPort.toString
)
val serverArgParser = ServerArgParser(args)
val main = new BitcoinSServerMain(serverArgParser)

View File

@ -40,10 +40,12 @@ class WalletRoutesSpec
val feeRateApi = ConstantFeeRateProvider(SatoshisPerVirtualByte.one)
val walletLoader: DLCWalletNeutrinoBackendLoader =
DLCWalletNeutrinoBackendLoader(walletHolder,
DLCWalletNeutrinoBackendLoader(
walletHolder,
mockChainApi,
mockNode,
feeRateApi)
feeRateApi
)
val walletRoutes: WalletRoutes =
WalletRoutes(walletLoader)(system, conf.walletConf)
@ -101,7 +103,8 @@ class WalletRoutesSpec
)
(mockWalletApi.findDLCByTemporaryContractId: Sha256Digest => Future[
Option[DLCStatus]])
Option[DLCStatus]
])
.expects(tempContractId)
.returning(Future.successful(Some(status)))
(mockWalletApi.getDLCOffer: Sha256Digest => Future[Option[DLCOffer]])
@ -110,7 +113,8 @@ class WalletRoutesSpec
val route =
walletRoutes.handleCommand(
ServerCommand("getdlcoffer", ujson.Arr(tempContractId.hex)))
ServerCommand("getdlcoffer", ujson.Arr(tempContractId.hex))
)
Get() ~> route ~> check {
assert(contentType == `application/json`)
@ -127,23 +131,28 @@ class WalletRoutesSpec
LnMessage(TLVGen.dlcAcceptTLV(offer.toTLV).sampleSome)
val acceptTLV = DLCAccept.fromTLV(dummyAcceptLnMsg.tlv, offer)
(mockWalletApi
.acceptDLCOffer(_: DLCOfferTLV,
.acceptDLCOffer(
_: DLCOfferTLV,
_: Option[InetSocketAddress],
_: Option[BitcoinAddress],
_: Option[BitcoinAddress]))
_: Option[BitcoinAddress]
))
.expects(expectedTlv, None, None, None)
.returning(Future.successful(acceptTLV))
val cmd = ServerCommand(
"acceptdlcoffer",
ujson.Arr(ujson.Str(tlv), ujson.Null, ujson.Null, ujson.Null))
ujson.Arr(ujson.Str(tlv), ujson.Null, ujson.Null, ujson.Null)
)
val route = walletRoutes.handleCommand(cmd)
Get() ~> route ~> check {
assert(contentType == `application/json`)
assert(
responseAs[
String] == s"""{"result":"${dummyAcceptLnMsg.hex}","error":null}""")
String
] == s"""{"result":"${dummyAcceptLnMsg.hex}","error":null}"""
)
}
}
}

View File

@ -60,15 +60,22 @@ class WebsocketTests extends BitcoinSServerMainBitcoindFixture {
val text = message.text
val dlcNodeNotificationOpt: Option[DLCNodeNotification[_]] = Try(
upickle.default.read[DLCNodeNotification[_]](text)(
WsPicklers.dlcNodeNotificationPickler)).toOption
WsPicklers.dlcNodeNotificationPickler
)
).toOption
val walletNotificationOpt: Option[WalletNotification[_]] = Try(
upickle.default.read[WalletNotification[_]](text)(
WsPicklers.walletNotificationPickler)).toOption
WsPicklers.walletNotificationPickler
)
).toOption
val chainNotificationOpt: Option[ChainNotification[_]] = Try(
upickle.default.read[ChainNotification[_]](text)(
WsPicklers.chainNotificationPickler)).toOption
WsPicklers.chainNotificationPickler
)
).toOption
walletNotificationOpt.getOrElse(
chainNotificationOpt.getOrElse(dlcNodeNotificationOpt.get))
chainNotificationOpt.getOrElse(dlcNodeNotificationOpt.get)
)
case msg =>
fail(s"Unexpected msg type received in the sink, msg=$msg")
}
@ -76,19 +83,27 @@ class WebsocketTests extends BitcoinSServerMainBitcoindFixture {
def buildReq(
conf: BitcoinSAppConfig,
rpcPassword: Option[String] = None): WebSocketRequest = {
rpcPassword: Option[String] = None
): WebSocketRequest = {
val headers: Vector[HttpHeader] = Vector(
Authorization(
BasicHttpCredentials("bitcoins",
rpcPassword.getOrElse(conf.rpcPassword))))
WebSocketRequest(s"ws://localhost:${conf.wsPort}/events",
extraHeaders = headers)
BasicHttpCredentials(
"bitcoins",
rpcPassword.getOrElse(conf.rpcPassword)
)
)
)
WebSocketRequest(
s"ws://localhost:${conf.wsPort}/events",
extraHeaders = headers
)
}
val websocketFlow: Flow[
Message,
Message,
(Future[Seq[WsNotification[_]]], Promise[Option[Message]])] = {
(Future[Seq[WsNotification[_]]], Promise[Option[Message]])
] = {
Flow
.fromSinkAndSourceCoupledMat(sink, Source.maybe[Message])(Keep.both)
}
@ -133,26 +148,32 @@ class WebsocketTests extends BitcoinSServerMainBitcoindFixture {
} yield {
assert(response.response.status == StatusCodes.Unauthorized)
val cliConfig = Config(rpcPortOpt = Some(server.conf.rpcPort),
rpcPassword = "wrong password")
val cliConfig = Config(
rpcPortOpt = Some(server.conf.rpcPort),
rpcPassword = "wrong password"
)
val cliResponse = exec(GetNewAddress(labelOpt = None), cliConfig)
assert(cliResponse.isFailure)
assert(
cliResponse.failed.get.getMessage == "The supplied authentication is invalid")
cliResponse.failed.get.getMessage == "The supplied authentication is invalid"
)
}
}
it must "receive updates when an address is generated" in {
serverWithBitcoind =>
val ServerWithBitcoind(_, server) = serverWithBitcoind
val cliConfig = Config(rpcPortOpt = Some(server.conf.rpcPort),
rpcPassword = server.conf.rpcPassword)
val cliConfig = Config(
rpcPortOpt = Some(server.conf.rpcPort),
rpcPassword = server.conf.rpcPassword
)
val req = buildReq(server.conf)
val notificationsF: (
Future[WebSocketUpgradeResponse],
(Future[Seq[WsNotification[_]]], Promise[Option[Message]])) = {
(Future[Seq[WsNotification[_]]], Promise[Option[Message]])
) = {
Http()
.singleWebSocketRequest(req, websocketFlow)
}
@ -171,20 +192,24 @@ class WebsocketTests extends BitcoinSServerMainBitcoindFixture {
notifications <- walletNotificationsF
} yield {
assert(
notifications.exists(_ == NewAddressNotification(expectedAddress)))
notifications.exists(_ == NewAddressNotification(expectedAddress))
)
}
}
it must "receive updates when a transaction is broadcast" in {
serverWithBitcoind =>
val ServerWithBitcoind(bitcoind, server) = serverWithBitcoind
val cliConfig = Config(rpcPortOpt = Some(server.conf.rpcPort),
rpcPassword = server.conf.rpcPassword)
val cliConfig = Config(
rpcPortOpt = Some(server.conf.rpcPort),
rpcPassword = server.conf.rpcPassword
)
val req = buildReq(server.conf)
val tuple: (
Future[WebSocketUpgradeResponse],
(Future[Seq[WsNotification[_]]], Promise[Option[Message]])) = {
(Future[Seq[WsNotification[_]]], Promise[Option[Message]])
) = {
Http()
.singleWebSocketRequest(req, websocketFlow)
}
@ -196,11 +221,12 @@ class WebsocketTests extends BitcoinSServerMainBitcoindFixture {
for {
address <- addressF
cmd = SendToAddress(destination = address,
cmd = SendToAddress(
destination = address,
amount = Bitcoins.one,
satoshisPerVirtualByte =
Some(SatoshisPerVirtualByte.one),
noBroadcast = false)
satoshisPerVirtualByte = Some(SatoshisPerVirtualByte.one),
noBroadcast = false
)
txIdStr = ConsoleCli.exec(cmd, cliConfig)
expectedTxId = DoubleSha256DigestBE.fromHex(txIdStr.get)
getTxCmd = GetTransaction(expectedTxId)
@ -217,13 +243,16 @@ class WebsocketTests extends BitcoinSServerMainBitcoindFixture {
it must "receive updates when a transaction is processed" in {
serverWithBitcoind =>
val ServerWithBitcoind(bitcoind, server) = serverWithBitcoind
val cliConfig = Config(rpcPortOpt = Some(server.conf.rpcPort),
rpcPassword = server.conf.rpcPassword)
val cliConfig = Config(
rpcPortOpt = Some(server.conf.rpcPort),
rpcPassword = server.conf.rpcPassword
)
val req = buildReq(server.conf)
val tuple: (
Future[WebSocketUpgradeResponse],
(Future[Seq[WsNotification[_]]], Promise[Option[Message]])) = {
(Future[Seq[WsNotification[_]]], Promise[Option[Message]])
) = {
Http()
.singleWebSocketRequest(req, websocketFlow)
}
@ -235,11 +264,12 @@ class WebsocketTests extends BitcoinSServerMainBitcoindFixture {
for {
address <- addressF
cmd = SendToAddress(destination = address,
cmd = SendToAddress(
destination = address,
amount = Bitcoins.one,
satoshisPerVirtualByte =
Some(SatoshisPerVirtualByte.one),
noBroadcast = false)
satoshisPerVirtualByte = Some(SatoshisPerVirtualByte.one),
noBroadcast = false
)
txIdStr = ConsoleCli.exec(cmd, cliConfig)
expectedTxId = DoubleSha256DigestBE.fromHex(txIdStr.get)
getTxCmd = GetTransaction(expectedTxId)
@ -255,13 +285,16 @@ class WebsocketTests extends BitcoinSServerMainBitcoindFixture {
it must "receive updates when a block is processed" in { serverWithBitcoind =>
val ServerWithBitcoind(bitcoind, server) = serverWithBitcoind
val cliConfig = Config(rpcPortOpt = Some(server.conf.rpcPort),
rpcPassword = server.conf.rpcPassword)
val cliConfig = Config(
rpcPortOpt = Some(server.conf.rpcPort),
rpcPassword = server.conf.rpcPassword
)
val req = buildReq(server.conf)
val tuple: (
Future[WebSocketUpgradeResponse],
(Future[Seq[WsNotification[_]]], Promise[Option[Message]])) = {
(Future[Seq[WsNotification[_]]], Promise[Option[Message]])
) = {
Http()
.singleWebSocketRequest(req, websocketFlow)
}
@ -278,13 +311,15 @@ class WebsocketTests extends BitcoinSServerMainBitcoindFixture {
cmd = GetBlockHeader(hash = hashes.head)
getBlockHeaderResultStr = ConsoleCli.exec(cmd, cliConfig)
getBlockHeaderResult = upickle.default.read(getBlockHeaderResultStr.get)(
Picklers.getBlockHeaderResultPickler)
Picklers.getBlockHeaderResultPickler
)
_ <- PekkoUtil.nonBlockingSleep(timeout)
_ = promise.success(None)
notifications <- notificationsF
} yield {
val count = notifications.count(
_ == BlockProcessedNotification(getBlockHeaderResult))
_ == BlockProcessedNotification(getBlockHeaderResult)
)
assert(count == 1, s"count=$count")
}
}
@ -292,13 +327,16 @@ class WebsocketTests extends BitcoinSServerMainBitcoindFixture {
it must "get notifications for reserving and unreserving utxos" in {
serverWithBitcoind =>
val ServerWithBitcoind(_, server) = serverWithBitcoind
val cliConfig = Config(rpcPortOpt = Some(server.conf.rpcPort),
rpcPassword = server.conf.rpcPassword)
val cliConfig = Config(
rpcPortOpt = Some(server.conf.rpcPort),
rpcPassword = server.conf.rpcPassword
)
val req = buildReq(server.conf)
val tuple: (
Future[WebSocketUpgradeResponse],
(Future[Seq[WsNotification[_]]], Promise[Option[Message]])) = {
(Future[Seq[WsNotification[_]]], Promise[Option[Message]])
) = {
Http()
.singleWebSocketRequest(req, websocketFlow)
}
@ -327,13 +365,16 @@ class WebsocketTests extends BitcoinSServerMainBitcoindFixture {
it must "receive updates when an offer is added and removed" in {
serverWithBitcoind =>
val ServerWithBitcoind(_, server) = serverWithBitcoind
val cliConfig = Config(rpcPortOpt = Some(server.conf.rpcPort),
rpcPassword = server.conf.rpcPassword)
val cliConfig = Config(
rpcPortOpt = Some(server.conf.rpcPort),
rpcPassword = server.conf.rpcPassword
)
val req = buildReq(server.conf)
val notificationsF: (
Future[WebSocketUpgradeResponse],
(Future[Seq[WsNotification[_]]], Promise[Option[Message]])) = {
(Future[Seq[WsNotification[_]]], Promise[Option[Message]])
) = {
Http()
.singleWebSocketRequest(req, websocketFlow)
}
@ -348,7 +389,8 @@ class WebsocketTests extends BitcoinSServerMainBitcoindFixture {
ConsoleCli
.exec(
CliCommand.AddDLCOffer(offer = offer, message = "msg", peer = "uri"),
cliConfig)
cliConfig
)
.get
ConsoleCli
@ -376,23 +418,28 @@ class WebsocketTests extends BitcoinSServerMainBitcoindFixture {
it must "receive updates when a rescan is complete" in { serverWithBitcoind =>
val ServerWithBitcoind(_, server) = serverWithBitcoind
val cliConfig = Config(rpcPortOpt = Some(server.conf.rpcPort),
rpcPassword = server.conf.rpcPassword)
val cliConfig = Config(
rpcPortOpt = Some(server.conf.rpcPort),
rpcPassword = server.conf.rpcPassword
)
val req = buildReq(server.conf)
val tuple: (
Future[WebSocketUpgradeResponse],
(Future[Seq[WsNotification[_]]], Promise[Option[Message]])) = {
(Future[Seq[WsNotification[_]]], Promise[Option[Message]])
) = {
Http()
.singleWebSocketRequest(req, websocketFlow)
}
val notificationsF = tuple._2._1
val promise = tuple._2._2
val cmd = Rescan(batchSize = None,
val cmd = Rescan(
batchSize = None,
startBlock = None,
endBlock = None,
force = true,
ignoreCreationTime = false)
ignoreCreationTime = false
)
val _ = ConsoleCli.exec(cmd, cliConfig)
for {
_ <- PekkoUtil.nonBlockingSleep(10.second)
@ -410,7 +457,8 @@ class WebsocketTests extends BitcoinSServerMainBitcoindFixture {
val req = buildReq(server.conf)
val tuple: (
Future[WebSocketUpgradeResponse],
(Future[Seq[WsNotification[_]]], Promise[Option[Message]])) = {
(Future[Seq[WsNotification[_]]], Promise[Option[Message]])
) = {
Http()
.singleWebSocketRequest(req, websocketFlow)
}
@ -434,7 +482,8 @@ class WebsocketTests extends BitcoinSServerMainBitcoindFixture {
val req = buildReq(server.conf)
val tuple: (
Future[WebSocketUpgradeResponse],
(Future[Seq[WsNotification[_]]], Promise[Option[Message]])) = {
(Future[Seq[WsNotification[_]]], Promise[Option[Message]])
) = {
Http()
.singleWebSocketRequest(req, websocketFlow)
}
@ -453,13 +502,16 @@ class WebsocketTests extends BitcoinSServerMainBitcoindFixture {
it must "receive dlc node updates" in { serverWithBitcoind =>
val ServerWithBitcoind(_, server) = serverWithBitcoind
val cliConfig = Config(rpcPortOpt = Some(server.conf.rpcPort),
rpcPassword = server.conf.rpcPassword)
val cliConfig = Config(
rpcPortOpt = Some(server.conf.rpcPort),
rpcPassword = server.conf.rpcPassword
)
val req = buildReq(server.conf)
val notificationsF: (
Future[WebSocketUpgradeResponse],
(Future[Seq[WsNotification[_]]], Promise[Option[Message]])) = {
(Future[Seq[WsNotification[_]]], Promise[Option[Message]])
) = {
Http()
.singleWebSocketRequest(req, websocketFlow)
}
@ -474,10 +526,12 @@ class WebsocketTests extends BitcoinSServerMainBitcoindFixture {
InetSocketAddress.createUnresolved("127.0.0.1", NetworkUtil.randomPort())
val acceptMsg = {
AcceptDLC(offer = offer,
AcceptDLC(
offer = offer,
externalPayoutAddressOpt = None,
externalChangeAddressOpt = None,
peerAddr = peerAddr)
peerAddr = peerAddr
)
}
for {
_ <- setupF
@ -488,13 +542,16 @@ class WebsocketTests extends BitcoinSServerMainBitcoindFixture {
} yield {
assert(notifications.exists(_ == DLCNodeConnectionInitiated(peerAddr)))
assert(notifications.exists(_ == DLCNodeConnectionFailed(peerAddr)))
assert(notifications.exists(n =>
assert(
notifications.exists(n =>
n match {
case DLCAcceptFailed((id, error)) =>
id == offer.tlv.tempContractId && error.startsWith(
"Connection refused")
"Connection refused"
)
case _ => false
}))
})
)
}
}

View File

@ -7,8 +7,8 @@ import org.bitcoins.core.protocol.BitcoinAddress
import scala.concurrent.Future
/** ScalaMock cannot stub traits with protected methods,
* so we need to stub them manually.
/** ScalaMock cannot stub traits with protected methods, so we need to stub them
* manually.
*/
abstract class MockWalletApi extends DLCNeutrinoHDWalletApi {
@ -18,7 +18,8 @@ abstract class MockWalletApi extends DLCNeutrinoHDWalletApi {
override def getDefaultAccount(): Future[AccountDb] = stub
override def getDefaultAccountForType(
addressType: AddressType): Future[AccountDb] = stub
addressType: AddressType
): Future[AccountDb] = stub
private def stub[T] =
Future.failed[T](new RuntimeException("Not implemented"))

View File

@ -27,18 +27,20 @@ import java.util.concurrent.TimeUnit
import scala.concurrent.Future
import scala.concurrent.duration.{DurationInt, FiniteDuration}
/** A unified config class for all submodules of Bitcoin-S
* that accepts configuration. Thanks to implicit definitions
* in this case class' companion object an instance
* of this class can be passed in anywhere a wallet,
* chain or node config is required.
/** A unified config class for all submodules of Bitcoin-S that accepts
* configuration. Thanks to implicit definitions in this case class' companion
* object an instance of this class can be passed in anywhere a wallet, chain
* or node config is required.
*
* @param directory The data directory of this app configuration
* @param confs A sequence of optional configuration overrides
* @param directory
* The data directory of this app configuration
* @param confs
* A sequence of optional configuration overrides
*/
case class BitcoinSAppConfig(
baseDatadir: Path,
configOverrides: Vector[Config])(implicit system: ActorSystem)
configOverrides: Vector[Config]
)(implicit system: ActorSystem)
extends StartStopAsync[AppConfigMarker]
with BitcoinSLogger {
import system.dispatcher
@ -109,7 +111,8 @@ case class BitcoinSAppConfig(
_ <- startedNonTorConfigsF
} yield {
logger.info(
s"Done starting BitcoinSAppConfig, it took=${TimeUtil.currentEpochMs - start}ms")
s"Done starting BitcoinSAppConfig, it took=${TimeUtil.currentEpochMs - start}ms"
)
StartedBitcoinSAppConfig(startedTorDependentConfigsF.map(_ => ()))
}
}
@ -132,9 +135,11 @@ case class BitcoinSAppConfig(
/** The underlying config the result of our fields derive from */
lazy val config: Config = {
val finalConfig =
AppConfig.getBaseConfig(baseDatadir = baseDatadir,
AppConfig.getBaseConfig(
baseDatadir = baseDatadir,
DEFAULT_BITCOIN_S_CONF_FILE,
configOverrides)
configOverrides
)
val resolved = finalConfig.resolve()
resolved.checkValid(ConfigFactory.defaultReference(), "bitcoin-s")
@ -146,8 +151,8 @@ case class BitcoinSAppConfig(
def wsPort: Int = config.getIntOrElse("bitcoin-s.server.wsport", 19999)
/** How long until we forcefully terminate connections to the server
* when shutting down the server
/** How long until we forcefully terminate connections to the server when
* shutting down the server
*/
def terminationDeadline: FiniteDuration = {
val opt = config.getDurationOpt("bitcoin-s.server.termination-deadline")
@ -157,7 +162,8 @@ case class BitcoinSAppConfig(
new FiniteDuration(duration.toNanos, TimeUnit.NANOSECONDS)
} else {
sys.error(
s"Can only have a finite duration for termination deadline, got=$duration")
s"Can only have a finite duration for termination deadline, got=$duration"
)
}
case None => 5.seconds // 5 seconds by default
}
@ -190,13 +196,14 @@ case class BitcoinSAppConfig(
}
}
/** Implicit conversions that allow a unified configuration
* to be passed in wherever a specializes one is required
/** Implicit conversions that allow a unified configuration to be passed in
* wherever a specializes one is required
*/
object BitcoinSAppConfig extends BitcoinSLogger {
def fromConfig(config: Config)(implicit
system: ActorSystem): BitcoinSAppConfig = {
def fromConfig(
config: Config
)(implicit system: ActorSystem): BitcoinSAppConfig = {
val configDataDir: Path = Paths.get(config.getString("bitcoin-s.datadir"))
BitcoinSAppConfig(configDataDir, Vector(config))
}
@ -206,33 +213,36 @@ object BitcoinSAppConfig extends BitcoinSLogger {
}
def fromDatadir(datadir: Path, confs: Config*)(implicit
system: ActorSystem): BitcoinSAppConfig = {
system: ActorSystem
): BitcoinSAppConfig = {
BitcoinSAppConfig(datadir, confs.toVector)
}
def fromDatadirWithServerArgs(
datadir: Path,
serverArgsParser: ServerArgParser)(implicit
system: ActorSystem): BitcoinSAppConfig = {
serverArgsParser: ServerArgParser
)(implicit system: ActorSystem): BitcoinSAppConfig = {
fromDatadir(datadir, serverArgsParser.toConfig)
}
/** Constructs an app configuration from the default Bitcoin-S
* data directory and given list of configuration overrides.
/** Constructs an app configuration from the default Bitcoin-S data directory
* and given list of configuration overrides.
*/
def fromDefaultDatadir(confs: Config*)(implicit
system: ActorSystem): BitcoinSAppConfig =
system: ActorSystem
): BitcoinSAppConfig =
BitcoinSAppConfig(AppConfig.DEFAULT_BITCOIN_S_DATADIR, confs.toVector)
def fromDefaultDatadirWithBundleConf(confs: Vector[Config] = Vector.empty)(
implicit system: ActorSystem): BitcoinSAppConfig = {
def fromDefaultDatadirWithBundleConf(
confs: Vector[Config] = Vector.empty
)(implicit system: ActorSystem): BitcoinSAppConfig = {
fromDatadirWithBundleConf(AppConfig.DEFAULT_BITCOIN_S_DATADIR, confs)
}
def fromDatadirWithBundleConf(
datadir: Path,
confs: Vector[Config] = Vector.empty)(implicit
system: ActorSystem): BitcoinSAppConfig = {
confs: Vector[Config] = Vector.empty
)(implicit system: ActorSystem): BitcoinSAppConfig = {
val baseConf: BitcoinSAppConfig =
fromDatadir(datadir, confs: _*)
@ -250,22 +260,20 @@ object BitcoinSAppConfig extends BitcoinSLogger {
}
/** Resolve BitcoinSAppConfig in the following order of precedence
* 1. Server args
* 2. bitcoin-s-bundle.conf
* 3. bitcoin-s.conf
* 4. application.conf
* 5. reference.conf
* 1. Server args 2. bitcoin-s-bundle.conf 3. bitcoin-s.conf 4.
* application.conf 5. reference.conf
*/
def fromDatadirWithBundleConfWithServerArgs(
datadir: Path,
serverArgParser: ServerArgParser)(implicit
system: ActorSystem): BitcoinSAppConfig = {
serverArgParser: ServerArgParser
)(implicit system: ActorSystem): BitcoinSAppConfig = {
fromDatadirWithBundleConf(datadir, Vector(serverArgParser.toConfig))
}
/** Creates a BitcoinSAppConfig the the given daemon args to a server */
def fromDefaultDatadirWithServerArgs(serverArgParser: ServerArgParser)(
implicit system: ActorSystem): BitcoinSAppConfig = {
def fromDefaultDatadirWithServerArgs(
serverArgParser: ServerArgParser
)(implicit system: ActorSystem): BitcoinSAppConfig = {
val config = serverArgParser.toConfig
fromConfig(config)
}

View File

@ -51,8 +51,8 @@ import scala.concurrent.{Await, Future, Promise}
class BitcoinSServerMain(override val serverArgParser: ServerArgParser)(implicit
override val system: ActorSystem,
val conf: BitcoinSAppConfig)
extends BitcoinSServerRunner[WalletHolder] {
val conf: BitcoinSAppConfig
) extends BitcoinSServerRunner[WalletHolder] {
implicit lazy val nodeConf: NodeAppConfig = conf.nodeConf
implicit lazy val chainConf: ChainAppConfig = conf.chainConf
@ -69,8 +69,10 @@ class BitcoinSServerMain(override val serverArgParser: ServerArgParser)(implicit
logger.info(s"Start on network $network")
startedConfigF.failed.foreach { err =>
logger.error(s"Failed to initialize configuration for BitcoinServerMain",
err)
logger.error(
s"Failed to initialize configuration for BitcoinServerMain",
err
)
}
for {
@ -90,14 +92,16 @@ class BitcoinSServerMain(override val serverArgParser: ServerArgParser)(implicit
}
} yield {
logger.info(
s"Done start BitcoinSServerMain, it took=${TimeUtil.currentEpochMs - startTime}ms")
s"Done start BitcoinSServerMain, it took=${TimeUtil.currentEpochMs - startTime}ms"
)
start
}
}
private def initializeChainState(
chainHandler: ChainHandler,
nodeType: NodeType): Future[Unit] = {
nodeType: NodeType
): Future[Unit] = {
val syncF = chainHandler.setSyncing(true)
val blockCountF = chainHandler.getBlockCount()
nodeType match {
@ -117,7 +121,8 @@ class BitcoinSServerMain(override val serverArgParser: ServerArgParser)(implicit
syncF.map(_ => ())
case NodeType.FullNode =>
sys.error(
s"Full not is not implemented, not sure what to do with chainstate")
s"Full not is not implemented, not sure what to do with chainstate"
)
}
}
@ -160,11 +165,13 @@ class BitcoinSServerMain(override val serverArgParser: ServerArgParser)(implicit
}
/** Start the bitcoin-s wallet server with a neutrino backend
* @param startedTorConfigF a future that is completed when tor is fully started
* @param startedTorConfigF
* a future that is completed when tor is fully started
* @return
*/
def startBitcoinSBackend(
startedTorConfigF: Future[Unit]): Future[WalletHolder] = {
startedTorConfigF: Future[Unit]
): Future[WalletHolder] = {
logger.info(s"startBitcoinSBackend()")
val start = System.currentTimeMillis()
@ -174,7 +181,8 @@ class BitcoinSServerMain(override val serverArgParser: ServerArgParser)(implicit
// get a node that isn't started
val nodeF = nodeConf.createNode(
peers = nodeConf.peers,
walletCreationTimeOpt = Some(creationTime))(chainConf, system)
walletCreationTimeOpt = Some(creationTime)
)(chainConf, system)
val defaultApi =
MempoolSpaceProvider(HourFeeTarget, network, torConf.socks5ProxyParams)
@ -183,17 +191,20 @@ class BitcoinSServerMain(override val serverArgParser: ServerArgParser)(implicit
conf.walletConf.feeProviderNameOpt,
conf.walletConf.feeProviderTargetOpt,
torConf.socks5ProxyParams,
network)
network
)
// get our wallet
val walletHolder = WalletHolder.empty
val neutrinoWalletLoaderF = {
for {
node <- nodeF
} yield {
val l = DLCWalletNeutrinoBackendLoader(walletHolder,
val l = DLCWalletNeutrinoBackendLoader(
walletHolder,
chainApi,
nodeApi = node,
feeRateApi = feeProvider)
feeRateApi = feeProvider
)
walletLoaderApiOpt = Some(l)
l
}
@ -229,7 +240,8 @@ class BitcoinSServerMain(override val serverArgParser: ServerArgParser)(implicit
_ <- startedNodeF
walletWithConfigs <- neutrinoWalletLoader.load(
walletNameOpt = walletNameOpt,
aesPasswordOpt = conf.walletConf.aesPasswordOpt)
aesPasswordOpt = conf.walletConf.aesPasswordOpt
)
} yield {
walletWithConfigs
}
@ -265,7 +277,8 @@ class BitcoinSServerMain(override val serverArgParser: ServerArgParser)(implicit
_ = {
logger.info(
s"Starting ${nodeConf.nodeType.shortName} node sync, it took=${System
.currentTimeMillis() - start}ms")
.currentTimeMillis() - start}ms"
)
}
// make sure callbacks are registered before we start sync
_ <- callbacksF
@ -274,7 +287,8 @@ class BitcoinSServerMain(override val serverArgParser: ServerArgParser)(implicit
} yield {
nodeOpt = Some(node)
logger.info(
s"Done starting Main! It took ${System.currentTimeMillis() - start}ms")
s"Done starting Main! It took ${System.currentTimeMillis() - start}ms"
)
()
}
@ -288,7 +302,8 @@ class BitcoinSServerMain(override val serverArgParser: ServerArgParser)(implicit
wsQueue: SourceQueueWithComplete[WsNotification[_]],
chainApi: ChainApi,
walletConf: WalletAppConfig,
dlcConf: DLCAppConfig): Unit = {
dlcConf: DLCAppConfig
): Unit = {
val chainCallbacks = WebsocketUtil.buildChainCallbacks(wsQueue, chainApi)
chainConf.addCallbacks(chainCallbacks)
@ -311,7 +326,8 @@ class BitcoinSServerMain(override val serverArgParser: ServerArgParser)(implicit
/** Returns blockchain info, in case of [[InWarmUp]] exception it retries.
*/
private def getBlockChainInfo(
client: BitcoindRpcClient): Future[GetBlockChainInfoResult] = {
client: BitcoindRpcClient
): Future[GetBlockChainInfoResult] = {
val promise = Promise[GetBlockChainInfoResult]()
val interval = 1.second
val maxTries = 12
@ -346,11 +362,13 @@ class BitcoinSServerMain(override val serverArgParser: ServerArgParser)(implicit
private[this] var nodeOpt: Option[Node] = None
/** Start the bitcoin-s wallet server with a bitcoind backend
* @param startedTorConfigF a future that is completed when tor is fully started
* @param startedTorConfigF
* a future that is completed when tor is fully started
* @return
*/
private def startBitcoindBackend(
startedTorConfigF: Future[Unit]): Future[WalletHolder] = {
startedTorConfigF: Future[Unit]
): Future[WalletHolder] = {
logger.info(s"startBitcoindBackend()")
val bitcoindF = for {
client <- bitcoindRpcConf.clientF
@ -373,7 +391,8 @@ class BitcoinSServerMain(override val serverArgParser: ServerArgParser)(implicit
val walletNameF = for {
lastLoadedWallet <- getLastLoadedWalletName()
walletName = lastLoadedWallet.getOrElse(
WalletAppConfig.DEFAULT_WALLET_NAME)
WalletAppConfig.DEFAULT_WALLET_NAME
)
} yield walletName
val walletHolder = WalletHolder.empty
@ -388,7 +407,8 @@ class BitcoinSServerMain(override val serverArgParser: ServerArgParser)(implicit
} yield BitcoindRpcBackendUtil.buildBitcoindNodeApi(
bitcoind,
Future.successful(walletHolder),
Some(chainCallbacks))
Some(chainCallbacks)
)
val feeProviderF = bitcoindF.map { bitcoind =>
FeeProviderFactory.getFeeProviderOrElse(
@ -406,10 +426,12 @@ class BitcoinSServerMain(override val serverArgParser: ServerArgParser)(implicit
nodeApi <- nodeApiF
feeProvider <- feeProviderF
} yield {
val l = DLCWalletBitcoindBackendLoader(walletHolder = walletHolder,
val l = DLCWalletBitcoindBackendLoader(
walletHolder = walletHolder,
bitcoind = bitcoind,
nodeApi = nodeApi,
feeProvider = feeProvider)
feeProvider = feeProvider
)
walletLoaderApiOpt = Some(l)
l
@ -421,8 +443,10 @@ class BitcoinSServerMain(override val serverArgParser: ServerArgParser)(implicit
_ <- isTorStartedF
loadWalletApi <- loadWalletApiF
walletName <- walletNameF
result <- loadWalletApi.load(Some(walletName),
conf.walletConf.aesPasswordOpt)
result <- loadWalletApi.load(
Some(walletName),
conf.walletConf.aesPasswordOpt
)
} yield result
}
@ -442,7 +466,8 @@ class BitcoinSServerMain(override val serverArgParser: ServerArgParser)(implicit
bitcoindNetwork = blockchainInfo.chain
_ = require(
bitcoindNetwork == network,
s"bitcoind ($bitcoindNetwork) on different network than wallet ($network)")
s"bitcoind ($bitcoindNetwork) on different network than wallet ($network)"
)
_ <- startHttpServer(
nodeApiF = Future.successful(bitcoind),
chainApi = bitcoind,
@ -453,8 +478,10 @@ class BitcoinSServerMain(override val serverArgParser: ServerArgParser)(implicit
wsSource = wsSource
)
walletName <- walletNameF
walletCallbacks = WebsocketUtil.buildWalletCallbacks(wsQueue,
walletName)
walletCallbacks = WebsocketUtil.buildWalletCallbacks(
wsQueue,
walletName
)
chainCallbacks <- chainCallbacksF
(wallet, walletConfig, dlcConfig) <- walletF
_ = walletConfig.addCallbacks(walletCallbacks)
@ -462,7 +489,8 @@ class BitcoinSServerMain(override val serverArgParser: ServerArgParser)(implicit
bitcoindSyncState <- syncWalletWithBitcoindAndStartPolling(
bitcoind,
wallet,
Some(chainCallbacks))
Some(chainCallbacks)
)
_ = {
bitcoindSyncStateOpt = Some(bitcoindSyncState)
}
@ -493,9 +521,8 @@ class BitcoinSServerMain(override val serverArgParser: ServerArgParser)(implicit
dlcNodeF: Future[DLCNode],
torConfStarted: Future[Unit],
serverCmdLineArgs: ServerArgParser,
wsSource: Source[WsNotification[_], NotUsed])(implicit
system: ActorSystem,
conf: BitcoinSAppConfig): Future[Server] = {
wsSource: Source[WsNotification[_], NotUsed]
)(implicit system: ActorSystem, conf: BitcoinSAppConfig): Future[Server] = {
implicit val nodeConf: NodeAppConfig = conf.nodeConf
implicit val walletConf: WalletAppConfig = conf.walletConf
@ -543,13 +570,15 @@ class BitcoinSServerMain(override val serverArgParser: ServerArgParser)(implicit
val server = {
serverCmdLineArgs.rpcPortOpt match {
case Some(rpcport) =>
Server(conf = nodeConf,
Server(
conf = nodeConf,
handlersF = handlers,
rpcbindOpt = rpcBindConfOpt,
rpcport = rpcport,
rpcPassword = conf.rpcPassword,
wsConfigOpt = Some(wsServerConfig),
wsSource)
wsSource
)
case None =>
Server(
conf = nodeConf,
@ -570,26 +599,30 @@ class BitcoinSServerMain(override val serverArgParser: ServerArgParser)(implicit
}
}
/** Syncs the bitcoin-s wallet against bitcoind and then
* starts rpc polling if zmq isn't enabled, otherwise it starts zmq polling.
/** Syncs the bitcoin-s wallet against bitcoind and then starts rpc polling if
* zmq isn't enabled, otherwise it starts zmq polling.
*
* The key thing this helper method does is it logs errors based on the
* future returned by this method. This is needed because we don't want
* to block the rest of the application from starting if we have to
* do a ton of syncing. However, we don't want to swallow
* exceptions thrown by this method.
* @return the [[Cancellable]] representing the schedule job that polls the mempool. You can call .cancel() to stop this
* future returned by this method. This is needed because we don't want to
* block the rest of the application from starting if we have to do a ton of
* syncing. However, we don't want to swallow exceptions thrown by this
* method.
* @return
* the [[Cancellable]] representing the schedule job that polls the
* mempool. You can call .cancel() to stop this
*/
private def syncWalletWithBitcoindAndStartPolling(
bitcoind: BitcoindRpcClient,
wallet: NeutrinoHDWalletApi,
chainCallbacksOpt: Option[ChainCallbacks]): Future[BitcoindSyncState] = {
chainCallbacksOpt: Option[ChainCallbacks]
): Future[BitcoindSyncState] = {
val f = for {
_ <- handlePotentialBitcoindLostBlock(bitcoind, wallet)
syncF = BitcoindRpcBackendUtil.syncWalletToBitcoind(
bitcoind,
wallet,
chainCallbacksOpt)(system)
chainCallbacksOpt
)(system)
_ = syncF.map(_ => wallet.updateUtxoPendingStates())
// don't start polling until initial sync is done
@ -603,15 +636,18 @@ class BitcoinSServerMain(override val serverArgParser: ServerArgParser)(implicit
.executeOnTxReceivedCallbacks(tx)
}
val combinedCancellable =
BitcoindPollingCancellable(blockingPollingCancellable,
mempoolCancellable)
BitcoindPollingCancellable(
blockingPollingCancellable,
mempoolCancellable
)
Future.successful(combinedCancellable)
} else {
Future {
BitcoindRpcBackendUtil.startZMQWalletCallbacks(
wallet,
bitcoindRpcConf.zmqConfig)
bitcoindRpcConf.zmqConfig
)
BitcoindPollingCancellabe.none
}
}
@ -624,13 +660,15 @@ class BitcoinSServerMain(override val serverArgParser: ServerArgParser)(implicit
f
}
/** Surprisingly on some OSes like umbrel bitcoind can lose blocks during the shutdown process
* This means next time we boot up, our wallet will have more blocks than bitcoind!
* Eventually bitcoind will synchrnoize with the network. This waits until bitcoind is synced
/** Surprisingly on some OSes like umbrel bitcoind can lose blocks during the
* shutdown process This means next time we boot up, our wallet will have
* more blocks than bitcoind! Eventually bitcoind will synchrnoize with the
* network. This waits until bitcoind is synced
*/
private def handlePotentialBitcoindLostBlock(
bitcoind: BitcoindRpcClient,
wallet: WalletApi): Future[Unit] = {
wallet: WalletApi
): Future[Unit] = {
AsyncUtil.retryUntilSatisfiedF(
conditionF = { () =>
for {
@ -645,17 +683,18 @@ class BitcoinSServerMain(override val serverArgParser: ServerArgParser)(implicit
)
}
/** Builds a websocket queue that you can feed elements to.
* The Source can be wired up with Directives.handleWebSocketMessages
* to create a flow that emits websocket messages
/** Builds a websocket queue that you can feed elements to. The Source can be
* wired up with Directives.handleWebSocketMessages to create a flow that
* emits websocket messages
*/
private def buildWsSource: (
SourceQueueWithComplete[WsNotification[_]],
Source[WsNotification[_], NotUsed]) = {
Source[WsNotification[_], NotUsed]
) = {
val maxBufferSize: Int = 25
/** This will queue [[maxBufferSize]] elements in the queue. Once the buffer size is reached,
* we will drop the first element in the buffer
/** This will queue [[maxBufferSize]] elements in the queue. Once the buffer
* size is reached, we will drop the first element in the buffer
*/
val tuple = {
// from: https://github.com/akka/akka-http/issues/3039#issuecomment-610263181
@ -703,7 +742,8 @@ object BitcoinSServerMain extends BitcoinSAppScalaDaemon {
implicit lazy val conf: BitcoinSAppConfig =
BitcoinSAppConfig(
datadirParser.datadir,
Vector(datadirParser.baseConfig, serverCmdLineArgs.toConfig))(system)
Vector(datadirParser.baseConfig, serverCmdLineArgs.toConfig)
)(system)
val m = new BitcoinSServerMain(serverCmdLineArgs)
@ -711,7 +751,8 @@ object BitcoinSServerMain extends BitcoinSAppScalaDaemon {
sys.addShutdownHook {
logger.info(
s"@@@@@@@@@@@@@@@@@@@@@ Shutting down ${getClass.getSimpleName} @@@@@@@@@@@@@@@@@@@@@")
s"@@@@@@@@@@@@@@@@@@@@@ Shutting down ${getClass.getSimpleName} @@@@@@@@@@@@@@@@@@@@@"
)
Await.result(m.stop(), 10.seconds)
()
}

View File

@ -32,15 +32,19 @@ import java.util.concurrent.atomic.{AtomicBoolean, AtomicInteger}
import scala.concurrent.duration.{DurationInt, FiniteDuration}
import scala.concurrent.{ExecutionContext, Future, Promise}
/** Useful utilities to use in the wallet project for syncing things against bitcoind */
/** Useful utilities to use in the wallet project for syncing things against
* bitcoind
*/
object BitcoindRpcBackendUtil extends BitcoinSLogger {
/** Has the wallet process all the blocks it has not seen up until bitcoind's chain tip */
/** Has the wallet process all the blocks it has not seen up until bitcoind's
* chain tip
*/
def syncWalletToBitcoind(
bitcoind: BitcoindRpcClient,
wallet: NeutrinoHDWalletApi,
chainCallbacksOpt: Option[ChainCallbacks])(implicit
system: ActorSystem): Future[Unit] = {
chainCallbacksOpt: Option[ChainCallbacks]
)(implicit system: ActorSystem): Future[Unit] = {
logger.info("Syncing wallet to bitcoind")
import system.dispatcher
@ -59,7 +63,8 @@ object BitcoindRpcBackendUtil extends BitcoinSLogger {
}
}
_ = logger.info(
s"Syncing from bitcoind with bitcoindHeight=$bitcoindHeight walletHeight=${heightRange.start}")
s"Syncing from bitcoind with bitcoindHeight=$bitcoindHeight walletHeight=${heightRange.start}"
)
syncFlow <- buildBitcoindSyncSink(bitcoind, wallet)
stream = Source(heightRange).toMat(syncFlow)(Keep.right)
} yield stream
@ -76,7 +81,8 @@ object BitcoindRpcBackendUtil extends BitcoinSLogger {
// if bitcoind is not in sync, we cannot be done syncing. Keep the syncing flag to true
// so do nothing in this case
logger.warn(
s"We synced against bitcoind, but bitcoind is not in sync with the network.")
s"We synced against bitcoind, but bitcoind is not in sync with the network."
)
Future.unit
}
}
@ -85,14 +91,15 @@ object BitcoindRpcBackendUtil extends BitcoinSLogger {
res.map(_ => ())
}
/** Gets the height range for syncing against bitcoind when we don't have a [[org.bitcoins.core.api.wallet.WalletStateDescriptor]]
* to read the sync height from.
/** Gets the height range for syncing against bitcoind when we don't have a
* [[org.bitcoins.core.api.wallet.WalletStateDescriptor]] to read the sync
* height from.
*/
private def getHeightRangeNoWalletState(
wallet: NeutrinoHDWalletApi,
bitcoind: BitcoindRpcClient,
bitcoindHeight: Int)(implicit
ex: ExecutionContext): Future[Range.Inclusive] = {
bitcoindHeight: Int
)(implicit ex: ExecutionContext): Future[Range.Inclusive] = {
for {
txDbs <- wallet.listTransactions()
lastConfirmedOpt = txDbs
@ -108,7 +115,8 @@ object BitcoindRpcBackendUtil extends BitcoinSLogger {
range <- heightOpt match {
case Some(height) =>
logger.info(
s"Last tx occurred at block $height, syncing from there")
s"Last tx occurred at block $height, syncing from there"
)
val range = height.to(bitcoindHeight)
Future.successful(range)
case None =>
@ -123,8 +131,8 @@ object BitcoindRpcBackendUtil extends BitcoinSLogger {
private def setSyncingFlag(
syncing: Boolean,
bitcoind: BitcoindRpcClient,
chainCallbacksOpt: Option[ChainCallbacks])(implicit
ec: ExecutionContext): Future[Unit] = {
chainCallbacksOpt: Option[ChainCallbacks]
)(implicit ec: ExecutionContext): Future[Unit] = {
val oldSyncingFlagF = bitcoind.isSyncing()
for {
oldFlag <- oldSyncingFlagF
@ -146,14 +154,16 @@ object BitcoindRpcBackendUtil extends BitcoinSLogger {
}
}
/** Helper method to sync the wallet until the bitcoind height.
* This method returns a Sink that you can give block heights too and
* the sink will synchronize our bitcoin-s wallet against bitcoind
/** Helper method to sync the wallet until the bitcoind height. This method
* returns a Sink that you can give block heights too and the sink will
* synchronize our bitcoin-s wallet against bitcoind
*/
private def buildBitcoindSyncSink(
bitcoind: BitcoindRpcClient,
wallet: NeutrinoHDWalletApi)(implicit
system: ActorSystem): Future[Sink[Int, Future[NeutrinoHDWalletApi]]] = {
wallet: NeutrinoHDWalletApi
)(implicit
system: ActorSystem
): Future[Sink[Int, Future[NeutrinoHDWalletApi]]] = {
import system.dispatcher
val hasFiltersF = bitcoind
@ -165,8 +175,8 @@ object BitcoindRpcBackendUtil extends BitcoinSLogger {
val numParallelism = FutureUtil.getParallelism
// feeding blockchain hashes into this sync
// will sync our wallet with those blockchain hashes
val syncWalletSinkF: Future[
Sink[DoubleSha256DigestBE, Future[NeutrinoHDWalletApi]]] = {
val syncWalletSinkF
: Future[Sink[DoubleSha256DigestBE, Future[NeutrinoHDWalletApi]]] = {
for {
hasFilters <- hasFiltersF
@ -197,8 +207,8 @@ object BitcoindRpcBackendUtil extends BitcoinSLogger {
def createWalletWithBitcoindCallbacks(
bitcoind: BitcoindRpcClient,
wallet: Wallet,
chainCallbacksOpt: Option[ChainCallbacks])(implicit
system: ActorSystem): Wallet = {
chainCallbacksOpt: Option[ChainCallbacks]
)(implicit system: ActorSystem): Wallet = {
// We need to create a promise so we can inject the wallet with the callback
// after we have created it into SyncUtil.getNodeApiWalletCallback
// so we don't lose the internal state of the wallet
@ -207,7 +217,8 @@ object BitcoindRpcBackendUtil extends BitcoinSLogger {
val nodeApi = BitcoindRpcBackendUtil.buildBitcoindNodeApi(
bitcoind,
walletCallbackP.future,
chainCallbacksOpt)
chainCallbacksOpt
)
val pairedWallet = Wallet(
nodeApi = nodeApi,
chainQueryApi = bitcoind,
@ -221,10 +232,12 @@ object BitcoindRpcBackendUtil extends BitcoinSLogger {
def startZMQWalletCallbacks(
wallet: NeutrinoHDWalletApi,
zmqConfig: ZmqConfig)(implicit
ec: ExecutionContext): WalletZmqSubscribers = {
require(zmqConfig != ZmqConfig.empty,
"Must have the zmq raw configs defined to setup ZMQ callbacks")
zmqConfig: ZmqConfig
)(implicit ec: ExecutionContext): WalletZmqSubscribers = {
require(
zmqConfig != ZmqConfig.empty,
"Must have the zmq raw configs defined to setup ZMQ callbacks"
)
val rawTxSub = zmqConfig.rawTx.map { zmq =>
val rawTxListener: Option[Transaction => Unit] = Some {
@ -238,18 +251,21 @@ object BitcoindRpcBackendUtil extends BitcoinSLogger {
}
}
new ZMQSubscriber(socket = zmq,
new ZMQSubscriber(
socket = zmq,
hashTxListener = None,
hashBlockListener = None,
rawTxListener = rawTxListener,
rawBlockListener = None)
rawBlockListener = None
)
}
val rawBlockSub = zmqConfig.rawBlock.map { zmq =>
val rawBlockListener: Option[Block => Unit] = Some {
{ block: Block =>
logger.info(
s"Received block ${block.blockHeader.hashBE.hex}, processing")
s"Received block ${block.blockHeader.hashBE.hex}, processing"
)
val f = wallet.processBlock(block)
f.failed.foreach { err =>
logger.error("failed to process raw block zmq message", err)
@ -258,11 +274,13 @@ object BitcoindRpcBackendUtil extends BitcoinSLogger {
}
}
new ZMQSubscriber(socket = zmq,
new ZMQSubscriber(
socket = zmq,
hashTxListener = None,
hashBlockListener = None,
rawTxListener = None,
rawBlockListener = rawBlockListener)
rawBlockListener = rawBlockListener
)
}
val subs = WalletZmqSubscribers(rawTxSub, rawBlockSub)
@ -273,18 +291,19 @@ object BitcoindRpcBackendUtil extends BitcoinSLogger {
def createDLCWalletWithBitcoindCallbacks(
bitcoind: BitcoindRpcClient,
wallet: DLCWallet,
chainCallbacksOpt: Option[ChainCallbacks])(implicit
system: ActorSystem): DLCWallet = {
chainCallbacksOpt: Option[ChainCallbacks]
)(implicit system: ActorSystem): DLCWallet = {
// We need to create a promise so we can inject the wallet with the callback
// after we have created it into SyncUtil.getNodeApiWalletCallback
// so we don't lose the internal state of the wallet
val walletCallbackP = Promise[DLCWallet]()
val pairedWallet = DLCWallet(
nodeApi =
BitcoindRpcBackendUtil.buildBitcoindNodeApi(bitcoind,
nodeApi = BitcoindRpcBackendUtil.buildBitcoindNodeApi(
bitcoind,
walletCallbackP.future,
chainCallbacksOpt),
chainCallbacksOpt
),
chainQueryApi = bitcoind,
feeRateApi = wallet.feeRateApi
)(wallet.walletConfig, wallet.dlcConfig)
@ -296,9 +315,10 @@ object BitcoindRpcBackendUtil extends BitcoinSLogger {
private def filterSyncSink(
bitcoindRpcClient: BlockchainRpc,
wallet: NeutrinoHDWalletApi)(implicit ec: ExecutionContext): Sink[
DoubleSha256DigestBE,
Future[NeutrinoHDWalletApi]] = {
wallet: NeutrinoHDWalletApi
)(implicit
ec: ExecutionContext
): Sink[DoubleSha256DigestBE, Future[NeutrinoHDWalletApi]] = {
val numParallelism = FutureUtil.getParallelism
val sink: Sink[DoubleSha256DigestBE, Future[NeutrinoHDWalletApi]] =
@ -317,39 +337,46 @@ object BitcoindRpcBackendUtil extends BitcoinSLogger {
sink
}
/** Creates an anonymous [[NodeApi]] that downloads blocks using
* akka streams from bitcoind, and then calls [[NeutrinoWalletApi.processBlock]]
/** Creates an anonymous [[NodeApi]] that downloads blocks using akka streams
* from bitcoind, and then calls [[NeutrinoWalletApi.processBlock]]
*/
def buildBitcoindNodeApi(
bitcoindRpcClient: BitcoindRpcClient,
walletF: Future[WalletApi],
chainCallbacksOpt: Option[ChainCallbacks])(implicit
system: ActorSystem): NodeApi = {
chainCallbacksOpt: Option[ChainCallbacks]
)(implicit system: ActorSystem): NodeApi = {
import system.dispatcher
new NodeApi {
override def downloadBlocks(
blockHashes: Vector[DoubleSha256DigestBE]): Future[Unit] = {
blockHashes: Vector[DoubleSha256DigestBE]
): Future[Unit] = {
logger.info(s"Fetching ${blockHashes.length} blocks from bitcoind")
val numParallelism = FutureUtil.getParallelism
val source = Source(blockHashes)
val fetchBlocksFlow = BitcoindStreamUtil.fetchBlocksBitcoind(
bitcoindRpcClient = bitcoindRpcClient,
parallelism = numParallelism)
parallelism = numParallelism
)
val sinkF: Future[
Sink[(Block, GetBlockHeaderResult), Future[WalletApi]]] = {
val sinkF
: Future[Sink[(Block, GetBlockHeaderResult), Future[WalletApi]]] = {
walletF.map { initWallet =>
Sink.foldAsync[WalletApi, (Block, GetBlockHeaderResult)](
initWallet) {
case (wallet: WalletApi,
(block: Block, blockHeaderResult: GetBlockHeaderResult)) =>
initWallet
) {
case (
wallet: WalletApi,
(block: Block, blockHeaderResult: GetBlockHeaderResult)
) =>
val blockProcessedF = wallet.processBlock(block)
val executeCallbackF: Future[WalletApi] = {
for {
wallet <- blockProcessedF
_ <- handleChainCallbacks(chainCallbacksOpt,
blockHeaderResult)
_ <- handleChainCallbacks(
chainCallbacksOpt,
blockHeaderResult
)
} yield wallet
}
@ -374,7 +401,8 @@ object BitcoindRpcBackendUtil extends BitcoinSLogger {
/** Broadcasts the given transaction over the P2P network
*/
override def broadcastTransactions(
transactions: Vector[Transaction]): Future[Unit] = {
transactions: Vector[Transaction]
): Future[Unit] = {
bitcoindRpcClient.broadcastTransactions(transactions)
}
@ -386,8 +414,8 @@ object BitcoindRpcBackendUtil extends BitcoinSLogger {
private def handleChainCallbacks(
chainCallbacksOpt: Option[ChainCallbacks],
blockHeaderResult: GetBlockHeaderResult)(implicit
ec: ExecutionContext): Future[Unit] = {
blockHeaderResult: GetBlockHeaderResult
)(implicit ec: ExecutionContext): Future[Unit] = {
chainCallbacksOpt match {
case None => Future.unit
case Some(callback) =>
@ -400,19 +428,22 @@ object BitcoindRpcBackendUtil extends BitcoinSLogger {
}
}
/** Starts the [[ActorSystem]] to poll the [[BitcoindRpcClient]] for its block count,
* if it has changed, it will then request those blocks to process them
/** Starts the [[ActorSystem]] to poll the [[BitcoindRpcClient]] for its block
* count, if it has changed, it will then request those blocks to process
* them
*
* @param startCount The starting block height of the wallet
* @param interval The amount of time between polls, this should not be too aggressive
* as the wallet will need to process the new blocks
* @param startCount
* The starting block height of the wallet
* @param interval
* The amount of time between polls, this should not be too aggressive as
* the wallet will need to process the new blocks
*/
def startBitcoindBlockPolling(
wallet: WalletApi,
bitcoind: BitcoindRpcClient,
chainCallbacksOpt: Option[ChainCallbacks],
interval: FiniteDuration = 10.seconds)(implicit
system: ActorSystem): Cancellable = {
interval: FiniteDuration = 10.seconds
)(implicit system: ActorSystem): Cancellable = {
import system.dispatcher
val processingBitcoindBlocks = new AtomicBoolean(false)
@ -431,10 +462,12 @@ object BitcoindRpcBackendUtil extends BitcoinSLogger {
res <-
if (!rescanning) {
val pollFOptF =
pollBitcoind(wallet = wallet,
pollBitcoind(
wallet = wallet,
bitcoind = bitcoind,
chainCallbacksOpt = chainCallbacksOpt,
prevCount = walletSyncState.height)
prevCount = walletSyncState.height
)
pollFOptF.flatMap {
case Some(pollF) => pollF
@ -442,16 +475,19 @@ object BitcoindRpcBackendUtil extends BitcoinSLogger {
}
} else {
logger.info(
s"Skipping scanning the blockchain during wallet rescan")
s"Skipping scanning the blockchain during wallet rescan"
)
Future.unit
}
} yield res
f.onComplete { _ =>
processingBitcoindBlocks.set(false)
BitcoindRpcBackendUtil.setSyncingFlag(false,
BitcoindRpcBackendUtil.setSyncingFlag(
false,
bitcoind,
chainCallbacksOpt)
chainCallbacksOpt
)
} // reset polling variable
f.failed.foreach(err =>
logger.error(s"Failed to poll bitcoind", err))
@ -465,14 +501,16 @@ object BitcoindRpcBackendUtil extends BitcoinSLogger {
}
/** Polls bitcoind for syncing the blockchain
* @return None if there was nothing to sync, else the Future[Done] that is completed when the sync is finished.
* @return
* None if there was nothing to sync, else the Future[Done] that is
* completed when the sync is finished.
*/
private def pollBitcoind(
wallet: WalletApi,
bitcoind: BitcoindRpcClient,
chainCallbacksOpt: Option[ChainCallbacks],
prevCount: Int)(implicit
system: ActorSystem): Future[Option[Future[Done]]] = {
prevCount: Int
)(implicit system: ActorSystem): Future[Option[Future[Done]]] = {
import system.dispatcher
val atomicPrevCount = new AtomicInteger(prevCount)
val queueSource: Source[Int, SourceQueueWithComplete[Int]] =
@ -492,7 +530,8 @@ object BitcoindRpcBackendUtil extends BitcoinSLogger {
atomicPrevCount.set(prevCount)
logger.error(
s"Processing blocks from bitcoind polling failed, range=[$prevCount, $failedCount]",
err)
err
)
}
for {
@ -523,7 +562,8 @@ object BitcoindRpcBackendUtil extends BitcoinSLogger {
retval <- {
if (prevCount < count) {
logger.info(
s"Bitcoind has new block(s), requesting... ${count - prevCount} blocks")
s"Bitcoind has new block(s), requesting... ${count - prevCount} blocks"
)
val setSyncFlagF = setSyncingFlag(true, bitcoind, chainCallbacksOpt)
setSyncFlagF.map { _ =>
// use .tail so we don't process the previous block that we already did
@ -531,8 +571,11 @@ object BitcoindRpcBackendUtil extends BitcoinSLogger {
range.foreach(r => queue.offer(r))
}
} else if (prevCount > count) {
Future.failed(new RuntimeException(
s"Bitcoind is at a block height ($count) before the wallet's ($prevCount)"))
Future.failed(
new RuntimeException(
s"Bitcoind is at a block height ($count) before the wallet's ($prevCount)"
)
)
} else {
logger.debug(s"In sync $prevCount count=$count")
Future.unit
@ -548,15 +591,16 @@ object BitcoindRpcBackendUtil extends BitcoinSLogger {
def startBitcoindMempoolPolling(
wallet: WalletApi,
bitcoind: BitcoindRpcClient,
interval: FiniteDuration = 10.seconds)(
processTx: Transaction => Future[Unit])(implicit
system: ActorSystem,
ec: ExecutionContext): Cancellable = {
interval: FiniteDuration = 10.seconds
)(
processTx: Transaction => Future[Unit]
)(implicit system: ActorSystem, ec: ExecutionContext): Cancellable = {
@volatile var prevMempool: Set[DoubleSha256DigestBE] =
Set.empty[DoubleSha256DigestBE]
def getDiffAndReplace(
newMempool: Set[DoubleSha256DigestBE]): Set[DoubleSha256DigestBE] =
newMempool: Set[DoubleSha256DigestBE]
): Set[DoubleSha256DigestBE] =
synchronized {
val txids = newMempool.diff(prevMempool)
prevMempool = newMempool
@ -594,14 +638,16 @@ object BitcoindRpcBackendUtil extends BitcoinSLogger {
.run()
} yield {
logger.debug(
s"Done processing ${newTxIds.size} new mempool transactions")
s"Done processing ${newTxIds.size} new mempool transactions"
)
()
}
res.onComplete(_ => processingMempool.set(false))
res
} else {
logger.info(
s"Skipping scanning the mempool since a previously scheduled task is still running")
s"Skipping scanning the mempool since a previously scheduled task is still running"
)
Future.unit
}
}
@ -625,9 +671,12 @@ object BitcoindRpcBackendUtil extends BitcoinSLogger {
}
}
/** Checks if bitcoind has all blocks for the headers it has seen on the network */
private def isBitcoindInSync(bitcoind: BitcoindRpcClient)(implicit
ec: ExecutionContext): Future[Boolean] = {
/** Checks if bitcoind has all blocks for the headers it has seen on the
* network
*/
private def isBitcoindInSync(
bitcoind: BitcoindRpcClient
)(implicit ec: ExecutionContext): Future[Boolean] = {
for {
blockchainInfo <- bitcoind.getBlockChainInfo
} yield {

View File

@ -17,7 +17,8 @@ import scala.concurrent.Future
case class ChainRoutes(
chain: ChainApi,
network: BitcoinNetwork,
startedTorConfigF: Future[Unit])(implicit system: ActorSystem)
startedTorConfigF: Future[Unit]
)(implicit system: ActorSystem)
extends ServerRoute {
import system.dispatcher
@ -59,8 +60,8 @@ case class ChainRoutes(
for {
results <- resultsF
} yield {
val json = upickle.default.writeJs(results.head)(
Picklers.getBlockHeaderResultPickler)
val json = upickle.default
.writeJs(results.head)(Picklers.getBlockHeaderResultPickler)
Server.httpSuccess(json)
}
}

View File

@ -172,7 +172,8 @@ case class CoreRoutes()(implicit system: ActorSystem, config: BitcoinSAppConfig)
case DecodeContractInfo(contractInfo) =>
complete {
Server.httpSuccess(
writeJs(contractInfo)(contractInfoV0TLVJsonWriter))
writeJs(contractInfo)(contractInfoV0TLVJsonWriter)
)
}
}
@ -181,7 +182,8 @@ case class CoreRoutes()(implicit system: ActorSystem, config: BitcoinSAppConfig)
case DecodeAnnouncement(announcement) =>
complete {
Server.httpSuccess(
writeJs(announcement)(oracleAnnouncementTLVJsonWriter))
writeJs(announcement)(oracleAnnouncementTLVJsonWriter)
)
}
}

View File

@ -42,10 +42,12 @@ case class DLCRoutes(dlcNode: DLCNodeApi)(implicit system: ActorSystem)
case AcceptDLC(offer, address, payoutAddressOpt, changeAddressOpt) =>
complete {
dlcNode
.acceptDLCOffer(address,
.acceptDLCOffer(
address,
offer,
payoutAddressOpt,
changeAddressOpt)
changeAddressOpt
)
.map { accept =>
Server.httpSuccess(accept.toMessage.hex)
}
@ -63,9 +65,11 @@ case class DLCRoutes(dlcNode: DLCNodeApi)(implicit system: ActorSystem)
case _: EnumEventDescriptorV0TLV =>
EnumSingleOracleInfo(create.announcementTLV)
}
val contractInfo = SingleContractInfo(create.totalCollateral,
val contractInfo = SingleContractInfo(
create.totalCollateral,
create.ContractDescriptorTLV,
oracleInfo)
oracleInfo
)
Server.httpSuccess(contractInfo.hex)
}
}
@ -91,9 +95,11 @@ case class DLCRoutes(dlcNode: DLCNodeApi)(implicit system: ActorSystem)
withValidServerCommand(OfferAdd.fromJsArr(arr)) { register =>
complete {
dlcNode.wallet
.registerIncomingDLCOffer(register.offerTLV,
.registerIncomingDLCOffer(
register.offerTLV,
register.peer,
register.message)
register.message
)
.map { hash =>
Server.httpSuccess(hash.hex)
}
@ -133,8 +139,8 @@ case class DLCRoutes(dlcNode: DLCNodeApi)(implicit system: ActorSystem)
case ServerCommand("contacts-list", _) =>
complete {
dlcNode.wallet.listDLCContacts().map { contacts =>
val json = contacts.map(c =>
upickle.default.writeJs(c)(Picklers.contactDbPickler))
val json = contacts
.map(c => upickle.default.writeJs(c)(Picklers.contactDbPickler))
Server.httpSuccess(json)
}
}
@ -171,7 +177,8 @@ case class DLCRoutes(dlcNode: DLCNodeApi)(implicit system: ActorSystem)
val contactId =
dlcContactAdd.address.getHostName + ":" + dlcContactAdd.address.getPort
Server.httpSuccess(
ujson.Obj("dlcId" -> dlcId, "contactId" -> contactId))
ujson.Obj("dlcId" -> dlcId, "contactId" -> contactId)
)
}
}
}

View File

@ -22,8 +22,9 @@ import org.bitcoins.wallet.config.WalletAppConfig
import scala.concurrent.{ExecutionContext, Future}
/** A trait used to help load a different load and discard the current wallet in memory
* This trait encapsulates the heavy lifting done in the 'loadwallet' RPC command
/** A trait used to help load a different load and discard the current wallet in
* memory This trait encapsulates the heavy lifting done in the 'loadwallet'
* RPC command
*/
sealed trait DLCWalletLoaderApi
extends BitcoinSLogger
@ -42,8 +43,8 @@ sealed trait DLCWalletLoaderApi
def load(
walletNameOpt: Option[String],
aesPasswordOpt: Option[AesPassword]): Future[
(WalletHolder, WalletAppConfig, DLCAppConfig)]
aesPasswordOpt: Option[AesPassword]
): Future[(WalletHolder, WalletAppConfig, DLCAppConfig)]
protected def loadWallet(
walletHolder: WalletHolder,
@ -51,17 +52,21 @@ sealed trait DLCWalletLoaderApi
nodeApi: NodeApi,
feeProviderApi: FeeRateApi,
walletNameOpt: Option[String],
aesPasswordOpt: Option[AesPassword])(implicit
ec: ExecutionContext): Future[
(DLCNeutrinoHDWalletApi, WalletAppConfig, DLCAppConfig)] = {
aesPasswordOpt: Option[AesPassword]
)(implicit
ec: ExecutionContext
): Future[(DLCNeutrinoHDWalletApi, WalletAppConfig, DLCAppConfig)] = {
logger.info(
s"Loading wallet with bitcoind backend, walletName=${walletNameOpt.getOrElse("DEFAULT")}")
s"Loading wallet with bitcoind backend, walletName=${walletNameOpt.getOrElse("DEFAULT")}"
)
val walletName =
walletNameOpt.getOrElse(WalletAppConfig.DEFAULT_WALLET_NAME)
for {
(walletConfig, dlcConfig) <- updateWalletConfigs(walletName,
aesPasswordOpt)
(walletConfig, dlcConfig) <- updateWalletConfigs(
walletName,
aesPasswordOpt
)
_ <- {
if (walletHolder.isInitialized) {
walletHolder
@ -83,8 +88,8 @@ sealed trait DLCWalletLoaderApi
protected def updateWalletConfigs(
walletName: String,
aesPasswordOpt: Option[AesPassword])(implicit
ec: ExecutionContext): Future[(WalletAppConfig, DLCAppConfig)] = {
aesPasswordOpt: Option[AesPassword]
)(implicit ec: ExecutionContext): Future[(WalletAppConfig, DLCAppConfig)] = {
val walletNameArgOpt = ArgumentSource.RpcArgument(walletName)
val aesPasswordArgOpt = aesPasswordOpt match {
case None =>
@ -93,8 +98,11 @@ sealed trait DLCWalletLoaderApi
Some(ArgumentSource.RpcArgument(pw))
}
val kmConfigF = Future.successful(
conf.walletConf.kmConf.copy(walletNameOverride = Some(walletNameArgOpt),
aesPasswordOverride = aesPasswordArgOpt))
conf.walletConf.kmConf.copy(
walletNameOverride = Some(walletNameArgOpt),
aesPasswordOverride = aesPasswordArgOpt
)
)
(for {
kmConfig <- kmConfigF
@ -107,34 +115,37 @@ sealed trait DLCWalletLoaderApi
} yield (walletConfig, dlcConfig))
}
protected def updateWalletName(walletNameOpt: Option[String])(implicit
ec: ExecutionContext): Future[Unit] = {
protected def updateWalletName(
walletNameOpt: Option[String]
)(implicit ec: ExecutionContext): Future[Unit] = {
val nodeStateDAO: NodeStateDescriptorDAO =
NodeStateDescriptorDAO()(ec, conf.nodeConf)
nodeStateDAO.updateWalletName(walletNameOpt)
}
protected def restartRescanIfNeeded(wallet: DLCNeutrinoHDWalletApi)(implicit
ec: ExecutionContext): Future[RescanState] = {
protected def restartRescanIfNeeded(
wallet: DLCNeutrinoHDWalletApi
)(implicit ec: ExecutionContext): Future[RescanState] = {
for {
isRescanning <- wallet.isRescanning()
res <-
if (isRescanning)
wallet.rescanNeutrinoWallet(startOpt = None,
wallet.rescanNeutrinoWallet(
startOpt = None,
endOpt = None,
addressBatchSize =
wallet.discoveryBatchSize(),
addressBatchSize = wallet.discoveryBatchSize(),
useCreationTime = true,
force = true)
force = true
)
else Future.successful(RescanState.RescanDone)
} yield res
}
/** Store a rescan state for the wallet that is currently loaded
* This is needed because we don't save rescan state anywhere else.
/** Store a rescan state for the wallet that is currently loaded This is
* needed because we don't save rescan state anywhere else.
*/
@volatile private[this] var rescanStateOpt: Option[
RescanState.RescanStarted] = None
@volatile private[this] var rescanStateOpt
: Option[RescanState.RescanStarted] = None
def setRescanState(rescanState: RescanState): Unit = {
rescanState match {
@ -156,13 +167,15 @@ sealed trait DLCWalletLoaderApi
case scala.util.control.NonFatal(exn) =>
logger.error(
s"Failed to reset rescanState in wallet loader. Resetting rescan state",
exn)
exn
)
rescanStateOpt = None
}
rescanStateOpt = Some(started)
} else {
sys.error(
s"Cannot run multiple rescans at the same time, got=$started have=$rescanStateOpt")
s"Cannot run multiple rescans at the same time, got=$started have=$rescanStateOpt"
)
}
}
}
@ -188,14 +201,15 @@ sealed trait DLCWalletLoaderApi
()
}
@volatile private[this] var currentWalletAppConfigOpt: Option[
WalletAppConfig] = None
@volatile private[this] var currentWalletAppConfigOpt
: Option[WalletAppConfig] = None
@volatile private[this] var currentDLCAppConfigOpt: Option[DLCAppConfig] =
None
protected def stopOldWalletAppConfig(
newWalletConfig: WalletAppConfig): Future[Unit] = {
newWalletConfig: WalletAppConfig
): Future[Unit] = {
currentWalletAppConfigOpt match {
case Some(current) =>
// stop the old config
@ -214,7 +228,8 @@ sealed trait DLCWalletLoaderApi
}
protected def stopOldDLCAppConfig(
newDlcConfig: DLCAppConfig): Future[Unit] = {
newDlcConfig: DLCAppConfig
): Future[Unit] = {
currentDLCAppConfigOpt match {
case Some(current) =>
// stop the old config
@ -266,10 +281,11 @@ case class DLCWalletNeutrinoBackendLoader(
walletHolder: WalletHolder,
chainQueryApi: ChainQueryApi,
nodeApi: NodeApi,
feeRateApi: FeeRateApi)(implicit
feeRateApi: FeeRateApi
)(implicit
override val conf: BitcoinSAppConfig,
override val system: ActorSystem)
extends DLCWalletLoaderApi {
override val system: ActorSystem
) extends DLCWalletLoaderApi {
import system.dispatcher
implicit private val nodeConf: NodeAppConfig = conf.nodeConf
@ -278,8 +294,8 @@ case class DLCWalletNeutrinoBackendLoader(
override def load(
walletNameOpt: Option[String],
aesPasswordOpt: Option[AesPassword]): Future[
(WalletHolder, WalletAppConfig, DLCAppConfig)] = {
aesPasswordOpt: Option[AesPassword]
): Future[(WalletHolder, WalletAppConfig, DLCAppConfig)] = {
val stopCallbackF = nodeConf.callBacks match {
case stream: NodeCallbackStreamManager =>
stream.stop()
@ -320,17 +336,18 @@ case class DLCWalletBitcoindBackendLoader(
walletHolder: WalletHolder,
bitcoind: BitcoindRpcClient,
nodeApi: NodeApi,
feeProvider: FeeRateApi)(implicit
feeProvider: FeeRateApi
)(implicit
override val conf: BitcoinSAppConfig,
override val system: ActorSystem)
extends DLCWalletLoaderApi {
override val system: ActorSystem
) extends DLCWalletLoaderApi {
import system.dispatcher
implicit private val nodeConf: NodeAppConfig = conf.nodeConf
override def load(
walletNameOpt: Option[String],
aesPasswordOpt: Option[AesPassword]): Future[
(WalletHolder, WalletAppConfig, DLCAppConfig)] = {
aesPasswordOpt: Option[AesPassword]
): Future[(WalletHolder, WalletAppConfig, DLCAppConfig)] = {
val stopCallbackF = nodeConf.callBacks match {
case stream: NodeCallbackStreamManager =>
stream.stop()
@ -347,12 +364,14 @@ case class DLCWalletBitcoindBackendLoader(
nodeApi = nodeApi,
feeProviderApi = feeProvider,
walletNameOpt = walletNameOpt,
aesPasswordOpt = aesPasswordOpt)
aesPasswordOpt = aesPasswordOpt
)
_ <- stopOldWalletAppConfig(walletConfig)
_ <- stopOldDLCAppConfig(dlcConfig)
nodeCallbacks <- CallbackUtil.createBitcoindNodeCallbacksForWallet(
walletHolder)
walletHolder
)
_ = nodeConf.replaceCallbacks(nodeCallbacks)
_ <- walletHolder.replaceWallet(dlcWallet)
// do something with possible rescan?

View File

@ -45,7 +45,9 @@ object DecodeAccept extends ServerJsonModels {
case other =>
Failure(
new IllegalArgumentException(
s"Bad number of arguments: ${other.length} Expected: 1"))
s"Bad number of arguments: ${other.length} Expected: 1"
)
)
}
}
}
@ -75,7 +77,9 @@ object DecodeSign extends ServerJsonModels {
case other =>
Failure(
new IllegalArgumentException(
s"Bad number of arguments: ${other.length} Expected: 1"))
s"Bad number of arguments: ${other.length} Expected: 1"
)
)
}
}
}
@ -97,7 +101,9 @@ object DecodeAttestations extends ServerJsonModels {
case other =>
Failure(
new IllegalArgumentException(
s"Bad number of arguments: ${other.length}. Expected: 1"))
s"Bad number of arguments: ${other.length}. Expected: 1"
)
)
}
}
}
@ -106,7 +112,8 @@ case class DLCDataFromFile(
path: Path,
destinationOpt: Option[Path],
externalPayoutAddressOpt: Option[BitcoinAddress],
externalChangeAddressOpt: Option[BitcoinAddress])
externalChangeAddressOpt: Option[BitcoinAddress]
)
object DLCDataFromFile extends ServerJsonModels {
@ -115,7 +122,8 @@ object DLCDataFromFile extends ServerJsonModels {
pathJs: Value,
destJs: Value,
payoutAddressJs: Value,
changeAddressJs: Value) = Try {
changeAddressJs: Value
) = Try {
val path = new File(pathJs.str).toPath
val destJsOpt = nullToOpt(destJs)
val destOpt = destJsOpt.map(js => new File(js.str).toPath)
@ -143,7 +151,9 @@ object DLCDataFromFile extends ServerJsonModels {
case other =>
Failure(
new IllegalArgumentException(
s"Bad number of arguments: ${other.length}. Expected: 1"))
s"Bad number of arguments: ${other.length}. Expected: 1"
)
)
}
}
}
@ -151,7 +161,8 @@ object DLCDataFromFile extends ServerJsonModels {
case class OfferAdd(
offerTLV: DLCOfferTLV,
peer: Option[String],
message: Option[String])
message: Option[String]
)
object OfferAdd extends ServerJsonModels {
@ -167,7 +178,8 @@ object OfferAdd extends ServerJsonModels {
}
case other =>
val exn = new IllegalArgumentException(
s"Bad number or arguments to offer-add, got=${other.length} expected=3")
s"Bad number or arguments to offer-add, got=${other.length} expected=3"
)
Failure(exn)
}
}
@ -186,7 +198,8 @@ object OfferRemove {
}
case other =>
val exn = new IllegalArgumentException(
s"Bad number or arguments to offer-remove, got=${other.length} expected=1")
s"Bad number or arguments to offer-remove, got=${other.length} expected=1"
)
Failure(exn)
}
}
@ -195,7 +208,8 @@ object OfferRemove {
case class OfferSend(
remoteAddress: InetSocketAddress,
message: String,
offerE: Either[DLCOfferTLV, Sha256Digest])
offerE: Either[DLCOfferTLV, Sha256Digest]
)
object OfferSend extends ServerJsonModels {
@ -216,7 +230,8 @@ object OfferSend extends ServerJsonModels {
}
case other =>
val exn = new IllegalArgumentException(
s"Bad number or arguments to offer-send, got=${other.length} expected=3")
s"Bad number or arguments to offer-send, got=${other.length} expected=3"
)
Failure(exn)
}
}
@ -236,7 +251,8 @@ object GetDLCOffer {
}
case other =>
val exn = new IllegalArgumentException(
s"Bad number or arguments to offer-send, got=${other.length} expected=1")
s"Bad number or arguments to offer-send, got=${other.length} expected=1"
)
Failure(exn)
}
}
@ -251,7 +267,8 @@ trait ServerJsonModels {
case _: Value =>
throw Value.InvalidData(
js,
"Expected an OracleAnnouncementTLV as a hex string")
"Expected an OracleAnnouncementTLV as a hex string"
)
}
def jsToContractInfoTLV(js: Value): ContractInfoV0TLV =
@ -311,7 +328,8 @@ trait ServerJsonModels {
LockUnspentOutputParameter.fromJson(js)
def jsToLockUnspentOutputParameters(
js: Value): Seq[LockUnspentOutputParameter] = {
js: Value
): Seq[LockUnspentOutputParameter] = {
js.arr.foldLeft(Seq.empty[LockUnspentOutputParameter])((seq, outPoint) =>
seq :+ jsToLockUnspentOutputParameter(outPoint))
}
@ -337,11 +355,13 @@ trait ServerJsonModels {
case _: Value =>
throw Value.InvalidData(
js,
"Expected a SchnorrDigitalSignature as a hex string")
"Expected a SchnorrDigitalSignature as a hex string"
)
}
def jsToSchnorrDigitalSignatureVec(
js: Value): Vector[SchnorrDigitalSignature] = {
js: Value
): Vector[SchnorrDigitalSignature] = {
js.arr.foldLeft(Vector.empty[SchnorrDigitalSignature])((vec, sig) =>
vec :+ jsToSchnorrDigitalSignature(sig))
}
@ -353,7 +373,8 @@ trait ServerJsonModels {
case _: Value =>
throw Value.InvalidData(
js,
"Expected a OracleAttestmentTLV as a hex string")
"Expected a OracleAttestmentTLV as a hex string"
)
}
def jsToOracleAttestmentTLVVec(js: Value): Vector[OracleAttestmentTLV] = {
@ -384,7 +405,8 @@ trait ServerJsonModels {
}
def jsToWalletNameAndPassword(
js: Value): (Option[String], Option[AesPassword]) = {
js: Value
): (Option[String], Option[AesPassword]) = {
js match {
case Arr(arr) =>
if (arr.size >= 2) {
@ -403,14 +425,16 @@ trait ServerJsonModels {
case Arr(arr) => arr.map(_.str).toVector
case Null | False | True | Num(_) | Obj(_) =>
throw new IllegalArgumentException(
"mnemonic must be a string or array of strings")
"mnemonic must be a string or array of strings"
)
}
MnemonicCode.fromWords(mnemonicWords)
}
def jsToInetSocketAddress(
js: Value,
defaultPort: Int = -1): InetSocketAddress = {
defaultPort: Int = -1
): InetSocketAddress = {
js match {
case str: Str =>
val uri = new URI("tcp://" + str.str)

View File

@ -37,8 +37,8 @@ import scala.util.{Failure, Success}
case class WalletRoutes(loadWalletApi: DLCWalletLoaderApi)(implicit
system: ActorSystem,
walletConf: WalletAppConfig)
extends ServerRoute
walletConf: WalletAppConfig
) extends ServerRoute
with BitcoinSLogger {
import system.dispatcher
@ -64,7 +64,8 @@ case class WalletRoutes(loadWalletApi: DLCWalletLoaderApi)(implicit
private def handleBroadcastable(
tx: Transaction,
noBroadcast: Boolean): Future[NetworkElement] = {
noBroadcast: Boolean
): Future[NetworkElement] = {
if (noBroadcast) {
Future.successful(tx)
} else {
@ -154,11 +155,14 @@ case class WalletRoutes(loadWalletApi: DLCWalletLoaderApi)(implicit
val json = Obj(
"confirmed" -> Num(
formatCurrencyUnit(confirmed, isSats).toDouble),
formatCurrencyUnit(confirmed, isSats).toDouble
),
"unconfirmed" -> Num(
formatCurrencyUnit(unconfirmed, isSats).toDouble),
formatCurrencyUnit(unconfirmed, isSats).toDouble
),
"reserved" -> Num(
formatCurrencyUnit(reserved, isSats).toDouble),
formatCurrencyUnit(reserved, isSats).toDouble
),
"total" -> Num(formatCurrencyUnit(total, isSats).toDouble)
)
Server.httpSuccess(json)
@ -226,7 +230,8 @@ case class WalletRoutes(loadWalletApi: DLCWalletLoaderApi)(implicit
complete {
wallet.tagAddress(address, label).map { tagDb =>
Server.httpSuccess(
s"Added label \'${tagDb.tagName.name}\' to ${tagDb.address.value}")
s"Added label \'${tagDb.tagName.name}\' to ${tagDb.address.value}"
)
}
}
}
@ -263,8 +268,10 @@ case class WalletRoutes(loadWalletApi: DLCWalletLoaderApi)(implicit
val json: Vector[ujson.Obj] = grouped.map { case (address, labels) =>
val tagNames: Vector[ujson.Str] =
labels.map(l => ujson.Str(l.tagName.name))
ujson.Obj(("address", address.toString),
("labels", ujson.Arr.from(tagNames)))
ujson.Obj(
("address", address.toString),
("labels", ujson.Arr.from(tagNames))
)
}.toVector
Server.httpSuccess(ujson.Arr.from(json))
}
@ -298,11 +305,15 @@ case class WalletRoutes(loadWalletApi: DLCWalletLoaderApi)(implicit
dlcOpt <- wallet.findDLCByTemporaryContractId(tempContractId)
dlc = dlcOpt.getOrElse(
throw new IllegalArgumentException(
s"Cannot find a DLC with temp contact ID $tempContractId"))
s"Cannot find a DLC with temp contact ID $tempContractId"
)
)
offerOpt <- wallet.getDLCOffer(dlc.dlcId)
offer = offerOpt.getOrElse(
throw new IllegalArgumentException(
s"Cannot find an offer with for DLC ID ${dlc.dlcId}"))
s"Cannot find an offer with for DLC ID ${dlc.dlcId}"
)
)
} yield {
Server.httpSuccess(offer.toMessage.hex)
}
@ -358,14 +369,17 @@ case class WalletRoutes(loadWalletApi: DLCWalletLoaderApi)(implicit
case Failure(exception) =>
complete(Server.httpBadRequest(exception))
case Success(
CreateDLCOffer(contractInfo,
CreateDLCOffer(
contractInfo,
collateral,
feeRateOpt,
locktimeOpt,
refundLT,
payoutAddressOpt,
changeAddressOpt,
peerAddressOpt)) =>
peerAddressOpt
)
) =>
complete {
val announcements = contractInfo.oracleInfo match {
case OracleInfoV0TLV(announcement) => Vector(announcement)
@ -375,29 +389,34 @@ case class WalletRoutes(loadWalletApi: DLCWalletLoaderApi)(implicit
if (!announcements.forall(_.validateSignature)) {
throw new RuntimeException(
s"Received Oracle announcement with invalid signature! ${announcements
.map(_.hex)}")
.map(_.hex)}"
)
}
val offerF = locktimeOpt match {
case Some(locktime) =>
wallet
.createDLCOffer(contractInfo,
.createDLCOffer(
contractInfo,
collateral,
feeRateOpt,
locktime,
refundLT,
peerAddressOpt,
payoutAddressOpt,
changeAddressOpt)
changeAddressOpt
)
case None =>
wallet
.createDLCOffer(contractInfo,
.createDLCOffer(
contractInfo,
collateral,
feeRateOpt,
refundLT,
peerAddressOpt,
payoutAddressOpt,
changeAddressOpt)
changeAddressOpt
)
}
offerF.map { offer =>
@ -411,16 +430,21 @@ case class WalletRoutes(loadWalletApi: DLCWalletLoaderApi)(implicit
case Failure(exception) =>
complete(Server.httpBadRequest(exception))
case Success(
AcceptDLCOffer(offer,
AcceptDLCOffer(
offer,
payoutAddressOpt,
changeAddressOpt,
peerAddressOpt)) =>
peerAddressOpt
)
) =>
complete {
wallet
.acceptDLCOffer(offer.tlv,
.acceptDLCOffer(
offer.tlv,
peerAddressOpt,
payoutAddressOpt,
changeAddressOpt)
changeAddressOpt
)
.map { accept =>
Server.httpSuccess(accept.toMessage.hex)
}
@ -432,10 +456,8 @@ case class WalletRoutes(loadWalletApi: DLCWalletLoaderApi)(implicit
case Failure(exception) =>
complete(Server.httpBadRequest(exception))
case Success(
DLCDataFromFile(path,
destOpt,
payoutAddressOpt,
changeAddressOpt)) =>
DLCDataFromFile(path, destOpt, payoutAddressOpt, changeAddressOpt)
) =>
complete {
val hex = Files.readAllLines(path).get(0)
@ -445,10 +467,12 @@ case class WalletRoutes(loadWalletApi: DLCWalletLoaderApi)(implicit
.getOrElse(LnMessage(DLCOfferTLV.fromHex(hex)))
wallet
.acceptDLCOffer(offerMessage.tlv,
.acceptDLCOffer(
offerMessage.tlv,
None,
payoutAddressOpt,
changeAddressOpt)
changeAddressOpt
)
.map { accept =>
val ret = handleDestinationOpt(accept.toMessage.hex, destOpt)
Server.httpSuccess(ret)
@ -500,7 +524,8 @@ case class WalletRoutes(loadWalletApi: DLCWalletLoaderApi)(implicit
complete {
wallet.addDLCSigs(sigs.tlv).map { db =>
Server.httpSuccess(
s"Successfully added sigs to DLC ${db.contractIdOpt.get.toHex}")
s"Successfully added sigs to DLC ${db.contractIdOpt.get.toHex}"
)
}
}
}
@ -520,7 +545,8 @@ case class WalletRoutes(loadWalletApi: DLCWalletLoaderApi)(implicit
wallet.addDLCSigs(signMessage.tlv).map { db =>
Server.httpSuccess(
s"Successfully added sigs to DLC ${db.contractIdOpt.get.toHex}")
s"Successfully added sigs to DLC ${db.contractIdOpt.get.toHex}"
)
}
}
}
@ -604,7 +630,8 @@ case class WalletRoutes(loadWalletApi: DLCWalletLoaderApi)(implicit
Server.httpSuccess(ret)
case None =>
Server.httpError(
s"Cannot execute DLC with contractId=${contractId.toHex}")
s"Cannot execute DLC with contractId=${contractId.toHex}"
)
}
}
}
@ -627,15 +654,19 @@ case class WalletRoutes(loadWalletApi: DLCWalletLoaderApi)(implicit
case ServerCommand("sendtoaddress", arr) =>
withValidServerCommand(SendToAddress.fromJsArr(arr)) {
case SendToAddress(address,
case SendToAddress(
address,
bitcoins,
satoshisPerVirtualByteOpt,
noBroadcast) =>
noBroadcast
) =>
complete {
for {
tx <- wallet.sendToAddress(address,
tx <- wallet.sendToAddress(
address,
bitcoins,
satoshisPerVirtualByteOpt)
satoshisPerVirtualByteOpt
)
retStr <- handleBroadcastable(tx, noBroadcast)
} yield {
Server.httpSuccess(retStr.hex)
@ -644,16 +675,20 @@ case class WalletRoutes(loadWalletApi: DLCWalletLoaderApi)(implicit
}
case ServerCommand("sendfromoutpoints", arr) =>
withValidServerCommand(SendFromOutPoints.fromJsArr(arr)) {
case SendFromOutPoints(outPoints,
case SendFromOutPoints(
outPoints,
address,
bitcoins,
satoshisPerVirtualByteOpt) =>
satoshisPerVirtualByteOpt
) =>
complete {
for {
tx <- wallet.sendFromOutPoints(outPoints,
tx <- wallet.sendFromOutPoints(
outPoints,
address,
bitcoins,
satoshisPerVirtualByteOpt)
satoshisPerVirtualByteOpt
)
_ <- wallet.broadcastTransaction(tx)
} yield Server.httpSuccess(tx.txIdBE)
}
@ -675,10 +710,12 @@ case class WalletRoutes(loadWalletApi: DLCWalletLoaderApi)(implicit
case SendWithAlgo(address, bitcoins, satoshisPerVirtualByteOpt, algo) =>
complete {
for {
tx <- wallet.sendWithAlgo(address,
tx <- wallet.sendWithAlgo(
address,
bitcoins,
satoshisPerVirtualByteOpt,
algo)
algo
)
_ <- wallet.broadcastTransaction(tx)
} yield Server.httpSuccess(tx.txIdBE)
}
@ -698,9 +735,11 @@ case class WalletRoutes(loadWalletApi: DLCWalletLoaderApi)(implicit
case OpReturnCommit(message, hashMessage, satoshisPerVirtualByteOpt) =>
complete {
for {
tx <- wallet.makeOpReturnCommitment(message,
tx <- wallet.makeOpReturnCommitment(
message,
hashMessage,
satoshisPerVirtualByteOpt)
satoshisPerVirtualByteOpt
)
_ <- wallet.broadcastTransaction(tx)
} yield {
Server.httpSuccess(tx.txIdBE)
@ -838,9 +877,11 @@ case class WalletRoutes(loadWalletApi: DLCWalletLoaderApi)(implicit
case KeyManagerPassphraseChange(oldPassword, newPassword) =>
complete {
val path = walletConf.seedPath
WalletStorage.changeAesPassword(path,
WalletStorage.changeAesPassword(
path,
Some(oldPassword),
Some(newPassword))
Some(newPassword)
)
Server.httpSuccess(ujson.Null)
}
@ -867,16 +908,20 @@ case class WalletRoutes(loadWalletApi: DLCWalletLoaderApi)(implicit
val mnemonicState = passwordOpt match {
case Some(pass) =>
DecryptedMnemonic(mnemonic,
DecryptedMnemonic(
mnemonic,
creationTime,
backupTimeOpt = None,
imported = true)
imported = true
)
.encrypt(pass)
case None =>
DecryptedMnemonic(mnemonic,
DecryptedMnemonic(
mnemonic,
creationTime,
backupTimeOpt = None,
imported = true)
imported = true
)
}
WalletStorage.writeSeedToDisk(seedPath, mnemonicState)
@ -894,7 +939,8 @@ case class WalletRoutes(loadWalletApi: DLCWalletLoaderApi)(implicit
Server.httpSuccess(
WalletStorage
.readDecryptedSeedPhraseForBackup(seedPath, passwordOpt)
.get)
.get
)
}
}
@ -938,16 +984,20 @@ case class WalletRoutes(loadWalletApi: DLCWalletLoaderApi)(implicit
val mnemonicState = passwordOpt match {
case Some(pass) =>
DecryptedExtPrivKey(xprv,
DecryptedExtPrivKey(
xprv,
creationTime,
backupTimeOpt = None,
imported = true)
imported = true
)
.encrypt(pass)
case None =>
DecryptedExtPrivKey(xprv,
DecryptedExtPrivKey(
xprv,
creationTime,
backupTimeOpt = None,
imported = true)
imported = true
)
}
WalletStorage.writeSeedToDisk(seedPath, mnemonicState)
@ -1020,7 +1070,8 @@ case class WalletRoutes(loadWalletApi: DLCWalletLoaderApi)(implicit
.map(_ + "-")
.getOrElse(WalletAppConfig.DEFAULT_WALLET_NAME)
kmConf.seedFolder.resolve(
walletName + WalletStorage.ENCRYPTED_SEED_FILE_NAME)
walletName + WalletStorage.ENCRYPTED_SEED_FILE_NAME
)
}
/** Returns information about the state of our wallet */
@ -1048,7 +1099,8 @@ case class WalletRoutes(loadWalletApi: DLCWalletLoaderApi)(implicit
private def formatCurrencyUnit(
currencyUnit: CurrencyUnit,
isSats: Boolean): Double = {
isSats: Boolean
): Double = {
if (isSats) {
currencyUnit.satoshis.toBigDecimal.toDouble
} else {
@ -1066,14 +1118,16 @@ case class WalletRoutes(loadWalletApi: DLCWalletLoaderApi)(implicit
}
}
/** Gets the fee rate for the wallet with a timeout on the request of 1 second */
/** Gets the fee rate for the wallet with a timeout on the request of 1 second
*/
private def getFeeRate(): Future[FeeUnit] = {
val resultF = wallet
.getFeeRate()
.recover { case scala.util.control.NonFatal(exn) =>
logger.error(
s"Failed to fetch fee rate from wallet, returning -1 sats/vbyte",
exn)
exn
)
SatoshisPerVirtualByte.negativeOne
}
// due to tor variability, we need to make sure we give a prompt response.
@ -1124,7 +1178,9 @@ case class WalletRoutes(loadWalletApi: DLCWalletLoaderApi)(implicit
} else {
Future.failed(
new IllegalArgumentException(
s"Cannot rescan when a rescan is already ongoing for wallet"))
s"Cannot rescan when a rescan is already ongoing for wallet"
)
)
}
}

View File

@ -7,8 +7,8 @@ import java.util.concurrent.atomic.AtomicBoolean
case class WalletZmqSubscribers(
rawTxSubscriberOpt: Option[ZMQSubscriber],
rawBlockSubscriberOpt: Option[ZMQSubscriber])
extends StartStop[Unit] {
rawBlockSubscriberOpt: Option[ZMQSubscriber]
) extends StartStop[Unit] {
private val isStarted: AtomicBoolean = new AtomicBoolean(false)
override def start(): Unit = {

View File

@ -4,12 +4,17 @@ import org.bitcoins.server.util.{BitcoindPollingCancellable}
import scala.concurrent.Future
/** @param syncF the future that will be completed when the synchronization with bitcoind is complete
* @param pollingCancellable You can cancel bitcoind polling by calling [[BitcoindPollingCancellabe.cancel()]]
/** @param syncF
* the future that will be completed when the synchronization with bitcoind
* is complete
* @param pollingCancellable
* You can cancel bitcoind polling by calling
* [[BitcoindPollingCancellabe.cancel()]]
*/
case class BitcoindSyncState(
syncF: Future[Unit],
pollingCancellable: BitcoindPollingCancellable) {
pollingCancellable: BitcoindPollingCancellable
) {
/** Stops syncing and polling bitcoind */
def stop(): Future[Unit] = {

View File

@ -5,8 +5,8 @@ import org.bitcoins.commons.util.BitcoinSLogger
case class BitcoindPollingCancellable(
blockPollingCancellable: Cancellable,
mempoolPollingCancelable: Cancellable)
extends Cancellable
mempoolPollingCancelable: Cancellable
) extends Cancellable
with BitcoinSLogger {
override def cancel(): Boolean = {
@ -22,5 +22,6 @@ object BitcoindPollingCancellabe {
val none: BitcoindPollingCancellable = BitcoindPollingCancellable(
Cancellable.alreadyCancelled,
Cancellable.alreadyCancelled)
Cancellable.alreadyCancelled
)
}

View File

@ -18,8 +18,8 @@ import scala.concurrent.Future
object CallbackUtil extends BitcoinSLogger {
def createNeutrinoNodeCallbacksForWallet(
wallet: WalletApi with NeutrinoWalletApi)(implicit
system: ActorSystem): Future[NodeCallbackStreamManager] = {
wallet: WalletApi with NeutrinoWalletApi
)(implicit system: ActorSystem): Future[NodeCallbackStreamManager] = {
import system.dispatcher
val txSink = Sink.foreachAsync[Transaction](1) { case tx: Transaction =>
logger.debug(s"Receiving transaction txid=${tx.txIdBE.hex} as a callback")
@ -32,7 +32,8 @@ object CallbackUtil extends BitcoinSLogger {
Sink.foreachAsync[Vector[(DoubleSha256DigestBE, GolombFilter)]](1) {
case blockFilters: Vector[(DoubleSha256DigestBE, GolombFilter)] =>
logger.debug(
s"Executing onCompactFilters callback with filter count=${blockFilters.length}")
s"Executing onCompactFilters callback with filter count=${blockFilters.length}"
)
wallet
.processCompactFilters(blockFilters = blockFilters)
.map(_ => ())
@ -42,7 +43,8 @@ object CallbackUtil extends BitcoinSLogger {
val blockSink = {
Sink.foreachAsync[Block](1) { case block: Block =>
logger.debug(
s"Executing onBlock callback=${block.blockHeader.hashBE.hex}")
s"Executing onBlock callback=${block.blockHeader.hashBE.hex}"
)
wallet.processBlock(block).map(_ => ())
}
}
@ -50,7 +52,8 @@ object CallbackUtil extends BitcoinSLogger {
val onHeaderSink = {
Sink.foreachAsync(1) { headers: Vector[BlockHeader] =>
logger.debug(
s"Executing block header with header count=${headers.length}")
s"Executing block header with header count=${headers.length}"
)
if (headers.isEmpty) {
Future.unit
} else {
@ -85,18 +88,20 @@ object CallbackUtil extends BitcoinSLogger {
.map(_ => ())
}
val callbacks = NodeCallbacks(onTxReceived = Vector(onTx),
val callbacks = NodeCallbacks(
onTxReceived = Vector(onTx),
onBlockReceived = Vector(onBlock),
onCompactFiltersReceived =
Vector(onCompactFilters),
onBlockHeadersReceived = Vector(onHeaders))
onCompactFiltersReceived = Vector(onCompactFilters),
onBlockHeadersReceived = Vector(onHeaders)
)
val streamManager = NodeCallbackStreamManager(callbacks)
Future.successful(streamManager)
}
def createBitcoindNodeCallbacksForWallet(wallet: DLCNeutrinoHDWalletApi)(
implicit system: ActorSystem): Future[NodeCallbackStreamManager] = {
def createBitcoindNodeCallbacksForWallet(
wallet: DLCNeutrinoHDWalletApi
)(implicit system: ActorSystem): Future[NodeCallbackStreamManager] = {
import system.dispatcher
val txSink = Sink.foreachAsync[Transaction](1) { case tx: Transaction =>
logger.debug(s"Receiving transaction txid=${tx.txIdBE.hex} as a callback")

View File

@ -12,8 +12,8 @@ object ChainUtil {
def getBlockHeaderResult(
hashes: Vector[DoubleSha256DigestBE],
chain: ChainApi)(implicit
ec: ExecutionContext): Future[Vector[GetBlockHeaderResult]] = {
chain: ChainApi
)(implicit ec: ExecutionContext): Future[Vector[GetBlockHeaderResult]] = {
val headersF: Future[Vector[Option[BlockHeaderDb]]] =
chain.getHeaders(hashes)
val bestHeightF = chain.getBestBlockHeader().map(_.height)
@ -30,7 +30,8 @@ object ChainUtil {
headersWithConfs.map {
case None =>
sys.error(
s"Could not find block header or confirmations for the header ")
s"Could not find block header or confirmations for the header "
)
case Some((header, confs)) =>
val chainworkStr = {
val bytes = ByteVector(header.chainWork.toByteArray)

View File

@ -15,12 +15,12 @@ sealed trait WalletHolderWithLoaderApi {
case class WalletHolderWithNeutrinoLoaderApi(
walletHolder: WalletHolder,
loaderApi: DLCWalletNeutrinoBackendLoader)
extends WalletHolderWithLoaderApi
loaderApi: DLCWalletNeutrinoBackendLoader
) extends WalletHolderWithLoaderApi
case class WalletHolderWithBitcoindLoaderApi(
walletHolder: WalletHolder,
loaderApi: DLCWalletBitcoindBackendLoader)
extends WalletHolderWithLoaderApi {
loaderApi: DLCWalletBitcoindBackendLoader
) extends WalletHolderWithLoaderApi {
val bitcoind: BitcoindRpcClient = loaderApi.bitcoind
}

View File

@ -69,8 +69,8 @@ object WebsocketUtil extends BitcoinSLogger {
private def sendHeadersToWs(
notifications: Vector[ChainNotification[_]],
queue: SourceQueueWithComplete[WsNotification[_]])(implicit
ec: ExecutionContext): Future[Unit] = {
queue: SourceQueueWithComplete[WsNotification[_]]
)(implicit ec: ExecutionContext): Future[Unit] = {
for {
_ <- FutureUtil.sequentially(notifications) { case msg =>
val x: Future[Unit] = queue
@ -83,9 +83,11 @@ object WebsocketUtil extends BitcoinSLogger {
def buildChainCallbacks(
queue: SourceQueueWithComplete[WsNotification[_]],
chainApi: ChainApi)(implicit
chainApi: ChainApi
)(implicit
ec: ExecutionContext,
chainAppConfig: ChainAppConfig): ChainCallbacks = {
chainAppConfig: ChainAppConfig
): ChainCallbacks = {
val onBlockProcessed: OnBlockHeaderConnected = {
case headersWithHeight: Vector[(Int, BlockHeader)] =>
val hashes: Vector[DoubleSha256DigestBE] =
@ -166,15 +168,16 @@ object WebsocketUtil extends BitcoinSLogger {
ChainCallbacks.onBlockHeaderConnected(onBlockProcessed) +
ChainCallbacks.onOnSyncFlagChanged(onSyncFlagChanged) +
ChainCallbacks.onCompactFilterHeaderConnected(
onCompactFilterHeaderProcessed) +
onCompactFilterHeaderProcessed
) +
ChainCallbacks.onCompactFilterConnected(onCompactFilterProcessed)
}
/** Builds websocket callbacks for the wallet */
def buildWalletCallbacks(
walletQueue: SourceQueueWithComplete[WsNotification[_]],
walletName: String)(implicit
system: ActorSystem): WalletCallbackStreamManager = {
walletName: String
)(implicit system: ActorSystem): WalletCallbackStreamManager = {
import system.dispatcher
val onAddressCreated: OnNewAddressGenerated = { addr =>
val notification = WalletNotification.NewAddressNotification(addr)
@ -183,15 +186,19 @@ object WebsocketUtil extends BitcoinSLogger {
}
val onTxProcessed: OnTransactionProcessed = { tx =>
buildTxNotification(wsType = WalletWsType.TxProcessed,
buildTxNotification(
wsType = WalletWsType.TxProcessed,
tx = tx,
walletQueue = walletQueue)
walletQueue = walletQueue
)
}
val onTxBroadcast: OnTransactionBroadcast = { tx =>
buildTxNotification(wsType = WalletWsType.TxBroadcast,
buildTxNotification(
wsType = WalletWsType.TxBroadcast,
tx = tx,
walletQueue = walletQueue)
walletQueue = walletQueue
)
}
val onReservedUtxo: OnReservedUtxos = { utxos =>
@ -226,8 +233,9 @@ object WebsocketUtil extends BitcoinSLogger {
WalletCallbackStreamManager(callbacks = callbacks)
}
def buildTorCallbacks(queue: SourceQueueWithComplete[WsNotification[_]])(
implicit ec: ExecutionContext): TorCallbacks = {
def buildTorCallbacks(
queue: SourceQueueWithComplete[WsNotification[_]]
)(implicit ec: ExecutionContext): TorCallbacks = {
val onTorStarted: OnTorStarted = { _ =>
val notification = TorStartedNotification
val offerF = queue.offer(notification)
@ -240,8 +248,8 @@ object WebsocketUtil extends BitcoinSLogger {
private def buildTxNotification(
wsType: WalletWsType,
tx: Transaction,
walletQueue: SourceQueueWithComplete[WsNotification[_]])(implicit
ec: ExecutionContext): Future[Unit] = {
walletQueue: SourceQueueWithComplete[WsNotification[_]]
)(implicit ec: ExecutionContext): Future[Unit] = {
val notification = wsType match {
case WalletWsType.TxProcessed =>
WalletNotification.TxProcessedNotification(tx)
@ -259,8 +267,8 @@ object WebsocketUtil extends BitcoinSLogger {
}
def buildDLCWalletCallbacks(
walletQueue: SourceQueueWithComplete[WsNotification[_]])(implicit
system: ActorSystem): DLCWalletCallbackStreamManager = {
walletQueue: SourceQueueWithComplete[WsNotification[_]]
)(implicit system: ActorSystem): DLCWalletCallbackStreamManager = {
import system.dispatcher
val onStateChange: OnDLCStateChange = { status: DLCStatus =>
val notification = WalletNotification.DLCStateChangeNotification(status)
@ -284,14 +292,15 @@ object WebsocketUtil extends BitcoinSLogger {
import DLCWalletCallbacks._
val callbacks = onDLCStateChange(onStateChange) + onDLCOfferAdd(
onOfferAdd) + onDLCOfferRemove(onOfferRemove)
onOfferAdd
) + onDLCOfferRemove(onOfferRemove)
DLCWalletCallbackStreamManager(callbacks)
}
def buildDLCNodeCallbacks(
walletQueue: SourceQueueWithComplete[WsNotification[_]])(implicit
ec: ExecutionContext): DLCNodeCallbacks = {
walletQueue: SourceQueueWithComplete[WsNotification[_]]
)(implicit ec: ExecutionContext): DLCNodeCallbacks = {
val onConnectionInitiated: OnPeerConnectionInitiated = { payload =>
val notification =

View File

@ -12,7 +12,8 @@ abstract class AsyncUtil extends AsyncUtilApi {
private def retryRunnable(
condition: => Boolean,
p: Promise[Boolean]): Runnable =
p: Promise[Boolean]
): Runnable =
new Runnable {
override def run(): Unit = {
@ -24,8 +25,8 @@ abstract class AsyncUtil extends AsyncUtilApi {
def retryUntilSatisfied(
condition: => Boolean,
interval: FiniteDuration = AsyncUtil.DEFAULT_INTERVAL,
maxTries: Int = DEFAULT_MAX_TRIES)(implicit
ec: ExecutionContext): Future[Unit] = {
maxTries: Int = DEFAULT_MAX_TRIES
)(implicit ec: ExecutionContext): Future[Unit] = {
val f = () => Future(condition)
retryUntilSatisfiedF(f, interval, maxTries)
}
@ -35,32 +36,43 @@ abstract class AsyncUtil extends AsyncUtilApi {
case object Exponential extends RetryMode
/** The returned Future completes when condition becomes true
* @param conditionF The condition being waited on
* @param duration The interval between calls to check condition
* @param maxTries If condition is tried this many times, the Future fails
* @param system An ActorSystem to schedule calls to condition
* @return A Future[Unit] that succeeds if condition becomes true and fails otherwise
* @param conditionF
* The condition being waited on
* @param duration
* The interval between calls to check condition
* @param maxTries
* If condition is tried this many times, the Future fails
* @param system
* An ActorSystem to schedule calls to condition
* @return
* A Future[Unit] that succeeds if condition becomes true and fails
* otherwise
*/
def retryUntilSatisfiedF(
conditionF: () => Future[Boolean],
interval: FiniteDuration = AsyncUtil.DEFAULT_INTERVAL,
maxTries: Int = DEFAULT_MAX_TRIES,
mode: RetryMode = Linear)(implicit ec: ExecutionContext): Future[Unit] = {
mode: RetryMode = Linear
)(implicit ec: ExecutionContext): Future[Unit] = {
if (mode == Exponential) {
val millis = interval.toMillis
if (millis > 0) {
require((millis << maxTries) > 0,
s"Too many tries for retryUntilSatisfied(): $maxTries")
require(
(millis << maxTries) > 0,
s"Too many tries for retryUntilSatisfied(): $maxTries"
)
}
}
val stackTrace: Array[StackTraceElement] =
Thread.currentThread().getStackTrace
retryUntilSatisfiedWithCounter(conditionF = conditionF,
retryUntilSatisfiedWithCounter(
conditionF = conditionF,
interval = interval,
maxTries = maxTries,
stackTrace = stackTrace,
mode = mode)
mode = mode
)
}
// Has a different name so that default values are permitted
@ -70,14 +82,18 @@ abstract class AsyncUtil extends AsyncUtilApi {
counter: Int = 0,
maxTries: Int,
stackTrace: Array[StackTraceElement],
mode: RetryMode)(implicit ec: ExecutionContext): Future[Unit] = {
mode: RetryMode
)(implicit ec: ExecutionContext): Future[Unit] = {
conditionF().flatMap { condition =>
if (condition) {
Future.unit
} else if (counter == maxTries) {
Future.failed(AsyncUtil.RpcRetryException(
Future.failed(
AsyncUtil.RpcRetryException(
s"Condition timed out after $maxTries attempts with interval=$interval waiting periods",
stackTrace))
stackTrace
)
)
} else {
val p = Promise[Boolean]()
val runnable = retryRunnable(condition, p)
@ -92,29 +108,35 @@ abstract class AsyncUtil extends AsyncUtilApi {
p.future.flatMap {
case true => Future.unit
case false =>
retryUntilSatisfiedWithCounter(conditionF = conditionF,
retryUntilSatisfiedWithCounter(
conditionF = conditionF,
interval = interval,
counter = counter + 1,
maxTries = maxTries,
stackTrace = stackTrace,
mode = mode)
mode = mode
)
}
}
}
}
/** Returns a future that resolved when the condition becomes true, the condition
* is checked maxTries times, or overallTimeout is reached
* @param condition The blocking condition
* @param duration The interval between calls to check condition
* @param maxTries If condition is tried this many times, an exception is thrown
* @param system An ActorSystem to schedule calls to condition
/** Returns a future that resolved when the condition becomes true, the
* condition is checked maxTries times, or overallTimeout is reached
* @param condition
* The blocking condition
* @param duration
* The interval between calls to check condition
* @param maxTries
* If condition is tried this many times, an exception is thrown
* @param system
* An ActorSystem to schedule calls to condition
*/
def awaitCondition(
condition: () => Boolean,
interval: FiniteDuration = AsyncUtil.DEFAULT_INTERVAL,
maxTries: Int = DEFAULT_MAX_TRIES)(implicit
ec: ExecutionContext): Future[Unit] = {
maxTries: Int = DEFAULT_MAX_TRIES
)(implicit ec: ExecutionContext): Future[Unit] = {
// type hackery here to go from () => Boolean to () => Future[Boolean]
// to make sure we re-evaluate every time retryUntilSatisfied is called
@ -127,21 +149,25 @@ abstract class AsyncUtil extends AsyncUtilApi {
def awaitConditionF(
conditionF: () => Future[Boolean],
interval: FiniteDuration = AsyncUtil.DEFAULT_INTERVAL,
maxTries: Int = DEFAULT_MAX_TRIES)(implicit
ec: ExecutionContext): Future[Unit] = {
maxTries: Int = DEFAULT_MAX_TRIES
)(implicit ec: ExecutionContext): Future[Unit] = {
retryUntilSatisfiedF(conditionF = conditionF,
retryUntilSatisfiedF(
conditionF = conditionF,
interval = interval,
maxTries = maxTries)
maxTries = maxTries
)
}
override def nonBlockingSleep(duration: FiniteDuration): Future[Unit] = {
val p = Promise[Unit]()
val r: Runnable = () => p.success(())
AsyncUtil.scheduler.scheduleOnce(duration.toMillis,
AsyncUtil.scheduler.scheduleOnce(
duration.toMillis,
TimeUnit.MILLISECONDS,
r)
r
)
p.future
}
}
@ -150,8 +176,8 @@ object AsyncUtil extends AsyncUtil {
case class RpcRetryException(
message: String,
caller: Array[StackTraceElement])
extends Exception(message) {
caller: Array[StackTraceElement]
) extends Exception(message) {
/*
Someone who calls a method in this class will be interested
@ -161,10 +187,12 @@ object AsyncUtil extends AsyncUtil {
*
* This trims the top of the stack trace to exclude these internal calls.
*/
val internalFiles: Vector[String] = Vector("AsyncUtil.scala",
val internalFiles: Vector[String] = Vector(
"AsyncUtil.scala",
"RpcUtil.scala",
"TestAsyncUtil.scala",
"TestRpcUtil.scala")
"TestRpcUtil.scala"
)
private val relevantStackTrace =
caller.tail.dropWhile(elem => internalFiles.contains(elem.getFileName))
@ -182,7 +210,9 @@ object AsyncUtil extends AsyncUtil {
*/
private[bitcoins] val DEFAULT_MAX_TRIES: Int = 50
/** Gives you a thread factory with the given prefix with a counter appended to the name */
/** Gives you a thread factory with the given prefix with a counter appended
* to the name
*/
def getNewThreadFactory(prefix: String): ThreadFactory = {
new ThreadFactory {
private val atomicInteger = new AtomicInteger(0)

View File

@ -13,13 +13,15 @@ import scala.concurrent.Future
import scala.concurrent.duration.DurationInt
import scala.util.{Failure, Success}
/** This test spins up one test node and [[NetworkSize]] sender nodes, which open channels with the test one.
* Then each sender node sends [[PaymentCount]] payments to the test node one by one. For each payment the
* test node generates an invoice and the send node pays it using `sendtonode` API call.
/** This test spins up one test node and [[NetworkSize]] sender nodes, which
* open channels with the test one. Then each sender node sends
* [[PaymentCount]] payments to the test node one by one. For each payment the
* test node generates an invoice and the send node pays it using `sendtonode`
* API call.
*
* The test keeps track of times when a payment was initiated, when the payment ID was received,
* and when the corresponding web socket event was received. It writes all results into [[OutputFileName]]
* in CSV format.
* The test keeps track of times when a payment was initiated, when the payment
* ID was received, and when the corresponding web socket event was received.
* It writes all results into [[OutputFileName]] in CSV format.
*/
object EclairBench extends App with EclairRpcTestUtil {
@ -72,7 +74,8 @@ object EclairBench extends App with EclairRpcTestUtil {
def sendPayments(
network: EclairNetwork,
amount: MilliSatoshis,
count: Int): Future[Vector[PaymentId]] =
count: Int
): Future[Vector[PaymentId]] =
for {
_ <- network.testEclairNode.getInfo
paymentIds <- Future.sequence(network.networkEclairNodes.map { node =>
@ -104,22 +107,26 @@ object EclairBench extends App with EclairRpcTestUtil {
val _ = logEvent(event)
}
_ = println(
s"Set up $NetworkSize nodes, that will send $PaymentCount payments to the test node each")
s"Set up $NetworkSize nodes, that will send $PaymentCount payments to the test node each"
)
_ = println(
s"Test node data directory: ${network.testEclairNode.instance.authCredentials.datadir
.getOrElse("")}")
.getOrElse("")}"
)
_ = println("Testing...")
_ <- sendPayments(network, PaymentAmount, PaymentCount)
_ <- TestAsyncUtil.retryUntilSatisfied(
condition = paymentLog.size() == NetworkSize * PaymentCount,
interval = 1.second,
maxTries = 100)
maxTries = 100
)
_ <-
TestAsyncUtil
.retryUntilSatisfied(
condition = EclairBenchUtil.paymentLogValues().forall(_.completed),
interval = 1.second,
maxTries = 100)
maxTries = 100
)
.recover { case ex: Throwable => ex.printStackTrace() }
_ = println("\nDone!")
} yield {
@ -128,13 +135,15 @@ object EclairBench extends App with EclairRpcTestUtil {
}
val res: Future[Unit] = for {
network <- EclairNetwork.start(TestEclairVersion,
network <- EclairNetwork.start(
TestEclairVersion,
TestEclairCommit,
SenderEclairVersion,
SenderEclairCommit,
NetworkSize,
ChannelAmount,
LogbackXml)
LogbackXml
)
log <- runTests(network).recoverWith { case e: Throwable =>
e.printStackTrace()
Future.successful(Vector.empty[PaymentLogEntry])
@ -145,17 +154,20 @@ object EclairBench extends App with EclairRpcTestUtil {
val first = log.head
val csv =
Vector(
"time,number_of_payments,payment_hash,payment_id,event,payment_sent_at,payment_id_received_at,event_received_at,received_in,completed_in") ++
"time,number_of_payments,payment_hash,payment_id,event,payment_sent_at,payment_id_received_at,event_received_at,received_in,completed_in"
) ++
log.zipWithIndex
.map { case (x, i) =>
s"${x.paymentSentAt - first.paymentSentAt},${i + 1},${x.toCSV}"
}
val outputFile = new File(OutputFileName)
Files.write(outputFile.toPath,
Files.write(
outputFile.toPath,
EclairBenchUtil.convertStrings(csv),
StandardOpenOption.CREATE,
StandardOpenOption.WRITE,
StandardOpenOption.TRUNCATE_EXISTING)
StandardOpenOption.TRUNCATE_EXISTING
)
println(s"The test results was written in ${outputFile.getAbsolutePath}")
}
}

View File

@ -22,11 +22,14 @@ object PaymentLog {
event: Option[WebSocketEvent] = None,
paymentSentAt: Long,
paymentIdReceivedAt: Long = 0,
eventReceivedAt: Long = 0) {
eventReceivedAt: Long = 0
) {
def withPaymentHash(paymentHash: Sha256Digest): PaymentLogEntry =
copy(paymentHash = Some(paymentHash),
paymentSentAt = System.currentTimeMillis())
copy(
paymentHash = Some(paymentHash),
paymentSentAt = System.currentTimeMillis()
)
def withPaymentId(id: PaymentId): PaymentLogEntry =
copy(id = Some(id), paymentIdReceivedAt = System.currentTimeMillis())
@ -47,7 +50,8 @@ object PaymentLog {
part.timestamp.toEpochMilli
case None =>
throw new RuntimeException(
s"PaymentReceived but with no parts, got $e")
s"PaymentReceived but with no parts, got $e"
)
}
case PaymentFailed(_, _, _, timestamp) => timestamp.toEpochMilli
case _: WebSocketEvent =>
@ -61,14 +65,17 @@ object PaymentLog {
.getOrElse("")},${id.map(_.toString).getOrElse("")},${event
.map(_.getClass.getName.split('$').last)
.getOrElse(
"")},$paymentSentAt,$paymentIdReceivedAt,$eventReceivedAt,${paymentIdReceivedAt - paymentSentAt},${eventReceivedAt - paymentIdReceivedAt}"""
""
)},$paymentSentAt,$paymentIdReceivedAt,$eventReceivedAt,${paymentIdReceivedAt - paymentSentAt},${eventReceivedAt - paymentIdReceivedAt}"""
}
object PaymentLogEntry {
def apply(paymentHash: Sha256Digest): PaymentLogEntry = {
PaymentLogEntry(paymentSentAt = System.currentTimeMillis(),
paymentHash = Some(paymentHash))
PaymentLogEntry(
paymentSentAt = System.currentTimeMillis(),
paymentHash = Some(paymentHash)
)
}
}
@ -87,13 +94,15 @@ object PaymentLog {
def logPaymentId(
paymentHash: Sha256Digest,
paymentId: PaymentId): PaymentLogEntry = {
paymentId: PaymentId
): PaymentLogEntry = {
paymentLog.compute(
paymentHash,
new BiFunction[Sha256Digest, PaymentLogEntry, PaymentLogEntry] {
override def apply(
hash: Sha256Digest,
old: PaymentLogEntry): PaymentLogEntry = {
old: PaymentLogEntry
): PaymentLogEntry = {
val log = if (old == null) {
PaymentLogEntry(paymentSentAt = 0, paymentHash = Some(hash))
} else {
@ -121,7 +130,8 @@ object PaymentLog {
new BiFunction[Sha256Digest, PaymentLogEntry, PaymentLogEntry] {
override def apply(
hash: Sha256Digest,
old: PaymentLogEntry): PaymentLogEntry = {
old: PaymentLogEntry
): PaymentLogEntry = {
val log = if (old == null) {
PaymentLogEntry(paymentSentAt = 0, paymentHash = Some(hash))
} else {
@ -136,7 +146,8 @@ object PaymentLog {
new BiFunction[Sha256Digest, Promise[Unit], Promise[Unit]] {
override def apply(
hash: Sha256Digest,
old: Promise[Unit]): Promise[Unit] = {
old: Promise[Unit]
): Promise[Unit] = {
val promise = if (old == null) {
Promise[Unit]()
} else {

View File

@ -39,8 +39,8 @@ class BitcoindInstanceTest extends BitcoindRpcTest {
pw.close()
}
/** Tests that the client can call the isStartedF method
* without throwing and then start
/** Tests that the client can call the isStartedF method without throwing and
* then start
*/
private def testClientStart(client: BitcoindRpcClient): Future[Assertion] =
synchronized {
@ -71,7 +71,8 @@ class BitcoindInstanceTest extends BitcoindRpcTest {
val instance = BitcoindInstanceLocal.fromConfig(conf, newestBitcoindBinary)
assert(
instance.authCredentials
.isInstanceOf[BitcoindAuthCredentials.CookieBased])
.isInstanceOf[BitcoindAuthCredentials.CookieBased]
)
val cli = BitcoindRpcClient.withActorSystem(instance)
testClientStart(cli)
@ -91,7 +92,8 @@ class BitcoindInstanceTest extends BitcoindRpcTest {
val instance = BitcoindInstanceLocal.fromConfig(conf, newestBitcoindBinary)
assert(
instance.authCredentials
.isInstanceOf[BitcoindAuthCredentials.PasswordBased])
.isInstanceOf[BitcoindAuthCredentials.PasswordBased]
)
testClientStart(BitcoindRpcClient.withActorSystem(instance))
}
@ -116,8 +118,10 @@ class BitcoindInstanceTest extends BitcoindRpcTest {
val conf = BitcoindConfig(confStr, FileUtil.tmpDir())
val authCredentials =
BitcoindAuthCredentials.PasswordBased(username = "bitcoin-s",
password = "strong_password")
BitcoindAuthCredentials.PasswordBased(
username = "bitcoin-s",
password = "strong_password"
)
val instance =
BitcoindInstanceLocal(
network = RegTest,
@ -167,8 +171,10 @@ class BitcoindInstanceTest extends BitcoindRpcTest {
val conf = BitcoindConfig(confStr, FileUtil.tmpDir())
val authCredentials =
BitcoindAuthCredentials.PasswordBased(username = "bitcoin-s",
password = "strong_password")
BitcoindAuthCredentials.PasswordBased(
username = "bitcoin-s",
password = "strong_password"
)
val instance =
BitcoindInstanceLocal(
network = RegTest,

View File

@ -35,7 +35,8 @@ class TestRpcUtilTest extends BitcoindFixturesCachedPairNewest {
assert(dir.isDirectory)
assert(dir.getPath().startsWith(scala.util.Properties.tmpDir))
assert(
dir.listFiles.contains(new File(dir.getAbsolutePath + "/bitcoin.conf")))
dir.listFiles.contains(new File(dir.getAbsolutePath + "/bitcoin.conf"))
)
FileUtil.deleteTmpDir(dir)
assert(!dir.exists)
}
@ -63,15 +64,18 @@ class TestRpcUtilTest extends BitcoindFixturesCachedPairNewest {
val allClients = nodes.toVector
for {
heightPreGeneration <- first.getBlockCount()
_ <- BitcoindRpcTestUtil.generateAllAndSync(allClients,
blocks = blocksToGenerate)
_ <- BitcoindRpcTestUtil.generateAllAndSync(
allClients,
blocks = blocksToGenerate
)
firstHash <- first.getBestBlockHash()
secondHash <- second.getBestBlockHash()
heightPostGeneration <- first.getBlockCount()
} yield {
assert(firstHash == secondHash)
assert(
heightPostGeneration - heightPreGeneration == blocksToGenerate * allClients.length)
heightPostGeneration - heightPreGeneration == blocksToGenerate * allClients.length
)
}
}
@ -82,10 +86,12 @@ class TestRpcUtilTest extends BitcoindFixturesCachedPairNewest {
address <- second.getNewAddress
txid <- first.sendToAddress(address, Bitcoins.one)
hashes <- BitcoindRpcTestUtil.generateAndSync(Vector(first, second))
vout <- BitcoindRpcTestUtil.findOutput(first,
vout <- BitcoindRpcTestUtil.findOutput(
first,
txid,
Bitcoins.one,
Some(hashes.head))
Some(hashes.head)
)
tx <- first.getRawTransaction(txid, Some(hashes.head))
} yield {
assert(tx.vout(vout.toInt).value == Bitcoins.one)

View File

@ -230,10 +230,13 @@ class BlockchainRpcTest extends BitcoindFixturesCachedPairNewest {
txs <- Future.sequence(
block.transactions
.filterNot(_.isCoinbase)
.map(tx => client.getTransaction(tx.txIdBE)))
.map(tx => client.getTransaction(tx.txIdBE))
)
prevFilter <- client.getBlockFilter(block.blockHeader.previousBlockHashBE,
FilterType.Basic)
prevFilter <- client.getBlockFilter(
block.blockHeader.previousBlockHashBE,
FilterType.Basic
)
} yield {
val pubKeys = txs.flatMap(_.hex.outputs.map(_.scriptPubKey)).toVector
val filter = BlockFilter(block, pubKeys)
@ -242,7 +245,8 @@ class BlockchainRpcTest extends BitcoindFixturesCachedPairNewest {
blockFilter.header == filter
.getHeader(prevFilter.header.flip)
.hash
.flip)
.flip
)
}
}
}

View File

@ -132,17 +132,21 @@ class MempoolRpcTest extends BitcoindFixturesCachedPairNewest {
for {
_ <- client.generate(1)
address1 <- client.getNewAddress
txid1 <- BitcoindRpcTestUtil.fundMemPoolTransaction(client,
txid1 <- BitcoindRpcTestUtil.fundMemPoolTransaction(
client,
address1,
Bitcoins(2))
Bitcoins(2)
)
mempool <- client.getRawMemPool
address2 <- client.getNewAddress
createdTx <- {
val input: TransactionInput =
TransactionInput(TransactionOutPoint(txid1.flip, UInt32.zero),
TransactionInput(
TransactionOutPoint(txid1.flip, UInt32.zero),
ScriptSignature.empty,
UInt32.max - UInt32.one)
UInt32.max - UInt32.one
)
client
.createRawTransaction(Vector(input), Map(address2 -> Bitcoins.one))
}

View File

@ -37,8 +37,10 @@ class MessageRpcTest extends BitcoindRpcTest {
for {
client <- clientF
signature <- client.signMessageWithPrivKey(privKey.toPrivateKeyBytes(),
message)
signature <- client.signMessageWithPrivKey(
privKey.toPrivateKeyBytes(),
message
)
validity <- client.verifyMessage(address, signature, message)
} yield assert(validity)
}

View File

@ -56,12 +56,15 @@ class MiningRpcTest extends BitcoindRpcTest {
TransactionInput(
TransactionOutPoint(output.txid.flip, UInt32(output.vout)),
ScriptSignature.empty,
UInt32.max - UInt32(2))
UInt32.max - UInt32(2)
)
val inputs = Vector(input)
val outputs =
Map(address -> Bitcoins(0.5),
changeAddress -> Bitcoins(output.amount.toBigDecimal - 0.55))
Map(
address -> Bitcoins(0.5),
changeAddress -> Bitcoins(output.amount.toBigDecimal - 0.55)
)
client.createRawTransaction(inputs, outputs)
}
@ -97,7 +100,9 @@ class MiningRpcTest extends BitcoindRpcTest {
foundBlocks.foreach { case found: GetBlockWithTransactionsResultV22 =>
assert(
found.tx.exists(
_.vout.exists(_.scriptPubKey.address == Some(address))))
_.vout.exists(_.scriptPubKey.address == Some(address))
)
)
}
succeed
}

View File

@ -23,7 +23,9 @@ import java.io.File
import scala.concurrent.Future
import scala.concurrent.duration.DurationInt
/** These tests are all copied over from WalletRpcTest and changed to be for multi-wallet */
/** These tests are all copied over from WalletRpcTest and changed to be for
* multi-wallet
*/
class MultiWalletRpcTest extends BitcoindFixturesCachedPairNewest {
val walletName = "other"
@ -43,9 +45,12 @@ class MultiWalletRpcTest extends BitcoindFixturesCachedPairNewest {
clientsF.flatMap(setupWalletClient)
}
/** We need to test bitcoin core's wallet specific features, so we need to set that up */
private def setupWalletClient(pair: NodePair[BitcoindRpcClient]): Future[
NodePair[BitcoindRpcClient]] = {
/** We need to test bitcoin core's wallet specific features, so we need to set
* that up
*/
private def setupWalletClient(
pair: NodePair[BitcoindRpcClient]
): Future[NodePair[BitcoindRpcClient]] = {
val NodePair(client: BitcoindRpcClient, walletClient: BitcoindRpcClient) =
pair
for {
@ -228,9 +233,11 @@ class MultiWalletRpcTest extends BitcoindFixturesCachedPairNewest {
for {
address <- otherClient.getNewAddress(Some(walletName))
_ <- client.walletPassphrase(password, 1000, Some(walletName))
txid <- client.sendToAddress(address,
txid <- client.sendToAddress(
address,
Bitcoins(1),
walletNameOpt = Some(walletName))
walletNameOpt = Some(walletName)
)
transaction <-
client.getTransaction(txid, walletNameOpt = Some(walletName))
} yield {
@ -248,8 +255,10 @@ class MultiWalletRpcTest extends BitcoindFixturesCachedPairNewest {
_ <- client.walletPassphrase(password, 1000, Some(walletName))
txid <-
client
.sendMany(Map(address1 -> Bitcoins(1), address2 -> Bitcoins(2)),
walletNameOpt = Some(walletName))
.sendMany(
Map(address1 -> Bitcoins(1), address2 -> Bitcoins(2)),
walletNameOpt = Some(walletName)
)
transaction <-
client.getTransaction(txid, walletNameOpt = Some(walletName))
} yield {
@ -286,20 +295,27 @@ class MultiWalletRpcTest extends BitcoindFixturesCachedPairNewest {
for {
firstResult <-
client
.createMultiSig(2,
.createMultiSig(
2,
Vector(privKey1.publicKey, privKey2.publicKey),
AddressType.Bech32,
walletNameOpt = Some(walletName))
walletNameOpt = Some(walletName)
)
address2 = firstResult.address
secondResult <-
client
.importMulti(
Vector(
RpcOpts.ImportMultiRequest(RpcOpts.ImportMultiAddress(address1),
UInt32(0)),
RpcOpts.ImportMultiRequest(RpcOpts.ImportMultiAddress(address2),
UInt32(0))),
RpcOpts.ImportMultiRequest(
RpcOpts.ImportMultiAddress(address1),
UInt32(0)
),
RpcOpts.ImportMultiRequest(
RpcOpts.ImportMultiAddress(address2),
UInt32(0)
)
),
rescan = false,
walletNameOpt = Some(walletName)
)
@ -343,7 +359,9 @@ class MultiWalletRpcTest extends BitcoindFixturesCachedPairNewest {
assert(transaction.inputs.length == 1)
assert(
transaction.outputs.contains(
TransactionOutput(Bitcoins(1), address.scriptPubKey)))
TransactionOutput(Bitcoins(1), address.scriptPubKey)
)
)
}
}

View File

@ -15,8 +15,10 @@ class MultisigRpcTest extends BitcoindRpcTest {
BitcoindRpcTestUtil.instance(versionOpt = Some(BitcoindVersion.newest))
lazy val clientF: Future[BitcoindRpcClient] =
BitcoindRpcTestUtil.startedBitcoindRpcClient(instanceOpt = Some(instance),
clientAccum = clientAccum)
BitcoindRpcTestUtil.startedBitcoindRpcClient(
instanceOpt = Some(instance),
clientAccum = clientAccum
)
behavior of "MultisigRpc"
@ -29,9 +31,11 @@ class MultisigRpcTest extends BitcoindRpcTest {
for {
client <- clientF
_ <- client.createMultiSig(2,
_ <- client.createMultiSig(
2,
Vector(pubKey1, pubKey2),
AddressType.Bech32)
AddressType.Bech32
)
} yield succeed
}

View File

@ -98,14 +98,20 @@ class RawTransactionRpcTest extends BitcoindRpcTest {
address <- otherClient.getNewAddress
input0 = TransactionOutPoint(transaction0.txid.flip,
UInt32(transaction0.blockindex.get))
input1 = TransactionOutPoint(transaction1.txid.flip,
UInt32(transaction1.blockindex.get))
input0 = TransactionOutPoint(
transaction0.txid.flip,
UInt32(transaction0.blockindex.get)
)
input1 = TransactionOutPoint(
transaction1.txid.flip,
UInt32(transaction1.blockindex.get)
)
transaction <- {
val sig: ScriptSignature = ScriptSignature.empty
val inputs = Vector(TransactionInput(input0, sig, UInt32(1)),
TransactionInput(input1, sig, UInt32(2)))
val inputs = Vector(
TransactionInput(input0, sig, UInt32(1)),
TransactionInput(input1, sig, UInt32(2))
)
val outputs = Map(address -> Bitcoins(1))
client.createRawTransaction(inputs, outputs)
}
@ -150,12 +156,16 @@ class RawTransactionRpcTest extends BitcoindRpcTest {
newAddress <- client.getNewAddress
rawCreatedTx <- {
val input =
TransactionInput(TransactionOutPoint(txid.flip, UInt32(output.n)),
TransactionInput(
TransactionOutPoint(txid.flip, UInt32(output.n)),
EmptyScriptSignature,
UInt32.max - UInt32.one)
UInt32.max - UInt32.one
)
client
.createRawTransaction(Vector(input),
Map(newAddress -> Bitcoins(sendAmt.satoshis)))
.createRawTransaction(
Vector(input),
Map(newAddress -> Bitcoins(sendAmt.satoshis))
)
}
result <- {
@ -166,7 +176,8 @@ class RawTransactionRpcTest extends BitcoindRpcTest {
scriptPubKey = ScriptPubKey.fromAsmHex(output.scriptPubKey.hex),
redeemScript = None,
amount = Some(fundAmt)
))
)
)
BitcoindRpcTestUtil.signRawTransaction(
client,
rawCreatedTx,
@ -183,7 +194,8 @@ class RawTransactionRpcTest extends BitcoindRpcTest {
.createRawTransaction(Vector(), Map(address -> Bitcoins(1)))
.flatMap { tx =>
recoverToSucceededIf[InvalidAddressOrKey](
client.abandonTransaction(tx.txId))
client.abandonTransaction(tx.txId)
)
}
}
}

View File

@ -35,8 +35,10 @@ class UTXORpcTest extends BitcoindRpcTest {
val vout1 = unspent(0).vout
val vout2 = unspent(1).vout
Vector(RpcOpts.LockUnspentOutputParameter(txid1, vout1),
RpcOpts.LockUnspentOutputParameter(txid2, vout2))
Vector(
RpcOpts.LockUnspentOutputParameter(txid1, vout1),
RpcOpts.LockUnspentOutputParameter(txid2, vout2)
)
}
firstSuccess <- client.lockUnspent(unlock = false, param)
locked <- client.listLockUnspent

View File

@ -52,7 +52,8 @@ class WalletRpcTest extends BitcoindFixturesCachedPairNewest {
lazy val walletClientF: Future[BitcoindRpcClient] = clientsF.flatMap { _ =>
val walletClient =
BitcoindRpcClient.withActorSystem(
BitcoindRpcTestUtil.instance(versionOpt = Some(BitcoindVersion.newest)))
BitcoindRpcTestUtil.instance(versionOpt = Some(BitcoindVersion.newest))
)
for {
_ <- startClient(walletClient)
@ -126,10 +127,12 @@ class WalletRpcTest extends BitcoindFixturesCachedPairNewest {
for {
_ <- {
val addrFuts =
List(client.getNewAddress,
List(
client.getNewAddress,
client.getNewAddress(AddressType.Bech32),
client.getNewAddress(AddressType.P2SHSegwit),
client.getNewAddress(AddressType.Legacy))
client.getNewAddress(AddressType.Legacy)
)
Future.sequence(addrFuts)
}
} yield succeed
@ -166,8 +169,10 @@ class WalletRpcTest extends BitcoindFixturesCachedPairNewest {
val client = nodePair.node1
for {
balance <- client.getUnconfirmedBalance
transaction <- BitcoindRpcTestUtil.sendCoinbaseTransaction(client,
client)
transaction <- BitcoindRpcTestUtil.sendCoinbaseTransaction(
client,
client
)
newBalance <- client.getUnconfirmedBalance
} yield {
assert(balance == Bitcoins(0))
@ -228,15 +233,18 @@ class WalletRpcTest extends BitcoindFixturesCachedPairNewest {
def getChangeAddressAndAmount(
client: BitcoindRpcClient,
address: BitcoinAddress,
txid: DoubleSha256DigestBE): Future[(BitcoinAddress, CurrencyUnit)] = {
txid: DoubleSha256DigestBE
): Future[(BitcoinAddress, CurrencyUnit)] = {
for {
rawTx <- client.getRawTransactionRaw(txid)
} yield {
val outs = rawTx.outputs.filterNot(_.value == amount)
val changeAddresses = outs
.map(out =>
(BitcoinAddress.fromScriptPubKey(out.scriptPubKey, networkParam),
out.value))
(
BitcoinAddress.fromScriptPubKey(out.scriptPubKey, networkParam),
out.value
))
assert(changeAddresses.size == 1)
assert(changeAddresses.head._1 != address)
(changeAddresses.head._1, changeAddresses.head._2)
@ -248,10 +256,12 @@ class WalletRpcTest extends BitcoindFixturesCachedPairNewest {
address <- client.getNewAddress
txid <- BitcoindRpcTestUtil.fundBlockChainTransaction(client,
txid <- BitcoindRpcTestUtil.fundBlockChainTransaction(
client,
otherClient,
address,
amount)
amount
)
(changeAddress, changeAmount) <-
getChangeAddressAndAmount(client, address, txid)
@ -269,7 +279,8 @@ class WalletRpcTest extends BitcoindFixturesCachedPairNewest {
// the change address should be added to an exiting address grouping
assert(
!groupingsBefore.exists(vec => vec.exists(_.address == changeAddress)))
!groupingsBefore.exists(vec => vec.exists(_.address == changeAddress))
)
val changeGroupingOpt =
groupingsAfter.find(vec => vec.exists(_.address == changeAddress))
@ -324,10 +335,12 @@ class WalletRpcTest extends BitcoindFixturesCachedPairNewest {
address <- otherClient.getNewAddress
txid <-
BitcoindRpcTestUtil
.fundBlockChainTransaction(client,
.fundBlockChainTransaction(
client,
otherClient,
address,
Bitcoins(1.5))
Bitcoins(1.5)
)
receivedList <- otherClient.listReceivedByAddress()
} yield {
val entryList =
@ -348,10 +361,12 @@ class WalletRpcTest extends BitcoindFixturesCachedPairNewest {
address <- otherClient.getNewAddress
txid <-
BitcoindRpcTestUtil
.fundBlockChainTransaction(client,
.fundBlockChainTransaction(
client,
otherClient,
address,
Bitcoins(1.5))
Bitcoins(1.5)
)
txs <- otherClient.listTransactions()
} yield {
assert(txs.nonEmpty)
@ -419,12 +434,15 @@ class WalletRpcTest extends BitcoindFixturesCachedPairNewest {
TransactionInput(
TransactionOutPoint(output.txid.flip, UInt32(output.vout)),
ScriptSignature.empty,
UInt32.max - UInt32(2))
UInt32.max - UInt32(2)
)
val inputs = Vector(input)
val outputs =
Map(address -> Bitcoins(0.5),
changeAddress -> Bitcoins(output.amount.toBigDecimal - 0.55))
Map(
address -> Bitcoins(0.5),
changeAddress -> Bitcoins(output.amount.toBigDecimal - 0.55)
)
client.createRawTransaction(inputs, outputs)
}
@ -453,7 +471,9 @@ class WalletRpcTest extends BitcoindFixturesCachedPairNewest {
} yield {
assert(
transaction.outputs.contains(
TransactionOutput(Bitcoins(1), address.scriptPubKey)))
TransactionOutput(Bitcoins(1), address.scriptPubKey)
)
)
}
}
@ -467,23 +487,31 @@ class WalletRpcTest extends BitcoindFixturesCachedPairNewest {
val spk = P2WPKHWitnessSPKV0(privKey.publicKey)
val importedAddress = Bech32Address.fromScriptPubKey(spk, np)
for {
fundingTxId <- otherClient.sendToAddress(importedAddress,
Bitcoins(1.01))
fundingTxId <- otherClient.sendToAddress(
importedAddress,
Bitcoins(1.01)
)
_ <- otherClient.generate(1)
vout <- otherClient
.getRawTransactionRaw(fundingTxId)
.map(_.outputs.zipWithIndex.find(
_._1.scriptPubKey == descriptor.scriptPubKey))
.map(
_.outputs.zipWithIndex
.find(_._1.scriptPubKey == descriptor.scriptPubKey)
)
.map(_.get._2)
fundingPrevOut = TransactionOutPoint(fundingTxId, vout)
fundingInput = TransactionInput(fundingPrevOut,
fundingInput = TransactionInput(
fundingPrevOut,
ScriptSignature.empty,
TransactionConstants.sequence)
TransactionConstants.sequence
)
address <- otherClient.getNewAddress
transaction <-
client
.createRawTransaction(inputs = Vector(fundingInput),
outputs = Map(address -> Bitcoins.one))
.createRawTransaction(
inputs = Vector(fundingInput),
outputs = Map(address -> Bitcoins.one)
)
signedTx <- client
.signRawTransactionWithKey(transaction, Vector(privKey))
.map(_.hex)
@ -499,7 +527,8 @@ class WalletRpcTest extends BitcoindFixturesCachedPairNewest {
P2WPKHV0InputInfo(outPoint, output.value, privKey.publicKey),
prevTx,
privKey,
HashType.sigHashAll),
HashType.sigHashAll
),
transaction,
isDummySignature = false
)
@ -507,7 +536,8 @@ class WalletRpcTest extends BitcoindFixturesCachedPairNewest {
signedTx match {
case btx: NonWitnessTransaction =>
assert(
btx.inputs.head.scriptSignature.signatures.head == partialSig.signature)
btx.inputs.head.scriptSignature.signatures.head == partialSig.signature
)
case wtx: WitnessTransaction =>
wtx.witness.head match {
case p2wpkh: P2WPKHWitnessV0 =>
@ -552,7 +582,8 @@ class WalletRpcTest extends BitcoindFixturesCachedPairNewest {
val psbt =
PSBT.fromBase64(
"cHNidP8BACoCAAAAAAFAQg8AAAAAABepFG6Rty1Vk+fUOR4v9E6R6YXDFkHwhwAAAAAAAA==")
"cHNidP8BACoCAAAAAAFAQg8AAAAAABepFG6Rty1Vk+fUOR4v9E6R6YXDFkHwhwAAAAAAAA=="
)
for {
result <- client.utxoUpdatePsbt(psbt, Seq(descriptor))
@ -578,15 +609,19 @@ class WalletRpcTest extends BitcoindFixturesCachedPairNewest {
val pubKey2 = ECPublicKey.freshPublicKey
for {
multiSigResult <- client.createMultiSig(2,
multiSigResult <- client.createMultiSig(
2,
Vector(pubKey1, pubKey2),
AddressType.Bech32)
AddressType.Bech32
)
} yield {
// just validate we are able to receive a sane descriptor
// no need to check checksum
assert(
multiSigResult.descriptor.startsWith(
s"wsh(multi(2,${pubKey1.hex},${pubKey2.hex}))#"))
s"wsh(multi(2,${pubKey1.hex},${pubKey2.hex}))#"
)
)
}
}

View File

@ -17,10 +17,12 @@ class BitcoindConfigTest extends BitcoinSUnitTest {
}
it must "parse networks" in {
val conf = BitcoindConfig("""
val conf = BitcoindConfig(
"""
|regtest=1
""".stripMargin,
tmpDir)
tmpDir
)
assert(conf.network == RegTest)
}

View File

@ -6,7 +6,8 @@ import org.bitcoins.rpc.client.common.BitcoindRpcClient
import org.bitcoins.testkit.rpc.BitcoindFixturesFundedCachedNewest
/** Tests for PSBT for RPC calls specific to V18 new PSBT calls
* @see https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#test-vectors
* @see
* https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#test-vectors
*/
class PsbtRpcTest extends BitcoindFixturesFundedCachedNewest {
@ -75,7 +76,8 @@ class PsbtRpcTest extends BitcoindFixturesFundedCachedNewest {
client: BitcoindRpcClient =>
val psbt =
PSBT.fromBase64(
"cHNidP8BACoCAAAAAAFAQg8AAAAAABepFG6Rty1Vk+fUOR4v9E6R6YXDFkHwhwAAAAAAAA==")
"cHNidP8BACoCAAAAAAFAQg8AAAAAABepFG6Rty1Vk+fUOR4v9E6R6YXDFkHwhwAAAAAAAA=="
)
val updatedF = client.utxoUpdatePsbt(psbt)
updatedF.map { result =>
@ -83,15 +85,17 @@ class PsbtRpcTest extends BitcoindFixturesFundedCachedNewest {
}
}
/** Join psbt looks at the characteristics of a vector of PSBTs and converts them into a singular PSBT.
* This test takes test vectors from BIP 157 each missing some characteristic covered by the other. When joined
* together the resulting PSBT represented as a string is very different so we can't just search for parts of either
* PSBT.
/** Join psbt looks at the characteristics of a vector of PSBTs and converts
* them into a singular PSBT. This test takes test vectors from BIP 157 each
* missing some characteristic covered by the other. When joined together the
* resulting PSBT represented as a string is very different so we can't just
* search for parts of either PSBT.
*/
it should "joinpsbts" in { client: BitcoindRpcClient =>
val seqofpsbts = Vector(
PSBT.fromBase64(
"cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAAAA"),
"cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAAAA"
),
// PSBT with one P2PKH input and one P2SH-P2WPKH input both with non-final scriptSigs. P2SH-P2WPKH input's redeemScript is available. Outputs filled.
PSBT.fromBase64(
"cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEA3wIAAAABJoFxNx7f8oXpN63upLN7eAAMBWbLs61kZBcTykIXG/YAAAAAakcwRAIgcLIkUSPmv0dNYMW1DAQ9TGkaXSQ18Jo0p2YqncJReQoCIAEynKnazygL3zB0DsA5BCJCLIHLRYOUV663b8Eu3ZWzASECZX0RjTNXuOD0ws1G23s59tnDjZpwq8ubLeXcjb/kzjH+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIACICAurVlmh8qAYEPtw94RbN8p1eklfBls0FXPaYyNAr8k6ZELSmumcAAACAAAAAgAIAAIAAIgIDlPYr6d8ZlSxVh3aK63aYBhrSxKJciU9H2MFitNchPQUQtKa6ZwAAAIABAACAAgAAgAA="

View File

@ -148,7 +148,8 @@ class BitcoindV22RpcClientTest extends BitcoindFixturesCachedPairV22 {
resultVecF.map { resultVec =>
resultVec.foreach { result =>
assert(
result.network == "ipv4" || result.network == "ipv6" || result.network == "onion" || result.network == "i2p")
result.network == "ipv4" || result.network == "ipv6" || result.network == "onion" || result.network == "i2p"
)
}
succeed
}
@ -198,7 +199,8 @@ class BitcoindV22RpcClientTest extends BitcoindFixturesCachedPairV22 {
client
.addMultiSigAddress(
2,
Vector(Left(pubKey1), Right(address.asInstanceOf[P2PKHAddress])))
Vector(Left(pubKey1), Right(address.asInstanceOf[P2PKHAddress]))
)
decoded <- client.decodeScript(multisig.redeemScript)
_ <- client.loadWallet("")
_ <- client.unloadWallet("decodeRWallet")
@ -222,22 +224,29 @@ class BitcoindV22RpcClientTest extends BitcoindFixturesCachedPairV22 {
address <- otherClient.getNewAddress
input0 = TransactionOutPoint(transaction0.txid.flip,
UInt32(transaction0.blockindex.get))
input1 = TransactionOutPoint(transaction1.txid.flip,
UInt32(transaction1.blockindex.get))
input0 = TransactionOutPoint(
transaction0.txid.flip,
UInt32(transaction0.blockindex.get)
)
input1 = TransactionOutPoint(
transaction1.txid.flip,
UInt32(transaction1.blockindex.get)
)
transactionFirst <- {
val sig: ScriptSignature = ScriptSignature.empty
val inputs = Vector(TransactionInput(input0, sig, UInt32(1)),
TransactionInput(input1, sig, UInt32(2)))
val inputs = Vector(
TransactionInput(input0, sig, UInt32(1)),
TransactionInput(input1, sig, UInt32(2))
)
val outputs = Map(address -> Bitcoins(1))
client.createRawTransaction(inputs, outputs)
}
fundedTransactionOne <- client.fundRawTransaction(transactionFirst)
signedTransactionOne <- BitcoindRpcTestUtil.signRawTransaction(
client,
fundedTransactionOne.hex)
fundedTransactionOne.hex
)
blocksTwo <- client.generate(2)
firstBlockTwo <- client.getBlock(blocksTwo(0))
@ -245,27 +254,35 @@ class BitcoindV22RpcClientTest extends BitcoindFixturesCachedPairV22 {
secondBlockTwo <- client.getBlock(blocksTwo(1))
transaction3 <- client.getTransaction(secondBlockTwo.tx(0))
input2 = TransactionOutPoint(transaction2.txid.flip,
UInt32(transaction2.blockindex.get))
input3 = TransactionOutPoint(transaction3.txid.flip,
UInt32(transaction3.blockindex.get))
input2 = TransactionOutPoint(
transaction2.txid.flip,
UInt32(transaction2.blockindex.get)
)
input3 = TransactionOutPoint(
transaction3.txid.flip,
UInt32(transaction3.blockindex.get)
)
transactionSecond <- {
val sig: ScriptSignature = ScriptSignature.empty
val inputs = Vector(TransactionInput(input2, sig, UInt32(1)),
TransactionInput(input3, sig, UInt32(2)))
val inputs = Vector(
TransactionInput(input2, sig, UInt32(1)),
TransactionInput(input3, sig, UInt32(2))
)
val outputs = Map(address -> Bitcoins(1))
client.createRawTransaction(inputs, outputs)
}
fundedTransactionTwo <- client.fundRawTransaction(transactionSecond)
signedTransactionTwo <- BitcoindRpcTestUtil.signRawTransaction(
client,
fundedTransactionTwo.hex)
fundedTransactionTwo.hex
)
_ <- client.generate(100) // Can't spend until depth 100
mempoolAccept <- client.testMempoolAccept(
Vector(signedTransactionOne.hex, signedTransactionTwo.hex))
Vector(signedTransactionOne.hex, signedTransactionTwo.hex)
)
} yield {
val mempooltxid: Int = mempoolAccept.length
assert(mempooltxid > 1)

View File

@ -81,7 +81,8 @@ class BitcoindV23RpcClientTest extends BitcoindFixturesFundedCachedV23 {
it should "analyze a descriptor" in { client =>
val descriptor =
Descriptor.fromString(
"pk(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)#gn28ywm7")
"pk(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)#gn28ywm7"
)
val descriptorF = client.getDescriptorInfo(descriptor)
descriptorF.map { result =>

View File

@ -42,7 +42,8 @@ class BitcoindV24RpcClientTest extends BitcoindFixturesFundedCachedV24 {
def indexSynced(client: BitcoindRpcClient): Future[Boolean] = {
client.getIndexInfo.map { indexes =>
indexes("txindex").best_block_height == 101 && indexes(
"basic block filter index").best_block_height == 101
"basic block filter index"
).best_block_height == 101
}
}
for {
@ -112,7 +113,8 @@ class BitcoindV24RpcClientTest extends BitcoindFixturesFundedCachedV24 {
it should "analyze a descriptor" in { client =>
val descriptor =
Descriptor.fromString(
"pk(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)#gn28ywm7")
"pk(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)#gn28ywm7"
)
val descriptorF = client.getDescriptorInfo(descriptor)
@ -165,7 +167,8 @@ class BitcoindV24RpcClientTest extends BitcoindFixturesFundedCachedV24 {
client.deriveAddresses(descriptor0, None).map(_.addresses)
val expected0 =
Vector("bcrt1qjqmxmkpmxt80xz4y3746zgt0q3u3ferr34acd5").map(
BitcoinAddress.fromString)
BitcoinAddress.fromString
)
val assert0 = addresses0F.map { addresses =>
assert(addresses == expected0)
}
@ -178,9 +181,11 @@ class BitcoindV24RpcClientTest extends BitcoindFixturesFundedCachedV24 {
val addresses1F =
client.deriveAddresses(descriptor1, Some(Vector(0, 2))).map(_.addresses)
val expected1 =
Vector("bcrt1qjqmxmkpmxt80xz4y3746zgt0q3u3ferr34acd5",
Vector(
"bcrt1qjqmxmkpmxt80xz4y3746zgt0q3u3ferr34acd5",
"bcrt1qhku5rq7jz8ulufe2y6fkcpnlvpsta7rq4442dy",
"bcrt1qpgptk2gvshyl0s9lqshsmx932l9ccsv265tvaq")
"bcrt1qpgptk2gvshyl0s9lqshsmx932l9ccsv265tvaq"
)
.map(BitcoinAddress.fromString)
val assert1 = assert0.flatMap(_ =>
@ -195,12 +200,14 @@ class BitcoindV24RpcClientTest extends BitcoindFixturesFundedCachedV24 {
val str1 =
"wpkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/*)#kft60nuy"
val descriptor = Descriptor.fromString(str1)
val imp = DescriptorsResult(desc = descriptor,
val imp = DescriptorsResult(
desc = descriptor,
timestamp = Instant.now().getEpochSecond,
active = true,
internal = None,
range = Some(Vector(0, 2)),
next = None)
next = None
)
val resultF = client.importDescriptors(Vector(imp))
@ -243,7 +250,8 @@ class BitcoindV24RpcClientTest extends BitcoindFixturesFundedCachedV24 {
}
prevFilter <- client.getBlockFilter(
block.blockHeader.previousBlockHashBE,
FilterType.Basic)
FilterType.Basic
)
} yield {
val pubKeys = fundingOutputs.map(_.scriptPubKey).toVector
val filter = BlockFilter(block, pubKeys)
@ -251,7 +259,8 @@ class BitcoindV24RpcClientTest extends BitcoindFixturesFundedCachedV24 {
assert(
blockFilter.header == filter
.getHeader(prevFilter.header.flip)
.hashBE)
.hashBE
)
}
}
@ -264,7 +273,8 @@ class BitcoindV24RpcClientTest extends BitcoindFixturesFundedCachedV24 {
val blockReward = 50
assert(immatureBalance.mine.immature.toBigDecimal >= 0)
assert(
immatureBalance.mine.trusted.toBigDecimal + blockReward == newImmatureBalance.mine.trusted.toBigDecimal)
immatureBalance.mine.trusted.toBigDecimal + blockReward == newImmatureBalance.mine.trusted.toBigDecimal
)
}
}
@ -313,7 +323,8 @@ class BitcoindV24RpcClientTest extends BitcoindFixturesFundedCachedV24 {
val psbt =
PSBT.fromBase64(
"cHNidP8BACoCAAAAAAFAQg8AAAAAABepFG6Rty1Vk+fUOR4v9E6R6YXDFkHwhwAAAAAAAA==")
"cHNidP8BACoCAAAAAAFAQg8AAAAAABepFG6Rty1Vk+fUOR4v9E6R6YXDFkHwhwAAAAAAAA=="
)
for {
result <- client.utxoUpdatePsbt(psbt, Seq(descriptor))
@ -328,15 +339,19 @@ class BitcoindV24RpcClientTest extends BitcoindFixturesFundedCachedV24 {
val pubKey2 = ECPublicKey.freshPublicKey
for {
multiSigResult <- client.createMultiSig(2,
multiSigResult <- client.createMultiSig(
2,
Vector(pubKey1, pubKey2),
AddressType.Bech32)
AddressType.Bech32
)
} yield {
// just validate we are able to receive a sane descriptor
// no need to check checksum
assert(
multiSigResult.descriptor.startsWith(
s"wsh(multi(2,${pubKey1.hex},${pubKey2.hex}))#"))
s"wsh(multi(2,${pubKey1.hex},${pubKey2.hex}))#"
)
)
}
}

View File

@ -5,10 +5,10 @@ import play.api.libs.json.{JsResult, JsValue}
import play.api.libs.json.JsError
import play.api.libs.json.JsSuccess
/** Represents failures that can happen when using the
* `bitcoind` RPC interface.
/** Represents failures that can happen when using the `bitcoind` RPC interface.
*
* @see [[https://github.com/bitcoin/bitcoin/blob/eb7daf4d600eeb631427c018a984a77a34aca66e/src/rpc/protocol.h#L32 protcol.h]]
* @see
* [[https://github.com/bitcoin/bitcoin/blob/eb7daf4d600eeb631427c018a984a77a34aca66e/src/rpc/protocol.h#L32 protcol.h]]
* for an enumeration of all error codes used
*/
sealed abstract class BitcoindException(private val message: String)
@ -19,7 +19,8 @@ sealed abstract class BitcoindException(private val message: String)
/** Wallet errors from `bitcoind` RPC calls
*
* @see [[https://github.com/bitcoin/bitcoin/blob/eb7daf4d600eeb631427c018a984a77a34aca66e/src/rpc/protocol.h#L32 protcol.h]]
* @see
* [[https://github.com/bitcoin/bitcoin/blob/eb7daf4d600eeb631427c018a984a77a34aca66e/src/rpc/protocol.h#L32 protcol.h]]
* for an enumeration of all error codes used
*/
@ -36,7 +37,8 @@ object BitcoindException {
exception <- BitcoindException.fromCodeAndMessage(code, message) match {
case None =>
JsError(
s"Could not construct bitcoind exception with code $code and message '$message'")
s"Could not construct bitcoind exception with code $code and message '$message'"
)
case Some(value) => JsSuccess(value)
}
} yield exception
@ -78,10 +80,12 @@ object BitcoindException {
NotSpecified(_)
)
/** Attempts to construct a BitcoindException from the given code and message */
/** Attempts to construct a BitcoindException from the given code and message
*/
def fromCodeAndMessage(
code: Int,
message: String): Option[BitcoindException] = {
message: String
): Option[BitcoindException] = {
val constructorOpt = all.find(func => func(message).code == code)
@ -93,8 +97,8 @@ object BitcoindException {
val code: Int = -32602
}
/** InternalError is only used for genuine errors in bitcoind
* (for example datadir corruption)
/** InternalError is only used for genuine errors in bitcoind (for example
* datadir corruption)
*/
final case class InternalError(private val message: String)
extends BitcoindException(message) {
@ -159,7 +163,8 @@ object BitcoindException {
extends BitcoindException(
if (message == "non-final")
"Transaction is not final. Try again in a bit."
else message) {
else message
) {
val code: Int = -26
}
@ -190,7 +195,8 @@ object BitcoindException {
/** P2P client errors
*
* @see [[https://github.com/bitcoin/bitcoin/blob/eb7daf4d600eeb631427c018a984a77a34aca66e/src/rpc/protocol.h#L32 protcol.h]]
* @see
* [[https://github.com/bitcoin/bitcoin/blob/eb7daf4d600eeb631427c018a984a77a34aca66e/src/rpc/protocol.h#L32 protcol.h]]
* for an enumeration of all error codes used
*/
sealed abstract class BitcoindP2PException(private val message: String)
@ -282,7 +288,9 @@ object BitcoindWalletException {
val code: Int = -14
}
/** Command given in wrong wallet encryption state (encrypting an encrypted wallet etc.) */
/** Command given in wrong wallet encryption state (encrypting an encrypted
* wallet etc.)
*/
final case class WrongEncState(private val message: String)
extends BitcoindWalletException(message) {
val code: Int = -15

Some files were not shown because too many files have changed in this diff Show More