Remove the clause in Transction.fromHex() where we throw in the case … (#1431)

* Remove the clause in Transction.fromHex() where we throw in the case of a witness transaction, do something smarter and look at bytes in the byte vector

* WIP add test case

* Add back try/catch as it's still necessary for cases of unsigned base transactions

* Add permalink to example test case
This commit is contained in:
Chris Stewart 2020-05-18 14:28:00 -05:00 committed by GitHub
parent 9172aa3206
commit f3469f8e28
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 44 additions and 25 deletions

View file

@ -85,6 +85,18 @@ class TransactionTest extends BitcoinSUnitTest {
tx.hex must be(rawTx)
}
it must "deserialize and serialize a base transaction with no inputs" in {
//this should be considered a base transaction since there is no script witnesses
val hex = "02000000" + //version
"0001" + //0 inputs, 1 output
"00e1f50500000000" + //1BTC
"17a9148fe46e05e329badba1c390a5ea2c0ad7de2059cd87" + //spk
"00000000" //locktime
val btx = Transaction.fromHex(hex)
btx.isInstanceOf[BaseTransaction] must be(true)
btx.hex must be(hex)
}
it must "serialize and deserialize a large tx" in {
val rawTx =
"0e2fddd0071fc32e0849ef3d3f6024aa6d73fa1eb91e3daad5a5dcfde8d45a376bc2f274b207d7017e8346304402203f7973c50fa84ab8960d5895bfcc73365101cc3967fa39528a94ab5e94de218d02207d8fc26f806d26407dd13f52be1d40c90b403690cce3933f71de57607a78848a2103bf87039d25c947357b31d005575d75071ddd6e97017e9efa57559b6a5daa03af1976a9143b75df7c44a47fed51374aef67bb7e7ae071b0a788acd3f37759c999d6f325fa7fb6445a5c6989d2fcee2b60c83cc1dd167d189488f74b09eade054ef5304847304502210087ebadf23475d287824ab171addc39907f5c93d14a5b463cbcc37805cd37cdaa02206cbca1f7768e99b65bc7a03d64056f1ce8c86ab78cbc7c19d49dab2c7084f5a830204fe815b8c4937864464ade73d3869e8888569e976742b666c742e8c60a46e5e14756035ccc736946304402204c544118e309de16cde7bc69e8018688aceb0a329e935bcf5d5f6aa8904937ad022043bee0a74e0cd9a6317d2abdab6cff004a6ebff2dc3f59b75a4e90aecf83c7b32103336d83e7f45e66b6655b25a4b3ee5679785378a89a9db407360dce45b24d67e0775bfdc70000000000000000000000000000000000000000000000000000000000000000fffffffffdb20100483046022100e52e3d78998643d2987a6210972984caaf33b53e2493daaf7dda6a00c7aade80022100d92576b1a7219c49d4ae85a7e450ee039d170a7aeed5fd7b34825faaab885055473045022100977591b85fd01d0969f39bd66f84b01adf8da5879ca6fc80c474a27502d9345f02201c415141bee3504b6eb6ff08c60e1e55b519ae80191bfa2aa9f1082581d88b5a473045022100999259a53b3b692519b95f058a6a70734f77ca4752103cb777f83ff17acff4d30220383b7debacc2dec8553d73ab1d52523a56921ba3904153fb43a4509d5dff0a2b4730450220609a352f8afd4aa49fef4bd81c13df2dbae87070217958099968b652d44d40d1022100d6f9902a240a4c516479b95a4a75ee42ea31441a7163fbcfeb1879de9907d0554630440220272828412f2c0833d9328209ec476240ee8ba51453773df4639001c84657a7ed02200b75a0bc08c002be39868a596d703ce3641b243286bede8264af0df7818843cb4830460221008ead4ad119e7350ea23de18c845a2018babea3ffd135575ece8f33ebf7f6bfe5022100e5e4067b788887edec933e7776799fe2d92915cb19e21cf7145fe4577b4eb469ffffffff0000000000000000000000000000000000000000000000000000000000000000ffffffff6946304402207b1000f0aea3a8f9f3952d13a7fd5eceb3740e6a4f2543987329f1a99a0bec6c0220457fcff820bac0c0f5c65c4228b89eadd380a98a99dfa2ed7c135617323b7ff021020610f065313942890798946ed97ca12d2852f66765bfd6785f90db650bb7d62affffffff42c761d25e0f10cfc2c82280f741e63e0aa184dd28627c487d6add381949be757d48ab4e85483046022100bceb7174237d148d3be472ad2fa9b19d2db5e9256943f32ed4abc6ae603602d8022100832b11ae88981e588daa6e65fcdd732ee186c21287589f7cbf0678109c483add210325259fd900969d3c16e870eeb9ecfd2bff8519c2aa81958a8bc19be6594a2d6e1976a9140bc44e1f010f5dfa4a102319e3a2445c164ce5d088ac5b0ddd9c652982074e88b4724293e21f651804b327586396ecf411784e80f6478fb23f606d569c746a47304502200ec20fae608b985e8b65f29a9e69ec671926eb837797763bf0178a515407b28a022100a131250b8be7fb2a533d44a1acf620155cf352ba8387f4e419b5b06c2b50901d2103d46e46269fc7ed661e4c34f714cfe6cd36231a77d79b7fd17edcb1741fdab9b781b905f5003c4edbe4"

View file

@ -2,21 +2,10 @@ package org.bitcoins.core.protocol.transaction
import org.bitcoins.core.number.{Int32, UInt32}
import org.bitcoins.core.protocol.script.ScriptWitness
import org.bitcoins.core.serializers.transaction.{
RawBaseTransactionParser,
RawWitnessTransactionParser
}
import org.bitcoins.crypto.{
CryptoUtil,
DoubleSha256Digest,
DoubleSha256DigestBE,
Factory,
NetworkElement
}
import org.bitcoins.core.serializers.transaction.{RawBaseTransactionParser, RawWitnessTransactionParser}
import org.bitcoins.crypto._
import scodec.bits.ByteVector
import scala.util.{Failure, Success, Try}
/**
* Created by chris on 7/14/15.
*/
@ -159,7 +148,7 @@ sealed abstract class WitnessTransaction extends Transaction {
val base = BaseTransaction(version, inputs, outputs, lockTime)
base.byteSize * 3 + byteSize
}
override def bytes = RawWitnessTransactionParser.write(this)
override def bytes: ByteVector = RawWitnessTransactionParser.write(this)
/**
* Updates the [[org.bitcoins.core.protocol.script.ScriptWitness ScriptWitness]] at the given index and
@ -175,14 +164,23 @@ sealed abstract class WitnessTransaction extends Transaction {
object Transaction extends Factory[Transaction] {
def fromBytes(bytes: ByteVector): Transaction = {
val wtxTry = Try(RawWitnessTransactionParser.read(bytes))
wtxTry match {
case Success(wtx) =>
wtx
case Failure(_) =>
val btx = RawBaseTransactionParser.read(bytes)
btx
override def fromBytes(bytes: ByteVector): Transaction = {
//see BIP141 for marker/flag bytes
//https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#transaction-id
if (bytes(4) == WitnessTransaction.marker && bytes(5) == WitnessTransaction.flag) {
//this throw/catch is _still_ necessary for the case where we have unsigned base transactions
//with zero inputs and 1 output which is serialized as "0001" at bytes 4 and 5.
//these transactions will not have a script witness associated with them making them invalid
//witness transactions (you need to have a witness to be considered a witness tx)
//see: https://github.com/bitcoin-s/bitcoin-s/blob/01d89df1b7c6bc4b1594406d54d5e6019705c654/core-test/src/test/scala/org/bitcoins/core/protocol/transaction/TransactionTest.scala#L88
try {
RawWitnessTransactionParser.read(bytes)
} catch {
case scala.util.control.NonFatal(_) =>
RawBaseTransactionParser.read(bytes)
}
} else {
RawBaseTransactionParser.read(bytes)
}
}
}
@ -235,4 +233,12 @@ object WitnessTransaction extends Factory[WitnessTransaction] {
EmptyWitness.fromInputs(btx.inputs))
case wtx: WitnessTransaction => wtx
}
val marker: Byte = 0.toByte
val flag: Byte = 1.toByte
/** These bytes -- at index 4 & 5 in a witness transaction -- are used to indicate a witness tx
* @see BIP141 https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#transaction-id
* */
val witBytes: ByteVector = ByteVector(marker, flag)
}

View file

@ -1,5 +1,6 @@
package org.bitcoins.core.serializers
import org.bitcoins.core.util.BitcoinSLogger
import org.bitcoins.crypto.BytesUtil
import scodec.bits.ByteVector
@ -7,7 +8,7 @@ import scodec.bits.ByteVector
* Created by chris on 1/11/16.
* A common trait for reading/writing bitcoin objects to/from bytes/hex
*/
abstract class RawBitcoinSerializer[T] {
abstract class RawBitcoinSerializer[T] extends BitcoinSLogger {
/** Reads a hexadecimal value and transforms it into the native scala type T. */
def read(hex: String): T = read(BytesUtil.decodeHex(hex))

View file

@ -20,7 +20,7 @@ sealed abstract class RawSerializerHelper {
bytes: ByteVector,
constructor: ByteVector => T): (Seq[T], ByteVector) = {
val count = CompactSizeUInt.parse(bytes)
val payload = bytes.splitAt(count.byteSize.toInt)._2
val (_,payload) = bytes.splitAt(count.byteSize.toInt)
var counter = 0
val b = Vector.newBuilder[T]
@tailrec

View file

@ -68,7 +68,7 @@ sealed abstract class RawWitnessTransactionParser
//notice we use the old serialization format if all witnesses are empty
// https://github.com/bitcoin/bitcoin/blob/e8cfe1ee2d01c493b758a67ad14707dca15792ea/src/primitives/transaction.h#L276-L281
if (tx.witness.exists(_ != EmptyScriptWitness)) {
val witConstant = ByteVector(0.toByte, 1.toByte)
val witConstant = WitnessTransaction.witBytes
version ++ witConstant ++ inputs ++ outputs ++ witness ++ lockTime
} else BaseTransaction(tx.version, tx.inputs, tx.outputs, tx.lockTime).bytes
}