Skip to content Skip to sidebar Skip to footer

Is There A Way For Smart Contract On Rsk To Fetch On-chain Data From Bitcoin Network Without Using Oracles?

Is there a way for smart contract on RSK to fetch on-chain data on Bitcoin not depending on trusted oracles? I just found a proposal called Open Bitcoin blockchain oracle (RSKIP220

Solution 1:

Here is a working example of how to use Bridge methods to access Bitcoin blocks. This code has been tested in RSK Testnet.

Bridge.sol:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;

interfaceBridge{
  functiongetBtcBlockchainBestChainHeight () externalviewreturns (int);
  functiongetStateForBtcReleaseClient () externalviewreturns ( bytes memory);
  functiongetStateForDebugging () externalviewreturns ( bytes memory  );
  functiongetBtcBlockchainInitialBlockHeight () externalviewreturns (int);
  functiongetBtcBlockchainBlockHashAtDepth ( int256 depth ) externalviewreturns ( bytes memory );
  functiongetBtcTxHashProcessedHeight (string calldata hash ) externalviewreturns ( int64 );
  functionisBtcTxHashAlreadyProcessed (string calldata hash ) externalviewreturns (bool);
  functiongetFederationAddress () externalviewreturns (string memory );
  functionregisterBtcTransaction ( bytes calldata atx, int256 height, bytes calldata pmt ) external;
  functionaddSignature ( bytes calldata pubkey, bytes[] calldata signatures, bytes calldata txhash ) external;
  functionreceiveHeaders ( bytes[] calldata blocks ) external;
  functionreceiveHeader ( bytes calldata ablock ) externalreturns ( int256 );
  functiongetFederationSize () externalviewreturns ( int256 );
  functiongetFederationThreshold () externalviewreturns ( int256 );
  functiongetFederatorPublicKey ( int256 index ) externalviewreturns ( bytes memory);
  functiongetFederatorPublicKeyOfType ( int256 index, string calldata atype ) externalreturns ( bytes memory);
  functiongetFederationCreationTime () externalviewreturns ( int256 );
  functiongetFederationCreationBlockNumber () externalviewreturns ( int256 );
  functiongetRetiringFederationAddress () externalviewreturns (string memory );
  functiongetRetiringFederationSize () externalviewreturns ( int256 );
  functiongetRetiringFederationThreshold () externalviewreturns ( int256 );
  functiongetRetiringFederatorPublicKey ( int256 index ) externalviewreturns ( bytes memory);
  functiongetRetiringFederatorPublicKeyOfType ( int256 index,string calldata atype ) externalviewreturns ( bytes memory);
  functiongetRetiringFederationCreationTime () externalviewreturns ( int256 );
  functiongetRetiringFederationCreationBlockNumber () externalviewreturns ( int256 );
  functioncreateFederation () externalreturns ( int256 );
  functionaddFederatorPublicKey ( bytes calldata  key ) externalreturns ( int256 );
  functionaddFederatorPublicKeyMultikey ( bytes calldata btcKey, bytes calldata rskKey, bytes calldata mstKey ) externalreturns ( int256 );
  functioncommitFederation ( bytes calldata hash ) externalreturns ( int256 );
  functionrollbackFederation () externalreturns ( int256 );
  functiongetPendingFederationHash () externalviewreturns ( bytes memory);
  functiongetPendingFederationSize () externalviewreturns ( int256 );
  functiongetPendingFederatorPublicKey ( int256 index ) externalviewreturns ( bytes memory);
  functiongetPendingFederatorPublicKeyOfType ( int256 index, string calldata atype ) externalviewreturns ( bytes memory);
  functiongetLockWhitelistSize () externalviewreturns ( int256 );
  functiongetLockWhitelistAddress ( int256 index ) externalviewreturns (string memory);
  functiongetLockWhitelistEntryByAddress (string calldata aaddress ) externalviewreturns ( int256 );
  functionaddLockWhitelistAddress (string calldata aaddress, int256 maxTransferValue ) externalreturns ( int256 );
  functionaddOneOffLockWhitelistAddress (string calldata aaddress, int256 maxTransferValue ) externalreturns ( int256 );
  functionaddUnlimitedLockWhitelistAddress (string calldata aaddress ) externalreturns ( int256 ); 
  functionremoveLockWhitelistAddress (string calldata aaddress ) externalreturns ( int256 );
  functionsetLockWhitelistDisableBlockDelay ( int256 disableDelay ) externalreturns ( int256 );
  functiongetFeePerKb () externalviewreturns ( int256 );
  functionvoteFeePerKbChange ( int256 feePerKb ) externalreturns ( int256 );
  functionupdateCollections () external;
  functiongetMinimumLockTxValue () externalviewreturns ( int256 );
  functiongetBtcTransactionConfirmations ( bytes32  txHash, bytes32 blockHash, uint256 merkleBranchPath, bytes32[] calldata merkleBranchHashes ) externalviewreturns ( int256 );
  functiongetLockingCap () externalviewreturns ( int256 );
  functionincreaseLockingCap ( int256 newLockingCap ) externalreturns (bool);
  functionregisterBtcCoinbaseTransaction ( bytes calldata btcTxSerialized, bytes32 blockHash, bytes calldata pmtSerialized, bytes32 witnessMerkleRoot, bytes32 witnessReservedValue ) external;
  functionhasBtcBlockCoinbaseTransactionInformation ( bytes32 blockHash ) externalreturns (bool);
  functionregisterFastBridgeBtcTransaction ( bytes calldata btcTxSerialized, uint256 height, bytes calldata pmtSerialized, bytes32 derivationArgumentsHash, bytes calldata userRefundBtcAddress, address liquidityBridgeContractAddress, bytes calldata liquidityProviderBtcAddress, bool shouldTransferToContract ) externalreturns ( int256 );
  functiongetActiveFederationCreationBlockHeight () externalviewreturns ( uint256 );
  functiongetBtcBlockchainBestBlockHeader () externalviewreturns ( bytes memory );
  functiongetBtcBlockchainBlockHeaderByHash ( bytes32 btcBlockHash ) externalviewreturns ( bytes memory );
  functiongetBtcBlockchainBlockHeaderByHeight ( uint256 btcBlockHeight ) externalviewreturns ( bytes memory );
  functiongetBtcBlockchainParentBlockHeaderByHash ( bytes32 btcBlockHash ) externalviewreturns ( bytes memory);
}

QueryDemo.sol:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;

import"./Bridge.sol";

contract QueryDemo {
    intpublic bestChainHeight;
    bytes public returned;
    bytes32 public headerHash;
    
    function reverse(uint256 input) internal pure returns(uint256 v) {
        v = input;
    
        // swap bytes
        v = ((v & 0xFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00) >> 8) |
            ((v & 0x00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF) << 8);
    
        // swap 2-byte long pairs
        v = ((v & 0xFFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000) >> 16) |
            ((v & 0x0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF) << 16);
    
        // swap 4-byte long pairs
        v = ((v & 0xFFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000) >> 32) |
            ((v & 0x00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF) << 32);
    
        // swap 8-byte long pairs
        v = ((v & 0xFFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF0000000000000000) >> 64) |
            ((v & 0x0000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF) << 64);
    
        // swap 16-byte long pairs
        v = (v >> 128) | (v << 128);
    }

    function clear()public   {
        headerHash =0;
        returned = "";
    } 
    
    function getBridge()private pure returns(Bridge) {
        return Bridge(address(0x01000006));
    }
    
    function getBtcBlockchainBestChainHeight()public {
        bestChainHeight = getBridge().getBtcBlockchainBestChainHeight();
    }
    
    // getBtcBlockchainBlockHashAtDepth:// This method throws an OOG because getBtcBlockchainBlockHashAtDepth() cannot be called// from a contract. Use getBtcBlockchainBestChainHeigh() and getBtcBlockchainBlockHeaderByHeight()// 
    function storeBtcBlockchainBlockHashAtDepth(int256 depth)public  {
      returned  = getBridge().getBtcBlockchainBlockHashAtDepth(depth); 
    }
    
    function getHeaderHash(bytes memory x)private pure returns(bytes32) {
        bytes32h= sha256(x);
        bytes32h2= sha256(abi.encodePacked(h));
        return bytes32(reverse(uint256(h2))); // to show it like Bitcoin does on the debugger 
    }
    
    function computeHeaderHash()private {
        headerHash = getHeaderHash(returned);
    }
    
    function storeBtcBlockchainBestBlockHeader(  ) external {
        returned = getBridge().getBtcBlockchainBestBlockHeader();
        computeHeaderHash();
    }
    
    function getBtcBlockchainBestBlockHeader(  ) external view returns(bytes memory) {
        return getBridge().getBtcBlockchainBestBlockHeader();
   
    }
    
    function storeBtcBlockchainBlockHeaderByHash( bytes32 btcBlockHash ) external {
        returned = getBridge().getBtcBlockchainBlockHeaderByHash ( btcBlockHash );
        computeHeaderHash();
    }
    
    function getBtcBlockchainBlockHeaderByHash( bytes32 btcBlockHash ) external view returns(bytes memory) {
        return  getBridge().getBtcBlockchainBlockHeaderByHash ( btcBlockHash );
    }
    
    function storeBtcBlockchainBlockHeaderByHeight( uint256 btcBlockHeight ) external {
        returned = getBridge().getBtcBlockchainBlockHeaderByHeight (btcBlockHeight);
        computeHeaderHash();
    }
    
    function getBtcBlockchainBlockHeaderByHeight( uint256 btcBlockHeight ) external view returns(bytes memory ret) {
        return getBridge().getBtcBlockchainBlockHeaderByHeight (btcBlockHeight);
    }
    
    function storeBtcBlockchainParentBlockHeaderByHash( bytes32 btcBlockHash ) external {
        returned = getBridge().getBtcBlockchainParentBlockHeaderByHash ( btcBlockHash);
        computeHeaderHash();
    }
    
    function getBtcBlockchainParentBlockHeaderByHash( bytes32 btcBlockHash ) external view returns(bytes memory ret) {
        return  getBridge().getBtcBlockchainParentBlockHeaderByHash ( btcBlockHash);
    }
    
    function testGetParentParentHeader()public view returns(bytes memory ret) {
        bytes memoryx= getBridge().getBtcBlockchainBlockHeaderByHeight (2064695);
        bytes32h=getHeaderHash(x);    
        
        // now the has has been computed. Use the has to get the parent block header
        ret=getBridge().getBtcBlockchainParentBlockHeaderByHash ( h);
    }
    
    function testStoreGetParentHeader()public {
        returned = getBridge().getBtcBlockchainBlockHeaderByHeight (2064695);
        computeHeaderHash();    
        
        // now the has has been computed. Use the has to get the parent block header
        returned = getBridge().getBtcBlockchainParentBlockHeaderByHash ( headerHash);
        computeHeaderHash();
    }
}

Solution 2:

RSKIP220 has indeed been been included in the IRIS 3.0.0 release of RSKj, and you can see part of the implementation:

Here in RepositoryBtcBlockStoreWithCache

public StoredBlock getStoredBlockAtMainChainHeight(int height)throws BlockStoreException {

... and here in BridgeMethods

    GET_BTC_BLOCKCHAIN_PARENT_BLOCK_HEADER_BY_HASH(
            CallTransaction.Function.fromSignature(
                    "getBtcBlockchainParentBlockHeaderByHash",
                    newString[]{"bytes32"},
                    newString[]{"bytes"}

Note that these methods are not exposed externally through a special RPC or something similar to that. Instead they are available on the RSK Bridge, via precompiled functions. Precompiled functions are functions that are included within the implementation of the RSK node itself, but exposed as if they were a smart contract.

This means that you can interact with them both

  • off-chain, for example in a DApp, using web3.js, ethers.js, etc
  • on-chain, for example within your own smart contract

To do so, you can use the ABI for the RSK Bridge:

{"name":"getBtcBlockchainParentBlockHeaderByHash","type":"function","constant":true,"inputs":[{"name":"btcBlockHash","type":"bytes32"}],"outputs":[{"name":"","type":"bytes"}]},

Post a Comment for "Is There A Way For Smart Contract On Rsk To Fetch On-chain Data From Bitcoin Network Without Using Oracles?"