// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8; import "./Borsh.sol"; library NearDecoder { using Borsh for Borsh.Data; using NearDecoder for Borsh.Data; uint8 constant VALIDATOR_V1 = 0; uint8 constant VALIDATOR_V2 = 1; struct PublicKey { bytes32 k; } function decodePublicKey(Borsh.Data memory data) internal pure returns (PublicKey memory res) { require(data.decodeU8() == 0, "Parse error: invalid key type"); res.k = data.decodeBytes32(); } struct Signature { bytes32 r; bytes32 s; } function decodeSignature(Borsh.Data memory data) internal pure returns (Signature memory res) { require(data.decodeU8() == 0, "Parse error: invalid signature type"); res.r = data.decodeBytes32(); res.s = data.decodeBytes32(); } struct BlockProducer { PublicKey publicKey; uint128 stake; // Flag indicating if this validator proposed to be a chunk-only producer (i.e. cannot become a block producer). bool isChunkOnly; } function decodeBlockProducer(Borsh.Data memory data) internal pure returns (BlockProducer memory res) { uint8 validator_version = data.decodeU8(); data.skipBytes(); res.publicKey = data.decodePublicKey(); res.stake = data.decodeU128(); if (validator_version == VALIDATOR_V2) { res.isChunkOnly = data.decodeU8() != 0; } else { res.isChunkOnly = false; } } function decodeBlockProducers(Borsh.Data memory data) internal pure returns (BlockProducer[] memory res) { uint length = data.decodeU32(); res = new BlockProducer[](length); for (uint i = 0; i < length; i++) { res[i] = data.decodeBlockProducer(); } } struct OptionalBlockProducers { bool some; BlockProducer[] blockProducers; bytes32 hash; // Additional computable element } function decodeOptionalBlockProducers(Borsh.Data memory data) internal view returns (OptionalBlockProducers memory res) { res.some = data.decodeBool(); if (res.some) { uint start = data.ptr; res.blockProducers = data.decodeBlockProducers(); res.hash = Utils.sha256Raw(start, data.ptr - start); } } struct OptionalSignature { bool some; Signature signature; } function decodeOptionalSignature(Borsh.Data memory data) internal pure returns (OptionalSignature memory res) { res.some = data.decodeBool(); if (res.some) { res.signature = data.decodeSignature(); } } struct BlockHeaderInnerLite { uint64 height; // Height of this block since the genesis block (height 0). bytes32 epoch_id; // Epoch start hash of this block's epoch. Used for retrieving validator information bytes32 next_epoch_id; bytes32 prev_state_root; // Root hash of the state at the previous block. bytes32 outcome_root; // Root of the outcomes of transactions and receipts. uint64 timestamp; // Timestamp at which the block was built. bytes32 next_bp_hash; // Hash of the next epoch block producers set bytes32 block_merkle_root; bytes32 hash; // Additional computable element } function decodeBlockHeaderInnerLite(Borsh.Data memory data) internal view returns (BlockHeaderInnerLite memory res) { res.hash = data.peekSha256(208); res.height = data.decodeU64(); res.epoch_id = data.decodeBytes32(); res.next_epoch_id = data.decodeBytes32(); res.prev_state_root = data.decodeBytes32(); res.outcome_root = data.decodeBytes32(); res.timestamp = data.decodeU64(); res.next_bp_hash = data.decodeBytes32(); res.block_merkle_root = data.decodeBytes32(); } struct LightClientBlock { bytes32 prev_block_hash; bytes32 next_block_inner_hash; BlockHeaderInnerLite inner_lite; bytes32 inner_rest_hash; OptionalBlockProducers next_bps; OptionalSignature[] approvals_after_next; bytes32 hash; bytes32 next_hash; } function decodeLightClientBlock(Borsh.Data memory data) internal view returns (LightClientBlock memory res) { res.prev_block_hash = data.decodeBytes32(); res.next_block_inner_hash = data.decodeBytes32(); res.inner_lite = data.decodeBlockHeaderInnerLite(); res.inner_rest_hash = data.decodeBytes32(); res.next_bps = data.decodeOptionalBlockProducers(); uint length = data.decodeU32(); res.approvals_after_next = new OptionalSignature[](length); for (uint i = 0; i < length; i++) { res.approvals_after_next[i] = data.decodeOptionalSignature(); } res.hash = sha256( abi.encodePacked(sha256(abi.encodePacked(res.inner_lite.hash, res.inner_rest_hash)), res.prev_block_hash) ); res.next_hash = sha256(abi.encodePacked(res.next_block_inner_hash, res.hash)); } }