int-cli
Version:
INT is the new generation of bottom-up created system of IoT and blockchain
403 lines (402 loc) • 13.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const util_1 = require("util");
const transaction_1 = require("./transaction");
const serializable_1 = require("../serializable");
const error_code_1 = require("../error_code");
const merkle = require("../lib/merkle");
const encoding_1 = require("../lib/encoding");
const assert = require("assert");
const digest = require('../lib/digest');
class BlockHeader extends serializable_1.SerializableWithHash {
constructor() {
super();
this.m_number = 0;
this.m_storageHash = encoding_1.Encoding.NULL_HASH;
this.m_preBlockHash = encoding_1.Encoding.NULL_HASH;
this.m_receiptHash = encoding_1.Encoding.NULL_HASH;
this.m_merkleRoot = encoding_1.Encoding.NULL_HASH;
this.m_timestamp = -1;
}
get number() {
return this.m_number;
}
get storageHash() {
return this.m_storageHash;
}
set storageHash(h) {
this.m_storageHash = h;
}
get preBlockHash() {
return this.m_preBlockHash;
}
get timestamp() {
return this.m_timestamp;
}
set timestamp(n) {
this.m_timestamp = n;
}
isPreBlock(header) {
return (this.m_number + 1 === header.m_number) && (this.m_hash === header.m_preBlockHash);
}
setPreBlock(header) {
if (header) {
this.m_number = header.m_number + 1;
this.m_preBlockHash = header.hash;
}
else {
// gensis block
this.m_number = 0;
this.m_preBlockHash = encoding_1.Encoding.NULL_HASH;
}
}
get merkleRoot() {
return this.m_merkleRoot;
}
hasTransaction(txHash) {
// TODO: find hash from txHash
return false;
}
_genMerkleRoot(txs) {
const leaves = [];
for (const tx of txs) {
leaves.push(Buffer.from(tx.hash, 'hex'));
}
const [root, malleated] = merkle.createRoot(leaves);
if (malleated) {
return encoding_1.Encoding.NULL_HASH;
}
return root.toString('hex');
}
_genReceiptHash(receipts) {
if (!receipts.length) {
return encoding_1.Encoding.NULL_HASH;
}
let writer = new serializable_1.BufferWriter();
for (const receipt of receipts) {
receipt.encode(writer);
}
return digest.hash256(writer.render()).toString('hex');
}
/**
* virtual
* verify hash here
*/
async verify(chain) {
return { err: error_code_1.ErrorCode.RESULT_OK, valid: true };
}
verifyContent(content) {
if (this.m_merkleRoot !== this._genMerkleRoot(content.transactions)) {
return false;
}
if (this.m_receiptHash !== this._genReceiptHash(content.receipts)) {
return false;
}
return true;
}
updateContent(content) {
this.m_merkleRoot = this._genMerkleRoot(content.transactions);
this.m_receiptHash = this._genReceiptHash(content.receipts);
}
_encodeHashContent(writer) {
try {
writer.writeU32(this.m_number);
writer.writeI32(this.m_timestamp);
writer.writeHash(this.m_merkleRoot);
writer.writeHash(this.m_storageHash);
writer.writeHash(this.m_receiptHash);
writer.writeHash(this.m_preBlockHash);
}
catch (e) {
return error_code_1.ErrorCode.RESULT_INVALID_FORMAT;
}
return error_code_1.ErrorCode.RESULT_OK;
}
_decodeHashContent(reader) {
try {
this.m_number = reader.readU32();
this.m_timestamp = reader.readI32();
this.m_merkleRoot = reader.readHash('hex');
this.m_storageHash = reader.readHash('hex');
this.m_receiptHash = reader.readHash('hex');
this.m_preBlockHash = reader.readHash('hex');
}
catch (e) {
return error_code_1.ErrorCode.RESULT_INVALID_FORMAT;
}
return error_code_1.ErrorCode.RESULT_OK;
}
stringify() {
let obj = super.stringify();
obj.number = this.number;
obj.timestamp = this.timestamp;
obj.preBlock = this.preBlockHash;
obj.merkleRoot = this.merkleRoot;
obj.storageHash = this.m_storageHash;
obj.m_receiptHash = this.m_receiptHash;
return obj;
}
}
exports.BlockHeader = BlockHeader;
class BlockContent {
constructor(transactionType, receiptType) {
this.m_transactions = new Array();
this.m_preBlockEventReceipts = new Array();
this.m_txReceipts = new Map();
this.m_postBlockEventReceipts = new Array();
this.m_receipts = new Array();
this.m_transactionType = transactionType;
this.m_receiptType = receiptType;
}
get transactions() {
const t = this.m_transactions;
return t;
}
get receipts() {
const r = this.m_receipts;
return r;
}
get preBlockEventReceipts() {
const r = this.m_preBlockEventReceipts;
return r;
}
get transactionReceipts() {
const r = this.m_txReceipts;
return r;
}
get postBlockEventReceipts() {
const r = this.m_postBlockEventReceipts;
return r;
}
get eventLogs() {
let logs = [];
for (let r of this.m_receipts) {
logs.push(...r.eventLogs);
}
return logs;
}
hasTransaction(txHash) {
for (const tx of this.m_transactions) {
if (tx.hash === txHash) {
return true;
}
}
return false;
}
getTransaction(arg) {
if (typeof (arg) === 'string') {
for (const tx of this.m_transactions) {
if (tx.hash === arg) {
return tx;
}
}
}
else if (typeof (arg) === 'number') {
if (arg >= 0 && arg < this.m_transactions.length) {
return this.m_transactions[arg];
}
}
return null;
}
getReceipt(options) {
if (util_1.isString(options)) {
return this.m_txReceipts.get(options);
}
else {
if (options.sourceType === transaction_1.ReceiptSourceType.preBlockEvent) {
return this.m_preBlockEventReceipts[options.eventIndex];
}
else if (options.sourceType === transaction_1.ReceiptSourceType.postBlockEvent) {
return this.m_postBlockEventReceipts[options.eventIndex];
}
else {
assert(false, `invalid receipt source type ${options.sourceType}`);
return undefined;
}
}
}
addTransaction(tx) {
this.m_transactions.push(tx);
}
setReceipts(receipts) {
let txReceipts = new Map();
let txReceiptsArr = [];
let preBlockEventReceipts = [];
let postBlockEventReceipts = [];
for (let r of receipts) {
if (r.sourceType === transaction_1.ReceiptSourceType.transaction) {
txReceipts.set(r.transactionHash, r);
txReceiptsArr.push(r);
}
else if (r.sourceType === transaction_1.ReceiptSourceType.preBlockEvent) {
preBlockEventReceipts.push(r);
}
else if (r.sourceType === transaction_1.ReceiptSourceType.postBlockEvent) {
postBlockEventReceipts.push(r);
}
else {
assert(false, `invalid receipt source type ${r.sourceType}`);
return;
}
}
this.m_txReceipts = txReceipts;
this.m_preBlockEventReceipts = preBlockEventReceipts;
this.m_postBlockEventReceipts = postBlockEventReceipts;
let _receipts = [];
_receipts.push(...preBlockEventReceipts);
_receipts.push(...txReceiptsArr);
_receipts.push(...postBlockEventReceipts);
this.m_receipts = _receipts;
}
encode(writer) {
try {
writer.writeU16(this.m_transactions.length);
for (let tx of this.m_transactions) {
const err = tx.encode(writer);
if (err) {
return err;
}
}
const receiptLength = this.m_txReceipts.size
+ this.m_preBlockEventReceipts.length
+ this.m_postBlockEventReceipts.length;
if (receiptLength) {
if (this.m_txReceipts.size !== this.m_transactions.length) {
return error_code_1.ErrorCode.RESULT_INVALID_BLOCK;
}
writer.writeU16(receiptLength);
for (let tx of this.m_transactions) {
let r = this.m_txReceipts.get(tx.hash);
assert(r);
const err = r.encode(writer);
if (err) {
return err;
}
}
for (let r of this.m_preBlockEventReceipts) {
const err = r.encode(writer);
if (err) {
return err;
}
}
for (let r of this.m_postBlockEventReceipts) {
const err = r.encode(writer);
if (err) {
return err;
}
}
}
else {
writer.writeU16(0);
}
}
catch (e) {
return error_code_1.ErrorCode.RESULT_INVALID_FORMAT;
}
return error_code_1.ErrorCode.RESULT_OK;
}
decode(reader) {
this.m_transactions = [];
this.m_txReceipts = new Map();
let txCount;
try {
txCount = reader.readU16();
}
catch (e) {
return error_code_1.ErrorCode.RESULT_INVALID_FORMAT;
}
for (let ix = 0; ix < txCount; ++ix) {
let tx = new this.m_transactionType();
let err = tx.decode(reader);
if (err !== error_code_1.ErrorCode.RESULT_OK) {
return err;
}
this.m_transactions.push(tx);
}
const rs = reader.readU16();
let receipts = [];
if (rs) {
for (let ix = 0; ix < txCount; ++ix) {
let receipt = new this.m_receiptType();
const err = receipt.decode(reader);
if (err !== error_code_1.ErrorCode.RESULT_OK) {
return err;
}
receipts.push(receipt);
}
for (let ix = 0; ix < rs - txCount; ++ix) {
let receipt = new transaction_1.Receipt();
const err = receipt.decode(reader);
if (err !== error_code_1.ErrorCode.RESULT_OK) {
return err;
}
receipts.push(receipt);
}
}
this.setReceipts(receipts);
return error_code_1.ErrorCode.RESULT_OK;
}
}
exports.BlockContent = BlockContent;
class Block {
constructor(options) {
this.m_transactionType = options.transactionType;
this.m_headerType = options.headerType;
this.m_header = new this.m_headerType();
this.m_receiptType = options.receiptType;
if (options.header) {
let writer = new serializable_1.BufferWriter();
let err = options.header.encode(writer);
assert(!err, `encode header failed with err ${err}`);
let reader = new serializable_1.BufferReader(writer.render());
err = this.m_header.decode(reader);
assert(!err, `clone header failed with err ${err}`);
}
this.m_content = new BlockContent(this.m_transactionType, this.m_receiptType);
}
clone() {
let writer = new serializable_1.BufferWriter();
let err = this.encode(writer);
assert(!err, `encode block failed ${err}`);
let reader = new serializable_1.BufferReader(writer.render());
let newBlock = new Block({
headerType: this.m_headerType,
transactionType: this.m_transactionType,
receiptType: this.m_receiptType,
});
err = newBlock.decode(reader);
assert(!err, `clone block ${this.m_header.hash} failed for ${err}`);
return newBlock;
}
get header() {
return this.m_header;
}
get content() {
return this.m_content;
}
get hash() {
return this.m_header.hash;
}
get number() {
return this.m_header.number;
}
encode(writer) {
let err = this.m_header.encode(writer);
if (err) {
return err;
}
return this.m_content.encode(writer);
}
decode(reader) {
let err = this.m_header.decode(reader);
if (err !== error_code_1.ErrorCode.RESULT_OK) {
return err;
}
return this.m_content.decode(reader);
}
verify() {
// 验证content hash
return this.m_header.verifyContent(this.m_content);
}
}
exports.Block = Block;