@atomiqlabs/chain-starknet
Version:
Starknet specific base implementation
143 lines (129 loc) • 4.36 kB
text/typescript
import {BtcHeader} from "@atomiqlabs/base";
import {Buffer} from "buffer";
import {BigNumberish} from "starknet";
import {toHex, u32ArrayToBuffer, u32ReverseEndianness} from "../../../utils/Utils";
import {sha256} from "@noble/hashes/sha2";
export type StarknetBtcHeaderType = {
reversed_version: BigNumberish;
previous_blockhash: BigNumberish[];
merkle_root: BigNumberish[];
reversed_timestamp: BigNumberish;
nbits: BigNumberish;
nonce: BigNumberish;
hash?: Buffer
}
/**
* Representing a new bitcoin blockheader struct to be submitted to the Starknet BTC relay smart contract
*
* @category BTC Relay
*/
export class StarknetBtcHeader implements BtcHeader {
private readonly reversed_version: number;
private readonly previous_blockhash: number[];
private readonly merkle_root: number[];
private readonly reversed_timestamp: number;
private readonly nbits: number;
private readonly nonce: number;
private readonly hash?: Buffer;
/**
* Constructs the bitcoin blockheader from a struct as returned by the starknet.js lib
*
* @param obj Struct as returned by the starknet.js lib
*
* @internal
*/
constructor(obj: StarknetBtcHeaderType) {
this.reversed_version = Number(obj.reversed_version);
this.previous_blockhash = obj.previous_blockhash.map(val => Number(val));
this.merkle_root = obj.merkle_root.map(val => Number(val));
this.reversed_timestamp = Number(obj.reversed_timestamp);
this.nbits = Number(obj.nbits);
this.nonce = Number(obj.nonce);
this.hash = obj.hash;
}
/**
* @inheritDoc
*/
getMerkleRoot(): Buffer {
return u32ArrayToBuffer(this.merkle_root);
}
/**
* @inheritDoc
*/
getNbits(): number {
return u32ReverseEndianness(this.nbits);
}
/**
* @inheritDoc
*/
getNonce(): number {
return u32ReverseEndianness(this.nonce);
}
/**
* @inheritDoc
*/
getReversedPrevBlockhash(): Buffer {
return u32ArrayToBuffer(this.previous_blockhash);
}
/**
* @inheritDoc
*/
getTimestamp(): number {
return u32ReverseEndianness(this.reversed_timestamp);
}
/**
* @inheritDoc
*/
getVersion(): number {
return u32ReverseEndianness(this.reversed_version);
}
/**
* @inheritDoc
*/
getHash(): Buffer {
if(this.hash!=null) return this.hash;
const buffer = Buffer.alloc(80);
buffer.writeUInt32BE(this.reversed_version, 0);
u32ArrayToBuffer(this.previous_blockhash).copy(buffer, 4);
u32ArrayToBuffer(this.merkle_root).copy(buffer, 36);
buffer.writeUInt32BE(this.reversed_timestamp, 68);
buffer.writeUInt32BE(this.nbits, 72);
buffer.writeUInt32BE(this.nonce, 76);
return Buffer.from(sha256(sha256(buffer)));
}
/**
* Serializes the bitcoin blockheader struct to an array of felt252 of length 20
*/
serialize(): BigNumberish[] {
return [
this.reversed_version,
...this.previous_blockhash,
...this.merkle_root,
this.reversed_timestamp,
this.nbits,
this.nonce
];
}
/**
* Deserializes the store bitcoin blockheader from its felt252 array representation
*
* @param span felt252 array encoding the stored blockheader, has to be at least 20 felts long
*/
static fromSerializedFeltArray(span: BigNumberish[]): StarknetBtcHeader {
if(span.length<20) throw new Error("Invalid serialized data size!");
const reversed_version = toHex(span.shift()!);
const previous_blockhash = span.splice(0, 8).map(val => toHex(val));
const merkle_root = span.splice(0, 8).map(val => toHex(val));
const reversed_timestamp = toHex(span.shift()!);
const nbits = toHex(span.shift()!);
const nonce = toHex(span.shift()!);
return new StarknetBtcHeader({
reversed_version,
previous_blockhash,
merkle_root,
reversed_timestamp,
nbits,
nonce
});
}
}