Skip to content

Commit

Permalink
Initial Commit
Browse files Browse the repository at this point in the history
  • Loading branch information
jonathan committed Jul 7, 2021
0 parents commit de0afbc
Show file tree
Hide file tree
Showing 67 changed files with 5,276 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules/
build/
dist/
.idea/
package-lock.json
8 changes: 8 additions & 0 deletions .solhint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"extends": "solhint:recommended",
"rules": {
"compiler-version": ["error","^0.6.12"],
"mark-callable-contracts": ["off"],
"reason-string": ["warn",{"maxLength":32}]
}
}
1 change: 1 addition & 0 deletions .solhintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
contracts/0x
40 changes: 40 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
### Rif Relay Contracts

This project is part of the rif relay ecosystem, it contains all the contracts that
the rif relay system use.

#### How to deploy contracts

To deploy you can use 2 ways:

1. Configure the `truffle.js` file on the root of the project to set
your network and later run `npx truffle migrate --network <YOUR_NETWORK>`.

2. Configure the `truffle.js` file on the root of the project to set the rsk
network and then run `npm run deploy <YOUR_NETWORK>`.

That will start the migration, at the end you should see a summary with all the
contract addresses.

#### How to compile contracts

Just run `npx truffle compile` at the root of the project and that will compile the
contracts and generate a folder build with all the compiled contracts inside.

#### How to generate a dist version
Run this command `npm run dist` to generate the dist folder with the distributable
version inside.

#### How to use the contracts as library

To use this project as a dependency you can install it on your project like any
other dependency like this `npm i --save @rsksmart/rif-relay-contracts`. That will
provide you with a way to get the contracts and interfaces. You can use them
like this:

```javascript
import {RelayHub, IForwarder} from '@rsksmart/rif-relay-contracts';

const relayHubContractAbi = RelayHub.abi;
const iForwarderAbi = IForwarder.abi;
```
25 changes: 25 additions & 0 deletions contracts/Migrations.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// SPDX-License-Identifier:MIT
pragma solidity ^0.6.12;

contract Migrations {
address public owner;
// solhint-disable-next-line var-name-mixedcase
uint public last_completed_migration;

constructor() public {
owner = msg.sender;
}

modifier restricted() {
if (msg.sender == owner) _;
}

function setCompleted(uint completed) public restricted {
last_completed_migration = completed;
}

function upgrade(address newAddress) public restricted {
Migrations upgraded = Migrations(newAddress);
upgraded.setCompleted(last_completed_migration);
}
}
90 changes: 90 additions & 0 deletions contracts/Penalizer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// SPDX-License-Identifier:MIT
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;

import "@openzeppelin/contracts/cryptography/ECDSA.sol";

import "./utils/RLPReader.sol";
import "./utils/RSKAddrValidator.sol";
import "./interfaces/IRelayHub.sol";
import "./interfaces/IPenalizer.sol";

contract Penalizer is IPenalizer{

string public override versionPenalizer = "2.0.1+enveloping.penalizer.ipenalizer";

mapping(bytes32 => bool) public penalizedTransactions;

using ECDSA for bytes32;

function decodeTransaction(bytes memory rawTransaction) private pure returns (Transaction memory transaction) {
(transaction.nonce,
transaction.gasPrice,
transaction.gasLimit,
transaction.to,
transaction.value,
transaction.data) = RLPReader.decodeTransaction(rawTransaction);
return transaction;
}

modifier relayManagerOnly(IRelayHub hub) {
require(hub.isRelayManagerStaked(msg.sender), "Unknown relay manager");
_;
}

function penalizeRepeatedNonce(
bytes memory unsignedTx1,
bytes memory signature1,
bytes memory unsignedTx2,
bytes memory signature2,
IRelayHub hub
)
public
override
relayManagerOnly(hub)
{
// Can be called by a relay manager only.
// If a relay attacked the system by signing multiple transactions with the same nonce
// (so only one is accepted), anyone can grab both transactions from the blockchain and submit them here.
// Check whether unsignedTx1 != unsignedTx2, that both are signed by the same address,
// and that unsignedTx1.nonce == unsignedTx2.nonce.
// If all conditions are met, relay is considered an "offending relay".
// The offending relay will be unregistered immediately, its stake will be forfeited and given
// to the address who reported it (msg.sender), thus incentivizing anyone to report offending relays.
// If reported via a relay, the forfeited stake is split between
// msg.sender (the relay used for reporting) and the address that reported it.

bytes32 txHash1 = keccak256(abi.encodePacked(unsignedTx1));
bytes32 txHash2 = keccak256(abi.encodePacked(unsignedTx2));

// check that transactions were not already penalized
require(!penalizedTransactions[txHash1] || !penalizedTransactions[txHash2], "Transactions already penalized");

address addr1 = txHash1.recover(signature1);
address addr2 = txHash2.recover(signature2);

require(addr1 == addr2, "Different signer");
require(RSKAddrValidator.checkPKNotZero(addr1), "ecrecover failed");

Transaction memory decodedTx1 = decodeTransaction(unsignedTx1);
Transaction memory decodedTx2 = decodeTransaction(unsignedTx2);

// checking that the same nonce is used in both transaction, with both signed by the same address
// and the actual data is different
// note: we compare the hash of the tx to save gas over iterating both byte arrays
require(decodedTx1.nonce == decodedTx2.nonce, "Different nonce");

bytes memory dataToCheck1 =
abi.encodePacked(decodedTx1.data, decodedTx1.gasLimit, decodedTx1.to, decodedTx1.value);

bytes memory dataToCheck2 =
abi.encodePacked(decodedTx2.data, decodedTx2.gasLimit, decodedTx2.to, decodedTx2.value);

require(keccak256(dataToCheck1) != keccak256(dataToCheck2), "tx is equal");

penalizedTransactions[txHash1] = true;
penalizedTransactions[txHash2] = true;

hub.penalize(addr1, msg.sender);
}
}
Loading

0 comments on commit de0afbc

Please sign in to comment.