Skip to content
This repository has been archived by the owner on Jun 21, 2024. It is now read-only.

Commit

Permalink
add basic fee model to incentivize relayers
Browse files Browse the repository at this point in the history
Signed-off-by: Gregory Hill <[email protected]>
  • Loading branch information
gregdhill committed Aug 26, 2020
1 parent bace76b commit 7754f2f
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 23 deletions.
3 changes: 1 addition & 2 deletions contracts/IRelay.sol
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ interface IRelay {
* @param proof Merkle proof
* @param confirmations Required confirmations (insecure)
* @param insecure Check custom inclusion confirmations
* @return True if txid is included, false otherwise
*/
function verifyTx(
uint32 height,
Expand All @@ -75,5 +74,5 @@ interface IRelay {
bytes calldata proof,
uint256 confirmations,
bool insecure
) external view returns (bool);
) external payable;
}
40 changes: 29 additions & 11 deletions contracts/Relay.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@

pragma solidity ^0.6.0;

import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';
import {SafeMath} from '@openzeppelin/contracts/math/SafeMath.sol';
import {BytesLib} from '@interlay/bitcoin-spv-sol/contracts/BytesLib.sol';
import {BTCUtils} from '@interlay/bitcoin-spv-sol/contracts/BTCUtils.sol';
import {ValidateSPV} from '@interlay/bitcoin-spv-sol/contracts/ValidateSPV.sol';
import {IRelay} from './IRelay.sol';

