mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2024-11-19 01:40:55 +01:00
Lots of misc. improvements from dlc branch (#1583)
This commit is contained in:
parent
80930e07c2
commit
914c905bd7
@ -55,7 +55,7 @@ case class BitcoindConfig(
|
||||
/** The optional index of the first header section encountered */
|
||||
private lazy val firstHeaderSectionIndex: Option[Int] = {
|
||||
val indices =
|
||||
List(RegTest, TestNet3, MainNet).map(headerSectionIndex).flatten
|
||||
List(RegTest, TestNet3, MainNet).flatMap(headerSectionIndex)
|
||||
if (indices.nonEmpty) Some(indices.min) else None
|
||||
}
|
||||
|
||||
@ -111,13 +111,13 @@ case class BitcoindConfig(
|
||||
String] = collectFrom(lines)(_)
|
||||
|
||||
/** The blockchain network associated with this `bitcoind` config */
|
||||
lazy val network = {
|
||||
lazy val network: NetworkParameters = {
|
||||
val networkStrOpt =
|
||||
collectAllLines {
|
||||
case (network @ ("testnet" | "regtest" | "mainnet"), "1") => network
|
||||
}.lastOption
|
||||
|
||||
val networkOpt = networkStrOpt.flatMap(Networks.fromString)
|
||||
val networkOpt = networkStrOpt.flatMap(Networks.fromStringOpt)
|
||||
|
||||
(networkOpt, networkStrOpt) match {
|
||||
case (None, Some(badStr)) =>
|
||||
@ -323,7 +323,7 @@ object BitcoindConfig extends BitcoinSLogger {
|
||||
* default configuration is returned.
|
||||
*/
|
||||
def fromDefaultDatadir: BitcoindConfig = {
|
||||
if (DEFAULT_CONF_FILE.isFile()) {
|
||||
if (DEFAULT_CONF_FILE.isFile) {
|
||||
apply(DEFAULT_CONF_FILE)
|
||||
} else {
|
||||
BitcoindConfig.empty
|
||||
@ -353,8 +353,7 @@ object BitcoindConfig extends BitcoinSLogger {
|
||||
}
|
||||
|
||||
/** Default location of bitcoind conf file */
|
||||
val DEFAULT_CONF_FILE: File = DEFAULT_DATADIR
|
||||
.toPath()
|
||||
val DEFAULT_CONF_FILE: File = DEFAULT_DATADIR.toPath
|
||||
.resolve("bitcoin.conf")
|
||||
.toFile
|
||||
|
||||
|
@ -314,7 +314,7 @@ lazy val dbCommons = project
|
||||
name := "bitcoin-s-db-commons",
|
||||
libraryDependencies ++= Deps.dbCommons
|
||||
)
|
||||
.dependsOn(core)
|
||||
.dependsOn(core, appCommons)
|
||||
|
||||
lazy val dbCommonsTest = project
|
||||
.in(file("db-commons-test"))
|
||||
|
@ -23,10 +23,10 @@ class NetworkParametersTest extends BitcoinSUnitTest {
|
||||
}
|
||||
|
||||
it must "get the correct Network from string" in {
|
||||
assert(Networks.fromString("mainnet").contains(MainNet))
|
||||
assert(Networks.fromString("testnet").contains(TestNet3))
|
||||
assert(Networks.fromString("regtest").contains(RegTest))
|
||||
assert(Networks.fromString("").isEmpty)
|
||||
assert(Networks.fromString("craig wright is a fraud").isEmpty)
|
||||
assert(Networks.fromString("mainnet") == MainNet)
|
||||
assert(Networks.fromString("testnet") == TestNet3)
|
||||
assert(Networks.fromString("regtest") == RegTest)
|
||||
assert(Networks.fromStringOpt("").isEmpty)
|
||||
assert(Networks.fromStringOpt("craig wright is a fraud").isEmpty)
|
||||
}
|
||||
}
|
||||
|
@ -22,8 +22,8 @@ class ScriptPubKeyTest extends BitcoinSUnitTest {
|
||||
OP_EQUALVERIFY,
|
||||
OP_CHECKSIG)
|
||||
//from b30d3148927f620f5b1228ba941c211fdabdae75d0ba0b688a58accbf018f3cc
|
||||
val rawScriptPubKey = TestUtil.rawP2PKHScriptPubKey
|
||||
val scriptPubKey = ScriptPubKey(rawScriptPubKey)
|
||||
val rawScriptPubKey: String = TestUtil.rawP2PKHScriptPubKey
|
||||
val scriptPubKey: ScriptPubKey = ScriptPubKey(rawScriptPubKey)
|
||||
|
||||
"ScriptPubKey" must "give the expected asm from creating a scriptPubKey from hex" in {
|
||||
scriptPubKey.asm must be(expectedAsm)
|
||||
@ -35,9 +35,8 @@ class ScriptPubKeyTest extends BitcoinSUnitTest {
|
||||
val witnessProgram = Seq(ScriptConstant(pubKeyHash.bytes))
|
||||
val asm = OP_0 +: BytesToPushOntoStack(20) +: witnessProgram
|
||||
val witnessScriptPubKey = WitnessScriptPubKey(asm)
|
||||
witnessScriptPubKey.isDefined must be(true)
|
||||
witnessScriptPubKey.get.witnessVersion must be(WitnessVersion0)
|
||||
witnessScriptPubKey.get.witnessProgram must be(witnessProgram)
|
||||
witnessScriptPubKey.witnessVersion must be(WitnessVersion0)
|
||||
witnessScriptPubKey.witnessProgram must be(witnessProgram)
|
||||
}
|
||||
|
||||
it must "determine the correct descriptors" in {
|
||||
|
@ -45,7 +45,7 @@ class WitnessScriptPubKeySpec extends Properties("WitnessScriptPubKeySpec") {
|
||||
property("witnessScriptPubKey fromAsm symmetry") = {
|
||||
Prop.forAll(ScriptGenerators.witnessScriptPubKey) {
|
||||
case (witScriptPubKey, _) =>
|
||||
WitnessScriptPubKey(witScriptPubKey.asm).get == witScriptPubKey
|
||||
WitnessScriptPubKey(witScriptPubKey.asm) == witScriptPubKey
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,49 +1,16 @@
|
||||
package org.bitcoins.core.wallet.builder
|
||||
|
||||
import org.bitcoins.core.crypto.{
|
||||
BaseTxSigComponent,
|
||||
WitnessTxSigComponentP2SH,
|
||||
WitnessTxSigComponentRaw
|
||||
}
|
||||
import org.bitcoins.core.currency.{Bitcoins, CurrencyUnits, Satoshis}
|
||||
import org.bitcoins.core.number.UInt32
|
||||
import org.bitcoins.core.policy.Policy
|
||||
import org.bitcoins.core.protocol.script.{
|
||||
CLTVScriptPubKey,
|
||||
CSVScriptPubKey,
|
||||
ConditionalScriptPubKey,
|
||||
EmptyScriptPubKey,
|
||||
MultiSignatureScriptPubKey,
|
||||
NonStandardScriptPubKey,
|
||||
P2PKHScriptPubKey,
|
||||
P2PKScriptPubKey,
|
||||
P2PKWithTimeoutScriptPubKey,
|
||||
P2SHScriptPubKey,
|
||||
P2SHScriptSignature,
|
||||
P2WSHWitnessV0,
|
||||
UnassignedWitnessScriptPubKey,
|
||||
WitnessCommitment,
|
||||
WitnessScriptPubKey,
|
||||
WitnessScriptPubKeyV0
|
||||
}
|
||||
import org.bitcoins.core.protocol.transaction.{
|
||||
BaseTransaction,
|
||||
Transaction,
|
||||
TransactionConstants,
|
||||
TransactionInput,
|
||||
TransactionOutPoint,
|
||||
TransactionOutput,
|
||||
WitnessTransaction
|
||||
}
|
||||
import org.bitcoins.core.script.PreExecutionScriptProgram
|
||||
import org.bitcoins.core.protocol.script._
|
||||
import org.bitcoins.core.protocol.transaction._
|
||||
import org.bitcoins.core.script.constant.ScriptNumber
|
||||
import org.bitcoins.core.script.crypto.HashType
|
||||
import org.bitcoins.core.script.interpreter.ScriptInterpreter
|
||||
import org.bitcoins.core.util.BitcoinScriptUtil
|
||||
import org.bitcoins.core.wallet.fee.{SatoshisPerByte, SatoshisPerVirtualByte}
|
||||
import org.bitcoins.core.wallet.utxo.{
|
||||
ConditionalPath,
|
||||
InputInfo,
|
||||
InputSigningInfo,
|
||||
LockTimeInputInfo,
|
||||
ScriptSignatureParams
|
||||
}
|
||||
@ -297,64 +264,6 @@ class RawTxSignerTest extends BitcoinSAsyncTest {
|
||||
)
|
||||
}
|
||||
|
||||
def verifyScript(
|
||||
tx: Transaction,
|
||||
utxos: Vector[InputSigningInfo[InputInfo]]): Boolean = {
|
||||
val programs: Vector[PreExecutionScriptProgram] =
|
||||
tx.inputs.zipWithIndex.toVector.map {
|
||||
case (input: TransactionInput, idx: Int) =>
|
||||
val outpoint = input.previousOutput
|
||||
|
||||
val creditingTx =
|
||||
utxos.find(u => u.outPoint.txId == outpoint.txId).get
|
||||
|
||||
val output = creditingTx.output
|
||||
|
||||
val spk = output.scriptPubKey
|
||||
|
||||
val amount = output.value
|
||||
|
||||
val txSigComponent = spk match {
|
||||
case witSPK: WitnessScriptPubKeyV0 =>
|
||||
val o = TransactionOutput(amount, witSPK)
|
||||
WitnessTxSigComponentRaw(tx.asInstanceOf[WitnessTransaction],
|
||||
UInt32(idx),
|
||||
o,
|
||||
Policy.standardFlags)
|
||||
case _: UnassignedWitnessScriptPubKey => ???
|
||||
case x @ (_: P2PKScriptPubKey | _: P2PKHScriptPubKey |
|
||||
_: P2PKWithTimeoutScriptPubKey | _: MultiSignatureScriptPubKey |
|
||||
_: WitnessCommitment | _: CSVScriptPubKey |
|
||||
_: CLTVScriptPubKey | _: ConditionalScriptPubKey |
|
||||
_: NonStandardScriptPubKey | EmptyScriptPubKey) =>
|
||||
val o = TransactionOutput(CurrencyUnits.zero, x)
|
||||
BaseTxSigComponent(tx, UInt32(idx), o, Policy.standardFlags)
|
||||
|
||||
case _: P2SHScriptPubKey =>
|
||||
val p2shScriptSig =
|
||||
tx.inputs(idx).scriptSignature.asInstanceOf[P2SHScriptSignature]
|
||||
p2shScriptSig.redeemScript match {
|
||||
|
||||
case _: WitnessScriptPubKey =>
|
||||
WitnessTxSigComponentP2SH(
|
||||
transaction = tx.asInstanceOf[WitnessTransaction],
|
||||
inputIndex = UInt32(idx),
|
||||
output = output,
|
||||
flags = Policy.standardFlags)
|
||||
|
||||
case _ =>
|
||||
BaseTxSigComponent(tx,
|
||||
UInt32(idx),
|
||||
output,
|
||||
Policy.standardFlags)
|
||||
}
|
||||
}
|
||||
|
||||
PreExecutionScriptProgram(txSigComponent)
|
||||
}
|
||||
ScriptInterpreter.runAllVerify(programs)
|
||||
}
|
||||
|
||||
it should "sign a mix of spks in a tx and then have it verified" in {
|
||||
forAllAsync(CreditingTxGen.inputsAndOutputs(),
|
||||
ScriptGenerators.scriptPubKey) {
|
||||
@ -369,7 +278,7 @@ class RawTxSignerTest extends BitcoinSAsyncTest {
|
||||
RawTxSigner.sign(utx, creditingTxsInfo.toVector, fee))
|
||||
|
||||
txF.map { tx =>
|
||||
assert(verifyScript(tx, creditingTxsInfo.toVector))
|
||||
assert(BitcoinScriptUtil.verifyScript(tx, creditingTxsInfo.toVector))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -388,7 +297,7 @@ class RawTxSignerTest extends BitcoinSAsyncTest {
|
||||
RawTxSigner.sign(utx, creditingTxsInfo.toVector, fee))
|
||||
|
||||
txF.map { tx =>
|
||||
assert(verifyScript(tx, creditingTxsInfo.toVector))
|
||||
assert(BitcoinScriptUtil.verifyScript(tx, creditingTxsInfo.toVector))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package org.bitcoins.core.config
|
||||
|
||||
import org.bitcoins.core.protocol.blockchain._
|
||||
import org.bitcoins.crypto.StringFactory
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
sealed abstract class NetworkParameters {
|
||||
@ -73,8 +74,8 @@ sealed abstract class MainNet extends BitcoinNetwork {
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
override def dnsSeeds = {
|
||||
List(
|
||||
override def dnsSeeds: Vector[String] = {
|
||||
Vector(
|
||||
"seed.bitcoin.sipa.be",
|
||||
"dnsseed.bluematt.me",
|
||||
"dnsseed.bitcoin.dashjr.org",
|
||||
@ -89,7 +90,7 @@ sealed abstract class MainNet extends BitcoinNetwork {
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
override def magicBytes = ByteVector(0xf9, 0xbe, 0xb4, 0xd9)
|
||||
override def magicBytes: ByteVector = ByteVector(0xf9, 0xbe, 0xb4, 0xd9)
|
||||
|
||||
}
|
||||
|
||||
@ -119,7 +120,7 @@ sealed abstract class TestNet3 extends BitcoinNetwork {
|
||||
/*
|
||||
* @inheritdoc
|
||||
*/
|
||||
override def magicBytes = ByteVector(0x0b, 0x11, 0x09, 0x07)
|
||||
override def magicBytes: ByteVector = ByteVector(0x0b, 0x11, 0x09, 0x07)
|
||||
|
||||
}
|
||||
|
||||
@ -146,24 +147,44 @@ sealed abstract class RegTest extends BitcoinNetwork {
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
override def magicBytes = ByteVector(0xfa, 0xbf, 0xb5, 0xda)
|
||||
override def magicBytes: ByteVector = ByteVector(0xfa, 0xbf, 0xb5, 0xda)
|
||||
}
|
||||
|
||||
final case object RegTest extends RegTest
|
||||
// $COVERAGE-ON$
|
||||
|
||||
object Networks {
|
||||
object Networks extends StringFactory[NetworkParameters] {
|
||||
val knownNetworks: Seq[NetworkParameters] = BitcoinNetworks.knownNetworks
|
||||
val secretKeyBytes: Seq[ByteVector] = BitcoinNetworks.secretKeyBytes
|
||||
val p2pkhNetworkBytes: Seq[ByteVector] = BitcoinNetworks.p2pkhNetworkBytes
|
||||
val p2shNetworkBytes: Seq[ByteVector] = BitcoinNetworks.p2shNetworkBytes
|
||||
|
||||
def fromString(string: String): NetworkParameters =
|
||||
BitcoinNetworks.fromString(string)
|
||||
|
||||
def magicToNetwork: Map[ByteVector, NetworkParameters] =
|
||||
BitcoinNetworks.magicToNetwork
|
||||
|
||||
def bytesToNetwork: Map[ByteVector, NetworkParameters] =
|
||||
BitcoinNetworks.bytesToNetwork
|
||||
}
|
||||
|
||||
object BitcoinNetworks extends StringFactory[BitcoinNetwork] {
|
||||
val knownNetworks: Seq[NetworkParameters] = Seq(MainNet, TestNet3, RegTest)
|
||||
val secretKeyBytes: Seq[ByteVector] = knownNetworks.map(_.privateKey)
|
||||
val p2pkhNetworkBytes: Seq[ByteVector] = knownNetworks.map(_.p2pkhNetworkByte)
|
||||
val p2shNetworkBytes: Seq[ByteVector] = knownNetworks.map(_.p2shNetworkByte)
|
||||
|
||||
/** Uses the notation used in `bitcoin.conf` */
|
||||
def fromString(string: String): Option[NetworkParameters] = string match {
|
||||
case "mainnet" => Some(MainNet)
|
||||
case "testnet" => Some(TestNet3)
|
||||
case "regtest" => Some(RegTest)
|
||||
case _: String => None
|
||||
override def fromString(string: String): BitcoinNetwork = string match {
|
||||
case "mainnet" => MainNet
|
||||
case "main" => MainNet
|
||||
case "testnet3" => TestNet3
|
||||
case "testnet" => TestNet3
|
||||
case "test" => TestNet3
|
||||
case "regtest" => RegTest
|
||||
case _: String =>
|
||||
throw new IllegalArgumentException(s"Invalid network $string")
|
||||
}
|
||||
|
||||
/** Map of magic network bytes to the corresponding network */
|
||||
|
@ -12,30 +12,40 @@ package object currency {
|
||||
/** Provides natural language syntax for bitcoins */
|
||||
implicit class BitcoinsInt(private val i: Int) extends AnyVal {
|
||||
def bitcoins: Bitcoins = Bitcoins(i)
|
||||
|
||||
def bitcoin: Bitcoins = bitcoins
|
||||
|
||||
def BTC: Bitcoins = bitcoins
|
||||
}
|
||||
|
||||
/** Provides natural language syntax for bitcoins */
|
||||
implicit class BitcoinsLong(private val i: Long) extends AnyVal {
|
||||
def bitcoins: Bitcoins = Bitcoins(i)
|
||||
|
||||
def bitcoin: Bitcoins = bitcoins
|
||||
|
||||
def BTC: Bitcoins = bitcoins
|
||||
}
|
||||
|
||||
/** Provides natural language syntax for satoshis */
|
||||
implicit class SatoshisInt(private val i: Int) extends AnyVal {
|
||||
def satoshis: Satoshis = Satoshis(i)
|
||||
|
||||
def satoshi: Satoshis = satoshis
|
||||
|
||||
def sats: Satoshis = satoshis
|
||||
|
||||
def sat: Satoshis = satoshis
|
||||
}
|
||||
|
||||
/** Provides natural language syntax for satoshis */
|
||||
implicit class SatoshisLong(private val i: Long) extends AnyVal {
|
||||
def satoshis: Satoshis = Satoshis(i)
|
||||
|
||||
def satoshi: Satoshis = satoshis
|
||||
|
||||
def sats: Satoshis = satoshis
|
||||
|
||||
def sat: Satoshis = satoshis
|
||||
}
|
||||
|
||||
@ -80,4 +90,9 @@ package object currency {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
implicit val satoshisOrdering: Ordering[Satoshis] =
|
||||
new Ordering[Satoshis] {
|
||||
override def compare(x: Satoshis, y: Satoshis): Int = x.compare(y)
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,18 @@ case class HDAccount(
|
||||
|
||||
object HDAccount {
|
||||
|
||||
def fromPath(path: BIP32Path): Option[HDAccount] = {
|
||||
|
||||
HDCoin.fromPath(BIP32Path(path.path.init)).flatMap { coin =>
|
||||
val lastNode = path.path.last
|
||||
if (lastNode.hardened) {
|
||||
Some(HDAccount(coin, lastNode.index))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** This method is meant to take in an arbitrary bip32 path and see
|
||||
* if it has the same account as the given account
|
||||
*
|
||||
|
@ -6,3 +6,18 @@ case class HDCoin(purpose: HDPurpose, coinType: HDCoinType) extends BIP32Path {
|
||||
|
||||
def toAccount(index: Int): HDAccount = HDAccount(this, index)
|
||||
}
|
||||
|
||||
object HDCoin {
|
||||
|
||||
def fromPath(path: BIP32Path): Option[HDCoin] = {
|
||||
if (path.path.length == 2) {
|
||||
HDPurposes.fromNode(path.path.head).map { purpose =>
|
||||
val coinType = HDCoinType.fromInt(path.path.last.index)
|
||||
|
||||
HDCoin(purpose, coinType)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,4 +42,11 @@ object HDCoinType {
|
||||
case TestNet3 | RegTest => Testnet
|
||||
}
|
||||
}
|
||||
|
||||
def fromNode(node: BIP32Node): HDCoinType = {
|
||||
require(node.hardened,
|
||||
s"Cannot construct HDCoinType from un-hardened node: $node")
|
||||
|
||||
fromInt(node.index)
|
||||
}
|
||||
}
|
||||
|
@ -28,4 +28,10 @@ object HDPurposes {
|
||||
|
||||
/** Tries to turn the provided integer into a HD purpose path segment */
|
||||
def fromConstant(i: Int): Option[HDPurpose] = all.find(_.constant == i)
|
||||
|
||||
def fromNode(node: BIP32Node): Option[HDPurpose] = {
|
||||
require(node.hardened,
|
||||
s"Cannot construct HDPurpose from un-hardened node: $node")
|
||||
fromConstant(node.index)
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ sealed abstract class Address {
|
||||
/** The [[org.bitcoins.core.protocol.script.ScriptPubKey ScriptPubKey]] the address represents */
|
||||
def scriptPubKey: ScriptPubKey
|
||||
|
||||
override def toString = value
|
||||
override def toString: String = value
|
||||
}
|
||||
|
||||
sealed abstract class BitcoinAddress extends Address
|
||||
@ -67,7 +67,7 @@ sealed abstract class P2SHAddress extends BitcoinAddress {
|
||||
Base58.encode(bytes ++ checksum)
|
||||
}
|
||||
|
||||
override def scriptPubKey = P2SHScriptPubKey(hash)
|
||||
override def scriptPubKey: P2SHScriptPubKey = P2SHScriptPubKey(hash)
|
||||
|
||||
override def hash: Sha256Hash160Digest
|
||||
}
|
||||
@ -122,6 +122,9 @@ object Bech32Address extends AddressFactory[Bech32Address] {
|
||||
//require(verifyChecksum(hrp, data), "checksum did not pass")
|
||||
}
|
||||
|
||||
def empty(network: NetworkParameters = MainNet): Bech32Address =
|
||||
fromScriptPubKey(P2WSHWitnessSPKV0(EmptyScriptPubKey), network)
|
||||
|
||||
def apply(
|
||||
witSPK: WitnessScriptPubKey,
|
||||
networkParameters: NetworkParameters): Bech32Address = {
|
||||
@ -153,31 +156,30 @@ object Bech32Address extends AddressFactory[Bech32Address] {
|
||||
* [[org.bitcoins.core.protocol.script.WitnessScriptPubKey WitnessScriptPubKey]] */
|
||||
def fromStringToWitSPK(string: String): Try[WitnessScriptPubKey] = {
|
||||
val decoded = fromStringT(string)
|
||||
decoded.flatMap {
|
||||
case bech32Addr =>
|
||||
val bytes = bech32Addr.data
|
||||
val (v, _) = (bytes.head, bytes.tail)
|
||||
val convertedProg = NumberUtil.convertUInt5sToUInt8(bytes.tail)
|
||||
val progBytes = UInt8.toBytes(convertedProg)
|
||||
val witVersion = WitnessVersion(v.toInt)
|
||||
val pushOp = BitcoinScriptUtil.calculatePushOp(progBytes)
|
||||
witVersion match {
|
||||
case Some(v) =>
|
||||
val witSPK = WitnessScriptPubKey(
|
||||
List(v.version) ++ pushOp ++ List(ScriptConstant(progBytes)))
|
||||
witSPK match {
|
||||
case Some(spk) => Success(spk)
|
||||
case None =>
|
||||
Failure(
|
||||
new IllegalArgumentException(
|
||||
"Failed to decode bech32 into a witSPK"))
|
||||
}
|
||||
case None =>
|
||||
Failure(
|
||||
new IllegalArgumentException(
|
||||
"Witness version was not valid, got: " + v))
|
||||
}
|
||||
|
||||
decoded.flatMap { bech32Addr =>
|
||||
val bytes = bech32Addr.data
|
||||
val (v, _) = (bytes.head, bytes.tail)
|
||||
val convertedProg = NumberUtil.convertUInt5sToUInt8(bytes.tail)
|
||||
val progBytes = UInt8.toBytes(convertedProg)
|
||||
val witVersion = WitnessVersion(v.toInt)
|
||||
val pushOp = BitcoinScriptUtil.calculatePushOp(progBytes)
|
||||
witVersion match {
|
||||
case Some(v) =>
|
||||
val witSPK = Try(
|
||||
WitnessScriptPubKey(
|
||||
List(v.version) ++ pushOp ++ List(ScriptConstant(progBytes))))
|
||||
witSPK match {
|
||||
case Success(spk) => Success(spk)
|
||||
case Failure(err) =>
|
||||
Failure(
|
||||
new IllegalArgumentException(
|
||||
"Failed to decode bech32 into a witSPK: " + err.getMessage))
|
||||
}
|
||||
case None =>
|
||||
Failure(
|
||||
new IllegalArgumentException(
|
||||
"Witness version was not valid, got: " + v))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,8 +80,8 @@ object P2PKHScriptPubKey extends ScriptFactory[P2PKHScriptPubKey] {
|
||||
|
||||
def fromAsm(asm: Seq[ScriptToken]): P2PKHScriptPubKey = {
|
||||
buildScript(asm.toVector,
|
||||
P2PKHScriptPubKeyImpl(_),
|
||||
isP2PKHScriptPubKey(_),
|
||||
P2PKHScriptPubKeyImpl.apply,
|
||||
isP2PKHScriptPubKey,
|
||||
"Given asm was not a p2pkh scriptPubKey, got: " + asm)
|
||||
}
|
||||
|
||||
@ -118,8 +118,7 @@ sealed trait MultiSignatureScriptPubKey extends RawScriptPubKey {
|
||||
asmWithoutPushOps.indexOf(OP_CHECKMULTISIG)
|
||||
else asmWithoutPushOps.indexOf(OP_CHECKMULTISIGVERIFY)
|
||||
//magic number 2 represents the maxSig operation and the OP_CHECKMULTISIG operation at the end of the asm
|
||||
val numSigsRequired = asmWithoutPushOps(
|
||||
opCheckMultiSigIndex - maxSigs.toInt - 2)
|
||||
val numSigsRequired = asmWithoutPushOps(opCheckMultiSigIndex - maxSigs - 2)
|
||||
numSigsRequired match {
|
||||
case x: ScriptNumber => x.toInt
|
||||
case c: ScriptConstant
|
||||
@ -216,8 +215,8 @@ object MultiSignatureScriptPubKey
|
||||
|
||||
def fromAsm(asm: Seq[ScriptToken]): MultiSignatureScriptPubKey = {
|
||||
buildScript(asm.toVector,
|
||||
MultiSignatureScriptPubKeyImpl(_),
|
||||
isMultiSignatureScriptPubKey(_),
|
||||
MultiSignatureScriptPubKeyImpl.apply,
|
||||
isMultiSignatureScriptPubKey,
|
||||
"Given asm was not a MultSignatureScriptPubKey, got: " + asm)
|
||||
}
|
||||
|
||||
@ -225,11 +224,10 @@ object MultiSignatureScriptPubKey
|
||||
|
||||
/** Determines if the given script tokens are a multisignature `scriptPubKey` */
|
||||
def isMultiSignatureScriptPubKey(asm: Seq[ScriptToken]): Boolean = {
|
||||
val isNotEmpty = asm.size > 0
|
||||
val containsMultiSigOp = asm.contains(OP_CHECKMULTISIG) || asm.contains(
|
||||
OP_CHECKMULTISIGVERIFY)
|
||||
|
||||
if (isNotEmpty && containsMultiSigOp) {
|
||||
if (asm.nonEmpty && containsMultiSigOp) {
|
||||
//we need either the first or second asm operation to indicate how many signatures are required
|
||||
val hasRequiredSignaturesTry = Try {
|
||||
asm.headOption match {
|
||||
@ -258,7 +256,7 @@ object MultiSignatureScriptPubKey
|
||||
.isInstanceOf[ScriptNumber] || op == OP_CHECKMULTISIG ||
|
||||
op == OP_CHECKMULTISIGVERIFY)
|
||||
|
||||
val result = isNotEmpty && containsMultiSigOp && hasRequiredSignatures &&
|
||||
val result = asm.nonEmpty && containsMultiSigOp && hasRequiredSignatures &&
|
||||
hasMaximumSignatures && isStandardOps
|
||||
result
|
||||
case (Success(_), Failure(_)) => false
|
||||
@ -330,8 +328,8 @@ object P2SHScriptPubKey extends ScriptFactory[P2SHScriptPubKey] {
|
||||
|
||||
def fromAsm(asm: Seq[ScriptToken]): P2SHScriptPubKey = {
|
||||
buildScript(asm.toVector,
|
||||
P2SHScriptPubKeyImpl(_),
|
||||
isP2SHScriptPubKey(_),
|
||||
P2SHScriptPubKeyImpl.apply,
|
||||
isP2SHScriptPubKey,
|
||||
"Given asm was not a p2sh scriptPubkey, got: " + asm)
|
||||
}
|
||||
|
||||
@ -366,8 +364,8 @@ object P2PKScriptPubKey extends ScriptFactory[P2PKScriptPubKey] {
|
||||
|
||||
def fromAsm(asm: Seq[ScriptToken]): P2PKScriptPubKey = {
|
||||
buildScript(asm.toVector,
|
||||
P2PKScriptPubKeyImpl(_),
|
||||
isP2PKScriptPubKey(_),
|
||||
P2PKScriptPubKeyImpl.apply,
|
||||
isP2PKScriptPubKey,
|
||||
"Given asm was not a p2pk scriptPubKey, got: " + asm)
|
||||
}
|
||||
|
||||
@ -386,10 +384,9 @@ sealed trait LockTimeScriptPubKey extends RawScriptPubKey {
|
||||
|
||||
/** Determines the nested `ScriptPubKey` inside the `LockTimeScriptPubKey` */
|
||||
def nestedScriptPubKey: RawScriptPubKey = {
|
||||
val bool: Boolean = asm.head.isInstanceOf[ScriptNumberOperation]
|
||||
bool match {
|
||||
case true => RawScriptPubKey(asm.slice(3, asm.length))
|
||||
case false => RawScriptPubKey(asm.slice(4, asm.length))
|
||||
asm.head match {
|
||||
case _: ScriptNumberOperation => RawScriptPubKey(asm.slice(3, asm.length))
|
||||
case _: ScriptToken => RawScriptPubKey(asm.slice(4, asm.length))
|
||||
}
|
||||
}
|
||||
|
||||
@ -442,8 +439,8 @@ object CLTVScriptPubKey extends ScriptFactory[CLTVScriptPubKey] {
|
||||
|
||||
def fromAsm(asm: Seq[ScriptToken]): CLTVScriptPubKey = {
|
||||
buildScript(asm.toVector,
|
||||
CLTVScriptPubKeyImpl(_),
|
||||
isCLTVScriptPubKey(_),
|
||||
CLTVScriptPubKeyImpl.apply,
|
||||
isCLTVScriptPubKey,
|
||||
"Given asm was not a CLTVScriptPubKey, got: " + asm)
|
||||
}
|
||||
|
||||
@ -530,8 +527,8 @@ object CSVScriptPubKey extends ScriptFactory[CSVScriptPubKey] {
|
||||
|
||||
def fromAsm(asm: Seq[ScriptToken]): CSVScriptPubKey = {
|
||||
buildScript(asm.toVector,
|
||||
CSVScriptPubKeyImpl(_),
|
||||
isCSVScriptPubKey(_),
|
||||
CSVScriptPubKeyImpl.apply,
|
||||
isCSVScriptPubKey,
|
||||
"Given asm was not a CSVScriptPubKey, got: " + asm)
|
||||
}
|
||||
|
||||
@ -887,10 +884,25 @@ sealed trait P2PKWithTimeoutScriptPubKey extends RawScriptPubKey {
|
||||
lazy val pubKey: ECPublicKey =
|
||||
ECPublicKey.fromBytes(asm(2).bytes)
|
||||
|
||||
lazy val lockTime: ScriptNumber = ScriptNumber.fromBytes(asm(5).bytes)
|
||||
private lazy val smallCSVOpt: Option[Long] = {
|
||||
asm(4) match {
|
||||
case num: ScriptNumberOperation => Some(num.toLong)
|
||||
case _: ScriptToken => None
|
||||
}
|
||||
}
|
||||
|
||||
lazy val timeoutPubKey: ECPublicKey =
|
||||
ECPublicKey.fromBytes(asm(9).bytes)
|
||||
lazy val lockTime: ScriptNumber = {
|
||||
smallCSVOpt
|
||||
.map(ScriptNumber.apply)
|
||||
.getOrElse(ScriptNumber(asm(5).bytes))
|
||||
}
|
||||
|
||||
lazy val timeoutPubKey: ECPublicKey = {
|
||||
smallCSVOpt match {
|
||||
case Some(_) => ECPublicKey.fromBytes(asm(8).bytes)
|
||||
case None => ECPublicKey.fromBytes(asm(9).bytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object P2PKWithTimeoutScriptPubKey
|
||||
@ -930,18 +942,35 @@ object P2PKWithTimeoutScriptPubKey
|
||||
}
|
||||
|
||||
def isP2PKWithTimeoutScriptPubKey(asm: Seq[ScriptToken]): Boolean = {
|
||||
if (asm.length == 12) {
|
||||
val pubKey = ECPublicKey.fromBytes(asm(2).bytes)
|
||||
val lockTimeTry = Try(ScriptNumber.fromBytes(asm(5).bytes))
|
||||
val timeoutPubKey = ECPublicKey.fromBytes(asm(9).bytes)
|
||||
|
||||
lockTimeTry match {
|
||||
case Success(lockTime) =>
|
||||
asm == P2PKWithTimeoutScriptPubKey(pubKey, lockTime, timeoutPubKey).asm
|
||||
case Failure(_) => false
|
||||
}
|
||||
} else {
|
||||
if (asm.length < 5) {
|
||||
false
|
||||
} else {
|
||||
val (smallCSVOpt, requiredSize) = asm(4) match {
|
||||
case num: ScriptNumberOperation => (Some(num.toLong), 11)
|
||||
case _: ScriptToken => (None, 12)
|
||||
}
|
||||
|
||||
if (asm.length == requiredSize) {
|
||||
val pubKey = ECPublicKey.fromBytes(asm(2).bytes)
|
||||
|
||||
val lockTimeTry = smallCSVOpt match {
|
||||
case Some(num) => Success(ScriptNumber(num))
|
||||
case None => Try(ScriptNumber.fromBytes(asm(5).bytes))
|
||||
}
|
||||
|
||||
val timeoutPubKey = smallCSVOpt match {
|
||||
case Some(_) => ECPublicKey.fromBytes(asm(8).bytes)
|
||||
case None => ECPublicKey.fromBytes(asm(9).bytes)
|
||||
}
|
||||
|
||||
lockTimeTry match {
|
||||
case Success(lockTime) =>
|
||||
asm == P2PKWithTimeoutScriptPubKey(pubKey, lockTime, timeoutPubKey).asm
|
||||
case Failure(_) => false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -958,7 +987,7 @@ object NonStandardScriptPubKey extends ScriptFactory[NonStandardScriptPubKey] {
|
||||
|
||||
def fromAsm(asm: Seq[ScriptToken]): NonStandardScriptPubKey = {
|
||||
//everything can be a NonStandardScriptPubkey, thus the trivially true function
|
||||
buildScript(asm.toVector, NonStandardScriptPubKeyImpl(_), { _ =>
|
||||
buildScript(asm.toVector, NonStandardScriptPubKeyImpl.apply, { _ =>
|
||||
true
|
||||
}, "")
|
||||
}
|
||||
@ -1030,7 +1059,7 @@ object ScriptPubKey extends ScriptFactory[ScriptPubKey] {
|
||||
if (nonWitnessScriptPubKey
|
||||
.isInstanceOf[NonStandardScriptPubKey] && WitnessScriptPubKey
|
||||
.isWitnessScriptPubKey(asm)) {
|
||||
WitnessScriptPubKey(asm).get
|
||||
WitnessScriptPubKey(asm)
|
||||
} else {
|
||||
nonWitnessScriptPubKey
|
||||
}
|
||||
@ -1044,10 +1073,10 @@ object ScriptPubKey extends ScriptFactory[ScriptPubKey] {
|
||||
* [[org.bitcoins.core.protocol.script.ScriptWitness ScriptWitness]] */
|
||||
sealed trait WitnessScriptPubKey extends ScriptPubKey {
|
||||
def witnessProgram: Seq[ScriptToken]
|
||||
def witnessVersion = WitnessVersion(asm.head)
|
||||
def witnessVersion: WitnessVersion = WitnessVersion(asm.head)
|
||||
}
|
||||
|
||||
object WitnessScriptPubKey {
|
||||
object WitnessScriptPubKey extends ScriptFactory[WitnessScriptPubKey] {
|
||||
|
||||
/** Witness scripts must begin with one of these operations, see
|
||||
* [[https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki BIP141]] */
|
||||
@ -1069,18 +1098,20 @@ object WitnessScriptPubKey {
|
||||
OP_15,
|
||||
OP_16)
|
||||
|
||||
val unassignedWitVersions = validWitVersions.tail
|
||||
val unassignedWitVersions: Seq[ScriptNumberOperation] = validWitVersions.tail
|
||||
|
||||
def apply(asm: Seq[ScriptToken]): Option[WitnessScriptPubKey] = fromAsm(asm)
|
||||
def apply(asm: Seq[ScriptToken]): WitnessScriptPubKey = fromAsm(asm)
|
||||
|
||||
def fromAsm(asm: Seq[ScriptToken]): Option[WitnessScriptPubKey] = asm match {
|
||||
def fromAsm(asm: Seq[ScriptToken]): WitnessScriptPubKey = asm match {
|
||||
case _ if P2WPKHWitnessSPKV0.isValid(asm) =>
|
||||
Some(P2WPKHWitnessSPKV0.fromAsm(asm))
|
||||
P2WPKHWitnessSPKV0.fromAsm(asm)
|
||||
case _ if P2WSHWitnessSPKV0.isValid(asm) =>
|
||||
Some(P2WSHWitnessSPKV0.fromAsm(asm))
|
||||
P2WSHWitnessSPKV0.fromAsm(asm)
|
||||
case _ if WitnessScriptPubKey.isWitnessScriptPubKey(asm) =>
|
||||
Some(UnassignedWitnessScriptPubKey(asm))
|
||||
case _ => None
|
||||
UnassignedWitnessScriptPubKey(asm)
|
||||
case _ =>
|
||||
throw new IllegalArgumentException(
|
||||
"Given asm was not a WitnessScriptPubKey, got: " + asm)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1130,7 +1161,7 @@ object WitnessScriptPubKeyV0 {
|
||||
* [[https://github.com/bitcoin/bitcoin/blob/449f9b8debcceb61a92043bc7031528a53627c47/src/script/script.cpp#L215-L229]]
|
||||
*/
|
||||
def isValid(asm: Seq[ScriptToken]): Boolean = {
|
||||
WitnessScriptPubKey.isWitnessScriptPubKey(asm) && asm.headOption == Some(
|
||||
WitnessScriptPubKey.isWitnessScriptPubKey(asm) && asm.headOption.contains(
|
||||
OP_0)
|
||||
}
|
||||
}
|
||||
@ -1152,8 +1183,8 @@ object P2WPKHWitnessSPKV0 extends ScriptFactory[P2WPKHWitnessSPKV0] {
|
||||
|
||||
override def fromAsm(asm: Seq[ScriptToken]): P2WPKHWitnessSPKV0 = {
|
||||
buildScript(asm.toVector,
|
||||
P2WPKHWitnessSPKV0Impl(_),
|
||||
isValid(_),
|
||||
P2WPKHWitnessSPKV0Impl.apply,
|
||||
isValid,
|
||||
s"Given asm was not a P2WPKHWitnessSPKV0, got $asm")
|
||||
}
|
||||
|
||||
@ -1197,8 +1228,8 @@ object P2WSHWitnessSPKV0 extends ScriptFactory[P2WSHWitnessSPKV0] {
|
||||
|
||||
override def fromAsm(asm: Seq[ScriptToken]): P2WSHWitnessSPKV0 = {
|
||||
buildScript(asm.toVector,
|
||||
P2WSHWitnessSPKV0Impl(_),
|
||||
isValid(_),
|
||||
P2WSHWitnessSPKV0Impl.apply,
|
||||
isValid,
|
||||
s"Given asm was not a P2WSHWitnessSPKV0, got $asm")
|
||||
}
|
||||
|
||||
@ -1242,8 +1273,8 @@ object UnassignedWitnessScriptPubKey
|
||||
override def fromAsm(asm: Seq[ScriptToken]): UnassignedWitnessScriptPubKey = {
|
||||
buildScript(
|
||||
asm.toVector,
|
||||
UnassignedWitnessScriptPubKeyImpl(_),
|
||||
WitnessScriptPubKey.isWitnessScriptPubKey(_),
|
||||
UnassignedWitnessScriptPubKeyImpl.apply,
|
||||
WitnessScriptPubKey.isWitnessScriptPubKey,
|
||||
"Given asm was not a valid witness script pubkey: " + asm
|
||||
)
|
||||
}
|
||||
@ -1284,8 +1315,8 @@ object WitnessCommitment extends ScriptFactory[WitnessCommitment] {
|
||||
|
||||
override def fromAsm(asm: Seq[ScriptToken]): WitnessCommitment = {
|
||||
buildScript(asm.toVector,
|
||||
WitnessCommitmentImpl(_),
|
||||
isWitnessCommitment(_),
|
||||
WitnessCommitmentImpl.apply,
|
||||
isWitnessCommitment,
|
||||
"Given asm was not a valid witness commitment, got: " + asm)
|
||||
}
|
||||
|
||||
|
@ -148,7 +148,7 @@ sealed trait P2SHScriptSignature extends ScriptSignature {
|
||||
//if we have an EmptyScriptSignature, we need to check if the rest of the asm
|
||||
//is a Witness script. It is not necessarily a witness script, since this code
|
||||
//path might be used for signing a normal p2sh spk in TransactionSignatureSerializer
|
||||
WitnessScriptPubKey(asm.tail).get
|
||||
WitnessScriptPubKey(asm.tail)
|
||||
} else {
|
||||
ScriptPubKey.fromAsmBytes(asm.last.bytes)
|
||||
}
|
||||
|
@ -88,6 +88,22 @@ case class PSBT(
|
||||
PSBT(global, inputs, outputs)
|
||||
}
|
||||
|
||||
def finalizeInput(index: Int): Try[PSBT] = {
|
||||
require(index >= 0 && index < inputMaps.size,
|
||||
s"Index must be within 0 and the number of inputs, got: $index")
|
||||
val inputMap = inputMaps(index)
|
||||
if (inputMap.isFinalized) {
|
||||
Success(this)
|
||||
} else {
|
||||
inputMap.finalize(transaction.inputs(index)).map { finalizedInputMap =>
|
||||
val newInputMaps =
|
||||
inputMaps.updated(index, finalizedInputMap)
|
||||
|
||||
PSBT(globalMap, newInputMaps, outputMaps)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Finalizes this PSBT if possible, returns a Failure otherwise
|
||||
* @see [[https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#input-finalizer]]
|
||||
*/
|
||||
@ -502,28 +518,8 @@ case class PSBT(
|
||||
inputIndex: Int): PSBT =
|
||||
addSignature(PartialSignature(pubKey, sig), inputIndex)
|
||||
|
||||
def addSignature(
|
||||
partialSignature: PartialSignature,
|
||||
inputIndex: Int): PSBT = {
|
||||
require(
|
||||
inputIndex < inputMaps.size,
|
||||
s"index must be less than the number of input maps present in the psbt, $inputIndex >= ${inputMaps.size}")
|
||||
require(
|
||||
!inputMaps(inputIndex).isFinalized,
|
||||
s"Cannot update an InputPSBTMap that is finalized, index: $inputIndex")
|
||||
require(
|
||||
!inputMaps(inputIndex).partialSignatures
|
||||
.exists(_.pubKey == partialSignature.pubKey),
|
||||
s"Input has already been signed by ${partialSignature.pubKey}"
|
||||
)
|
||||
|
||||
val newElements = inputMaps(inputIndex).elements :+ partialSignature
|
||||
|
||||
val newInputMaps =
|
||||
inputMaps.updated(inputIndex, InputPSBTMap(newElements))
|
||||
|
||||
PSBT(globalMap, newInputMaps, outputMaps)
|
||||
}
|
||||
def addSignature(partialSignature: PartialSignature, inputIndex: Int): PSBT =
|
||||
addSignatures(Vector(partialSignature), inputIndex)
|
||||
|
||||
/** Adds all the PartialSignatures to the input map at the given index */
|
||||
def addSignatures(
|
||||
@ -551,6 +547,51 @@ case class PSBT(
|
||||
PSBT(globalMap, newInputMaps, outputMaps)
|
||||
}
|
||||
|
||||
def verifyFinalizedInput(index: Int): Boolean = {
|
||||
val inputMap = inputMaps(index)
|
||||
require(inputMap.isFinalized, "Input must be finalized to verify")
|
||||
|
||||
val wUtxoOpt = inputMap.witnessUTXOOpt
|
||||
val utxoOpt = inputMap.nonWitnessOrUnknownUTXOOpt
|
||||
|
||||
val newInput = {
|
||||
val input = transaction.inputs(index)
|
||||
val scriptSigOpt = inputMap.finalizedScriptSigOpt
|
||||
val scriptSig =
|
||||
scriptSigOpt.map(_.scriptSig).getOrElse(EmptyScriptSignature)
|
||||
TransactionInput(input.previousOutput, scriptSig, input.sequence)
|
||||
}
|
||||
|
||||
val tx = transaction.updateInput(index, newInput)
|
||||
|
||||
wUtxoOpt match {
|
||||
case Some(wUtxo) =>
|
||||
inputMap.finalizedScriptWitnessOpt match {
|
||||
case Some(scriptWit) =>
|
||||
val wtx = {
|
||||
val wtx = WitnessTransaction.toWitnessTx(transaction)
|
||||
wtx.updateWitness(index, scriptWit.scriptWitness)
|
||||
}
|
||||
val output = wUtxo.witnessUTXO
|
||||
|
||||
ScriptInterpreter.verifyInputScript(wtx, index, output)
|
||||
case None =>
|
||||
false
|
||||
}
|
||||
case None =>
|
||||
utxoOpt match {
|
||||
case Some(utxo) =>
|
||||
val input = tx.inputs(index)
|
||||
val output =
|
||||
utxo.transactionSpent.outputs(input.previousOutput.vout.toInt)
|
||||
|
||||
ScriptInterpreter.verifyInputScript(tx, index, output)
|
||||
case None =>
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the serialized from the serialized, fully signed transaction from
|
||||
* this PSBT and validates the script signatures using the ScriptInterpreter.
|
||||
|
@ -2,7 +2,9 @@ package org.bitcoins.core.util
|
||||
|
||||
import org.bitcoins.core.consensus.Consensus
|
||||
import org.bitcoins.core.crypto._
|
||||
import org.bitcoins.core.currency.CurrencyUnits
|
||||
import org.bitcoins.core.number.UInt32
|
||||
import org.bitcoins.core.policy.Policy
|
||||
import org.bitcoins.core.protocol.CompactSizeUInt
|
||||
import org.bitcoins.core.protocol.script.{
|
||||
CLTVScriptPubKey,
|
||||
@ -10,6 +12,12 @@ import org.bitcoins.core.protocol.script.{
|
||||
EmptyScriptPubKey,
|
||||
_
|
||||
}
|
||||
import org.bitcoins.core.protocol.transaction.{
|
||||
Transaction,
|
||||
TransactionInput,
|
||||
TransactionOutput,
|
||||
WitnessTransaction
|
||||
}
|
||||
import org.bitcoins.core.script.constant._
|
||||
import org.bitcoins.core.script.crypto.{
|
||||
OP_CHECKMULTISIG,
|
||||
@ -18,13 +26,18 @@ import org.bitcoins.core.script.crypto.{
|
||||
OP_CHECKSIGVERIFY
|
||||
}
|
||||
import org.bitcoins.core.script.flag.{ScriptFlag, ScriptFlagUtil}
|
||||
import org.bitcoins.core.script.interpreter.ScriptInterpreter
|
||||
import org.bitcoins.core.script.result.{
|
||||
ScriptError,
|
||||
ScriptErrorPubKeyType,
|
||||
ScriptErrorWitnessPubKeyType
|
||||
}
|
||||
import org.bitcoins.core.script.ExecutionInProgressScriptProgram
|
||||
import org.bitcoins.core.script.{
|
||||
ExecutionInProgressScriptProgram,
|
||||
PreExecutionScriptProgram
|
||||
}
|
||||
import org.bitcoins.core.serializers.script.ScriptParser
|
||||
import org.bitcoins.core.wallet.utxo.{InputInfo, ScriptSignatureParams}
|
||||
import org.bitcoins.crypto.{ECDigitalSignature, ECPublicKey}
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
@ -92,8 +105,8 @@ trait BitcoinScriptUtil extends BitcoinSLogger {
|
||||
* which is how Bitcoin Core handles this
|
||||
*/
|
||||
def countsTowardsScriptOpLimit(token: ScriptToken): Boolean = token match {
|
||||
case scriptOp: ScriptOperation if (scriptOp.opCode > OP_16.opCode) => true
|
||||
case _: ScriptToken => false
|
||||
case scriptOp: ScriptOperation if scriptOp.opCode > OP_16.opCode => true
|
||||
case _: ScriptToken => false
|
||||
}
|
||||
|
||||
/**
|
||||
@ -130,8 +143,9 @@ trait BitcoinScriptUtil extends BitcoinSLogger {
|
||||
def numPossibleSignaturesOnStack(
|
||||
program: ExecutionInProgressScriptProgram): ScriptNumber = {
|
||||
require(
|
||||
program.script.headOption == Some(OP_CHECKMULTISIG) || program.script.headOption == Some(
|
||||
OP_CHECKMULTISIGVERIFY),
|
||||
program.script.headOption
|
||||
.contains(OP_CHECKMULTISIG) || program.script.headOption
|
||||
.contains(OP_CHECKMULTISIGVERIFY),
|
||||
"We can only parse the nubmer of signatures the stack when we are executing a OP_CHECKMULTISIG or OP_CHECKMULTISIGVERIFY op"
|
||||
)
|
||||
val nPossibleSignatures: ScriptNumber = program.stack.head match {
|
||||
@ -151,8 +165,9 @@ trait BitcoinScriptUtil extends BitcoinSLogger {
|
||||
def numRequiredSignaturesOnStack(
|
||||
program: ExecutionInProgressScriptProgram): ScriptNumber = {
|
||||
require(
|
||||
program.script.headOption == Some(OP_CHECKMULTISIG) || program.script.headOption == Some(
|
||||
OP_CHECKMULTISIGVERIFY),
|
||||
program.script.headOption
|
||||
.contains(OP_CHECKMULTISIG) || program.script.headOption
|
||||
.contains(OP_CHECKMULTISIGVERIFY),
|
||||
"We can only parse the nubmer of signatures the stack when we are executing a OP_CHECKMULTISIG or OP_CHECKMULTISIGVERIFY op"
|
||||
)
|
||||
val nPossibleSignatures = numPossibleSignaturesOnStack(program)
|
||||
@ -209,18 +224,18 @@ trait BitcoinScriptUtil extends BitcoinSLogger {
|
||||
//so we can push the constant "00" or "81" onto the stack with a BytesToPushOntoStack pushop
|
||||
pushOp == BytesToPushOntoStack(1)
|
||||
case _: ScriptToken
|
||||
if (token.bytes.size == 1 && ScriptNumberOperation
|
||||
if token.bytes.size == 1 && ScriptNumberOperation
|
||||
.fromNumber(token.toLong.toInt)
|
||||
.isDefined) =>
|
||||
.isDefined =>
|
||||
//could have used the ScriptNumberOperation to push the number onto the stack
|
||||
false
|
||||
case token: ScriptToken =>
|
||||
token.bytes.size match {
|
||||
case size if (size == 0) => pushOp == OP_0
|
||||
case size if (size <= 75) => token.bytes.size == pushOp.toLong
|
||||
case size if (size <= 255) => pushOp == OP_PUSHDATA1
|
||||
case size if (size <= 65535) => pushOp == OP_PUSHDATA2
|
||||
case _: Long =>
|
||||
case size if size == 0 => pushOp == OP_0
|
||||
case size if size <= 75 => token.bytes.size == pushOp.toLong
|
||||
case size if size <= 255 => pushOp == OP_PUSHDATA1
|
||||
case size if size <= 65535 => pushOp == OP_PUSHDATA2
|
||||
case _: Long =>
|
||||
//default case is true because we have to use the largest push op as possible which is OP_PUSHDATA4
|
||||
true
|
||||
}
|
||||
@ -270,7 +285,7 @@ trait BitcoinScriptUtil extends BitcoinSLogger {
|
||||
// If the most-significant-byte - excluding the sign bit - is zero
|
||||
// then we're not minimal. Note how this test also rejects the
|
||||
// negative-zero encoding, 0x80.
|
||||
if ((bytes.size > 0 && (bytes.last & 0x7f) == 0)) {
|
||||
if (bytes.size > 0 && (bytes.last & 0x7f) == 0) {
|
||||
// One exception: if there's more than one byte and the most
|
||||
// significant bit of the second-most-significant-byte is set
|
||||
// it would conflict with the sign bit. An example of this case
|
||||
@ -548,15 +563,13 @@ trait BitcoinScriptUtil extends BitcoinSLogger {
|
||||
* [[org.bitcoins.core.script.crypto.OP_CHECKMULTISIG OP_CHECKMULTISIG]] with
|
||||
* [[org.bitcoins.core.script.constant.ScriptNumber.zero ScriptNumber.zero]] */
|
||||
def minimalDummy(asm: Seq[ScriptToken]): Seq[ScriptToken] = {
|
||||
if (asm.headOption == Some(OP_0)) ScriptNumber.zero +: asm.tail
|
||||
if (asm.headOption.contains(OP_0)) ScriptNumber.zero +: asm.tail
|
||||
else asm
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that all the [[org.bitcoins.crypto.ECPublicKey ECPublicKey]] in this script
|
||||
* is compressed public keys, this is required for BIP143
|
||||
* @param spk
|
||||
* @return
|
||||
*/
|
||||
def isOnlyCompressedPubKey(spk: ScriptPubKey): Boolean = {
|
||||
spk match {
|
||||
@ -564,7 +577,7 @@ trait BitcoinScriptUtil extends BitcoinSLogger {
|
||||
case p2pkWithTimeout: P2PKWithTimeoutScriptPubKey =>
|
||||
p2pkWithTimeout.pubKey.isCompressed && p2pkWithTimeout.timeoutPubKey.isCompressed
|
||||
case m: MultiSignatureScriptPubKey =>
|
||||
!m.publicKeys.exists(k => !k.isCompressed)
|
||||
m.publicKeys.forall(_.isCompressed)
|
||||
case l: LockTimeScriptPubKey =>
|
||||
isOnlyCompressedPubKey(l.nestedScriptPubKey)
|
||||
case conditional: ConditionalScriptPubKey =>
|
||||
@ -592,6 +605,62 @@ trait BitcoinScriptUtil extends BitcoinSLogger {
|
||||
val script: Vector[ScriptToken] = ScriptParser.fromBytes(scriptPubKeyBytes)
|
||||
f(script)
|
||||
}
|
||||
|
||||
def verifyScript(
|
||||
tx: Transaction,
|
||||
utxos: Seq[ScriptSignatureParams[InputInfo]]): Boolean = {
|
||||
val programs: Seq[PreExecutionScriptProgram] = tx.inputs.zipWithIndex.map {
|
||||
case (input: TransactionInput, idx: Int) =>
|
||||
val outpoint = input.previousOutput
|
||||
|
||||
val creditingTx = utxos.find(u => u.outPoint == outpoint).get
|
||||
|
||||
val output = creditingTx.output
|
||||
|
||||
val spk = output.scriptPubKey
|
||||
|
||||
val amount = output.value
|
||||
|
||||
val txSigComponent = spk match {
|
||||
case witSPK: WitnessScriptPubKeyV0 =>
|
||||
val o = TransactionOutput(amount, witSPK)
|
||||
WitnessTxSigComponentRaw(tx.asInstanceOf[WitnessTransaction],
|
||||
UInt32(idx),
|
||||
o,
|
||||
Policy.standardFlags)
|
||||
case _: UnassignedWitnessScriptPubKey => ???
|
||||
case x @ (_: P2PKScriptPubKey | _: P2PKHScriptPubKey |
|
||||
_: P2PKWithTimeoutScriptPubKey | _: MultiSignatureScriptPubKey |
|
||||
_: WitnessCommitment | _: CSVScriptPubKey | _: CLTVScriptPubKey |
|
||||
_: ConditionalScriptPubKey | _: NonStandardScriptPubKey |
|
||||
EmptyScriptPubKey) =>
|
||||
val o = TransactionOutput(CurrencyUnits.zero, x)
|
||||
BaseTxSigComponent(tx, UInt32(idx), o, Policy.standardFlags)
|
||||
|
||||
case _: P2SHScriptPubKey =>
|
||||
val p2shScriptSig =
|
||||
tx.inputs(idx).scriptSignature.asInstanceOf[P2SHScriptSignature]
|
||||
p2shScriptSig.redeemScript match {
|
||||
|
||||
case _: WitnessScriptPubKey =>
|
||||
WitnessTxSigComponentP2SH(transaction =
|
||||
tx.asInstanceOf[WitnessTransaction],
|
||||
inputIndex = UInt32(idx),
|
||||
output = output,
|
||||
flags = Policy.standardFlags)
|
||||
|
||||
case _ =>
|
||||
BaseTxSigComponent(tx,
|
||||
UInt32(idx),
|
||||
output,
|
||||
Policy.standardFlags)
|
||||
}
|
||||
}
|
||||
|
||||
PreExecutionScriptProgram(txSigComponent)
|
||||
}
|
||||
ScriptInterpreter.runAllVerify(programs)
|
||||
}
|
||||
}
|
||||
|
||||
object BitcoinScriptUtil extends BitcoinScriptUtil
|
||||
|
@ -60,7 +60,8 @@ case class RawTxBuilder() {
|
||||
|
||||
/** Returns a RawTxBuilderWithFinalizer where building can continue
|
||||
* and where buildTx can be called once building is completed. */
|
||||
def setFinalizer(finalizer: RawTxFinalizer): RawTxBuilderWithFinalizer = {
|
||||
def setFinalizer[F <: RawTxFinalizer](
|
||||
finalizer: F): RawTxBuilderWithFinalizer[F] = {
|
||||
RawTxBuilderWithFinalizer(this, finalizer)
|
||||
}
|
||||
|
||||
@ -141,9 +142,9 @@ case class RawTxBuilder() {
|
||||
* access to the RawTxFinalizer's buildTx method which
|
||||
* completes the RawTxBuilder and then finalized the result.
|
||||
*/
|
||||
case class RawTxBuilderWithFinalizer(
|
||||
case class RawTxBuilderWithFinalizer[F <: RawTxFinalizer](
|
||||
builder: RawTxBuilder,
|
||||
finalizer: RawTxFinalizer) {
|
||||
finalizer: F) {
|
||||
|
||||
/** Completes the builder and finalizes the result */
|
||||
def buildTx()(implicit ec: ExecutionContext): Future[Transaction] = {
|
||||
|
@ -1,6 +1,7 @@
|
||||
package org.bitcoins.core.wallet.builder
|
||||
|
||||
import org.bitcoins.core.currency.{CurrencyUnit, Satoshis}
|
||||
import org.bitcoins.core.number.Int64
|
||||
import org.bitcoins.core.policy.Policy
|
||||
import org.bitcoins.core.protocol.script.ScriptPubKey
|
||||
import org.bitcoins.core.protocol.transaction._
|
||||
@ -248,7 +249,8 @@ object StandardNonInteractiveFinalizer {
|
||||
outputs: Seq[TransactionOutput],
|
||||
utxos: Seq[InputSigningInfo[InputInfo]],
|
||||
feeRate: FeeUnit,
|
||||
changeSPK: ScriptPubKey): RawTxBuilderWithFinalizer = {
|
||||
changeSPK: ScriptPubKey): RawTxBuilderWithFinalizer[
|
||||
StandardNonInteractiveFinalizer] = {
|
||||
val inputs = InputUtil.calcSequenceForInputs(utxos, Policy.isRBFEnabled)
|
||||
val lockTime = TxUtil.calcLockTime(utxos).get
|
||||
val builder = RawTxBuilder().setLockTime(lockTime) ++= outputs ++= inputs
|
||||
@ -271,3 +273,67 @@ object StandardNonInteractiveFinalizer {
|
||||
builderF.flatMap(_.buildTx())
|
||||
}
|
||||
}
|
||||
|
||||
case class SubtractFeeFromOutputsFinalizer(
|
||||
inputInfos: Vector[InputInfo],
|
||||
feeRate: FeeUnit)
|
||||
extends RawTxFinalizer {
|
||||
override def buildTx(txBuilderResult: RawTxBuilderResult)(
|
||||
implicit ec: ExecutionContext): Future[Transaction] = {
|
||||
val RawTxBuilderResult(version, inputs, outputs, lockTime) = txBuilderResult
|
||||
|
||||
val witnesses = inputInfos.map(InputInfo.getScriptWitness)
|
||||
val txWithPossibleWitness = TransactionWitness.fromWitOpt(witnesses) match {
|
||||
case _: EmptyWitness =>
|
||||
BaseTransaction(version, inputs, outputs, lockTime)
|
||||
case wit: TransactionWitness =>
|
||||
WitnessTransaction(version, inputs, outputs, lockTime, wit)
|
||||
}
|
||||
|
||||
val dummyTxF = TxUtil.addDummySigs(txWithPossibleWitness, inputInfos)
|
||||
|
||||
val outputsAfterFeeF = dummyTxF.map { dummyTx =>
|
||||
SubtractFeeFromOutputsFinalizer.subtractFees(dummyTx,
|
||||
feeRate,
|
||||
outputs.map(_.scriptPubKey))
|
||||
}
|
||||
|
||||
outputsAfterFeeF.map { outputsAfterFee =>
|
||||
BaseTransaction(version, inputs, outputsAfterFee, lockTime)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object SubtractFeeFromOutputsFinalizer {
|
||||
|
||||
def subtractFees(
|
||||
tx: Transaction,
|
||||
feeRate: FeeUnit,
|
||||
spks: Vector[ScriptPubKey]): Vector[TransactionOutput] = {
|
||||
val fee = feeRate.calc(tx)
|
||||
|
||||
val outputs = tx.outputs.zipWithIndex.filter {
|
||||
case (output, _) => spks.contains(output.scriptPubKey)
|
||||
}
|
||||
val unchangedOutputs = tx.outputs.zipWithIndex.filterNot {
|
||||
case (output, _) => spks.contains(output.scriptPubKey)
|
||||
}
|
||||
|
||||
val feePerOutput = Satoshis(Int64(fee.satoshis.toLong / outputs.length))
|
||||
val feeRemainder = Satoshis(Int64(fee.satoshis.toLong % outputs.length))
|
||||
|
||||
val newOutputsWithoutRemainder = outputs.map {
|
||||
case (output, index) =>
|
||||
(TransactionOutput(output.value - feePerOutput, output.scriptPubKey),
|
||||
index)
|
||||
}
|
||||
val (lastOutput, lastOutputIndex) = newOutputsWithoutRemainder.last
|
||||
val newLastOutput = TransactionOutput(lastOutput.value - feeRemainder,
|
||||
lastOutput.scriptPubKey)
|
||||
val newOutputs = newOutputsWithoutRemainder
|
||||
.dropRight(1)
|
||||
.:+((newLastOutput, lastOutputIndex))
|
||||
|
||||
(newOutputs ++ unchangedOutputs).sortBy(_._2).map(_._1).toVector
|
||||
}
|
||||
}
|
||||
|
@ -81,6 +81,10 @@ case class ECSignatureParams[+InputType <: InputInfo](
|
||||
extends InputSigningInfo[InputType] {
|
||||
override def signers: Vector[Sign] = Vector(signer)
|
||||
|
||||
def toScriptSignatureParams: ScriptSignatureParams[InputType] = {
|
||||
ScriptSignatureParams(inputInfo, signer, hashType)
|
||||
}
|
||||
|
||||
def mapInfo[T <: InputInfo](func: InputType => T): ECSignatureParams[T] = {
|
||||
this.copy(inputInfo = func(this.inputInfo))
|
||||
}
|
||||
|
@ -1,26 +1,30 @@
|
||||
package org.bitcoins.db
|
||||
|
||||
import org.bitcoins.commons.jsonmodels.dlc.DLCMessage.ContractInfo
|
||||
import org.bitcoins.core.config.{BitcoinNetwork, BitcoinNetworks}
|
||||
import org.bitcoins.core.crypto._
|
||||
import org.bitcoins.core.currency.{CurrencyUnit, Satoshis}
|
||||
import org.bitcoins.core.gcs.FilterType
|
||||
import org.bitcoins.core.hd._
|
||||
import org.bitcoins.core.number.{Int32, UInt32, UInt64}
|
||||
import org.bitcoins.core.protocol.BitcoinAddress
|
||||
import org.bitcoins.core.protocol.script.{ScriptPubKey, ScriptWitness}
|
||||
import org.bitcoins.core.protocol.transaction.{
|
||||
Transaction,
|
||||
TransactionOutPoint,
|
||||
TransactionOutput
|
||||
}
|
||||
import org.bitcoins.core.protocol.{
|
||||
Bech32Address,
|
||||
BitcoinAddress,
|
||||
BlockTimeStamp
|
||||
}
|
||||
import org.bitcoins.core.psbt.InputPSBTMap
|
||||
import org.bitcoins.core.psbt.InputPSBTRecord.PartialSignature
|
||||
import org.bitcoins.core.script.ScriptType
|
||||
import org.bitcoins.core.serializers.script.RawScriptWitnessParser
|
||||
import org.bitcoins.core.wallet.fee.SatoshisPerByte
|
||||
import org.bitcoins.core.wallet.fee.{SatoshisPerByte, SatoshisPerVirtualByte}
|
||||
import org.bitcoins.core.wallet.utxo.TxoState
|
||||
import org.bitcoins.crypto.{
|
||||
DoubleSha256DigestBE,
|
||||
ECPublicKey,
|
||||
Sha256Hash160Digest
|
||||
}
|
||||
import org.bitcoins.crypto._
|
||||
import scodec.bits.ByteVector
|
||||
import slick.jdbc.{GetResult, JdbcProfile}
|
||||
|
||||
@ -76,9 +80,19 @@ class DbCommonsColumnMappers(val profile: JdbcProfile) {
|
||||
MappedColumnType
|
||||
.base[BigInt, BigDecimal](BigDecimal(_), _.toBigInt)
|
||||
|
||||
implicit val sha256DigestBEMapper: BaseColumnType[Sha256DigestBE] =
|
||||
MappedColumnType.base[Sha256DigestBE, String](_.hex, Sha256DigestBE.fromHex)
|
||||
|
||||
implicit val ecPublicKeyMapper: BaseColumnType[ECPublicKey] =
|
||||
MappedColumnType.base[ECPublicKey, String](_.hex, ECPublicKey.fromHex)
|
||||
|
||||
implicit val schnorrPublicKeyMapper: BaseColumnType[SchnorrPublicKey] =
|
||||
MappedColumnType
|
||||
.base[SchnorrPublicKey, String](_.hex, SchnorrPublicKey.fromHex)
|
||||
|
||||
implicit val schnorrNonceMapper: BaseColumnType[SchnorrNonce] =
|
||||
MappedColumnType.base[SchnorrNonce, String](_.hex, SchnorrNonce.fromHex)
|
||||
|
||||
implicit val sha256Hash160DigestMapper: BaseColumnType[Sha256Hash160Digest] =
|
||||
MappedColumnType
|
||||
.base[Sha256Hash160Digest, String](_.hex, Sha256Hash160Digest.fromHex)
|
||||
@ -150,7 +164,7 @@ class DbCommonsColumnMappers(val profile: JdbcProfile) {
|
||||
|
||||
implicit val segwitPathMappper: BaseColumnType[SegWitHDPath] =
|
||||
MappedColumnType
|
||||
.base[SegWitHDPath, String](_.toString, SegWitHDPath.fromString(_)) // hm rethink .get?
|
||||
.base[SegWitHDPath, String](_.toString, SegWitHDPath.fromString) // hm rethink .get?
|
||||
|
||||
implicit val hdChainTypeMapper: BaseColumnType[HDChainType] =
|
||||
MappedColumnType.base[HDChainType, Int](_.index, HDChainType.fromInt)
|
||||
@ -163,6 +177,10 @@ class DbCommonsColumnMappers(val profile: JdbcProfile) {
|
||||
MappedColumnType
|
||||
.base[BitcoinAddress, String](_.value, BitcoinAddress.fromStringExn)
|
||||
|
||||
implicit val bech32AddressMapper: BaseColumnType[Bech32Address] =
|
||||
MappedColumnType
|
||||
.base[Bech32Address, String](_.value, Bech32Address.fromStringExn)
|
||||
|
||||
implicit val scriptTypeMapper: BaseColumnType[ScriptType] =
|
||||
MappedColumnType
|
||||
.base[ScriptType, String](_.toString, ScriptType.fromStringExn)
|
||||
@ -187,4 +205,55 @@ class DbCommonsColumnMappers(val profile: JdbcProfile) {
|
||||
MappedColumnType
|
||||
.base[SatoshisPerByte, Long](_.toLong, SatoshisPerByte.fromLong)
|
||||
}
|
||||
|
||||
implicit val hdAccountMapper: BaseColumnType[HDAccount] = {
|
||||
MappedColumnType.base[HDAccount, String](
|
||||
_.toString,
|
||||
str => HDAccount.fromPath(BIP32Path.fromString(str)).get)
|
||||
}
|
||||
|
||||
implicit val contractInfoMapper: BaseColumnType[ContractInfo] = {
|
||||
MappedColumnType
|
||||
.base[ContractInfo, String](_.hex, ContractInfo.fromHex)
|
||||
}
|
||||
|
||||
implicit val blockStampWithFutureMapper: BaseColumnType[BlockTimeStamp] = {
|
||||
MappedColumnType.base[BlockTimeStamp, Long](
|
||||
_.toUInt32.toLong,
|
||||
long => BlockTimeStamp(UInt32(long)))
|
||||
}
|
||||
|
||||
implicit val partialSigMapper: BaseColumnType[PartialSignature] = {
|
||||
MappedColumnType
|
||||
.base[PartialSignature, String](_.hex, PartialSignature.fromHex)
|
||||
}
|
||||
|
||||
implicit val partialSigsMapper: BaseColumnType[Vector[PartialSignature]] = {
|
||||
MappedColumnType
|
||||
.base[Vector[PartialSignature], String](
|
||||
_.foldLeft("")(_ ++ _.hex),
|
||||
hex =>
|
||||
if (hex.isEmpty) Vector.empty
|
||||
else InputPSBTMap(hex ++ "00").partialSignatures)
|
||||
}
|
||||
|
||||
implicit val satoshisPerVirtualByteMapper: BaseColumnType[
|
||||
SatoshisPerVirtualByte] = {
|
||||
MappedColumnType
|
||||
.base[SatoshisPerVirtualByte, String](
|
||||
_.currencyUnit.hex,
|
||||
hex => SatoshisPerVirtualByte(Satoshis.fromHex(hex)))
|
||||
}
|
||||
|
||||
implicit val networkMapper: BaseColumnType[BitcoinNetwork] = {
|
||||
MappedColumnType
|
||||
.base[BitcoinNetwork, String](_.name, BitcoinNetworks.fromString)
|
||||
}
|
||||
|
||||
implicit val schnorrDigitalSignatureMapper: BaseColumnType[
|
||||
SchnorrDigitalSignature] = {
|
||||
MappedColumnType.base[SchnorrDigitalSignature, String](
|
||||
_.hex,
|
||||
SchnorrDigitalSignature.fromHex)
|
||||
}
|
||||
}
|
||||
|
@ -140,7 +140,7 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
|
||||
|
||||
val datadir = conf.datadir
|
||||
val written = BitcoindConfig.writeConfigToFile(conf, datadir)
|
||||
logger.debug(s"Wrote conf to ${written}")
|
||||
logger.debug(s"Wrote conf to $written")
|
||||
written
|
||||
}
|
||||
|
||||
@ -178,7 +178,7 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
|
||||
|
||||
if (filtered.isEmpty)
|
||||
throw new RuntimeException(
|
||||
s"bitcoind ${known.toString} is not installed in ${binaryDirectory}. Run `sbt downloadBitcoind`")
|
||||
s"bitcoind ${known.toString} is not installed in $binaryDirectory. Run `sbt downloadBitcoind`")
|
||||
|
||||
// might be multiple versions downloaded for
|
||||
// each major version, i.e. 0.16.2 and 0.16.3
|
||||
@ -187,7 +187,7 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
|
||||
versionFolder
|
||||
.resolve("bin")
|
||||
.resolve(if (Properties.isWin) "bitcoind.exe" else "bitcoind")
|
||||
.toFile()
|
||||
.toFile
|
||||
}
|
||||
|
||||
/** Creates a `bitcoind` instance within the user temporary directory */
|
||||
@ -229,7 +229,7 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
|
||||
authCredentials = auth,
|
||||
zmqConfig = ZmqConfig.fromPort(zmqPort),
|
||||
binary = binary,
|
||||
datadir = configFile.getParent.toFile())
|
||||
datadir = configFile.getParent.toFile)
|
||||
|
||||
instance
|
||||
}
|
||||
@ -485,7 +485,7 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
|
||||
.map(info => info.isEmpty || info.head.connected.contains(false))
|
||||
.recoverWith {
|
||||
case exception: BitcoindException
|
||||
if exception.getMessage.contains("Node has not been added") =>
|
||||
if exception.getMessage().contains("Node has not been added") =>
|
||||
from.getPeerInfo.map(
|
||||
_.forall(_.networkInfo.addr != to.instance.uri))
|
||||
}
|
||||
@ -848,6 +848,19 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
|
||||
.flatMap(node.getBlockWithTransactions)
|
||||
}
|
||||
|
||||
/** Mines blocks until the specified block height. */
|
||||
def waitUntilBlock(
|
||||
blockHeight: Int,
|
||||
client: BitcoindRpcClient,
|
||||
addressForMining: BitcoinAddress)(
|
||||
implicit ec: ExecutionContext): Future[Unit] = {
|
||||
for {
|
||||
currentCount <- client.getBlockCount
|
||||
blocksToMine = blockHeight - currentCount
|
||||
_ <- client.generateToAddress(blocks = blocksToMine, addressForMining)
|
||||
} yield ()
|
||||
}
|
||||
|
||||
/**
|
||||
* Produces a confirmed transaction from `sender` to `address`
|
||||
* for `amount`
|
||||
@ -941,7 +954,7 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
|
||||
implicit system: ActorSystem): Future[BitcoindRpcClient] = {
|
||||
implicit val ec: ExecutionContextExecutor = system.dispatcher
|
||||
require(
|
||||
instance.datadir.getPath().startsWith(Properties.tmpDir),
|
||||
instance.datadir.getPath.startsWith(Properties.tmpDir),
|
||||
s"${instance.datadir} is not in user temp dir! This could lead to bad things happening.")
|
||||
|
||||
//start the bitcoind instance so eclair can properly use it
|
||||
|
@ -209,7 +209,7 @@ abstract class Wallet
|
||||
* finalizing and signing the transaction, then correctly processing and logging it
|
||||
*/
|
||||
private def finishSend(
|
||||
txBuilder: RawTxBuilderWithFinalizer,
|
||||
txBuilder: RawTxBuilderWithFinalizer[StandardNonInteractiveFinalizer],
|
||||
utxoInfos: Vector[ScriptSignatureParams[InputInfo]],
|
||||
sentAmount: CurrencyUnit,
|
||||
feeRate: FeeUnit): Future[Transaction] = {
|
||||
|
@ -70,8 +70,9 @@ trait FundTransactionHandling extends WalletLogger { self: WalletApi =>
|
||||
fromAccount: AccountDb,
|
||||
keyManagerOpt: Option[BIP39KeyManager],
|
||||
coinSelectionAlgo: CoinSelectionAlgo = CoinSelectionAlgo.AccumulateLargest,
|
||||
markAsReserved: Boolean = false): Future[
|
||||
(RawTxBuilderWithFinalizer, Vector[ScriptSignatureParams[InputInfo]])] = {
|
||||
markAsReserved: Boolean = false): Future[(
|
||||
RawTxBuilderWithFinalizer[StandardNonInteractiveFinalizer],
|
||||
Vector[ScriptSignatureParams[InputInfo]])] = {
|
||||
val utxosF = for {
|
||||
utxos <- listUtxos(fromAccount.hdAccount)
|
||||
|
||||
|
@ -44,6 +44,14 @@ private[wallet] trait UtxoHandling extends WalletLogger {
|
||||
spendingInfoDAO.findAllUnspentForAccount(hdAccount)
|
||||
}
|
||||
|
||||
/** Returns all the utxos originating from the given outpoints */
|
||||
def listUtxos(outPoints: Vector[TransactionOutPoint]): Future[
|
||||
Vector[SpendingInfoDb]] = {
|
||||
spendingInfoDAO
|
||||
.findAll()
|
||||
.map(_.filter(spendingInfo => outPoints.contains(spendingInfo.outPoint)))
|
||||
}
|
||||
|
||||
protected def updateUtxoConfirmedState(
|
||||
txo: SpendingInfoDb,
|
||||
blockHash: DoubleSha256DigestBE): Future[SpendingInfoDb] = {
|
||||
|
Loading…
Reference in New Issue
Block a user