Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright 2020 ACINQ SAS
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package fr.acinq.bitcoin
import fr.acinq.bitcoin.io.ByteArrayInput
import fr.acinq.bitcoin.io.Input
import fr.acinq.bitcoin.io.Output
import fr.acinq.secp256k1.Hex
import kotlin.experimental.and
import kotlin.jvm.JvmField
import kotlin.jvm.JvmStatic
/** This is the double hash of a serialized block header. */
public data class BlockHash(@JvmField val value: ByteVector32) {
public constructor(hash: ByteArray) : this(hash.byteVector32())
public constructor(hash: String) : this(ByteVector32(hash))
public constructor(blockId: BlockId) : this(blockId.value.reversed())
override fun toString(): String = value.toString()
}
/** This contains the same data as [BlockHash], but encoded with the opposite endianness. */
public data class BlockId(@JvmField val value: ByteVector32) {
public constructor(blockId: ByteArray) : this(blockId.byteVector32())
public constructor(blockId: String) : this(ByteVector32(blockId))
public constructor(hash: BlockHash) : this(hash.value.reversed())
override fun toString(): String = value.toString()
}
/**
* @param version Block version information, based upon the software version creating this block
* @param hashPreviousBlock The hash value of the previous block this particular block references.
* @param hashMerkleRoot The reference to a Merkle tree collection which is a hash of all transactions related to this block
* @param time A timestamp recording when this block was created (Will overflow in 2106[2])
* @param bits The calculated difficulty target being used for this block
* @param nonce The nonce used to generate this block… to allow variations of the header and compute different hashes
*/
public data class BlockHeader(
@JvmField val version: Long,
@JvmField val hashPreviousBlock: BlockHash,
@JvmField val hashMerkleRoot: ByteVector32,
@JvmField val time: Long,
@JvmField val bits: Long,
@JvmField val nonce: Long
) : BtcSerializable {
@JvmField
public val hash: BlockHash = BlockHash(Crypto.hash256(write(this)))
@JvmField
public val blockId: BlockId = BlockId(hash)
public fun setVersion(input: Long): BlockHeader = this.copy(version = input)
public fun setHashPreviousBlock(input: BlockHash): BlockHeader = this.copy(hashPreviousBlock = input)
public fun setHashMerkleRoot(input: ByteVector32): BlockHeader = this.copy(hashMerkleRoot = input)
public fun setTime(input: Long): BlockHeader = this.copy(time = input)
public fun setBits(input: Long): BlockHeader = this.copy(bits = input)
public fun setNonce(input: Long): BlockHeader = this.copy(nonce = input)
public fun difficulty(): UInt256 {
val (diff, neg, _) = UInt256.decodeCompact(bits)
return if (neg) -diff else diff
}
/**
*
* @return the amount of work represented by this block's difficulty target, as displayed by bitcoin core
*/
public fun blockProof(): UInt256 = blockProof(bits)
/**
* Proof of work: hash(header) <= target difficulty
*
* @return true if this block header validates its expected proof of work
*/
public fun checkProofOfWork(): Boolean {
val (target, _, _) = UInt256.decodeCompact(bits)
val hash = UInt256(blockId.value.toByteArray())
return hash <= target
}
@Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
public companion object : BtcSerializer() {
override fun read(input: Input, protocolVersion: Long): BlockHeader {
val version = uint32(input)
val hashPreviousBlock = BlockHash(hash(input))
val hashMerkleRoot = hash(input)
val time = uint32(input)
val bits = uint32(input)
val nonce = uint32(input)
return BlockHeader(
version.toLong(),
hashPreviousBlock,
hashMerkleRoot.byteVector32(),
time.toLong(),
bits.toLong(),
nonce.toLong()
)
}
@JvmStatic
override fun read(input: String): BlockHeader {
return super.read(input)
}
@JvmStatic
override fun read(input: ByteArray): BlockHeader {
return super.read(input)
}
override fun write(message: BlockHeader, output: Output, protocolVersion: Long) {
writeUInt32(message.version.toUInt(), output)
writeBytes(message.hashPreviousBlock.value, output)
writeBytes(message.hashMerkleRoot, output)
writeUInt32(message.time.toUInt(), output)
writeUInt32(message.bits.toUInt(), output)
writeUInt32(message.nonce.toUInt(), output)
}
@JvmStatic
override fun write(message: BlockHeader): ByteArray {
return super.write(message)
}
@JvmStatic
public fun getDifficulty(header: BlockHeader): UInt256 = header.difficulty()
/**
*
* @param bits difficulty target
* @return the amount of work represented by this difficulty target, as displayed
* by bitcoin core
*/
@JvmStatic
public fun blockProof(bits: Long): UInt256 {
val (target, negative, overflow) = UInt256.decodeCompact(bits)
return if (target == UInt256.Zero || negative || overflow) UInt256.Zero else {
// (~bnTarget / (bnTarget + 1)) + 1;
val work = target.inv()
work /= target.inc()
work.inc()
}
}
@JvmStatic
public fun blockProof(header: BlockHeader): UInt256 = blockProof(header.bits)
/**
* Proof of work: hash(header) <= target difficulty
*
* @param header block header
* @return true if the input block header validates its expected proof of work
*/
@JvmStatic
public fun checkProofOfWork(header: BlockHeader): Boolean = header.checkProofOfWork()
@JvmStatic
public fun calculateNextWorkRequired(lastHeader: BlockHeader, lastRetargetTime: Long): Long {
var actualTimespan = lastHeader.time - lastRetargetTime
val targetTimespan = 14 * 24 * 60 * 60L // two weeks
if (actualTimespan < targetTimespan / 4) actualTimespan = targetTimespan / 4
if (actualTimespan > targetTimespan * 4) actualTimespan = targetTimespan * 4
var (target, isnegative, overflow) = UInt256.decodeCompact(lastHeader.bits)
require(!isnegative)
require(!overflow)
target *= UInt256(actualTimespan)
target /= UInt256(targetTimespan)
val powLimit = UInt256(Hex.decode("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))
if (target > powLimit) target = powLimit
return target.encodeCompact(false)
}
}
override fun serializer(): BtcSerializer = Companion
}
/**
* see https://en.bitcoin.it/wiki/Protocol_specification#Merkle_Trees
*/
public object MerkleTree {
@JvmStatic
public tailrec fun computeRoot(tree: List): ByteVector32 {
return when {
tree.size == 1 -> tree[0]
(tree.size % 2) != 0 -> computeRoot(tree + listOf(tree.last())) // append last element again
else -> {
val tree1 = mutableListOf()
for (i in 0 until (tree.size / 2)) {
val hash = Crypto.hash256(tree[2 * i].toByteArray() + tree[2 * i + 1].toByteArray())
tree1.add(hash.byteVector32())
}
computeRoot(tree1.toList())
}
}
}
}
public data class Block(@JvmField val header: BlockHeader, @JvmField val tx: List) {
@JvmField
val hash: BlockHash = header.hash
@JvmField
val blockId: BlockId = header.blockId
/**
* Proof of work: hash(block) <= target difficulty
*
* @return true if the input block validates its expected proof of work
*/
public fun checkProofOfWork(): Boolean = BlockHeader.checkProofOfWork(header)
public companion object : BtcSerializer() {
override fun write(message: Block, out: Output, protocolVersion: Long) {
BlockHeader.write(message.header, out)
writeCollection(message.tx, out, Transaction, protocolVersion)
}
@JvmStatic
override fun write(message: Block): ByteArray {
return super.write(message)
}
override fun read(input: Input, protocolVersion: Long): Block {
val raw = bytes(input, 80)
val header = BlockHeader.read(raw)
return Block(header, readCollection(input, Transaction, protocolVersion))
}
@JvmStatic
override fun read(input: String): Block {
return super.read(input)
}
@JvmStatic
override fun read(input: ByteArray): Block {
return super.read(input)
}
@JvmStatic
override fun validate(message: Block) {
BlockHeader.validate(message.header)
require(message.header.hashMerkleRoot == MerkleTree.computeRoot(message.tx.map { it.hash.value })) { "invalid block: merkle root mismatch" }
require(message.tx.map { it.hash }.toSet().size == message.tx.size) { "invalid block: duplicate transactions" }
message.tx.map { Transaction.validate(it) }
}
/**
* Proof of work: hash(block) <= target difficulty
*
* @param block
* @return true if the input block validates its expected proof of work
*/
@JvmStatic
public fun checkProofOfWork(block: Block): Boolean = block.checkProofOfWork()
/**
* Verify a tx inclusion proof (a merkle proof that a set of transactions are included in a given block)
* Note that this method doesn't validate the header's proof of work.
*
* @param proof tx inclusion proof, in the format used by bitcoin core's 'verifytxoutproof' RPC call
* @return a (block header, matched tx ids and positions in the block) tuple
*/
@JvmStatic
public fun verifyTxOutProof(proof: ByteArray): Pair>> {
// a txout proof is generated for a given block and a given list of transactions, and contains a merkle proof that these transactions
// were indeed in the block. The format of a proof is:
// block header | number of transactions in the block | merkle node hashes | flag bits
// a flag bit is set if the current node is one of the leaf tx for which this proof was generated or one of its ancestors
// see https://en.bitcoin.it/wiki/BIP_0037#Partial_Merkle_branch_format for more details
val header = BlockHeader.read(proof.take(80).toByteArray())
val inputStream = ByteArrayInput(proof.drop(80).toByteArray())
val txCount = uint32(inputStream).toInt()
val hashes = readCollection(inputStream, { i, _ -> hash(i).byteVector32() }, null, Protocol.PROTOCOL_VERSION)
val bits = script(inputStream)
/**
* @param bits array of bytes
* @param pos position
* @return the bit value in the input byte array at position `pos`
*/
fun bit(bits: ByteArray, pos: Int): Boolean {
val elt = bits[pos / 8]
return (elt.and((1.shl(pos % 8)).toByte()) != 0.toByte())
}
/**
* @param height height for which we want to know the width of the tree (0 is the bottom of the tree)
* @return the width of a merkle tree at a given height
*/
fun calcTreeWidth(height: Int): Int = (txCount + (1.shl(height)) - 1).shr(height)
var bitsUsed = 0 // number of bits that we've used so far
var hashUsed = 0 // number of hashes that we've used so far
var matched: List> = listOf() // list of (txids, index) that we've matched so far
var height = 0 // current height
// find the height of the tree (leaves are at height = 0)
while (calcTreeWidth(height) > 1) {
height++
}
// traverse the tree and update the list of matched txids and positions
// return the hash of the node at (height, pos)
fun traverseAndExtract(height: Int, pos: Int): ByteVector32 {
// check if the current node is a tx for which we have a proof or one of its ancestors
val parentOfMatch = bit(bits, bitsUsed++)
return when {
height == 0 -> {
val hash = hashes[hashUsed++]
if (parentOfMatch) matched = matched + Pair(hash, pos)
hash
}
!parentOfMatch -> hashes[hashUsed++]
else -> {
// otherwise, descend into the subtrees to extract matched txids and hashes
val left = traverseAndExtract(height - 1, pos * 2)
val right = if ((pos * 2 + 1) < calcTreeWidth(height - 1)) {
val hash = traverseAndExtract(height - 1, pos * 2 + 1)
// The left and right branches should never be identical, as the transaction
// hashes covered by them must each be unique.
require(hash != left) { "invalid leaf hash" }
hash
} else {
// if we don't have enough leaves we duplicate the last one
left
}
// and combine them before returning
left.concat(right).sha256().sha256()
}
}
}
val root = traverseAndExtract(height, 0)
require(root == header.hashMerkleRoot) { "invalid merkle root: expected ${header.hashMerkleRoot.toHex()}, got ${root.toHex()}" }
return Pair(header, matched)
}
// genesis blocks
@JvmField
public val LivenetGenesisBlock: Block = run {
val script = listOf(
OP_PUSHDATA(writeUInt32(486604799u)),
OP_PUSHDATA(ByteVector("04")),
OP_PUSHDATA("The Times 03/Jan/2009 Chancellor on brink of second bailout for banks".encodeToByteArray())
)
val scriptPubKey = listOf(
OP_PUSHDATA(ByteVector("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f")),
OP_CHECKSIG
)
Block(
BlockHeader(
version = 1,
hashPreviousBlock = BlockHash(ByteVector32.Zeroes),
hashMerkleRoot = ByteVector32("3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"),
time = 1231006505,
bits = 0x1d00ffff,
nonce = 2083236893
),
listOf(
Transaction(
version = 1,
txIn = listOf(TxIn.coinbase(script)),
txOut = listOf(TxOut(amount = 5000000000.toSatoshi(), publicKeyScript = scriptPubKey)),
lockTime = 0
)
)
)
}
@JvmField
public val Testnet3GenesisBlock: Block = LivenetGenesisBlock.copy(
header = LivenetGenesisBlock.header.copy(time = 1296688602, nonce = 414098458)
)
@JvmField
public val Testnet4GenesisBlock: Block = run {
val script = listOf(
OP_PUSHDATA(writeUInt32(486604799u)),
OP_PUSHDATA(ByteVector("04")),
OP_PUSHDATA("03/May/2024 000000000000000000001ebd58c244970b3aa9d783bb001011fbe8ea8e98e00e".encodeToByteArray())
)
val scriptPubKey = listOf(
OP_PUSHDATA(ByteVector("000000000000000000000000000000000000000000000000000000000000000000")),
OP_CHECKSIG
)
Block(
BlockHeader(
version = 1,
hashPreviousBlock = BlockHash(ByteVector32.Zeroes),
hashMerkleRoot = ByteVector32("7aa0a7ae1e223414cb807e40cd57e667b718e42aaf9306db9102fe28912b7b4e").reversed(),
time = 1714777860,
bits = 0x1d00ffff,
nonce = 393743547
),
listOf(
Transaction(
version = 1,
txIn = listOf(TxIn.coinbase(script)),
txOut = listOf(TxOut(amount = 5000000000.toSatoshi(), publicKeyScript = scriptPubKey)),
lockTime = 0
)
)
)
}
@JvmField
public val RegtestGenesisBlock: Block = LivenetGenesisBlock.copy(
header = LivenetGenesisBlock.header.copy(
bits = 0x207fffffL,
nonce = 2,
time = 1296688602
)
)
@JvmField
public val SignetGenesisBlock: Block = LivenetGenesisBlock.copy(
header = LivenetGenesisBlock.header.copy(
bits = 503543726,
time = 1598918400,
nonce = 52613770
)
)
}
}