mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2024-11-19 01:40:55 +01:00
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:
parent
9442dba217
commit
afddf73c48
@ -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
|
||||
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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", ""))
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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]
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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 = {
|
||||
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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(
|
||||
|
@ -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]
|
||||
|
@ -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]
|
||||
|
@ -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]
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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]] =
|
||||
|
@ -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))
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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 = {
|
||||
|
@ -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)
|
||||
|
@ -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]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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) {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
@ -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}"""
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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(_))
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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(_))
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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`,
|
||||
|
@ -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
|
||||
|
@ -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]
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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`)
|
||||
|
@ -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}"""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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)
|
||||
|
@ -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}"""
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}))
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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"))
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
()
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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?
|
||||
|
@ -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)
|
||||
|
@ -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"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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 = {
|
||||
|
@ -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] = {
|
||||
|
@ -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
|
||||
)
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 =
|
||||
|
@ -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)
|
||||
|
@ -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}")
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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}))#"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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="
|
||||
|
@ -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)
|
||||
|
@ -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 =>
|
||||
|
@ -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}))#"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user