区块是区块链的核心单元。区块链由区块互相连接而成。
区块
区块由区块头和区块体两部分组成。其中区块的大小被限制在1M以内(为了防止资源浪费和DOS攻击),区块头的大小被固定为80个字节。
但目前随机比特币的发展,交易数量持续增加,1M的大小能存储的交易数量有限,导致大量的交易积压。因此目前正在考虑扩容方案。
区块头
区块头中记录了版本号、上一个区块的Hash地址、merkle根、区块创建时间戳、区块的工作量难度目标以及用于计算目标的参数值。
字段 | 大小 | 描述 |
---|---|---|
version | 4字节 | 版本号,⽤于跟踪软件/协议的更新 |
prevBlockHash | 32字节 | 上一个区块的Hash地址 |
merkleRoot | 32字节 | 该区块中交易的merkl e树根的哈希值(稍后详细说明) |
time | 4字节 | 该区块的创建时间戳 |
difficultyTarget | 4字节 | 该区块链工作量证明难度目标(稍后讲解工作量证明) |
nonce | 4字节 | 用于证明工作量的计算参数 |
区块体
区块体中记录了该区块存储的交易数量以及交易数据。
字段 | 大小 | 描述 |
---|---|---|
numTransactionsBytes | 1字节 | 交易数量占用的字节数 |
numTransactions | 0-8个字节 | 区块内存储的交易数量 |
transactions | 不确定 | 区块内存的多个交易数据 |
为了节约区块的存储空间,区块内的交易数量字段采用了压缩存储。在读取交易数量之前,会先读取numTransactionsBytes字段值。
- 如果该值小于253,则用直接将该值作为交易数量
- 如果该值等于253,则读取之后的两个字节作为交易数量
- 如果该值等于254,则读取之后的4个字节作为交易数量
- 否则,读取之后的8个字节作为交易数量
区块的核心代码
1. 区块中的核心常量定义
/** How many bytes are required to represent a block header WITHOUT the trailing 00 length byte. */
//区块头的大小,当前为80个字节
public static final int HEADER_SIZE = 80;
static final long ALLOWED_TIME_DRIFT = 2 * 60 * 60; // Same value as Bitcoin Core.
/**
* A constant shared by the entire network: how large in bytes a block is allowed to be. One day we may have to
* upgrade everyone to change this, so Bitcoin can continue to grow. For now it exists as an anti-DoS measure to
* avoid somebody creating a titanically huge but valid block and forcing everyone to download/store it forever.
*/
//全网共享的常量,用于表示区块的最大字节数。随着比特币会持续的发展,日后升级网络时可能会变更该数字。
//目前该值作为解决拒接攻击的一种措施,避免有人创建巨量的区块,造成整个网络的资源浪费。
public static final int MAX_BLOCK_SIZE = 1 * 1000 * 1000;
/**
* A "sigop" is a signature verification operation. Because they're expensive we also impose a separate limit on
* the number in a block to prevent somebody mining a huge block that has way more sigops than normal, so is very
* expensive/slow to verify.
*/
//sigop是签名校验操作,因此这个操作需要大量的资源,因此需要限制大小,防止资源浪费或降低网络性能
public static final int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE / 50;
/** A value for difficultyTarget (nBits) that allows half of all possible hash solutions. Used in unit testing. */
//工作量的难度目标
public static final long EASIEST_DIFFICULTY_TARGET = 0x207fFFFFL;
2. 区块中的核心变量定义
private long version; //区块链的版本号
private Sha256Hash prevBlockHash; //前一个区块的hash地址
private Sha256Hash merkleRoot; //交易标识的merkle根
private long time; //区块创建时间戳
private long difficultyTarget; // "nBits" //区块工作难度目标
private long nonce; //用于证明区块工作量的参数
// TODO: Get rid of all the direct accesses to this field. It's a long-since unnecessary holdover from the Dalvik days.
/** If null, it means this object holds only the headers. */
//区块中存储的交易数据
@Nullable List<Transaction> transactions;
/** Stores the hash of the block. If null, getHash() will recalculate it. */
//当前区块的hash地址
private Sha256Hash hash;
3. 解析区块二进制数据
//从原始字节数据中构造区块对象
@Override
protected void parse() throws ProtocolException {
// header
cursor = offset;
version = readUint32(); //读取4个字节的版本号
prevBlockHash = readHash(); //读取前一个区块的hash地址
merkleRoot = readHash(); //读取merkle交易树的根值
time = readUint32(); //读取区块的创建时间戳
difficultyTarget = readUint32(); //读取区块的难度目标
nonce = readUint32(); //读取区块用于计算难度的随机数
//通过区块头计算当前区块的hash地址
hash = Sha256Hash.wrapReversed(Sha256Hash.hashTwice(payload, offset, cursor - offset));
headerBytesValid = serializer.isParseRetainMode(); //是否缓存区块的hash地址
// transactions
//解析区块内的交易数据
parseTransactions(offset + HEADER_SIZE);
//计算区块的字节数
length = cursor - offset;
}
4. 解析区块内的交易数据
//解析区块内的交易数据
protected void parseTransactions(final int transactionsOffset) throws ProtocolException {
cursor = transactionsOffset; //设置读取数据的起始偏移地址
optimalEncodingMessageSize = HEADER_SIZE; //初始化编码后的区块大小
if (payload.length == cursor) {
// This message is just a header, it has no transactions.
transactionBytesValid = false;
return;
}
int numTransactions = (int) readVarInt(); //获取区块内的交易数据量
//累加编码后的区块大小,不同的整数经过编码后,占用的存储空间不一样,因此需要通过VarInt进行计算
optimalEncodingMessageSize += VarInt.sizeOf(numTransactions);
transactions = new ArrayList<>(numTransactions);
//逐一构造区块内的交易数据
for (int i = 0; i < numTransactions; i++) {
//构造区块内的交易数据
Transaction tx = new Transaction(params, payload, cursor, this, serializer, UNKNOWN_LENGTH);
// Label the transaction as coming from the P2P network, so code that cares where we first saw it knows.
tx.getConfidence().setSource(TransactionConfidence.Source.NETWORK);
transactions.add(tx);
cursor += tx.getMessageSize();
optimalEncodingMessageSize += tx.getOptimalEncodingMessageSize();
}
transactionBytesValid = serializer.isParseRetainMode();
}
上一篇:(一) 区块链数据结构-区块链
下一篇:(三) 区块链数据结构 – 交易