Skip to content

Commit

Permalink
bitcoin light chain support transaction verification
Browse files Browse the repository at this point in the history
  • Loading branch information
zfliex924 authored and charles2023wood committed Feb 19, 2024
1 parent 3e151f2 commit 8b8442e
Show file tree
Hide file tree
Showing 4 changed files with 1,687 additions and 0 deletions.
62 changes: 62 additions & 0 deletions contracts/BtcLightClient.sol
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ contract BtcLightClient is ILightClient, System, IParamSubscriber{

uint256 public storeBlockGasPrice;

mapping(uint32 => bytes32) public height2HashMap;

/*********************** events **************************/
event StoreHeaderFailed(bytes32 indexed blockHash, int256 indexed returnCode);
event StoreHeader(bytes32 indexed blockHash, address candidate, address indexed rewardAddr, uint32 indexed height, bytes32 bindingHash);
Expand Down Expand Up @@ -176,11 +178,20 @@ contract BtcLightClient is ILightClient, System, IParamSubscriber{
// that when an (existing) Tip becomes stale, the chain can continue with
// the alternate Tip
if (scoreBlock >= highScore) {
uint32 prevHeight = blockHeight - 1;
bytes32 prevHash = getPrevHash(blockHash);
while(height2HashMap[prevHeight] != prevHash && prevHeight + CONFIRM_BLOCK >= blockHeight) {
height2HashMap[prevHeight] = prevHash;
--prevHeight;
prevHash = getPrevHash(prevHash);
}

if (blockHeight > getHeight(heaviestBlock)) {
addMinerPower(blockHash);
}
heaviestBlock = blockHash;
highScore = scoreBlock;
height2HashMap[blockHeight] = blockHash;
}
emit StoreHeader(blockHash, candidateAddr, rewardAddr, blockHeight, bindingHash);
}
Expand Down Expand Up @@ -271,6 +282,49 @@ contract BtcLightClient is ILightClient, System, IParamSubscriber{
return count/4;
}
}

/// Checks if a tx is included and confirmed on Bitcoin
/// @dev Checks if the block is confirmed, and Merkle proof is valid
/// @param txid Desired tx Id in LE form
/// @param blockHeight of the desired tx
/// @param confirmBlock of the tx confirmation
/// @param nodes Part of the Merkle tree from the tx to the root in LE form (called Merkle proof)
/// @param index of the tx in Merkle tree
/// @return True if the provided tx is confirmed on Bitcoin
function checkTxProof(bytes32 txid, uint32 blockHeight, uint32 confirmBlock, bytes32[] calldata nodes, uint256 index) public view override returns (bool) {
bytes32 blockHash = height2HashMap[blockHeight];
if (blockHeight + confirmBlock > getChainTipHeight() || txid == bytes32(0) || blockHash == bytes32(0)) {
return false;
}

bytes32 root = bytes32(loadInt256(68, blockChain[blockHash]));
if (nodes.length == 0) {
return txid == root;
}

bytes32 current = txid;
for (uint256 i = 0; i < nodes.length; i++) {
if (index % 2 == 1) {
current = merkleStep(nodes[i], current);
} else {
current = merkleStep(current, nodes[i]);
}
index >>= 1;
}
return current == root;
}

function merkleStep(bytes32 l, bytes32 r) private view returns (bytes32 digest) {
assembly {
// solium-disable-previous-line security/no-inline-assembly
let ptr := mload(0x40)
mstore(ptr, l)
mstore(add(ptr, 0x20), r)
pop(staticcall(gas(), 2, ptr, 0x40, ptr, 0x20)) // sha256 #1
pop(staticcall(gas(), 2, ptr, 0x20, ptr, 0x20)) // sha256 #2
digest := mload(ptr)
}
}

