From f35975c4590f6ecaa53193be23408487f2942f3f Mon Sep 17 00:00:00 2001 From: Chris Stewart Date: Thu, 19 May 2016 15:19:50 -0500 Subject: [PATCH 1/7] Adding blockchain package, adding trait to represent block headers --- .../protocol/blockchain/BlockHeader.scala | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 src/main/scala/org/bitcoins/core/protocol/blockchain/BlockHeader.scala diff --git a/src/main/scala/org/bitcoins/core/protocol/blockchain/BlockHeader.scala b/src/main/scala/org/bitcoins/core/protocol/blockchain/BlockHeader.scala new file mode 100644 index 0000000000..1e513254e0 --- /dev/null +++ b/src/main/scala/org/bitcoins/core/protocol/blockchain/BlockHeader.scala @@ -0,0 +1,83 @@ +package org.bitcoins.core.protocol.blockchain + +/** + * Created by chris on 5/19/16. + * Nodes collect new transactions into a block, hash them into a hash tree, + * and scan through nonce values to make the block's hash satisfy proof-of-work + * requirements. When they solve the proof-of-work, they broadcast the block + * to everyone and the block is added to the block chain. The first transaction + * in the block is a special one that creates a new coin owned by the creator + * of the block. + * Bitcoin Developer reference link + * https://bitcoin.org/en/developer-reference#block-headers + * Bitcoin Core implementation: + * https://github.com/bitcoin/bitcoin/blob/master/src/primitives/block.h#L20 + */ +trait BlockHeader { + + /** + * The block version number indicates which set of block validation rules to follow. + * See the list of block versions below. + * See BIP9 for more information on what version number signify + * https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki + * @return the version number for this block + */ + def version : Long + + + /** + * A SHA256(SHA256()) hash in internal byte order of the previous block’s header. + * This ensures no previous block can be changed without also changing this block’s header. + * @return the previous block's hash + */ + def previousBlockHash : String + + /** + * A SHA256(SHA256()) hash in internal byte order. + * The merkle root is derived from the hashes of all transactions included in this block, + * ensuring that none of those transactions can be modified without modifying the header. + * https://bitcoin.org/en/developer-reference#merkle-trees + * @return the merkle root of the merkle tree + */ + def merkleRootHash : String + + + /** + * The block time is a Unix epoch time when the miner started hashing the header (according to the miner). + * Must be greater than or equal to the median time of the previous 11 blocks. + * Full nodes will not accept blocks with headers more than two hours in the future according to their clock. + * @return the time when the miner started solving the block + */ + def time : Long + + /** + * An encoded version of the target threshold this block’s header hash must be less than or equal to. + * See the nBits format described below. + * https://bitcoin.org/en/developer-reference#target-nbits + * @return + */ + def nBits : Long + + /** + * An arbitrary number miners change to modify the header hash in order to produce a hash below the target threshold. + * If all 32-bit values are tested, the time can be updated or the coinbase + * transaction can be changed and the merkle root updated. + * @return the nonce used to try and solve a block + */ + def nonce : Long +} + + +/** + * Companion object used for creating BlockHeaders + */ +object BlockHeader { + + private sealed case class BlockHeaderImpl(version : Long, previousBlockHash : String, + merkleRootHash : String, time : Long, nBits : Long, nonce : Long) extends BlockHeader + + def apply(version : Long, previousBlockHash : String, merkleRootHash : String, + time : Long, nBits : Long, nonce : Long) : BlockHeader = { + BlockHeaderImpl(version,previousBlockHash,merkleRootHash,time,nBits,nonce) + } +} \ No newline at end of file From 5c3c7a951283ffe77e638412b86cdc38b6cdbd06 Mon Sep 17 00:00:00 2001 From: Chris Stewart Date: Thu, 19 May 2016 15:28:37 -0500 Subject: [PATCH 2/7] Adding trait to represent blocks in our blockchain --- .../core/protocol/blockchain/Block.scala | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 src/main/scala/org/bitcoins/core/protocol/blockchain/Block.scala diff --git a/src/main/scala/org/bitcoins/core/protocol/blockchain/Block.scala b/src/main/scala/org/bitcoins/core/protocol/blockchain/Block.scala new file mode 100644 index 0000000000..daa7e3c2b5 --- /dev/null +++ b/src/main/scala/org/bitcoins/core/protocol/blockchain/Block.scala @@ -0,0 +1,50 @@ +package org.bitcoins.core.protocol.blockchain + +import org.bitcoins.core.protocol.CompactSizeUInt +import org.bitcoins.core.protocol.transaction.Transaction + +/** + * Created by chris on 5/19/16. + * Represents a block in our blockchain + * Bitcoin Core implementation: + * https://github.com/bitcoin/bitcoin/blob/master/src/primitives/block.h#L73 + * Bitcoin Developer Reference link: + * https://bitcoin.org/en/developer-reference#serialized-blocks + */ +trait Block { + + /** + * The block header for this block + * @return + */ + def blockHeader : BlockHeader + + + /** + * The total number of transactions in this block, + * including the coinbase transaction. + * @return + */ + def txCount : CompactSizeUInt + + /** + * The transactions contained in this block + * @return + */ + def transactions : Seq[Transaction] + +} + + +/** + * Companion object for creating Blocks + */ +object Block { + + private sealed case class BlockImpl(blockHeader : BlockHeader, + txCount : CompactSizeUInt, transactions : Seq[Transaction]) extends Block + + def apply(blockHeader : BlockHeader, txCount : CompactSizeUInt, transactions : Seq[Transaction]) : Block = { + BlockImpl(blockHeader, txCount, transactions) + } +} \ No newline at end of file From b45c2593abc9751ae6c2b58aabe2ed41eadb9696 Mon Sep 17 00:00:00 2001 From: Chris Stewart Date: Fri, 20 May 2016 08:02:23 -0500 Subject: [PATCH 3/7] Adding an unimplmeneted serializer for BlockHeaders --- .../core/protocol/blockchain/Block.scala | 1 - .../serializers/RawBitcoinSerializer.scala | 4 --- .../blockchain/RawBlockHeaderSerializer.scala | 29 +++++++++++++++++++ 3 files changed, 29 insertions(+), 5 deletions(-) create mode 100644 src/main/scala/org/bitcoins/core/serializers/blockchain/RawBlockHeaderSerializer.scala diff --git a/src/main/scala/org/bitcoins/core/protocol/blockchain/Block.scala b/src/main/scala/org/bitcoins/core/protocol/blockchain/Block.scala index daa7e3c2b5..d56d09f994 100644 --- a/src/main/scala/org/bitcoins/core/protocol/blockchain/Block.scala +++ b/src/main/scala/org/bitcoins/core/protocol/blockchain/Block.scala @@ -19,7 +19,6 @@ trait Block { */ def blockHeader : BlockHeader - /** * The total number of transactions in this block, * including the coinbase transaction. diff --git a/src/main/scala/org/bitcoins/core/serializers/RawBitcoinSerializer.scala b/src/main/scala/org/bitcoins/core/serializers/RawBitcoinSerializer.scala index 79f2ffa5b0..807661adc5 100644 --- a/src/main/scala/org/bitcoins/core/serializers/RawBitcoinSerializer.scala +++ b/src/main/scala/org/bitcoins/core/serializers/RawBitcoinSerializer.scala @@ -11,7 +11,6 @@ trait RawBitcoinSerializer[T] extends RawBitcoinSerializerHelper { /** * Reads a hexadecimal value and transforms it into the native * scala type T - * * @param hex * @return */ @@ -19,7 +18,6 @@ trait RawBitcoinSerializer[T] extends RawBitcoinSerializerHelper { /** * Reads in bytes and transforms it into the approriate scala type T - * * @param bytes * @return */ @@ -27,7 +25,6 @@ trait RawBitcoinSerializer[T] extends RawBitcoinSerializerHelper { /** * Reads in bytes and transforms it into the approriate scala type T - * * @param bytes * @return */ @@ -35,7 +32,6 @@ trait RawBitcoinSerializer[T] extends RawBitcoinSerializerHelper { /** * Takes a type T and writes it into the appropriate hexadecimal serialization for type T - * * @param t * @return */ diff --git a/src/main/scala/org/bitcoins/core/serializers/blockchain/RawBlockHeaderSerializer.scala b/src/main/scala/org/bitcoins/core/serializers/blockchain/RawBlockHeaderSerializer.scala new file mode 100644 index 0000000000..517559074e --- /dev/null +++ b/src/main/scala/org/bitcoins/core/serializers/blockchain/RawBlockHeaderSerializer.scala @@ -0,0 +1,29 @@ +package org.bitcoins.core.serializers.blockchain + +import org.bitcoins.core.protocol.blockchain.BlockHeader +import org.bitcoins.core.serializers.RawBitcoinSerializer + +/** + * Created by chris on 5/19/16. + * Serializes block headers + * https://bitcoin.org/en/developer-reference#block-headers + */ +trait RawBlockHeaderSerializer extends RawBitcoinSerializer[BlockHeader] { + + /** + * Converts a list of bytes into a block header + * @param bytes the bytes to parsed into a block header + * @return the block header + */ + def read(bytes : List[Byte]) : BlockHeader = ??? + + /** + * Serializes the BlockHeader to a hexadecimal string + * @param blockHeader the block header to be serialized + * @return the hexadecimal string representing the block header + */ + def write(blockHeader: BlockHeader) : String = ??? + +} + +object RawBlockHeaderSerializer extends RawBlockHeaderSerializer \ No newline at end of file From 47ae19ad4bc3b65e79c7cd367973bebf92fa476f Mon Sep 17 00:00:00 2001 From: Chris Stewart Date: Fri, 20 May 2016 08:57:26 -0500 Subject: [PATCH 4/7] Adding RawBlockSerializer that is currently unimplemented --- .../blockchain/RawBlockSerializer.scala | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 src/main/scala/org/bitcoins/core/serializers/blockchain/RawBlockSerializer.scala diff --git a/src/main/scala/org/bitcoins/core/serializers/blockchain/RawBlockSerializer.scala b/src/main/scala/org/bitcoins/core/serializers/blockchain/RawBlockSerializer.scala new file mode 100644 index 0000000000..4455ba6269 --- /dev/null +++ b/src/main/scala/org/bitcoins/core/serializers/blockchain/RawBlockSerializer.scala @@ -0,0 +1,30 @@ +package org.bitcoins.core.serializers.blockchain + +import org.bitcoins.core.protocol.blockchain.Block +import org.bitcoins.core.serializers.RawBitcoinSerializer + +/** + * Created by chris on 5/20/16. + * Responsible for serializing blocks in our blockchain + * https://bitcoin.org/en/developer-reference#serialized-blocks + */ +trait RawBlockSerializer extends RawBitcoinSerializer[Block] { + + /** + * Takes a list of bytes and converts it into a Block + * @param bytes the bytes to be converted to a block + * @return the block object parsed from the list of bytes + */ + def read(bytes : List[Byte]) : Block = ??? + + /** + * Takes in a block and converts it a hexadecimal string + * @param block the block that needs to be converted to a hexadecimal string + * @return the hexadecimal string representing a block + */ + def write(block : Block) : String = ??? + +} + + +object RawBlockSerializer extends RawBlockSerializer \ No newline at end of file From db50658823ebd8bfa736819caea1b79cc93ceede Mon Sep 17 00:00:00 2001 From: Chris Stewart Date: Sun, 22 May 2016 12:32:52 -0500 Subject: [PATCH 5/7] Adding parent trait for individual blockchains called ChainParams --- .../protocol/blockchain/ChainParams.scala | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 src/main/scala/org/bitcoins/core/protocol/blockchain/ChainParams.scala diff --git a/src/main/scala/org/bitcoins/core/protocol/blockchain/ChainParams.scala b/src/main/scala/org/bitcoins/core/protocol/blockchain/ChainParams.scala new file mode 100644 index 0000000000..3187b63100 --- /dev/null +++ b/src/main/scala/org/bitcoins/core/protocol/blockchain/ChainParams.scala @@ -0,0 +1,53 @@ +package org.bitcoins.core.protocol.blockchain + +/** + * Created by chris on 5/22/16. + * CChainParams defines various tweakable parameters of a given instance of the + * Bitcoin system. There are three: the main network on which people trade goods + * and services, the public test network which gets reset from time to time and + * a regression test mode which is intended for private networks only. It has + * minimal difficulty to ensure that blocks can be found instantly. + * Mimics this C++ interface + * https://github.com/bitcoin/bitcoin/blob/master/src/chainparams.h#L42 + */ +trait ChainParams { + + sealed trait Base58Type + case object PubKeyAddress extends Base58Type + case object ScriptAddress extends Base58Type + case object SecretKey extends Base58Type + case object ExtPublicKey extends Base58Type + case object ExtSecretKey extends Base58Type + + /** + * The gensis block in the blockchain + * @return + */ + def genesisBlock: Block + + /** + * Filter transactions that do not match well-defined patterns + * inside of Policy + * @return + */ + def requireStandardTransaction: Boolean + + /** + * Takes in a Base58Type and returns its base58 prefix + * @param base58 + * @return + */ + def base58Prefix(base58 : Base58Type) : String = base58Prefix(base58) + + /** + * The mapping from a Base58Type to a String + * @return + */ + def base58Prefixes : Map[Base58Type,String] + + /** + * The seeds used to bootstrap the network + * @return + */ + def dnsSeeds : Seq[String] +} \ No newline at end of file From a3b8278045cca397e3a17c075bfbe08789d679eb Mon Sep 17 00:00:00 2001 From: Chris Stewart Date: Mon, 23 May 2016 15:32:10 -0500 Subject: [PATCH 6/7] Adding ChainParams & the respective ChainParams object for each network --- .../protocol/blockchain/ChainParams.scala | 98 ++++++++++++++++--- .../core/protocol/script/ScriptPubKey.scala | 2 - .../org/bitcoins/core/util/BitcoinSUtil.scala | 12 +++ .../interpreter/ScriptInterpreterTest.scala | 27 ++--- 4 files changed, 111 insertions(+), 28 deletions(-) diff --git a/src/main/scala/org/bitcoins/core/protocol/blockchain/ChainParams.scala b/src/main/scala/org/bitcoins/core/protocol/blockchain/ChainParams.scala index 3187b63100..166220233f 100644 --- a/src/main/scala/org/bitcoins/core/protocol/blockchain/ChainParams.scala +++ b/src/main/scala/org/bitcoins/core/protocol/blockchain/ChainParams.scala @@ -1,5 +1,7 @@ package org.bitcoins.core.protocol.blockchain +import org.bitcoins.core.util.BitcoinSUtil + /** * Created by chris on 5/22/16. * CChainParams defines various tweakable parameters of a given instance of the @@ -10,44 +12,112 @@ package org.bitcoins.core.protocol.blockchain * Mimics this C++ interface * https://github.com/bitcoin/bitcoin/blob/master/src/chainparams.h#L42 */ -trait ChainParams { - - sealed trait Base58Type - case object PubKeyAddress extends Base58Type - case object ScriptAddress extends Base58Type - case object SecretKey extends Base58Type - case object ExtPublicKey extends Base58Type - case object ExtSecretKey extends Base58Type +sealed trait ChainParams { + /** + * Return the BIP70 network string (main, test or regtest) + * + * @return + */ + def networkId : String /** * The gensis block in the blockchain + * * @return */ - def genesisBlock: Block + def genesisBlock : Block /** * Filter transactions that do not match well-defined patterns * inside of Policy + * * @return */ - def requireStandardTransaction: Boolean - + def requireStandardTransaction : Boolean + /** * Takes in a Base58Type and returns its base58 prefix + * * @param base58 * @return */ - def base58Prefix(base58 : Base58Type) : String = base58Prefix(base58) + def base58Prefix(base58 : Base58Type) : Seq[Byte] = base58Prefixes(base58) /** * The mapping from a Base58Type to a String + * * @return */ - def base58Prefixes : Map[Base58Type,String] + def base58Prefixes : Map[Base58Type,Seq[Byte]] /** * The seeds used to bootstrap the network + * * @return */ def dnsSeeds : Seq[String] -} \ No newline at end of file +} + + +/** + * This is the main network parameters + */ +object MainNetChainParams extends ChainParams { + override def networkId = "main" + override def genesisBlock = ??? + override def requireStandardTransaction = ??? + + override def base58Prefixes : Map[Base58Type,Seq[Byte]] = + Map(PubKeyAddress -> Seq(1.toByte, 0.toByte), + ScriptAddress -> Seq(1.toByte, 5.toByte), + SecretKey -> Seq(1.toByte, 128.toByte), + ExtPublicKey -> Seq(BitcoinSUtil.hexToByte("04"), BitcoinSUtil.hexToByte("88"), + BitcoinSUtil.hexToByte("b2"), BitcoinSUtil.hexToByte("1e")), + ExtSecretKey -> Seq(BitcoinSUtil.hexToByte("04"), BitcoinSUtil.hexToByte("88"), + BitcoinSUtil.hexToByte("ad"), BitcoinSUtil.hexToByte("e4"))) + + + override def dnsSeeds = Seq("seed.bitcoin.sipa.be","dnsseed.bluematt.me","dnsseed.bitcoin.dashjr.org", + "seed.bitcoinstats.com","bitseed.xf2.org","seed.bitcoin.jonasschnelli.ch") + +} + +object TestNetChainParams extends ChainParams { + + override def networkId = "test" + override def genesisBlock : Block = ??? + override def requireStandardTransaction = ??? + + override def base58Prefixes : Map[Base58Type,Seq[Byte]] = Map( + PubKeyAddress -> Seq(1.toByte, 111.toByte), + ScriptAddress -> Seq(1.toByte, 196.toByte), + SecretKey -> Seq(1.toByte, 239.toByte), + ExtPublicKey -> Seq(BitcoinSUtil.hexToByte("04"), BitcoinSUtil.hexToByte("35"), + BitcoinSUtil.hexToByte("87"), BitcoinSUtil.hexToByte("cf")), + ExtSecretKey -> Seq(BitcoinSUtil.hexToByte("04"), BitcoinSUtil.hexToByte("35"), + BitcoinSUtil.hexToByte("83"), BitcoinSUtil.hexToByte("94"))) + + + override def dnsSeeds = Seq("testnet-seed.bitcoin.petertodd.org", + "testnet-seed.bluematt.me","testnet-seed.bitcoin.schildbach.de") +} + + +object RegTestNetChainParams extends ChainParams { + override def networkId = "regtest" + override def genesisBlock : Block = ??? + override def requireStandardTransaction = ??? + + override def base58Prefixes : Map[Base58Type, Seq[Byte]] = TestNetChainParams.base58Prefixes + + override def dnsSeeds = Seq() +} + + + +sealed trait Base58Type +case object PubKeyAddress extends Base58Type +case object ScriptAddress extends Base58Type +case object SecretKey extends Base58Type +case object ExtPublicKey extends Base58Type +case object ExtSecretKey extends Base58Type \ No newline at end of file diff --git a/src/main/scala/org/bitcoins/core/protocol/script/ScriptPubKey.scala b/src/main/scala/org/bitcoins/core/protocol/script/ScriptPubKey.scala index 29bef462ba..4e92800227 100644 --- a/src/main/scala/org/bitcoins/core/protocol/script/ScriptPubKey.scala +++ b/src/main/scala/org/bitcoins/core/protocol/script/ScriptPubKey.scala @@ -44,7 +44,6 @@ trait MultiSignatureScriptPubKey extends ScriptPubKey { /** * Returns the amount of required signatures for this multisignature script pubkey output - * * @return */ def requiredSigs : Long = { @@ -65,7 +64,6 @@ trait MultiSignatureScriptPubKey extends ScriptPubKey { /** * The maximum amount of signatures for this multisignature script pubkey output - * * @return */ def maxSigs : Long = { diff --git a/src/main/scala/org/bitcoins/core/util/BitcoinSUtil.scala b/src/main/scala/org/bitcoins/core/util/BitcoinSUtil.scala index 1ead24fc1f..7dd9ad184d 100644 --- a/src/main/scala/org/bitcoins/core/util/BitcoinSUtil.scala +++ b/src/main/scala/org/bitcoins/core/util/BitcoinSUtil.scala @@ -53,6 +53,16 @@ trait BitcoinSUtil extends NumberUtil { def hexToInt(hex : String) : Int = toLong(hex).toInt + /** + * Converts a two character hex string to its byte representation + * @param hex + * @return + */ + def hexToByte(hex : String): Byte = { + require(hex.size == 2) + BitcoinSUtil.decodeHex(hex).head + } + def decodeBase58(base58 : String) : Seq[Byte] = Base58.decode(base58).toList def encodeBase58(bytes : Seq[Byte]) : String = Base58.encode(bytes.toArray) @@ -81,6 +91,8 @@ trait BitcoinSUtil extends NumberUtil { * @return */ def flipHalfByte(hex : String) = hex.grouped(2).map(_.reverse).mkString + + } object BitcoinSUtil extends BitcoinSUtil diff --git a/src/test/scala/org/bitcoins/core/script/interpreter/ScriptInterpreterTest.scala b/src/test/scala/org/bitcoins/core/script/interpreter/ScriptInterpreterTest.scala index eb86f0be42..1901dbd90f 100644 --- a/src/test/scala/org/bitcoins/core/script/interpreter/ScriptInterpreterTest.scala +++ b/src/test/scala/org/bitcoins/core/script/interpreter/ScriptInterpreterTest.scala @@ -3,21 +3,25 @@ package org.bitcoins.core.script.interpreter import java.io.File import com.sun.org.apache.bcel.internal.generic.NOP -import org.bitcoins.core.protocol.script.ScriptPubKey +import org.bitcoins.core.protocol.script.{EmptyScriptSignature, ScriptPubKey, ScriptSignature} import org.bitcoins.core.script.ScriptProgram import org.bitcoins.core.script.bitwise.{OP_EQUAL, OP_EQUALVERIFY} import org.bitcoins.core.script.constant._ import org.bitcoins.core.script.control.OP_VERIFY -import org.bitcoins.core.script.crypto.{OP_CHECKSIG, OP_HASH160} -import org.bitcoins.core.script.flag.ScriptFlagFactory +import org.bitcoins.core.script.crypto.{OP_CHECKSIG, OP_HASH160, SIGHASH_ALL} +import org.bitcoins.core.script.flag.{ScriptFlagFactory, ScriptVerifyP2SH} import org.bitcoins.core.script.interpreter.testprotocol.{CoreTestCase, CoreTestCaseProtocol} import org.bitcoins.core.script.reserved.OP_NOP import org.bitcoins.core.script.stack.OP_DUP -import org.bitcoins.core.util.{BitcoinSLogger, TestUtil, TransactionTestUtil} +import org.bitcoins.core.util._ import org.scalatest.{FlatSpec, MustMatchers} import org.slf4j.LoggerFactory import CoreTestCaseProtocol._ +import org.bitcoins.core.crypto.{ECFactory, TransactionSignatureSerializer} +import org.bitcoins.core.policy.Policy +import org.bitcoins.core.protocol.transaction.Transaction import org.bitcoins.core.protocol.transaction.testprotocol.CoreTransactionTestCase +import org.bitcoins.core.script.result.ScriptOk import spray.json._ import scala.io.Source @@ -28,8 +32,6 @@ class ScriptInterpreterTest extends FlatSpec with MustMatchers with ScriptInterp "ScriptInterpreter" must "evaluate all the scripts from the bitcoin core script_tests.json" in { - - val source = Source.fromURL(getClass.getResource("/script_tests.json")) @@ -37,12 +39,12 @@ class ScriptInterpreterTest extends FlatSpec with MustMatchers with ScriptInterp /* val lines = """ | [[ - | "0x47 0x3044022003fef42ed6c7be8917441218f525a60e2431be978e28b7aca4d7a532cc413ae8022067a1f82c74e8d69291b90d148778405c6257bbcfc2353cc38a3e1f22bf44254601 0x23 0x210279be667ef9dcbbac54a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac", - | "HASH160 0x14 0x23b0ad3477f2178bc0b3eed26e4e6316f4e83aa1 EQUAL", - | "P2SH", - | "EVAL_FALSE", - | "P2SH(P2PK), bad redeemscript" - | ]] + | "0x48 0x3045022100e58c2307fb6569d0f25b4375f1074e48592bfc62d423bdc8f016365c980c0ae602204984523016f6320dc275cb90c6c1be80b4c9753de2e9fe19f2e5290844587da101 0x20 0x0228b426496c6a96846561a40b241ead0e03fe217d52de26cf2e707f0f181999fb 0x19 0x76a914364ddb17f9997cd91984c897eb5c0123f0a4b43f88ac", + | "HASH160 0x14 0x8a24f3c75f6d401a977bc63f854cc5d7dadfa7de EQUAL", + | "P2SH", + "OK", + | "P2SH(P2PKH)" + |]] """.stripMargin*/ val lines = try source.getLines.filterNot(_.isEmpty).map(_.trim) mkString "\n" finally source.close() val json = lines.parseJson @@ -68,4 +70,5 @@ class ScriptInterpreterTest extends FlatSpec with MustMatchers with ScriptInterp } } } + } From 133877d9f2562c8e100ad137e27262e3c5990b1d Mon Sep 17 00:00:00 2001 From: Chris Stewart Date: Tue, 24 May 2016 09:53:39 -0500 Subject: [PATCH 7/7] Moving CompactUIntImpl into factory, adding NetworkElement trait to Block/BlockHeader to allow for easy access to hex/byte representations --- .../core/protocol/CommonStructures.scala | 13 +++-- .../core/protocol/blockchain/Block.scala | 21 ++++++-- .../protocol/blockchain/BlockHeader.scala | 11 +++- .../protocol/blockchain/ChainParams.scala | 52 +++++++++++++++++++ .../transaction/TransactionInput.scala | 15 ++++-- .../blockchain/RawBlockSerializer.scala | 2 +- .../org/bitcoins/core/util/NumberUtil.scala | 27 +++++----- .../protocol/blockchain/ChainParamsTest.scala | 15 ++++++ .../core/util/TransactionTestUtil.scala | 1 - 9 files changed, 130 insertions(+), 27 deletions(-) create mode 100644 src/test/scala/org/bitcoins/core/protocol/blockchain/ChainParamsTest.scala diff --git a/src/main/scala/org/bitcoins/core/protocol/CommonStructures.scala b/src/main/scala/org/bitcoins/core/protocol/CommonStructures.scala index 945b0cd975..fe305db685 100644 --- a/src/main/scala/org/bitcoins/core/protocol/CommonStructures.scala +++ b/src/main/scala/org/bitcoins/core/protocol/CommonStructures.scala @@ -11,13 +11,11 @@ trait CompactSizeUInt { /** * The number parsed from VarInt - * * @return */ def num : Long /** - * The length of the VarInt in bytes - * + * The length of the VarInt in bytes * @return */ def size : Long @@ -33,7 +31,14 @@ trait CompactSizeUInt { } -case class CompactSizeUIntImpl(num : Long, size : Long) extends CompactSizeUInt +object CompactSizeUInt { + private sealed case class CompactSizeUIntImpl(num : Long, size : Long) extends CompactSizeUInt + def apply(num : Long, size : Long) : CompactSizeUInt = { + CompactSizeUIntImpl(num,size) + } +} + + diff --git a/src/main/scala/org/bitcoins/core/protocol/blockchain/Block.scala b/src/main/scala/org/bitcoins/core/protocol/blockchain/Block.scala index d56d09f994..5d87775c93 100644 --- a/src/main/scala/org/bitcoins/core/protocol/blockchain/Block.scala +++ b/src/main/scala/org/bitcoins/core/protocol/blockchain/Block.scala @@ -1,7 +1,9 @@ package org.bitcoins.core.protocol.blockchain -import org.bitcoins.core.protocol.CompactSizeUInt +import org.bitcoins.core.protocol.{CompactSizeUInt, NetworkElement} import org.bitcoins.core.protocol.transaction.Transaction +import org.bitcoins.core.serializers.blockchain.RawBlockSerializer +import org.bitcoins.core.util.{BitcoinSLogger, CryptoUtil, Factory} /** * Created by chris on 5/19/16. @@ -11,7 +13,7 @@ import org.bitcoins.core.protocol.transaction.Transaction * Bitcoin Developer Reference link: * https://bitcoin.org/en/developer-reference#serialized-blocks */ -trait Block { +sealed trait Block extends NetworkElement with BitcoinSLogger { /** * The block header for this block @@ -32,13 +34,22 @@ trait Block { */ def transactions : Seq[Transaction] + /** + * Returns the block's hash + * @return + */ + def hash : Seq[Byte] = CryptoUtil.doubleSHA256(bytes) + + + def hex = RawBlockSerializer.write(this) + } /** * Companion object for creating Blocks */ -object Block { +object Block extends Factory[Block] { private sealed case class BlockImpl(blockHeader : BlockHeader, txCount : CompactSizeUInt, transactions : Seq[Transaction]) extends Block @@ -46,4 +57,8 @@ object Block { def apply(blockHeader : BlockHeader, txCount : CompactSizeUInt, transactions : Seq[Transaction]) : Block = { BlockImpl(blockHeader, txCount, transactions) } + + def fromBytes(bytes : Seq[Byte]) : Block = RawBlockSerializer.read(bytes) + + def apply(bytes : Seq[Byte]) : Block = fromBytes(bytes) } \ No newline at end of file diff --git a/src/main/scala/org/bitcoins/core/protocol/blockchain/BlockHeader.scala b/src/main/scala/org/bitcoins/core/protocol/blockchain/BlockHeader.scala index 1e513254e0..ecfcb9b662 100644 --- a/src/main/scala/org/bitcoins/core/protocol/blockchain/BlockHeader.scala +++ b/src/main/scala/org/bitcoins/core/protocol/blockchain/BlockHeader.scala @@ -1,5 +1,8 @@ package org.bitcoins.core.protocol.blockchain +import org.bitcoins.core.protocol.NetworkElement +import org.bitcoins.core.util.BitcoinSLogger + /** * Created by chris on 5/19/16. * Nodes collect new transactions into a block, hash them into a hash tree, @@ -13,13 +16,14 @@ package org.bitcoins.core.protocol.blockchain * Bitcoin Core implementation: * https://github.com/bitcoin/bitcoin/blob/master/src/primitives/block.h#L20 */ -trait BlockHeader { +sealed trait BlockHeader extends NetworkElement with BitcoinSLogger { /** * The block version number indicates which set of block validation rules to follow. * See the list of block versions below. * See BIP9 for more information on what version number signify * https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki + * * @return the version number for this block */ def version : Long @@ -28,6 +32,7 @@ trait BlockHeader { /** * A SHA256(SHA256()) hash in internal byte order of the previous block’s header. * This ensures no previous block can be changed without also changing this block’s header. + * * @return the previous block's hash */ def previousBlockHash : String @@ -37,6 +42,7 @@ trait BlockHeader { * The merkle root is derived from the hashes of all transactions included in this block, * ensuring that none of those transactions can be modified without modifying the header. * https://bitcoin.org/en/developer-reference#merkle-trees + * * @return the merkle root of the merkle tree */ def merkleRootHash : String @@ -46,6 +52,7 @@ trait BlockHeader { * The block time is a Unix epoch time when the miner started hashing the header (according to the miner). * Must be greater than or equal to the median time of the previous 11 blocks. * Full nodes will not accept blocks with headers more than two hours in the future according to their clock. + * * @return the time when the miner started solving the block */ def time : Long @@ -54,6 +61,7 @@ trait BlockHeader { * An encoded version of the target threshold this block’s header hash must be less than or equal to. * See the nBits format described below. * https://bitcoin.org/en/developer-reference#target-nbits + * * @return */ def nBits : Long @@ -62,6 +70,7 @@ trait BlockHeader { * An arbitrary number miners change to modify the header hash in order to produce a hash below the target threshold. * If all 32-bit values are tested, the time can be updated or the coinbase * transaction can be changed and the merkle root updated. + * * @return the nonce used to try and solve a block */ def nonce : Long diff --git a/src/main/scala/org/bitcoins/core/protocol/blockchain/ChainParams.scala b/src/main/scala/org/bitcoins/core/protocol/blockchain/ChainParams.scala index 166220233f..24036bdc16 100644 --- a/src/main/scala/org/bitcoins/core/protocol/blockchain/ChainParams.scala +++ b/src/main/scala/org/bitcoins/core/protocol/blockchain/ChainParams.scala @@ -1,5 +1,11 @@ package org.bitcoins.core.protocol.blockchain +import org.bitcoins.core.currency.CurrencyUnit +import org.bitcoins.core.protocol.{CompactSizeUInt} +import org.bitcoins.core.protocol.script.{ScriptPubKey, ScriptSignature} +import org.bitcoins.core.protocol.transaction.{Transaction, TransactionConstants, TransactionInput, TransactionOutput} +import org.bitcoins.core.script.constant.{BytesToPushOntoStack, ScriptConstant, ScriptNumber} +import org.bitcoins.core.script.crypto.OP_CHECKSIG import org.bitcoins.core.util.BitcoinSUtil /** @@ -56,6 +62,52 @@ sealed trait ChainParams { * @return */ def dnsSeeds : Seq[String] + + + /** + * Creates the genesis block for this blockchain + * Mimics this function in bitcoin core + * https://github.com/bitcoin/bitcoin/blob/master/src/chainparams.cpp#L51 + * @param time the time when the miner started hashing the block header + * @param nonce the nonce to mine the block + * @param nBits An encoded version of the target threshold this block’s header hash must be less than or equal to. + * @param version the block version + * @param amount the block reward for the gensis block (50 BTC in Bitcoin) + * @return the newly minted genesis block + */ + def createGenesisBlock(time : Long, nonce : Long, nBits : Long, version : Int, amount : CurrencyUnit) : Block = { + val timestamp = "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks" + val genesisOutputScript = ScriptPubKey.fromAsm( + Seq(ScriptConstant("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51e" + + "c112de5c384df7ba0b8d578a4c702b6bf11d5f"), OP_CHECKSIG)) + createGenesisBlock(timestamp,genesisOutputScript,time,nonce,nBits,version,amount) + } + + /** + * @param timestamp a piece of data to signify when this block was first created - satoshi used an article headline + * @param scriptPubKey the scriptPubKey that needs to be satisfied in able to spend the genesis block reward + * @param time the time when the miner started hashing the block header + * @param nonce the nonce used to mine the block + * @param nBits An encoded version of the target threshold this block's header hash must be less than or equal to + * @param version the block version + * @param amount the block reward for the genesis block (50 BTC in Bitcoin) + * @return the newly minted genesis block + */ + def createGenesisBlock(timestamp : String, scriptPubKey : ScriptPubKey, time : Long, nonce : Long, nBits : Long, + version : Int, amount : CurrencyUnit) : Block = { + val timestampHex = BitcoinSUtil.flipEndianess(timestamp.map(_.toByte)) + val scriptSignature = ScriptSignature.fromAsm(Seq(ScriptNumber(486604799), BytesToPushOntoStack(4), + ScriptConstant(timestampHex))) + val input = TransactionInput(scriptSignature) + val output = TransactionOutput(amount,scriptPubKey) + val tx = Transaction(TransactionConstants.version,Seq(input), Seq(output), TransactionConstants.lockTime) + val prevBlockHash = "00000000000000" + //TODO: Replace this with a merkle root hash computed algorithmically + val merkleRootHash = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b" + val genesisBlockHeader = BlockHeader(version,prevBlockHash,merkleRootHash,time,nBits,nonce) + val genesisBlock = Block(genesisBlockHeader,CompactSizeUInt(1,1),Seq(tx)) + genesisBlock + } } diff --git a/src/main/scala/org/bitcoins/core/protocol/transaction/TransactionInput.scala b/src/main/scala/org/bitcoins/core/protocol/transaction/TransactionInput.scala index 3f7f71e1be..dc43dae2e5 100644 --- a/src/main/scala/org/bitcoins/core/protocol/transaction/TransactionInput.scala +++ b/src/main/scala/org/bitcoins/core/protocol/transaction/TransactionInput.scala @@ -38,6 +38,7 @@ case object EmptyTransactionInput extends TransactionInput { */ sealed trait CoinbaseInput extends TransactionInput { override def previousOutput = EmptyTransactionOutPoint + override def sequence = TransactionConstants.sequence } @@ -50,7 +51,7 @@ object TransactionInput extends Factory[TransactionInput] { scriptSignature : ScriptSignature, sequence : Long) extends TransactionInput private sealed case class CoinbaseInputImpl( - scriptSignature : ScriptSignature, sequence : Long) extends CoinbaseInput + scriptSignature : ScriptSignature) extends CoinbaseInput private def factory(oldInput : TransactionInput, scriptPubKey: ScriptPubKey) : TransactionInput = { val scriptSig = ScriptSignature(scriptPubKey.hex) @@ -63,7 +64,6 @@ object TransactionInput extends Factory[TransactionInput] { /** * Creates a transaction input from a given output and the output's transaction - * * @param oldInput * @param output * @param outputsTransaction @@ -81,7 +81,7 @@ object TransactionInput extends Factory[TransactionInput] { private def factory(outPoint : TransactionOutPoint, scriptSignature : ScriptSignature, sequenceNumber : Long) : TransactionInput = { outPoint match { - case EmptyTransactionOutPoint => CoinbaseInputImpl(scriptSignature,sequenceNumber) + case EmptyTransactionOutPoint => CoinbaseInputImpl(scriptSignature) case _ : TransactionOutPoint => TransactionInputImpl(outPoint, scriptSignature, sequenceNumber) } } @@ -113,4 +113,13 @@ object TransactionInput extends Factory[TransactionInput] { def apply(outPoint : TransactionOutPoint, scriptSignature : ScriptSignature, sequenceNumber : Long) : TransactionInput = factory(outPoint,scriptSignature,sequenceNumber) def apply(bytes : Seq[Byte]) : TransactionInput = fromBytes(bytes) + + /** + * Creates a coinbase input - coinbase inputs always have an empty outpoint + * @param scriptSignature this can contain anything, miners use this to signify support for various protocol BIPs + * @return the coinbase input + */ + def apply(scriptSignature: ScriptSignature) : CoinbaseInput = { + CoinbaseInputImpl(scriptSignature) + } } \ No newline at end of file diff --git a/src/main/scala/org/bitcoins/core/serializers/blockchain/RawBlockSerializer.scala b/src/main/scala/org/bitcoins/core/serializers/blockchain/RawBlockSerializer.scala index 4455ba6269..54f0e18817 100644 --- a/src/main/scala/org/bitcoins/core/serializers/blockchain/RawBlockSerializer.scala +++ b/src/main/scala/org/bitcoins/core/serializers/blockchain/RawBlockSerializer.scala @@ -18,7 +18,7 @@ trait RawBlockSerializer extends RawBitcoinSerializer[Block] { def read(bytes : List[Byte]) : Block = ??? /** - * Takes in a block and converts it a hexadecimal string + * Takes in a block and converts it to a hexadecimal string * @param block the block that needs to be converted to a hexadecimal string * @return the hexadecimal string representing a block */ diff --git a/src/main/scala/org/bitcoins/core/util/NumberUtil.scala b/src/main/scala/org/bitcoins/core/util/NumberUtil.scala index 58fd2d4d98..6901a7e8d2 100644 --- a/src/main/scala/org/bitcoins/core/util/NumberUtil.scala +++ b/src/main/scala/org/bitcoins/core/util/NumberUtil.scala @@ -1,7 +1,7 @@ package org.bitcoins.core.util import org.bitcoins.core.protocol.script.{ScriptPubKey, ScriptSignature} -import org.bitcoins.core.protocol.{CompactSizeUInt, CompactSizeUIntImpl} +import org.bitcoins.core.protocol.{CompactSizeUInt} import org.slf4j.LoggerFactory /** @@ -142,13 +142,13 @@ trait NumberUtil extends BitcoinSLogger { def parseCompactSizeUInt(bytes : Seq[Byte]) : CompactSizeUInt = { require(bytes.size > 0, "Cannot parse a VarInt if the byte array is size 0") //8 bit number - if (parseLong(bytes.head) < 253) CompactSizeUIntImpl(parseLong(bytes.head),1) + if (parseLong(bytes.head) < 253) CompactSizeUInt(parseLong(bytes.head),1) //16 bit number - else if (parseLong(bytes.head) == 253) CompactSizeUIntImpl(parseLong(bytes.slice(1,3).reverse),3) + else if (parseLong(bytes.head) == 253) CompactSizeUInt(parseLong(bytes.slice(1,3).reverse),3) //32 bit number - else if (parseLong(bytes.head) == 254) CompactSizeUIntImpl(parseLong(bytes.slice(1,5).reverse),5) + else if (parseLong(bytes.head) == 254) CompactSizeUInt(parseLong(bytes.slice(1,5).reverse),5) //64 bit number - else CompactSizeUIntImpl(parseLong(bytes.slice(1,9).reverse),9) + else CompactSizeUInt(parseLong(bytes.slice(1,9).reverse),9) } /** @@ -179,13 +179,13 @@ trait NumberUtil extends BitcoinSLogger { */ def parseCompactSizeUInt(script : ScriptSignature) : CompactSizeUInt = { if (script.bytes.size <=252 ) { - CompactSizeUIntImpl(script.bytes.size,1) + CompactSizeUInt(script.bytes.size,1) } else if (script.bytes.size <= 0xffff) { - CompactSizeUIntImpl(script.bytes.size,3) + CompactSizeUInt(script.bytes.size,3) } else if (script.bytes.size <= 0xffffffff) { - CompactSizeUIntImpl(script.bytes.size,5) + CompactSizeUInt(script.bytes.size,5) } - else CompactSizeUIntImpl(script.bytes.size,9) + else CompactSizeUInt(script.bytes.size,9) } /** @@ -197,13 +197,12 @@ trait NumberUtil extends BitcoinSLogger { */ def parseCompactSizeUInt(scriptPubKey : ScriptPubKey) : CompactSizeUInt = { if (scriptPubKey.bytes.size <=252 ) { - CompactSizeUIntImpl(scriptPubKey.bytes.size,1) + CompactSizeUInt(scriptPubKey.bytes.size,1) } else if (scriptPubKey.bytes.size <= 0xffff) { - CompactSizeUIntImpl(scriptPubKey.bytes.size,3) + CompactSizeUInt(scriptPubKey.bytes.size,3) } else if (scriptPubKey.bytes.size <= 0xffffffff) { - CompactSizeUIntImpl(scriptPubKey.bytes.size,5) - } - else CompactSizeUIntImpl(scriptPubKey.bytes.size,9) + CompactSizeUInt(scriptPubKey.bytes.size,5) + } else CompactSizeUInt(scriptPubKey.bytes.size,9) } private def parseLong(hex : String) : Long = java.lang.Long.parseLong(hex,16) diff --git a/src/test/scala/org/bitcoins/core/protocol/blockchain/ChainParamsTest.scala b/src/test/scala/org/bitcoins/core/protocol/blockchain/ChainParamsTest.scala new file mode 100644 index 0000000000..755f834be4 --- /dev/null +++ b/src/test/scala/org/bitcoins/core/protocol/blockchain/ChainParamsTest.scala @@ -0,0 +1,15 @@ +package org.bitcoins.core.protocol.blockchain + +import org.bitcoins.core.currency.Bitcoins +import org.scalatest.{FlatSpec, MustMatchers} + +/** + * Created by chris on 5/24/16. + */ +class ChainParamsTest extends FlatSpec with MustMatchers { + + "ChainParams" must "create the bitcoin genesis block" in { + val genesisBlock = MainNetChainParams.createGenesisBlock(1231006505, 2083236893, 0x1d00ffff, 1, Bitcoins(50)) + genesisBlock.hash must be ("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f") + } +} diff --git a/src/test/scala/org/bitcoins/core/util/TransactionTestUtil.scala b/src/test/scala/org/bitcoins/core/util/TransactionTestUtil.scala index 9aae8b4f92..e6202b64b5 100644 --- a/src/test/scala/org/bitcoins/core/util/TransactionTestUtil.scala +++ b/src/test/scala/org/bitcoins/core/util/TransactionTestUtil.scala @@ -5,7 +5,6 @@ import org.bitcoinj.core.DumpedPrivateKey import org.bitcoins.core.config.TestNet3 import org.bitcoins.core.crypto.{ECFactory, ECPublicKey} import org.bitcoins.core.currency.CurrencyUnits -import org.bitcoins.core.protocol.{CompactSizeUIntImpl} import org.bitcoins.core.protocol.script._ import org.bitcoins.core.protocol.transaction._ import org.slf4j.LoggerFactory