-
Notifications
You must be signed in to change notification settings - Fork 63
Expand file tree
/
Copy pathblock-header-encoding.ts
More file actions
147 lines (136 loc) · 4.06 KB
/
block-header-encoding.ts
File metadata and controls
147 lines (136 loc) · 4.06 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
import {
flattenBinArray,
formatError,
numberToBinUint32LE,
readMultiple
} from "../lib.js";
import {
type MaybeReadResult,
type ReadPosition,
} from "../lib.js"
import {
readBytes,
readUint32LE,
} from "./read-components.js";
const SHA256HASHLEN = 32;
export enum HeaderDecodingError {
version = "Error reading version.",
previousBlock = "Error reading previous block.",
merkleRootHash = "Error reading merkle root hash",
time = "Error reading time",
difficultyTarget = "Error reading difficulty target",
nonce = "Error reading nonce",
generic = "Error reading header.",
endsWithUnexpectedBytes = "Error decoding header: the provided header includes unexpected bytes.",
}
/**
* Represents the header of a block in a blockchain.
*/
export type BlockHeader = {
/**
* The version of the block.
*/
version: number;
/**
* The hash of the previous block in the blockchain.
*/
previousBlockHash: Uint8Array;
/**
* The hash of the Merkle root of the transactions in the block.
*/
merkleRootHash: Uint8Array;
/**
* The Unix epoch time at which the block was created.
*/
time: number;
/**
* The target value for the block's proof-of-work.
*/
difficultyTarget: number;
/**
* A random value used in the proof-of-work calculation.
*/
nonce: number;
};
/**
* Attempts to read a BlockHeader from the provided binary data at the given position.
*
* @param {ReadPosition} position - The position in the binary data from which to start reading.
* @returns {MaybeReadResult<BlockHeader>} A parsed BlockHeader object if successful, or an error message if not.
*/
export const readHeader = (
position: ReadPosition,
): MaybeReadResult<BlockHeader> => {
const headerRead = readMultiple(position, [
readUint32LE,
readBytes(SHA256HASHLEN), // previous block hash
readBytes(SHA256HASHLEN), // merkle root
readUint32LE, // Unix epoch time
readUint32LE, // target difficulty A.K.A bits
readUint32LE, // nonce
]);
if (typeof headerRead === "string") {
return formatError(HeaderDecodingError.generic, headerRead);
}
const {
position: nextPosition,
result: [
version,
previousBlockHash,
merkleRootHash,
time,
difficultyTarget,
nonce,
],
} = headerRead;
return {
position: nextPosition,
result: {
version,
previousBlockHash: previousBlockHash.reverse(),
merkleRootHash: merkleRootHash.reverse(),
time,
difficultyTarget,
nonce,
},
};
};
/**
* Decodes a BlockHeader from a given Uint8Array containing its binary representation.
*
* @param {Uint8Array} bin - The binary data containing the encoded BlockHeader.
* @returns {BlockHeader | string} A parsed BlockHeader object if successful, or an error message if not.
*/
export const decodeHeader = (bin: Uint8Array): BlockHeader | string => {
const headerRead = readHeader({ bin, index: 0 });
if (typeof headerRead === "string") {
return headerRead;
}
if (headerRead.position.index !== bin.length) {
return formatError(
HeaderDecodingError.endsWithUnexpectedBytes,
`Encoded header ends at index ${headerRead.position.index - 1}, leaving ${bin.length - headerRead.position.index
} remaining bytes.`,
);
}
return headerRead.result;
};
/**
* Encodes a BlockHeader object into its binary representation.
*
* This function takes a `BlockHeader` object and returns a new `Uint8Array` containing its
* serialized form. The encoding process follows the little-endian convention for all numerical
* values (version, time, difficultyTarget, and nonce).
*
* @param {BlockHeader} header - The BlockHeader object to encode.
* @returns {Uint8Array} A new Uint8Array containing the binary representation of the BlockHeader.
*/
export const encodeHeader = (header: BlockHeader) =>
flattenBinArray([
numberToBinUint32LE(header.version),
header.previousBlockHash.reverse(),
header.merkleRootHash.reverse(),
numberToBinUint32LE(header.time),
numberToBinUint32LE(header.difficultyTarget),
numberToBinUint32LE(header.nonce),
]);