Merge pull request #110 from Christewart/address_refactor

Address refactor
This commit is contained in:
Chris Stewart 2018-02-08 07:03:21 -06:00 committed by GitHub
commit 368826ccbf
6 changed files with 91 additions and 68 deletions

View file

@ -1,16 +1,22 @@
package org.bitcoins.core.config
import org.bitcoins.core.protocol.blockchain._
/**
* Created by chris on 7/27/15.
*/
sealed abstract class NetworkParameters {
def p2pkhNetworkByte : Byte
def p2shNetworkByte : Byte
def privateKey : Byte
/** The parameters of the blockchain we are connecting to */
def chainParams: ChainParams
def p2pkhNetworkByte: Byte = chainParams.base58Prefix(Base58Type.PubKeyAddress).head
def p2shNetworkByte: Byte = chainParams.base58Prefix(Base58Type.ScriptAddress).head
def privateKey: Byte = chainParams.base58Prefix(Base58Type.SecretKey).head
def port : Int
def rpcPort: Int
def name: String
def name: String = chainParams.networkId
/** The seeds used to bootstrap the network */
def dnsSeeds : Seq[String]
@ -23,13 +29,17 @@ sealed abstract class NetworkParameters {
def magicBytes : Seq[Byte]
/** In bitcoin, the network recaculates the difficulty for the network every 2016 blocks */
def difficultyChangeThreshold = 2016
def difficultyChangeThreshold: Int
}
trait MainNet extends NetworkParameters {
override def p2pkhNetworkByte = 0x00
override def p2shNetworkByte = 0x05
override def privateKey = 0x80.toByte
sealed abstract class BitcoinNetwork extends NetworkParameters {
override def difficultyChangeThreshold: Int = 2016
override def chainParams: BitcoinChainParams
}
sealed abstract class MainNet extends BitcoinNetwork {
override def chainParams = MainNetChainParams
override def port = 8333
override def rpcPort = 8332
//mainnet doesn't need to be specified like testnet or regtest
@ -38,34 +48,52 @@ trait MainNet extends NetworkParameters {
"seed.bitcoinstats.com","bitseed.xf2.org","seed.bitcoin.jonasschnelli.ch")
override def magicBytes = Seq(0xf9.toByte, 0xbe.toByte, 0xb4.toByte, 0xd9.toByte)
override def difficultyChangeThreshold: Int = 2016
}
object MainNet extends MainNet
trait TestNet3 extends NetworkParameters {
override def p2pkhNetworkByte = 0x6F
override def p2shNetworkByte = 0xC4.toByte
override def privateKey = 0xEF.toByte
sealed abstract class TestNet3 extends NetworkParameters {
override def chainParams = TestNetChainParams
override def port = 18333
override def rpcPort = 18332
override def name = "testnet"
override def dnsSeeds = Seq("testnet-seed.bitcoin.petertodd.org",
"testnet-seed.bluematt.me","testnet-seed.bitcoin.schildbach.de")
override def magicBytes = Seq(0x0b.toByte, 0x11.toByte, 0x09.toByte, 0x07.toByte)
override def difficultyChangeThreshold: Int = 2016
}
object TestNet3 extends TestNet3
trait RegTest extends NetworkParameters {
override def p2pkhNetworkByte = TestNet3.p2pkhNetworkByte
override def p2shNetworkByte = TestNet3.p2shNetworkByte
override def privateKey = TestNet3.privateKey
sealed abstract class RegTest extends NetworkParameters {
override def chainParams: ChainParams = RegTestNetChainParams
override def port = 18444
override def rpcPort = TestNet3.rpcPort
override def name = "regtest"
override def dnsSeeds = Nil
override def magicBytes = Seq(0xfa.toByte, 0xbf.toByte, 0xb5.toByte, 0xda.toByte)
override def difficultyChangeThreshold: Int = 2016
}
object RegTest extends RegTest
object Networks {
val knownNetworks: Seq[NetworkParameters] = Seq(MainNet, TestNet3, RegTest)
val secretKeyBytes = knownNetworks.map(_.privateKey)
val p2pkhNetworkBytes = knownNetworks.map(_.p2pkhNetworkByte)
val p2shNetworkBytes = knownNetworks.map(_.p2shNetworkByte)
def byteToNetwork: Map[Byte, NetworkParameters] = Map(
MainNet.p2shNetworkByte -> MainNet,
MainNet.p2pkhNetworkByte -> MainNet,
MainNet.privateKey -> MainNet,
TestNet3.p2pkhNetworkByte -> TestNet3,
TestNet3.p2shNetworkByte -> TestNet3,
TestNet3.privateKey -> TestNet3
//ommitting regtest as it has the same network bytes as testnet3
)
}

View file

@ -4,9 +4,8 @@ import java.math.BigInteger
import java.security.SecureRandom
import org.bitcoin.NativeSecp256k1
import org.bitcoins.core.config.{MainNet, NetworkParameters, TestNet3}
import org.bitcoins.core.config.{NetworkParameters, Networks}
import org.bitcoins.core.protocol.NetworkElement
import org.bitcoins.core.protocol.blockchain.{MainNetChainParams, SecretKey, TestNetChainParams}
import org.bitcoins.core.util.{BitcoinSUtil, _}
import org.spongycastle.crypto.AsymmetricCipherKeyPair
import org.spongycastle.crypto.digests.SHA256Digest
@ -155,8 +154,7 @@ object ECPrivateKey extends Factory[ECPrivateKey] {
* @return
*/
def isCompressed(bytes : Seq[Byte]): Boolean = {
val validCompressedBytes: Seq[Byte] =
MainNetChainParams.base58Prefix(SecretKey) ++ TestNetChainParams.base58Prefixes(SecretKey)
val validCompressedBytes: Seq[Byte] = Networks.secretKeyBytes
val validCompressedBytesInHex: Seq[String] = validCompressedBytes.map(byte => BitcoinSUtil.encodeHex(byte))
val firstByteHex = BitcoinSUtil.encodeHex(bytes.head)
if (validCompressedBytesInHex.contains(firstByteHex)) bytes(bytes.length - 5) == 0x01.toByte
@ -199,11 +197,7 @@ object ECPrivateKey extends Factory[ECPrivateKey] {
val decoded = Base58.decodeCheck(wif)
decoded.map { bytes =>
val b = bytes.head
if (b == TestNetChainParams.base58Prefixes(SecretKey).head) {
TestNet3
} else if (b == MainNetChainParams.base58Prefixes(SecretKey).head) {
MainNet
} else throw new IllegalArgumentException("Cannot match wif private key with a network, prefix was: " + BitcoinSUtil.encodeHex(b))
Networks.byteToNetwork(b)
}
}
}

View file

@ -321,8 +321,7 @@ object P2PKHAddress {
decodeCheckP2PKH match {
case Success(bytes) =>
val firstByte = bytes.head
(firstByte == MainNet.p2pkhNetworkByte || firstByte == TestNet3.p2pkhNetworkByte ||
firstByte == RegTest.p2pkhNetworkByte) && bytes.size == 21
Networks.p2pkhNetworkBytes.contains(firstByte) && bytes.size == 21
case Failure(exception) => false
}
}
@ -357,9 +356,7 @@ object P2SHAddress {
decodeCheckP2SH match {
case Success(bytes) =>
val firstByte = bytes.head
((firstByte == MainNet.p2shNetworkByte || firstByte == TestNet3.p2shNetworkByte ||
RegTest.p2shNetworkByte == firstByte)
&& bytes.size == 21)
Networks.p2shNetworkBytes.contains(firstByte) && bytes.size == 21
case Failure(_) => false
}
}

View file

@ -1,15 +1,16 @@
package org.bitcoins.core.protocol.blockchain
import java.nio.charset.StandardCharsets
import org.bitcoins.core.consensus.Merkle
import org.bitcoins.core.crypto.DoubleSha256Digest
import org.bitcoins.core.currency.{CurrencyUnit, Satoshis}
import org.bitcoins.core.number.{Int64, UInt32, UInt64}
import org.bitcoins.core.protocol.CompactSizeUInt
import org.bitcoins.core.number.{Int64, UInt32}
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
import org.bitcoins.core.util.{BitcoinSUtil, BitcoinScriptUtil}
/**
* Created by chris on 5/22/16.
@ -21,7 +22,7 @@ import org.bitcoins.core.util.BitcoinSUtil
* Mimics this C++ interface
* https://github.com/bitcoin/bitcoin/blob/master/src/chainparams.h#L42
*/
sealed trait ChainParams {
sealed abstract class ChainParams {
/** Return the BIP70 network string ([[MainNetChainParams]], [[TestNetChainParams]] or [[RegTestNetChainParams]].) */
def networkId : String
@ -31,7 +32,7 @@ sealed trait ChainParams {
/** Filter transactions that do not match well-defined patterns
* inside of [[org.bitcoins.core.policy.Policy]]. */
def requireStandardTransaction : Boolean
def requireStandardTransaction : Boolean = true
/** Takes in a [[Base58Type]] and returns its base58 prefix. */
def base58Prefix(base58 : Base58Type) : Seq[Byte] = base58Prefixes(base58)
@ -70,72 +71,72 @@ sealed trait ChainParams {
*/
def createGenesisBlock(timestamp : String, scriptPubKey : ScriptPubKey, time : UInt32, nonce : UInt32, nBits : UInt32,
version : UInt32, amount : CurrencyUnit) : Block = {
val timestampHex = timestamp.toCharArray.map(_.toByte)
val timestampBytes = timestamp.getBytes(StandardCharsets.UTF_8)
//see https://bitcoin.stackexchange.com/questions/13122/scriptsig-coinbase-structure-of-the-genesis-block
//for a full breakdown of the genesis block & its script signature
val const = ScriptConstant(timestampBytes)
val scriptSignature = ScriptSignature.fromAsm(Seq(BytesToPushOntoStack(4), ScriptNumber(486604799),
BytesToPushOntoStack(1), ScriptNumber(4), BytesToPushOntoStack(69), ScriptConstant(timestampHex)))
BytesToPushOntoStack(1), ScriptNumber(4)) ++ BitcoinScriptUtil.calculatePushOp(const) ++ Seq(const))
val input = TransactionInput(scriptSignature)
val output = TransactionOutput(amount,scriptPubKey)
val tx = Transaction(TransactionConstants.version,Seq(input), Seq(output), TransactionConstants.lockTime)
val prevBlockHash = DoubleSha256Digest("0000000000000000000000000000000000000000000000000000000000000000")
val merkleRootHash = Merkle.computeMerkleRoot(Seq(tx))
val genesisBlockHeader = BlockHeader(version,prevBlockHash,merkleRootHash,time,nBits,nonce)
val genesisBlock = Block(genesisBlockHeader,CompactSizeUInt(UInt64.one,1),Seq(tx))
val genesisBlock = Block(genesisBlockHeader,Seq(tx))
genesisBlock
}
}
sealed abstract class BitcoinChainParams extends ChainParams
/** The Main Network parameters. */
object MainNetChainParams extends ChainParams {
object MainNetChainParams extends BitcoinChainParams {
override def networkId = "main"
override def genesisBlock : Block = createGenesisBlock(UInt32(1231006505), UInt32(2083236893), UInt32(0x1d00ffff), UInt32.one, Satoshis(Int64(5000000000L)))
override def requireStandardTransaction : Boolean = true
override def base58Prefixes : Map[Base58Type,Seq[Byte]] = Map(
PubKeyAddress -> BitcoinSUtil.decodeHex("00"),
ScriptAddress -> BitcoinSUtil.decodeHex("05"),
SecretKey -> BitcoinSUtil.decodeHex("80"),
ExtPublicKey -> Seq(BitcoinSUtil.hexToByte("04"), BitcoinSUtil.hexToByte("88"),
Base58Type.PubKeyAddress -> BitcoinSUtil.decodeHex("00"),
Base58Type.ScriptAddress -> BitcoinSUtil.decodeHex("05"),
Base58Type.SecretKey -> BitcoinSUtil.decodeHex("80"),
Base58Type.ExtPublicKey -> Seq(BitcoinSUtil.hexToByte("04"), BitcoinSUtil.hexToByte("88"),
BitcoinSUtil.hexToByte("b2"), BitcoinSUtil.hexToByte("1e")),
ExtSecretKey -> Seq(BitcoinSUtil.hexToByte("04"), BitcoinSUtil.hexToByte("88"),
Base58Type.ExtSecretKey -> Seq(BitcoinSUtil.hexToByte("04"), BitcoinSUtil.hexToByte("88"),
BitcoinSUtil.hexToByte("ad"), BitcoinSUtil.hexToByte("e4")))
}
object TestNetChainParams extends ChainParams {
object TestNetChainParams extends BitcoinChainParams {
override def networkId = "test"
override def genesisBlock : Block = createGenesisBlock(UInt32(1296688602), UInt32(414098458), UInt32(0x1d00ffff), UInt32.one, Satoshis(Int64(5000000000L)))
override def requireStandardTransaction : Boolean = true
override def base58Prefixes : Map[Base58Type,Seq[Byte]] = Map(
PubKeyAddress -> BitcoinSUtil.decodeHex("6f"),
ScriptAddress -> BitcoinSUtil.decodeHex("c4"),
SecretKey -> BitcoinSUtil.decodeHex("ef"),
ExtPublicKey -> Seq(BitcoinSUtil.hexToByte("04"), BitcoinSUtil.hexToByte("35"),
Base58Type.PubKeyAddress -> BitcoinSUtil.decodeHex("6f"),
Base58Type.ScriptAddress -> BitcoinSUtil.decodeHex("c4"),
Base58Type.SecretKey -> BitcoinSUtil.decodeHex("ef"),
Base58Type.ExtPublicKey -> Seq(BitcoinSUtil.hexToByte("04"), BitcoinSUtil.hexToByte("35"),
BitcoinSUtil.hexToByte("87"), BitcoinSUtil.hexToByte("cf")),
ExtSecretKey -> Seq(BitcoinSUtil.hexToByte("04"), BitcoinSUtil.hexToByte("35"),
Base58Type.ExtSecretKey -> Seq(BitcoinSUtil.hexToByte("04"), BitcoinSUtil.hexToByte("35"),
BitcoinSUtil.hexToByte("83"), BitcoinSUtil.hexToByte("94")))
}
object RegTestNetChainParams extends ChainParams {
object RegTestNetChainParams extends BitcoinChainParams {
override def networkId = "regtest"
override def genesisBlock : Block = createGenesisBlock(UInt32(1296688602), UInt32(2), UInt32(0x207fffff), UInt32.one, Satoshis(Int64(5000000000L)))
override def requireStandardTransaction : Boolean = TestNetChainParams.requireStandardTransaction
override def base58Prefixes : Map[Base58Type, Seq[Byte]] = TestNetChainParams.base58Prefixes
}
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 abstract class Base58Type
object 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
}

View file

@ -10,11 +10,11 @@ import scala.util.{Failure, Success, Try}
* Created by chris on 5/16/16.
* source of values: https://en.bitcoin.it/wiki/Base58Check_encoding
*/
trait Base58 extends BitcoinSLogger {
sealed abstract class Base58 {
import Base58Type._
val base58Characters = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
val base58Pairs = base58Characters.zipWithIndex.toMap
private val logger = BitcoinSLogger.logger
/** Verifies a given [[Base58Type]] string against its checksum (last 4 decoded bytes). */
def decodeCheck(input: String) : Try[Seq[Byte]] = {
val decoded : Seq[Byte] = decode(input)

View file

@ -95,6 +95,7 @@ class ChainParamsTest extends FlatSpec with MustMatchers {
}
it must "have the correct base58 prefix for MainNet" in {
import Base58Type._
//correct answers taken from https://en.bitcoin.it/wiki/List_of_address_prefixes
BitcoinSUtil.encodeHex(MainNetChainParams.base58Prefixes(PubKeyAddress)) must be ("00")
BitcoinSUtil.encodeHex(MainNetChainParams.base58Prefixes(ScriptAddress)) must be ("05")
@ -104,6 +105,7 @@ class ChainParamsTest extends FlatSpec with MustMatchers {
}
it must "have the correct base58 prefix for TestNet" in {
import Base58Type._
BitcoinSUtil.encodeHex(TestNetChainParams.base58Prefixes(PubKeyAddress)) must be ("6f")
BitcoinSUtil.encodeHex(TestNetChainParams.base58Prefixes(ScriptAddress)) must be ("c4")
BitcoinSUtil.encodeHex(TestNetChainParams.base58Prefixes(SecretKey)) must be ("ef")
@ -112,6 +114,7 @@ class ChainParamsTest extends FlatSpec with MustMatchers {
}
it must "have the correct base58 prefix for RegTest" in {
import Base58Type._
BitcoinSUtil.encodeHex(RegTestNetChainParams.base58Prefixes(PubKeyAddress)) must be ("6f")
BitcoinSUtil.encodeHex(RegTestNetChainParams.base58Prefixes(ScriptAddress)) must be ("c4")
BitcoinSUtil.encodeHex(RegTestNetChainParams.base58Prefixes(SecretKey)) must be ("ef")