Skip to content

Commit

Permalink
Use bitcoin-lib 0.34 (#2905)
Browse files Browse the repository at this point in the history
* Use bitcoin-lib 0.34

Includes support for testnet4.
  • Loading branch information
sstone authored Sep 10, 2024
1 parent d726ca1 commit 1ff5697
Show file tree
Hide file tree
Showing 24 changed files with 128 additions and 95 deletions.
4 changes: 3 additions & 1 deletion eclair-core/src/main/scala/fr/acinq/eclair/NodeParams.scala
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,9 @@ object NodeParams extends Logging {

private val chain2Hash: Map[String, BlockHash] = Map(
"regtest" -> Block.RegtestGenesisBlock.hash,
"testnet" -> Block.TestnetGenesisBlock.hash,
"testnet" -> Block.Testnet3GenesisBlock.hash,
"testnet3" -> Block.Testnet3GenesisBlock.hash,
"testnet4" -> Block.Testnet4GenesisBlock.hash,
"signet" -> Block.SignetGenesisBlock.hash,
"mainnet" -> Block.LivenetGenesisBlock.hash
)
Expand Down
1 change: 1 addition & 0 deletions eclair-core/src/main/scala/fr/acinq/eclair/Setup.scala
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ class Setup(val datadir: File,
_ <- chain match {
case "mainnet" => bitcoinClient.invoke("getrawtransaction", "2157b554dcfda405233906e461ee593875ae4b1b97615872db6a25130ecc1dd6") // coinbase of #500000
case "testnet" => bitcoinClient.invoke("getrawtransaction", "8f38a0dd41dc0ae7509081e262d791f8d53ed6f884323796d5ec7b0966dd3825") // coinbase of #1500000
case "testnet4" => bitcoinClient.invoke("getrawtransaction", "5c50d460b3b98ea0c70baa0f50d1f0cc6ffa553788b4a7e23918bcdd558828fa") // coinbase of #40000
case "signet" => bitcoinClient.invoke("getrawtransaction", "ff1027486b628b2d160859205a3401fb2ee379b43527153b0b50a92c17ee7955") // coinbase of #5000
case "regtest" => Future.successful(())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ object ExplorerApi {
case class BlockcypherExplorer()(implicit val sb: SttpBackend[Future, _]) extends Explorer {
override val name = "blockcypher.com"
override val baseUris = Map(
Block.TestnetGenesisBlock.hash -> uri"https://api.blockcypher.com/v1/btc/test3",
Block.Testnet3GenesisBlock.hash -> uri"https://api.blockcypher.com/v1/btc/test3",
Block.LivenetGenesisBlock.hash -> uri"https://api.blockcypher.com/v1/btc/main"
)

Expand Down Expand Up @@ -208,12 +208,12 @@ object ExplorerApi {
override val name = "blockstream.info"
override val baseUris = if (useTorEndpoints) {
Map(
Block.TestnetGenesisBlock.hash -> uri"http://explorerzydxu5ecjrkwceayqybizmpjjznk5izmitf2modhcusuqlid.onion/testnet/api",
Block.Testnet3GenesisBlock.hash -> uri"http://explorerzydxu5ecjrkwceayqybizmpjjznk5izmitf2modhcusuqlid.onion/testnet/api",
Block.LivenetGenesisBlock.hash -> uri"http://explorerzydxu5ecjrkwceayqybizmpjjznk5izmitf2modhcusuqlid.onion/api"
)
} else {
Map(
Block.TestnetGenesisBlock.hash -> uri"https://blockstream.info/testnet/api",
Block.Testnet3GenesisBlock.hash -> uri"https://blockstream.info/testnet/api",
Block.LivenetGenesisBlock.hash -> uri"https://blockstream.info/api"
)
}
Expand All @@ -224,13 +224,15 @@ object ExplorerApi {
override val name = "mempool.space"
override val baseUris = if (useTorEndpoints) {
Map(
Block.TestnetGenesisBlock.hash -> uri"http://mempoolhqx4isw62xs7abwphsq7ldayuidyx2v2oethdhhj6mlo2r6ad.onion/testnet/api",
Block.Testnet4GenesisBlock.hash -> uri"http://mempoolhqx4isw62xs7abwphsq7ldayuidyx2v2oethdhhj6mlo2r6ad.onion/testnet4/api",
Block.Testnet3GenesisBlock.hash -> uri"http://mempoolhqx4isw62xs7abwphsq7ldayuidyx2v2oethdhhj6mlo2r6ad.onion/testnet/api",
Block.LivenetGenesisBlock.hash -> uri"http://mempoolhqx4isw62xs7abwphsq7ldayuidyx2v2oethdhhj6mlo2r6ad.onion/api",
Block.SignetGenesisBlock.hash -> uri"http://mempoolhqx4isw62xs7abwphsq7ldayuidyx2v2oethdhhj6mlo2r6ad.onion/signet/api"
)
} else {
Map(
Block.TestnetGenesisBlock.hash -> uri"https://mempool.space/testnet/api",
Block.Testnet4GenesisBlock.hash -> uri"https://mempool.space/testnet4/api",
Block.Testnet3GenesisBlock.hash -> uri"https://mempool.space/testnet/api",
Block.LivenetGenesisBlock.hash -> uri"https://mempool.space/api",
Block.SignetGenesisBlock.hash -> uri"https://mempool.space/signet/api"
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -359,8 +359,17 @@ private class ReplaceableTxFunder(nodeParams: NodeParams,

// We create a PSBT with the non-wallet input already signed:
val psbt = new Psbt(locallySignedTx.txInfo.tx)
.updateWitnessInput(locallySignedTx.txInfo.input.outPoint, locallySignedTx.txInfo.input.txOut, null, fr.acinq.bitcoin.Script.parse(locallySignedTx.txInfo.input.redeemScript), fr.acinq.bitcoin.SigHash.SIGHASH_ALL, java.util.Map.of())
.flatMap(_.finalizeWitnessInput(0, locallySignedTx.txInfo.tx.txIn.head.witness))
.updateWitnessInput(
locallySignedTx.txInfo.input.outPoint,
locallySignedTx.txInfo.input.txOut,
null,
fr.acinq.bitcoin.Script.parse(locallySignedTx.txInfo.input.redeemScript),
fr.acinq.bitcoin.SigHash.SIGHASH_ALL,
java.util.Map.of(),
null,
null,
java.util.Map.of()
).flatMap(_.finalizeWitnessInput(0, locallySignedTx.txInfo.tx.txIn.head.witness))
psbt match {
case Left(failure) =>
log.error(s"cannot sign ${cmd.desc}: $failure")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import scodec.bits.ByteVector

object LocalChannelKeyManager {
def keyBasePath(chainHash: BlockHash): List[Long] = (chainHash: @unchecked) match {
case Block.RegtestGenesisBlock.hash | Block.TestnetGenesisBlock.hash | Block.SignetGenesisBlock.hash => DeterministicWallet.hardened(46) :: DeterministicWallet.hardened(1) :: Nil
case Block.RegtestGenesisBlock.hash | Block.Testnet4GenesisBlock.hash | Block.Testnet3GenesisBlock.hash | Block.SignetGenesisBlock.hash => DeterministicWallet.hardened(46) :: DeterministicWallet.hardened(1) :: Nil
case Block.LivenetGenesisBlock.hash => DeterministicWallet.hardened(47) :: DeterministicWallet.hardened(1) :: Nil
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ object LocalNodeKeyManager {
// Note that the node path and the above channel path are on different branches so even if the
// node key is compromised there is no way to retrieve the wallet keys
def keyBasePath(chainHash: BlockHash): List[Long] = (chainHash: @unchecked) match {
case Block.RegtestGenesisBlock.hash | Block.TestnetGenesisBlock.hash | Block.SignetGenesisBlock.hash => DeterministicWallet.hardened(46) :: DeterministicWallet.hardened(0) :: Nil
case Block.RegtestGenesisBlock.hash | Block.Testnet3GenesisBlock.hash | Block.Testnet4GenesisBlock.hash | Block.SignetGenesisBlock.hash => DeterministicWallet.hardened(46) :: DeterministicWallet.hardened(0) :: Nil
case Block.LivenetGenesisBlock.hash => DeterministicWallet.hardened(47) :: DeterministicWallet.hardened(0) :: Nil
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,15 @@ class LocalOnChainKeyManager(override val walletName: String, seed: ByteVector,
private val fingerPrintHex = String.format("%8s", fingerprint.toHexString).replace(' ', '0')
// Root BIP32 on-chain path: we use BIP84 (p2wpkh) paths: m/84h/{0h/1h}
private val rootPath = chainHash match {
case Block.RegtestGenesisBlock.hash | Block.TestnetGenesisBlock.hash | Block.SignetGenesisBlock.hash => "84h/1h"
case Block.RegtestGenesisBlock.hash | Block.Testnet3GenesisBlock.hash | Block.Testnet4GenesisBlock.hash | Block.SignetGenesisBlock.hash => "84h/1h"
case Block.LivenetGenesisBlock.hash => "84h/0h"
case _ => throw new IllegalArgumentException(s"invalid chain hash $chainHash")
}
private val rootKey = DeterministicWallet.derivePrivateKey(master, KeyPath(rootPath))

override def masterPubKey(account: Long): String = {
val prefix = chainHash match {
case Block.RegtestGenesisBlock.hash | Block.TestnetGenesisBlock.hash | Block.SignetGenesisBlock.hash => vpub
case Block.RegtestGenesisBlock.hash | Block.Testnet3GenesisBlock.hash | Block.Testnet4GenesisBlock.hash | Block.SignetGenesisBlock.hash => vpub
case Block.LivenetGenesisBlock.hash => zpub
case _ => throw new IllegalArgumentException(s"invalid chain hash $chainHash")
}
Expand Down Expand Up @@ -202,7 +202,16 @@ class LocalOnChainKeyManager(override val walletName: String, seed: ByteVector,
require(kmp2scala(input.getWitnessUtxo.publicKeyScript) == expectedScript, s"script mismatch (expected=$expectedScript, actual=${input.getWitnessUtxo.publicKeyScript}): bitcoin core may be malicious")

// Update the input with the right script for a p2wpkh input, which is a *p2pkh* script, then sign and finalize.
val updated: Either[UpdateFailure, Psbt] = psbt.updateWitnessInput(psbt.global.tx.txIn.get(pos).outPoint, input.getWitnessUtxo, null, Script.pay2pkh(pub), SigHash.SIGHASH_ALL, input.getDerivationPaths)
val updated: Either[UpdateFailure, Psbt] = psbt.updateWitnessInput(
psbt.global.tx.txIn.get(pos).outPoint,
input.getWitnessUtxo,
null,
Script.pay2pkh(pub),
SigHash.SIGHASH_ALL,
input.getDerivationPaths,
null,
null,
java.util.Map.of())
val signed = updated.flatMap(_.sign(priv, pos))
val finalized = signed.flatMap(s => {
require(s.getSig.last.toInt == SigHash.SIGHASH_ALL, "signature must end with SIGHASH_ALL")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,8 @@ object Bolt11Invoice {
val prefixes = Map(
Block.RegtestGenesisBlock.hash -> "lnbcrt",
Block.SignetGenesisBlock.hash -> "lntbs",
Block.TestnetGenesisBlock.hash -> "lntb",
Block.Testnet3GenesisBlock.hash -> "lntb",
Block.Testnet4GenesisBlock.hash -> "lntb",
Block.LivenetGenesisBlock.hash -> "lnbc"
)

Expand Down Expand Up @@ -531,7 +532,7 @@ object Bolt11Invoice {
val lowercaseInput = input.toLowerCase
val separatorIndex = lowercaseInput.lastIndexOf('1')
val hrp = lowercaseInput.take(separatorIndex)
val prefix: String = prefixes.values.find(prefix => hrp.startsWith(prefix)).getOrElse(throw new RuntimeException("unknown prefix"))
val prefix: String = prefixes.values.toSeq.sortBy(_.length).findLast(prefix => hrp.startsWith(prefix)).getOrElse(throw new RuntimeException("unknown prefix"))
val data = string2Bits(lowercaseInput.slice(separatorIndex + 1, lowercaseInput.length - 6)) // 6 == checksum size
val bolt11Data = Codecs.bolt11DataCodec.decode(data).require.value
val signature = ByteVector64(bolt11Data.signature.take(64))
Expand Down
20 changes: 10 additions & 10 deletions eclair-core/src/test/scala/fr/acinq/eclair/PackageSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -50,31 +50,31 @@ class PackageSpec extends AnyFunSuite {

// p2pkh
// valid chain
assert(addressToPublicKeyScript(Block.TestnetGenesisBlock.hash, Base58Check.encode(Base58.Prefix.PubkeyAddressTestnet, pub.hash160)) == Right(Script.pay2pkh(pub)))
assert(addressToPublicKeyScript(Block.TestnetGenesisBlock.hash, Base58Check.encode(Base58.Prefix.PubkeyAddressTestnet, pub.hash160)) == Right(Script.pay2pkh(pub)))
assert(addressToPublicKeyScript(Block.TestnetGenesisBlock.hash, Base58Check.encode(Base58.Prefix.PubkeyAddressTestnet, pub.hash160)) == Right(Script.pay2pkh(pub)))
assert(addressToPublicKeyScript(Block.Testnet3GenesisBlock.hash, Base58Check.encode(Base58.Prefix.PubkeyAddressTestnet, pub.hash160)) == Right(Script.pay2pkh(pub)))
assert(addressToPublicKeyScript(Block.Testnet3GenesisBlock.hash, Base58Check.encode(Base58.Prefix.PubkeyAddressTestnet, pub.hash160)) == Right(Script.pay2pkh(pub)))
assert(addressToPublicKeyScript(Block.Testnet3GenesisBlock.hash, Base58Check.encode(Base58.Prefix.PubkeyAddressTestnet, pub.hash160)) == Right(Script.pay2pkh(pub)))
assert(addressToPublicKeyScript(Block.LivenetGenesisBlock.hash, Base58Check.encode(Base58.Prefix.PubkeyAddress, pub.hash160)) == Right(Script.pay2pkh(pub)))

// wrong chain
val Left(failure) = addressToPublicKeyScript(Block.TestnetGenesisBlock.hash, Base58Check.encode(Base58.Prefix.PubkeyAddress, pub.hash160))
val Left(failure) = addressToPublicKeyScript(Block.Testnet3GenesisBlock.hash, Base58Check.encode(Base58.Prefix.PubkeyAddress, pub.hash160))
assert(failure.isInstanceOf[ChainHashMismatch])

assert(addressToPublicKeyScript(Block.TestnetGenesisBlock.hash, Base58Check.encode(Base58.Prefix.PubkeyAddress, pub.hash160)).isLeft)
assert(addressToPublicKeyScript(Block.Testnet3GenesisBlock.hash, Base58Check.encode(Base58.Prefix.PubkeyAddress, pub.hash160)).isLeft)
assert(addressToPublicKeyScript(Block.RegtestGenesisBlock.hash, Base58Check.encode(Base58.Prefix.PubkeyAddress, pub.hash160)).isLeft)
assert(addressToPublicKeyScript(Block.SignetGenesisBlock.hash, Base58Check.encode(Base58.Prefix.PubkeyAddress, pub.hash160)).isLeft)

// p2sh
val script = Script.write(Script.pay2wpkh(pub))

// valid chain
assert(addressToPublicKeyScript(Block.TestnetGenesisBlock.hash, Base58Check.encode(Base58.Prefix.ScriptAddressTestnet, Crypto.hash160(script))) == Right(Script.pay2sh(script)))
assert(addressToPublicKeyScript(Block.Testnet3GenesisBlock.hash, Base58Check.encode(Base58.Prefix.ScriptAddressTestnet, Crypto.hash160(script))) == Right(Script.pay2sh(script)))
assert(addressToPublicKeyScript(Block.RegtestGenesisBlock.hash, Base58Check.encode(Base58.Prefix.ScriptAddressTestnet, Crypto.hash160(script))) == Right(Script.pay2sh(script)))
assert(addressToPublicKeyScript(Block.SignetGenesisBlock.hash, Base58Check.encode(Base58.Prefix.ScriptAddressTestnet, Crypto.hash160(script))) == Right(Script.pay2sh(script)))
assert(addressToPublicKeyScript(Block.LivenetGenesisBlock.hash, Base58Check.encode(Base58.Prefix.ScriptAddress, Crypto.hash160(script))) == Right(Script.pay2sh(script)))

// wrong chain
assert(addressToPublicKeyScript(Block.LivenetGenesisBlock.hash, Base58Check.encode(Base58.Prefix.ScriptAddressTestnet, Crypto.hash160(script))).isLeft)
assert(addressToPublicKeyScript(Block.TestnetGenesisBlock.hash, Base58Check.encode(Base58.Prefix.ScriptAddress, Crypto.hash160(script))).isLeft)
assert(addressToPublicKeyScript(Block.Testnet3GenesisBlock.hash, Base58Check.encode(Base58.Prefix.ScriptAddress, Crypto.hash160(script))).isLeft)
assert(addressToPublicKeyScript(Block.RegtestGenesisBlock.hash, Base58Check.encode(Base58.Prefix.ScriptAddress, Crypto.hash160(script))).isLeft)
assert(addressToPublicKeyScript(Block.SignetGenesisBlock.hash, Base58Check.encode(Base58.Prefix.ScriptAddress, Crypto.hash160(script))).isLeft)
}
Expand All @@ -85,19 +85,19 @@ class PackageSpec extends AnyFunSuite {

// p2wpkh
assert(addressToPublicKeyScript(Block.LivenetGenesisBlock.hash, Bech32.encodeWitnessAddress("bc", 0, pub.hash160)) == Right(Script.pay2wpkh(pub)))
assert(addressToPublicKeyScript(Block.TestnetGenesisBlock.hash, Bech32.encodeWitnessAddress("tb", 0, pub.hash160)) == Right(Script.pay2wpkh(pub)))
assert(addressToPublicKeyScript(Block.Testnet3GenesisBlock.hash, Bech32.encodeWitnessAddress("tb", 0, pub.hash160)) == Right(Script.pay2wpkh(pub)))
assert(addressToPublicKeyScript(Block.RegtestGenesisBlock.hash, Bech32.encodeWitnessAddress("bcrt", 0, pub.hash160)) == Right(Script.pay2wpkh(pub)))
assert(addressToPublicKeyScript(Block.SignetGenesisBlock.hash, Bech32.encodeWitnessAddress("tb", 0, pub.hash160)) == Right(Script.pay2wpkh(pub)))

// wrong chain
assert(addressToPublicKeyScript(Block.TestnetGenesisBlock.hash, Bech32.encodeWitnessAddress("bc", 0, pub.hash160)).isLeft)
assert(addressToPublicKeyScript(Block.Testnet3GenesisBlock.hash, Bech32.encodeWitnessAddress("bc", 0, pub.hash160)).isLeft)
assert(addressToPublicKeyScript(Block.LivenetGenesisBlock.hash, Bech32.encodeWitnessAddress("tb", 0, pub.hash160)).isLeft)
assert(addressToPublicKeyScript(Block.LivenetGenesisBlock.hash, Bech32.encodeWitnessAddress("bcrt", 0, pub.hash160)).isLeft)
assert(addressToPublicKeyScript(Block.LivenetGenesisBlock.hash, Bech32.encodeWitnessAddress("tb", 0, pub.hash160)).isLeft)

val script = Script.write(Script.pay2wpkh(pub))
assert(addressToPublicKeyScript(Block.LivenetGenesisBlock.hash, Bech32.encodeWitnessAddress("bc", 0, Crypto.sha256(script))) == Right(Script.pay2wsh(script)))
assert(addressToPublicKeyScript(Block.TestnetGenesisBlock.hash, Bech32.encodeWitnessAddress("tb", 0, Crypto.sha256(script))) == Right(Script.pay2wsh(script)))
assert(addressToPublicKeyScript(Block.Testnet3GenesisBlock.hash, Bech32.encodeWitnessAddress("tb", 0, Crypto.sha256(script))) == Right(Script.pay2wsh(script)))
assert(addressToPublicKeyScript(Block.RegtestGenesisBlock.hash, Bech32.encodeWitnessAddress("bcrt", 0, Crypto.sha256(script))) == Right(Script.pay2wsh(script)))
assert(addressToPublicKeyScript(Block.SignetGenesisBlock.hash, Bech32.encodeWitnessAddress("tb", 0, Crypto.sha256(script))) == Right(Script.pay2wsh(script)))
}
Expand Down
4 changes: 2 additions & 2 deletions eclair-core/src/test/scala/fr/acinq/eclair/StartupSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ class StartupSpec extends AnyFunSuite {
def makeNodeParamsWithDefaults(conf: Config): NodeParams = {
val blockCount = new AtomicLong(0)
val feerates = new AtomicReference(FeeratesPerKw.single(feeratePerKw))
val nodeKeyManager = new LocalNodeKeyManager(randomBytes32(), chainHash = Block.TestnetGenesisBlock.hash)
val channelKeyManager = new LocalChannelKeyManager(randomBytes32(), chainHash = Block.TestnetGenesisBlock.hash)
val nodeKeyManager = new LocalNodeKeyManager(randomBytes32(), chainHash = Block.Testnet3GenesisBlock.hash)
val channelKeyManager = new LocalChannelKeyManager(randomBytes32(), chainHash = Block.Testnet3GenesisBlock.hash)
val db = TestDatabases.inMemoryDb()
NodeParams.makeNodeParams(conf, UUID.fromString("01234567-0123-4567-89ab-0123456789ab"), nodeKeyManager, channelKeyManager, None, None, db, blockCount, feerates)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,16 @@ class SingleKeyOnChainWallet extends OnChainWallet with OnchainPubkeyCache {
case (currentPsbt, (txIn, index)) => inputs.find(_.txid == txIn.outPoint.txid) match {
case Some(inputTx) =>
val sig = Transaction.signInput(tx, index, Script.pay2pkh(pubkey), SigHash.SIGHASH_ALL, inputTx.txOut.head.amount, SigVersion.SIGVERSION_WITNESS_V0, privkey)
val updated = currentPsbt.updateWitnessInput(txIn.outPoint, inputTx.txOut(txIn.outPoint.index.toInt), null, Script.pay2pkh(pubkey).map(scala2kmp).asJava, null, java.util.Map.of()).getRight
val updated = currentPsbt.updateWitnessInput(
txIn.outPoint,
inputTx.txOut(txIn.outPoint.index.toInt),
null,
Script.pay2pkh(pubkey).map(scala2kmp).asJava,
null,
java.util.Map.of(),
null,
null,
java.util.Map.of()).getRight
updated.finalizeWitnessInput(txIn.outPoint, Script.witnessPay2wpkh(pubkey, sig)).getRight
case None => currentPsbt
}
Expand Down
Loading

0 comments on commit 1ff5697

Please sign in to comment.