function slice(bytes memory input, uint256 start, uint256 end) internal pure returns (bytes memory _output) {
uint256 length = end - start;
Expand Down Expand Up @@ -439,6 +493,10 @@ contract BtcLightClient is ILightClient, System, IParamSubscriber{
return flip32Bytes(bytes32(loadInt256(36, blockChain[hash])));
}

function getMerkleRoot(bytes32 hash) public view returns (bytes32) {
return flip32Bytes(bytes32(loadInt256(68, blockChain[hash])));
}

function getCandidate(bytes32 hash) public view returns (address) {
return address(uint160(loadInt256(160, blockChain[hash]) >> 96));
}
Expand All @@ -465,6 +523,10 @@ contract BtcLightClient is ILightClient, System, IParamSubscriber{
return adjustmentHashes[index];
}

function getChainTipHeight() public override view returns (uint32) {
return getHeight(heaviestBlock);
}

// Bitcoin-way of computing the target from the 'bits' field of a blockheader
// based on http://www.righto.com/2014/02/bitcoin-mining-hard-way-algorithms.html#ref3
function targetFromBits(uint32 bits) internal pure returns (uint256 target) {
Expand Down
62 changes: 62 additions & 0 deletions contracts/BtcLightClient.template
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ contract BtcLightClient is ILightClient, System, IParamSubscriber{

uint256 public storeBlockGasPrice;

mapping(uint32 => bytes32) public height2HashMap;

/*********************** events **************************/
event StoreHeaderFailed(bytes32 indexed blockHash, int256 indexed returnCode);
event StoreHeader(bytes32 indexed blockHash, address candidate, address indexed rewardAddr, uint32 indexed height, bytes32 bindingHash);
Expand Down Expand Up @@ -176,11 +178,20 @@ contract BtcLightClient is ILightClient, System, IParamSubscriber{
// that when an (existing) Tip becomes stale, the chain can continue with
// the alternate Tip
if (scoreBlock >= highScore) {
uint32 prevHeight = blockHeight - 1;
bytes32 prevHash = getPrevHash(blockHash);
while(height2HashMap[prevHeight] != prevHash && prevHeight + CONFIRM_BLOCK >= blockHeight) {
height2HashMap[prevHeight] = prevHash;
--prevHeight;
prevHash = getPrevHash(prevHash);
}

if (blockHeight > getHeight(heaviestBlock)) {
addMinerPower(blockHash);
}
heaviestBlock = blockHash;
highScore = scoreBlock;
height2HashMap[blockHeight] = blockHash;
}
emit StoreHeader(blockHash, candidateAddr, rewardAddr, blockHeight, bindingHash);
}
Expand Down Expand Up @@ -271,6 +282,49 @@ contract BtcLightClient is ILightClient, System, IParamSubscriber{
return count/4;
}
}

/// Checks if a tx is included and confirmed on Bitcoin
/// @dev Checks if the block is confirmed, and Merkle proof is valid
/// @param txid Desired tx Id in LE form
/// @param blockHeight of the desired tx
/// @param confirmBlock of the tx confirmation
/// @param nodes Part of the Merkle tree from the tx to the root in LE form (called Merkle proof)
/// @param index of the tx in Merkle tree
/// @return True if the provided tx is confirmed on Bitcoin
function checkTxProof(bytes32 txid, uint32 blockHeight, uint32 confirmBlock, bytes32[] calldata nodes, uint256 index) public view override returns (bool) {
bytes32 blockHash = height2HashMap[blockHeight];
if (blockHeight + confirmBlock > getChainTipHeight() || txid == bytes32(0) || blockHash == bytes32(0)) {
return false;
}

bytes32 root = bytes32(loadInt256(68, blockChain[blockHash]));
if (nodes.length == 0) {
return txid == root;
}

bytes32 current = txid;
for (uint256 i = 0; i < nodes.length; i++) {
if (index % 2 == 1) {
current = merkleStep(nodes[i], current);
} else {
current = merkleStep(current, nodes[i]);
}
index >>= 1;
}
return current == root;
}

function merkleStep(bytes32 l, bytes32 r) private view returns (bytes32 digest) {
assembly {
// solium-disable-previous-line security/no-inline-assembly
let ptr := mload(0x40)
mstore(ptr, l)
mstore(add(ptr, 0x20), r)
pop(staticcall(gas(), 2, ptr, 0x40, ptr, 0x20)) // sha256 #1
pop(staticcall(gas(), 2, ptr, 0x20, ptr, 0x20)) // sha256 #2
digest := mload(ptr)
}
}

function slice(bytes memory input, uint256 start, uint256 end) internal pure returns (bytes memory _output) {
uint256 length = end - start;
Expand Down Expand Up @@ -439,6 +493,10 @@ contract BtcLightClient is ILightClient, System, IParamSubscriber{
return flip32Bytes(bytes32(loadInt256(36, blockChain[hash])));
}

function getMerkleRoot(bytes32 hash) public view returns (bytes32) {
return flip32Bytes(bytes32(loadInt256(68, blockChain[hash])));
}

function getCandidate(bytes32 hash) public view returns (address) {
return address(uint160(loadInt256(160, blockChain[hash]) >> 96));
}
Expand All @@ -465,6 +523,10 @@ contract BtcLightClient is ILightClient, System, IParamSubscriber{
return adjustmentHashes[index];
}

function getChainTipHeight() public override view returns (uint32) {
return getHeight(heaviestBlock);
}

// Bitcoin-way of computing the target from the 'bits' field of a blockheader
// based on http://www.righto.com/2014/02/bitcoin-mining-hard-way-algorithms.html#ref3
function targetFromBits(uint32 bits) internal pure returns (uint256 target) {
Expand Down
4 changes: 4 additions & 0 deletions contracts/interface/ILightClient.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,8 @@ interface ILightClient {
function getRoundCandidates(uint256 roundTimeTag) external view returns (address[] memory candidates);

function getRoundMiners(uint256 roundTimeTag, address candidate) external view returns (address[] memory miners);

function checkTxProof(bytes32 txid, uint32 blockHeight, uint32 confirmBlock, bytes32[] calldata nodes, uint256 index) external view returns (bool);

function getChainTipHeight() external view returns (uint32);
}
Loading

0 comments on commit 8b8442e

Please sign in to comment.