Block: move parse() to static constructor read()

This commit is contained in:
Andreas Schildbach 2023-04-14 23:00:33 +02:00
parent 3cf9e89893
commit 8a819c761c
4 changed files with 53 additions and 45 deletions

View File

@ -298,7 +298,7 @@ public class BitcoinSerializer extends MessageSerializer {
*/
@Override
public Block makeBlock(ByteBuffer payload) throws ProtocolException {
return new Block(payload);
return Block.read(payload);
}
/**

View File

@ -134,9 +134,54 @@ public class Block extends BaseMessage {
/** Stores the hash of the block. If null, getHash() will recalculate it. */
private Sha256Hash hash;
/**
* Deserialize this message from a given payload.
*
* @param payload payload to deserialize from
* @return read message
* @throws BufferUnderflowException if the read message extends beyond the remaining bytes of the payload
*/
public static Block read(ByteBuffer payload) throws BufferUnderflowException, ProtocolException {
// header
payload.mark();
long version = ByteUtils.readUint32(payload);
Sha256Hash prevBlockHash = Sha256Hash.read(payload);
Sha256Hash merkleRoot = Sha256Hash.read(payload);
Instant time = Instant.ofEpochSecond(ByteUtils.readUint32(payload));
long difficultyTarget = ByteUtils.readUint32(payload);
long nonce = ByteUtils.readUint32(payload);
payload.reset(); // read again from the mark for the hash
Sha256Hash hash = Sha256Hash.wrapReversed(Sha256Hash.hashTwice(Buffers.readBytes(payload, HEADER_SIZE)));
// transactions
List<Transaction> transactions = payload.hasRemaining() ? // otherwise this message is just a header
readTransactions(payload) :
null;
Block block = new Block(version, prevBlockHash, merkleRoot, time, difficultyTarget, nonce, transactions);
block.hash = hash;
return block;
}
/**
* Parse transactions from the block.
*/
private static List<Transaction> readTransactions(ByteBuffer payload) throws BufferUnderflowException,
ProtocolException {
VarInt numTransactionsVarInt = VarInt.read(payload);
check(numTransactionsVarInt.fitsInt(), BufferUnderflowException::new);
int numTransactions = numTransactionsVarInt.intValue();
List<Transaction> transactions = new ArrayList<>(Math.min(numTransactions, Utils.MAX_INITIAL_ARRAY_LENGTH));
MessageSerializer serializer = new DummySerializer(ProtocolVersion.CURRENT.intValue());
for (int i = 0; i < numTransactions; i++) {
Transaction tx = new Transaction(payload, serializer);
// 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);
}
return transactions;
}
/** Special case constructor, used for the genesis node, cloneAsHeader and unit tests. */
Block(long setVersion) {
super(new DummySerializer(ProtocolVersion.CURRENT.intValue()));
// Set up a few basic things. We are not complete after this though.
version = setVersion;
difficultyTarget = 0x1d07fff8L;
@ -144,15 +189,6 @@ public class Block extends BaseMessage {
prevBlockHash = Sha256Hash.ZERO_HASH;
}
/**
* Construct a block object from the Bitcoin wire format.
* @param payload the payload to extract the block from.
* @throws ProtocolException
*/
public Block(ByteBuffer payload) throws ProtocolException {
super(payload, new DummySerializer(ProtocolVersion.CURRENT.intValue()));
}
/**
* Construct a block initialized with all the given fields.
* @param version This should usually be set to 1 or 2, depending on if the height is in the coinbase input.
@ -172,8 +208,9 @@ public class Block extends BaseMessage {
this.time = time;
this.difficultyTarget = difficultyTarget;
this.nonce = nonce;
this.transactions = new LinkedList<>();
this.transactions.addAll(transactions);
if (transactions != null)
transactions = new LinkedList<>(transactions);
this.transactions = transactions;
}
/**
@ -194,38 +231,9 @@ public class Block extends BaseMessage {
transactions);
}
/**
* Parse transactions from the block.
*/
protected void parseTransactions(ByteBuffer payload) throws BufferUnderflowException, ProtocolException {
VarInt numTransactionsVarInt = VarInt.read(payload);
check(numTransactionsVarInt.fitsInt(), BufferUnderflowException::new);
int numTransactions = numTransactionsVarInt.intValue();
transactions = new ArrayList<>(Math.min(numTransactions, Utils.MAX_INITIAL_ARRAY_LENGTH));
for (int i = 0; i < numTransactions; i++) {
Transaction tx = new Transaction(payload, serializer);
// 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);
}
}
@Override
protected void parse(ByteBuffer payload) throws BufferUnderflowException, ProtocolException {
// header
payload.mark();
version = ByteUtils.readUint32(payload);
prevBlockHash = Sha256Hash.read(payload);
merkleRoot = Sha256Hash.read(payload);
time = Instant.ofEpochSecond(ByteUtils.readUint32(payload));
difficultyTarget = ByteUtils.readUint32(payload);
nonce = ByteUtils.readUint32(payload);
payload.reset(); // read again from the mark for the hash
hash = Sha256Hash.wrapReversed(Sha256Hash.hashTwice(Buffers.readBytes(payload, HEADER_SIZE)));
// transactions
if (payload.hasRemaining()) // otherwise this message is just a header
parseTransactions(payload);
throw new UnsupportedOperationException();
}
public static Block createGenesis() {

View File

@ -56,7 +56,7 @@ public class FilteredBlock extends BaseMessage {
*/
public static FilteredBlock read(ByteBuffer payload) throws BufferUnderflowException, ProtocolException {
byte[] headerBytes = Buffers.readBytes(payload, Block.HEADER_SIZE);
Block header = new Block(ByteBuffer.wrap(headerBytes));
Block header = Block.read(ByteBuffer.wrap(headerBytes));
PartialMerkleTree merkleTree = PartialMerkleTree.read(payload);
return new FilteredBlock(header, merkleTree);
}

View File

@ -63,7 +63,7 @@ public class HeadersMessage extends BaseMessage {
List<Block> blockHeaders = new ArrayList<>();
for (int i = 0; i < numHeaders; ++i) {
final Block newBlockHeader = new Block(payload);
final Block newBlockHeader = Block.read(payload);
if (newBlockHeader.hasTransactions()) {
throw new ProtocolException("Block header does not end with a null byte");
}