Moving CompactUIntImpl into factory, adding NetworkElement trait to Block/BlockHeader to allow for easy access to hex/byte representations

This commit is contained in:
Chris Stewart 2016-05-24 09:53:39 -05:00
parent ec5e2b85c3
commit 59c1968271
9 changed files with 130 additions and 27 deletions

View file

@ -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)
}
}

View file

@ -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)
}

View file

@ -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 blocks header.
* This ensures no previous block can be changed without also changing this blocks 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 blocks 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

View file

@ -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 blocks 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
}
}

View file

@ -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)
}
}

View file

@ -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
*/

View file

@ -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)

View file

@ -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")
}
}

View file

@ -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