UNPKG

lotus-sdk

Version:

Central repository for several classes of tools for integrating with, and building for, the Lotusia ecosystem

234 lines (233 loc) 7.41 kB
import { Preconditions } from '../util/preconditions.js'; import { BufferUtil } from '../util/buffer.js'; import { BufferReader } from '../encoding/bufferreader.js'; import { BufferWriter } from '../encoding/bufferwriter.js'; import { Hash } from '../crypto/hash.js'; import { JSUtil } from '../util/js.js'; import { Transaction } from '../transaction/index.js'; import { BN } from '../crypto/bn.js'; import { BlockHeader, } from './blockheader.js'; export class Block { static MAX_BLOCK_SIZE = 32 * 1024 * 1024; static START_OF_BLOCK = 0; static NULL_HASH = Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex'); static Values = { START_OF_BLOCK: 0, NULL_HASH: Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex'), }; header; metadata; transactions; _id; constructor(serialized) { if (!(this instanceof Block)) { return new Block(serialized); } if (serialized instanceof Block) { return Block.shallowCopy(serialized); } else if (typeof serialized === 'string' && JSUtil.isHexa(serialized)) { this.fromString(serialized); } else if (Buffer.isBuffer(serialized)) { this.fromBuffer(serialized); } else if (serialized && typeof serialized === 'object') { this.fromObject(serialized); } else { this._newBlock(); } } static shallowCopy(block) { const copy = new Block(block.toBuffer()); return copy; } static _from(arg) { let info = {}; if (Buffer.isBuffer(arg)) { info = Block._fromBufferReader(new BufferReader(arg)); } else if (typeof arg === 'object' && arg !== null) { info = Block._fromObject(arg); } else { throw new TypeError('Unrecognized argument for Block'); } return info; } static fromObject(obj) { const info = Block._fromObject(obj); return new Block(info); } static fromBufferReader(br) { Preconditions.checkArgument(br instanceof BufferReader, 'br is required'); const info = Block._fromBufferReader(br); return new Block(info); } static fromBuffer(buf) { return Block.fromBufferReader(new BufferReader(buf)); } static fromString(str) { const buf = Buffer.from(str, 'hex'); return Block.fromBuffer(buf); } static fromRawBlock(data) { if (!BufferUtil.isBuffer(data)) { data = Buffer.from(data, 'binary'); } const br = new BufferReader(data); br.pos = Block.Values.START_OF_BLOCK; const info = Block._fromBufferReader(br); return new Block(info); } static _fromObject(data) { const transactions = []; if (data.transactions) { data.transactions.forEach(tx => { if (tx instanceof Transaction) { transactions.push(tx); } else { transactions.push(new Transaction(tx)); } }); } return { header: data.header ? data.header instanceof BlockHeader ? data.header : new BlockHeader(data.header) : new BlockHeader(), metadata: data.metadata || 0x00, transactions: transactions, }; } static _fromBufferReader(br) { Preconditions.checkState(!br.finished(), 'No block data received'); const header = BlockHeader.fromBufferReader(br); const metadata = br.readUInt8(); const transactionCount = br.readVarintNum(); const transactions = []; for (let i = 0; i < transactionCount; i++) { const tx = new Transaction(); transactions.push(tx.fromBufferReader(br)); } return { header, metadata, transactions, }; } _newBlock() { this.header = new BlockHeader(); this.metadata = 0x00; this.transactions = []; } fromBuffer(buf) { const info = Block._fromBufferReader(new BufferReader(buf)); this.header = info.header; this.metadata = info.metadata; this.transactions = info.transactions; return this; } fromString(str) { const buf = Buffer.from(str, 'hex'); return this.fromBuffer(buf); } fromObject(obj) { const info = Block._fromObject(obj); this.header = info.header; this.metadata = info.metadata; this.transactions = info.transactions; return this; } toObject() { const transactions = this.transactions.map(tx => tx.toObject()); return { id: this.id, hash: this.hash, header: this.header.toObject(), metadata: this.metadata, transactions: transactions, }; } toJSON = this.toObject; toBuffer() { return this.toBufferWriter().concat(); } toString() { return this.toBuffer().toString('hex'); } toBufferWriter(bw) { if (!bw) { bw = new BufferWriter(); } bw.write(this.header.toBuffer()); bw.writeUInt8(this.metadata); bw.writeVarintNum(this.transactions.length); for (let i = 0; i < this.transactions.length; i++) { this.transactions[i].toBufferWriter(bw); } return bw; } getTransactionHashes() { const hashes = []; if (this.transactions.length === 0) { return [Block.Values.NULL_HASH]; } for (let t = 0; t < this.transactions.length; t++) { const tx = this.transactions[t]; const txid = tx._getTxid(); const hash = tx._getHash(); const buf = Buffer.concat([hash, txid]); const resultHash = Hash.sha256sha256(buf); hashes.push(resultHash); } return hashes; } getMerkleTree() { const tree = this.getTransactionHashes(); let j = 0; for (let size = tree.length; size > 1; size = Math.floor(size / 2)) { if (size % 2 === 1) { tree.push(Block.Values.NULL_HASH); size += 1; } for (let i = 0; i < size; i += 2) { const buf = Buffer.concat([tree[j + i], tree[j + i + 1]]); tree.push(Hash.sha256sha256(buf)); } j += size; } return tree; } getMerkleRoot() { const tree = this.getMerkleTree(); return tree[tree.length - 1]; } validMerkleRoot() { const h = new BN(this.header.merkleRoot.toString('hex'), 'hex'); const c = new BN(this.getMerkleRoot().toString('hex'), 'hex'); if (!h.eq(c)) { return false; } return true; } _getHash() { const header = this.header; return header._getHash(); } get hash() { if (!this._id) { this._id = this.header.id; } return this._id; } get id() { return this.hash; } inspect() { return '<Block ' + this.id + '>'; } }