/// @title BTC Relay
contract Relay is IRelay {
contract Relay is IRelay, Ownable {
using SafeMath for uint256;
using BytesLib for bytes;
using BTCUtils for bytes;
Expand All @@ -19,6 +20,8 @@ contract Relay is IRelay {
uint32 height;
// identifier of chain fork
uint64 chainId;
// relayer account
address payable author;
}

// mapping of block hashes to block headers (ALL ever submitted, i.e., incl. forks)
Expand Down Expand Up @@ -56,6 +59,8 @@ contract Relay is IRelay {
uint256 public constant MAIN_CHAIN_ID = 0;
uint256 public constant CONFIRMATIONS = 6;

uint256 public verifyCostWei = 1000;

// EXCEPTION MESSAGES
// OPTIMIZATION: limit string length to 32 bytes
string
Expand All @@ -76,13 +81,14 @@ contract Relay is IRelay {
string internal constant ERR_CONFIRMS = 'Insufficient confirmations';
string internal constant ERR_VERIFY_TX = 'Incorrect merkle proof';
string internal constant ERR_INVALID_TXID = 'Invalid tx identifier';
string internal constant ERR_INSUFFICIENT_PAYMENT = 'Insufficient payment';

/**
* @notice Initializes the relay with the provided block.
* @param header Genesis block header
* @param height Genesis block height
*/
constructor(bytes memory header, uint32 height) public {
constructor(bytes memory header, uint32 height) public Ownable() {
require(header.length == 80, ERR_INVALID_HEADER_SIZE);
require(height > 0, ERR_INVALID_GENESIS_HEIGHT);
bytes32 digest = header.hash256();
Expand All @@ -101,13 +107,20 @@ contract Relay is IRelay {
_epochEndTarget = target;
_epochEndTime = timestamp;

_storeBlockHeader(digest, height, chainId);
_storeBlockHeader(msg.sender, digest, height, chainId);
}

function setVerifyCostWei(uint256 price) external onlyOwner {
verifyCostWei = price;
}

/**
* @dev Core logic for block header validation
*/
function _submitBlockHeader(bytes memory header) internal virtual {
function _submitBlockHeader(address payable author, bytes memory header)
internal
virtual
{
require(header.length == 80, ERR_INVALID_HEADER_SIZE);

// Fail if block already exists
Expand Down Expand Up @@ -161,9 +174,9 @@ contract Relay is IRelay {
chainId = _incrementChainCounter();
_initializeFork(hashCurrBlock, hashPrevBlock, chainId, height);

_storeBlockHeader(hashCurrBlock, height, chainId);
_storeBlockHeader(author, hashCurrBlock, height, chainId);
} else {
_storeBlockHeader(hashCurrBlock, height, chainId);
_storeBlockHeader(author, hashCurrBlock, height, chainId);

if (chainId == MAIN_CHAIN_ID) {
_bestBlock = hashCurrBlock;
Expand All @@ -187,7 +200,7 @@ contract Relay is IRelay {
* @dev See {IRelay-submitBlockHeader}.
*/
function submitBlockHeader(bytes calldata header) external override {
_submitBlockHeader(header);
_submitBlockHeader(msg.sender, header);
}

/**
Expand All @@ -198,18 +211,20 @@ contract Relay is IRelay {

for (uint256 i = 0; i < headers.length / 80; i = i.add(1)) {
bytes memory header = headers.slice(i.mul(80), 80);
_submitBlockHeader(header);
_submitBlockHeader(msg.sender, header);
}
}

function _storeBlockHeader(
address payable author,
bytes32 digest,
uint32 height,
uint256 chainId
) internal {
_chain[height] = digest;
_headers[digest].height = height;
_headers[digest].chainId = uint64(chainId);
_headers[digest].author = author;
emit StoreHeader(digest, height);
}

Expand Down Expand Up @@ -371,7 +386,7 @@ contract Relay is IRelay {
bytes calldata proof,
uint256 confirmations,
bool insecure
) external override view returns (bool) {
) external override payable {
// txid must be little endian
require(txid != 0, ERR_INVALID_TXID);

Expand All @@ -382,9 +397,12 @@ contract Relay is IRelay {
}

require(_chain[height] == header.hash256(), ERR_BLOCK_NOT_FOUND);

// recoup fees
require(msg.value >= verifyCostWei, ERR_INSUFFICIENT_PAYMENT);
_headers[_chain[height]].author.transfer(msg.value);

bytes32 root = header.extractMerkleRootLE().toBytes32();
require(ValidateSPV.prove(txid, root, proof, index), ERR_VERIFY_TX);

return true;
}
}
9 changes: 6 additions & 3 deletions contracts/TestRelay.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ contract TestRelay is Relay {
/**
* @dev Override to remove the difficulty check
*/
function _submitBlockHeader(bytes memory header) internal override {
function _submitBlockHeader(address payable author, bytes memory header)
internal
override
{
require(header.length == 80, ERR_INVALID_HEADER_SIZE);

bytes32 hashPrevBlock = header.extractPrevBlockLE().toBytes32();
Expand Down Expand Up @@ -62,9 +65,9 @@ contract TestRelay is Relay {
chainId = _incrementChainCounter();
_initializeFork(hashCurrBlock, hashPrevBlock, chainId, height);

_storeBlockHeader(hashCurrBlock, height, chainId);
_storeBlockHeader(author, hashCurrBlock, height, chainId);
} else {
_storeBlockHeader(hashCurrBlock, height, chainId);
_storeBlockHeader(author, hashCurrBlock, height, chainId);

if (chainId == MAIN_CHAIN_ID) {
_bestBlock = hashCurrBlock;
Expand Down
2 changes: 1 addition & 1 deletion test/gas.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ describe('Gas', () => {
await relay.deployTransaction.wait(1)
).gasUsed?.toNumber();
// console.log(`Deploy: ${deployCost}`);
expect(deployCost).to.be.lt(2_000_000);
expect(deployCost).to.be.lt(2_300_000);

const result = await relay.submitBlockHeader(header1);
const updateCost = (await result.wait(1)).gasUsed?.toNumber();
Expand Down
21 changes: 15 additions & 6 deletions test/proof.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ async function getBestBlockHeight(relay: Relay): Promise<number> {
return height;
}

const verifyCostWei = 1000;

function deploy(
signer: Signer,
header: Arrayish,
Expand Down Expand Up @@ -107,7 +109,8 @@ describe('Proofs', () => {
genesisHeader,
tx.intermediateNodes,
0,
true
true,
{value: verifyCostWei}
);
});

Expand All @@ -124,7 +127,9 @@ describe('Proofs', () => {
'hex'
).reverse();

await relay.verifyTx(height, 0, txId, header, [], 0, true);
await relay.verifyTx(height, 0, txId, header, [], 0, true, {
value: verifyCostWei
});
});

const testnet1 = {
Expand Down Expand Up @@ -162,7 +167,8 @@ describe('Proofs', () => {
testnet1.header,
'0x' + proof,
0,
true
true,
{value: verifyCostWei}
);
});

Expand Down Expand Up @@ -201,7 +207,8 @@ describe('Proofs', () => {
testnet2.header,
'0x' + proof,
0,
true
true,
{value: verifyCostWei}
);
});

Expand Down Expand Up @@ -239,7 +246,8 @@ describe('Proofs', () => {
testnet3.header,
'0x' + proof,
0,
true
true,
{value: verifyCostWei}
);
});

Expand Down Expand Up @@ -279,7 +287,8 @@ describe('Proofs', () => {
'0x' + mainnet1.header,
'0x' + proof,
0,
true
true,
{value: verifyCostWei}
);
});
});

0 comments on commit 7754f2f

Please sign in to comment.