5

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) which will be implemented from the IRIS upgrade but couldn't find any resources on it. Does anybody know where I can find it?

Blog post mentioning RSKIP220: https://blog.rsk.co/noticia/iris-v3-0-0-is-here-what-you-need-to-know-about-rsk-upcoming-network-upgrade/

bguiz
  • 27,371
  • 47
  • 154
  • 243
Jack
  • 121
  • 4

2 Answers2

5

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;

interface Bridge {
  function getBtcBlockchainBestChainHeight (  ) external view returns ( int );
  function getStateForBtcReleaseClient (  ) external view returns ( bytes memory);
  function getStateForDebugging (  ) external view returns ( bytes memory  );
  function getBtcBlockchainInitialBlockHeight (  ) external view returns ( int );
  function getBtcBlockchainBlockHashAtDepth ( int256 depth ) external view returns ( bytes memory );
  function getBtcTxHashProcessedHeight ( string calldata hash ) external view returns ( int64 );
  function isBtcTxHashAlreadyProcessed ( string calldata hash ) external view returns ( bool );
  function getFederationAddress (  ) external view returns ( string memory );
  function registerBtcTransaction ( bytes calldata atx, int256 height, bytes calldata pmt ) external;
  function addSignature ( bytes calldata pubkey, bytes[] calldata signatures, bytes calldata txhash ) external;
  function receiveHeaders ( bytes[] calldata blocks ) external;
  function receiveHeader ( bytes calldata ablock ) external returns ( int256 );
  function getFederationSize (  ) external view returns ( int256 );
  function getFederationThreshold (  ) external view returns ( int256 );
  function getFederatorPublicKey ( int256 index ) external view returns ( bytes memory);
  function getFederatorPublicKeyOfType ( int256 index, string calldata atype ) external returns ( bytes memory);
  function getFederationCreationTime (  ) external view returns ( int256 );
  function getFederationCreationBlockNumber (  ) external view returns ( int256 );
  function getRetiringFederationAddress (  ) external view returns ( string memory );
  function getRetiringFederationSize (  ) external view returns ( int256 );
  function getRetiringFederationThreshold (  ) external view returns ( int256 );
  function getRetiringFederatorPublicKey ( int256 index ) external view returns ( bytes memory);
  function getRetiringFederatorPublicKeyOfType ( int256 index,string calldata atype ) external view returns ( bytes memory);
  function getRetiringFederationCreationTime (  ) external view returns ( int256 );
  function getRetiringFederationCreationBlockNumber (  ) external view returns ( int256 );
  function createFederation (  ) external returns ( int256 );
  function addFederatorPublicKey ( bytes calldata  key ) external returns ( int256 );
  function addFederatorPublicKeyMultikey ( bytes calldata btcKey, bytes calldata rskKey, bytes calldata mstKey ) external returns ( int256 );
  function commitFederation ( bytes calldata hash ) external returns ( int256 );
  function rollbackFederation (  ) external returns ( int256 );
  function getPendingFederationHash (  ) external view returns ( bytes memory);
  function getPendingFederationSize (  ) external view  returns ( int256 );
  function getPendingFederatorPublicKey ( int256 index ) external view returns ( bytes memory);
  function getPendingFederatorPublicKeyOfType ( int256 index, string calldata atype ) external view returns ( bytes memory);
  function getLockWhitelistSize (  ) external view returns ( int256 );
  function getLockWhitelistAddress ( int256 index ) external view returns ( string memory);
  function getLockWhitelistEntryByAddress ( string calldata aaddress ) external view  returns ( int256 );
  function addLockWhitelistAddress ( string calldata aaddress, int256 maxTransferValue ) external returns ( int256 );
  function addOneOffLockWhitelistAddress ( string calldata aaddress, int256 maxTransferValue ) external returns ( int256 );
  function addUnlimitedLockWhitelistAddress ( string calldata aaddress ) external returns ( int256 ); 
  function removeLockWhitelistAddress ( string calldata aaddress ) external returns ( int256 );
  function setLockWhitelistDisableBlockDelay ( int256 disableDelay ) external returns ( int256 );
  function getFeePerKb (  ) external view returns ( int256 );
  function voteFeePerKbChange ( int256 feePerKb ) external returns ( int256 );
  function updateCollections (  ) external;
  function getMinimumLockTxValue (  ) external view returns ( int256 );
  function getBtcTransactionConfirmations ( bytes32  txHash, bytes32 blockHash, uint256 merkleBranchPath, bytes32[] calldata merkleBranchHashes ) external view returns ( int256 );
  function getLockingCap (  ) external view returns ( int256 );
  function increaseLockingCap ( int256 newLockingCap ) external returns ( bool );
  function registerBtcCoinbaseTransaction ( bytes calldata btcTxSerialized, bytes32 blockHash, bytes calldata pmtSerialized, bytes32 witnessMerkleRoot, bytes32 witnessReservedValue ) external;
  function hasBtcBlockCoinbaseTransactionInformation ( bytes32 blockHash ) external returns ( bool );
  function registerFastBridgeBtcTransaction ( bytes calldata btcTxSerialized, uint256 height, bytes calldata pmtSerialized, bytes32 derivationArgumentsHash, bytes calldata userRefundBtcAddress, address liquidityBridgeContractAddress, bytes calldata liquidityProviderBtcAddress, bool shouldTransferToContract ) external returns ( int256 );
  function getActiveFederationCreationBlockHeight (  ) external view  returns ( uint256 );
  function getBtcBlockchainBestBlockHeader (  ) external view  returns ( bytes memory );
  function getBtcBlockchainBlockHeaderByHash ( bytes32 btcBlockHash ) external view returns ( bytes memory );
  function getBtcBlockchainBlockHeaderByHeight ( uint256 btcBlockHeight ) external view  returns ( bytes memory );
  function getBtcBlockchainParentBlockHeaderByHash ( bytes32 btcBlockHash ) external view  returns ( bytes memory);
}

QueryDemo.sol:

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

import "./Bridge.sol";

contract QueryDemo {
    int public 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) {
        bytes32 h = sha256(x);
        bytes32 h2 = 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 memory x = getBridge().getBtcBlockchainBlockHeaderByHeight (2064695);
        bytes32  h =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();
    }
}
Sergio Lerner
  • 216
  • 1
  • 4
  • Thanks. Do you know if a contract importing Bridge contract interface can extract a couple of data such as difficulty, number of txs, and timestamp from the latest bitcoin block? – Jack Aug 14 '21 at 09:14
  • 1
    Difficulty yes. Timestamp yes. Number of Txs requires that you show a Merkle path with a leaf node that is bigger than 64 bytes. That will provide the Merkle tree depth, and from that you can infer the number of transactions (bounded by a power of 2). There are other ways to obtain the exact number, by showing the depth and two right-most elements with the same hash. – Sergio Lerner Aug 15 '21 at 23:33
4

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",
                    new String[]{"bytes32"},
                    new String[]{"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"
      }
    ]
  },
bguiz
  • 27,371
  • 47
  • 154
  • 243