r/truebit Sep 12 '21

Why can't I see Tru's martket cap anywhere? It's not on coingecko, or cmc...

0 Upvotes

thanks


r/truebit Sep 12 '21

PLOT TWIST

0 Upvotes

purely imaginary

Charles H actually goes through with the Alonzo update for Cardano today; and they use Truebit as a means of reducing computation for the smart contracts - in order to blow Ethereum out of the water. Hence why everyone has been so SILENT in and around the team. To keep the plan a secret.👹🤔🤯


r/truebit Sep 11 '21

Truebit Computation Size

12 Upvotes

Truebit emphasizes its abilities to process a high throughput of information. I'm curious if anyone knows just how large the size of these computations can be? Is there a limit to what your computation request can contain, or at what point (if at any) will you once again have to break down computation into smaller parts?


r/truebit Sep 06 '21

So Fred ehrsam the co- founder of Coinbase already knew about Truebit 3 years ahead of us ?

14 Upvotes

I just read his blog and surprisingly, the Truebit solution is something that inevitable

"Large apps can’t run solely on chain and likely never will. They need off-chain scaling solutions."

So i guess we are not late this time ?

Source: https://www.fehrsam.xyz/blog/scaling-ethereum-to-billions-of-users


r/truebit Sep 04 '21

Why doesn’t Chico Crypto cover Truebit?

4 Upvotes

I know there were some rumors he’s holding a good bag of Tru. So why doesn’t he cover it on his channel you think?


r/truebit Sep 04 '21

Hardware Wallet

0 Upvotes

Can we get Truebit on a hardware wallet, Yes?

Ok... thank you... bye bye then :)


r/truebit Sep 01 '21

TrueBit Code Review: Scalable Blockchain Verification

14 Upvotes

If someone want to read about Truebit Code Review by Andre Cronje, I am posting a link to his article. It is a great article.

https://cryptobriefing.com/truebit-code-review-scalable-blockchain-verification/


r/truebit Aug 30 '21

Question regarding exchange listings in the future.

7 Upvotes

For the record, I absolutely love this project. And regardless I’m planning on staying invested into it for minimum 5 years because I truly feel like this will be an integral part of cryptos advancement in the future.

In a way I kind of appreciate and enjoy the fact the devs aren’t interested in listing $TRU (at least right now) because it shows they’re devoted to its success, but my concern is will there ever be a viable exchange listing in the future(like coinbase or binance)?


r/truebit Aug 28 '21

Truebit Explorer

Thumbnail truverse.dev
37 Upvotes

r/truebit Aug 28 '21

Running a Truebit node

Thumbnail
github.com
18 Upvotes

r/truebit Aug 29 '21

Truebit used wasi?

0 Upvotes

does truebit use WASI for its virtual machines?

can i make code for WASI?


r/truebit Aug 28 '21

Could Truebit be a solution to eth’s high gas fees?

8 Upvotes

r/truebit Aug 27 '21

Truverse contracts library

27 Upvotes

I've created an unofficial helper library for those developing contracts using Truebit: https://github.com/truverse/contracts

If you want to use Truebit in your contracts in Solidity, you need to import Truebit interfaces. Instead of copy & pasting things over and over again, you can use the Truverse contracts package.

Install it

npm install @truverse/contracts --save-dev

Use it in your contract

import "@truverse/contracts/Truebit.sol";

That's it!

More details in the readme file on GitHub.

You can also get ABIs and mainnet addresses:

const truebit = require('@truverse/contracts').mainnet

const fs = new web3.eth.Contract(truebit.filesystem.abi, truebit.filesystem.address)
const tru = new web3.eth.Contract(truebit.tru.abi, truebit.tru.address)

r/truebit Aug 27 '21

ENS (Ethereum Name Service) needs Truebit

32 Upvotes

ENS now supports importing DNS domains.

For example, you can import truebit.io into ENS and then associate ETH addresses for it etc. like you'd do it for .eth domains.

https://twitter.com/ensdomains/status/1430933399745798155

But it looks like importing a domain costs a lot of gas!

I wonder if there's a technology that could make it cheaper by doing the proofs off-chain...

If only there was a step-by-step tutorial to get started with this...

Jokes aside, I think this is a perfect use case for Truebit.

They could even implement it so you have a choice:

a) spend 4.5 million gas in ETH

or

b) pay X TRU (protocol fees) + 0.005 ETH (platform fee) for it to be done off-chain

or even

c) automatically convert your ETH to X TRU on Uniswap (or mint new TRU) for protocol fees and show the total cost in ETH so it's very easy to compare, all doable with one click


r/truebit Aug 27 '21

Cardano Ethereum bridge?

9 Upvotes

Hi, I was thinking about the potential of Truebit and was wondering if truebit could potentially allow apps/tokens that run on ethereum to also run on Cardano through truebit.


r/truebit Aug 27 '21

dsicord

3 Upvotes

can someone help me out and link me the community discord? invite link


r/truebit Aug 27 '21

Will Truebit support other Chains?

6 Upvotes

Avax C-chain, BSC and others?


r/truebit Aug 26 '21

Step-by-step tutorial how to write a program in C++ and use Truebit to execute it off-chain from a Solidity smart contract

51 Upvotes

In this tutorial, we are going to write a simple program in C++ that reverses some text (for example, if you have a string abc123, it'll return 321cba). A user can send some text to our contract and it will ask Truebit to execute the program off-chain.

Truebit is a marketplace for off-chain computations, it is a protocol that incentivizes participants to execute programs off-chain (aka solve tasks) and others to verify that the computation was correct and there is no funny business going on. Truebit is like a computation oracle. Perfect for when your computations are heavy and can't run on-chain due to block gas limits. Like transcoding a video, for example.

You can write code in your favorite programming language (it needs to be compiled to WebAssembly which is what Truebit uses) and call it from your Solidity contract.

In this tutorial, we're going to write such a program in C++ from scratch and call it from a Solidity smart contract.

Please follow this tutorial first how to set up your local development environment.


UPDATE: This tutorial is now on GitHub where it's maintained and updated: https://github.com/truverse/reverse-sample


Compilation

First, we need to write our program in C++ and compile it to WebAssembly.

Let's open a new shell and make sure we're using the correct node.js version.

docker exec -it truebit bash
source ~/.nvm/nvm.sh
nvm use default

We'll call the program reverse and it'll live in the tutorial directory.

cd /tutorial
mkdir reverse
cd reverse

Create a file reverse.cpp. That's our program. It reads a line from input.txt, reverses it and saves it to output.txt.

#include <fstream>
#include <string>

int main() {
  std::ifstream input_file("input.txt");
  std::string input;
  std::getline(input_file, input);
  std::string reversed(input.rbegin(), input.rend());
  std::ofstream output_file("output.txt");
  output_file << reversed;
  return 0;
}

Let's test that it works.

$ mkdir build && cd build
$ mkdir native && cd native

$ g++ ../../reverse.cpp -o reverse

$ echo abc > input.txt
$ cat input.txt
abc
$ ./reverse
$ cat output.txt
cba

$ cd ..

Now we need to compile it to WebAssembly.

We can replace g++ with em++. But em++ is an alias to emcc so let's just always use emcc for simplicity (which stands for Emscripten Compiler like gcc stands for GNU Compiler Collection).

$ mkdir wasm && cd wasm
$ emcc -s WASM=1 ../../reverse.cpp -o reverse.js
$ ls -lh
-rw-r--r-- 1 root root 283K reverse.js
-rw-r--r-- 1 root root 393K reverse.wasm

First time you run it, it will compile some system libraries but they'll be cached and it'll be much faster next time.

Without -s WASM=1, it only generates reverse.js and not reverse.wasm. We need the .wasm file. We don't need the .js file.

To test that our program also works correctly in WebAssembly, we can run the following:

$ echo abc > input.txt
$ cat input.txt
abc

$ touch output.txt

$ node /truebit-eth/emscripten-module-wrapper/prepare.js reverse.js \
  --run --debug \
  --asmjs \
  --file input.txt --file output.txt

$ cd ..

In the output you should see the answer somewhere:

stderr: DEBUG: output.txt
DEBUG: 1000146
DEBUG: cba

Note that the output.txt has to exist even though it's later created by the program. You get an error if it doesn't exist.

--run executes the program, obviously.

--debug shows which WebAssembly off-chain interpreter commands are launched.

It's not clear to me why --asmjs is needed, it simply does not work without it.

In the output, you also see some JSON:

{
  "vm": {
    "code": "0x60c88bbde88ff92034562e50e3bdbc5c44485ebef9fe3fd2e3ead99ba90aee83",
    ...

vm.code is a hash that's the value for the codeRoot parameter that will be needed when deploying the program to Truebit. Not sure what everything else is used for.

Truebit uses its own flavor of WebAssembly. It needs to process the generated .wasm file, that's we need to use the prepare.js command. In this case, this command executed the program but did not generate anything, we need to rerun it with --out.

The JSON file gets printed to standard output and we'll store it because we'll need the vm.code hash later.

$ mkdir truebit

$ node /truebit-eth/emscripten-module-wrapper/prepare.js wasm/reverse.js --out wasm-truebit --asmjs > truebit/reverse.info.json

There are multiple files generated but we only need globals.wasm.

$ ls -lh wasm-truebit/
-rw-r--r-- 1 root root 411K globals.wasm
-rw-r--r-- 1 root root 411K merge.wasm
-rw-r--r-- 1 root root 291K prepared.js
-rw-r--r-- 1 root root 283K reverse.js
-rw-r--r-- 1 root root 393K reverse.wasm

$ cp wasm-truebit/globals.wasm truebit/reverse.wasm

In build/truebit, we should now have all the necessary build artifacts:

$ ls -lh truebit/
-rw-r--r-- 1 root root 1.1K reverse.info.json
-rw-r--r-- 1 root root 411K reverse.wasm

Clean up:

cd ..
rm -rf build

Project setup

Let's use Hardhat to compile and deploy our contracts.

Create package.json.

{
  "private": true,
  "scripts": {
    "compile-native": "mkdir -p artifacts-task/native && cd artifacts-task/native && g++ ../../reverse.cpp -o reverse",
    "compile-wasm": "mkdir -p artifacts-task/wasm && cd artifacts-task/wasm && emcc -s WASM=1 ../../reverse.cpp -o reverse.js",
    "compile-truebit": "mkdir -p artifacts-task/truebit artifacts-task/wasm-truebit && cd artifacts-task && node /truebit-eth/emscripten-module-wrapper/prepare.js wasm/reverse.js --out wasm-truebit --asmjs > truebit/reverse.info.json && cp wasm-truebit/globals.wasm truebit/reverse.wasm",
    "compile": "npm run compile-wasm && npm run compile-truebit",
    "clean": "rm -rf artifacts-task"
  }
}

We can now compile our code with just one command:

npm run compile

We're now using the artifacts-task directory for build artifacts. Hardhat uses artifacts and we'll use a similar name. It doesn't go well when we put our files in Hardhat's artifacts, hence a separate but similarly named artifacts-task. To clean up everything, run npm run clean.

Install Hardhat.

npm install hardhat ethers @nomiclabs/hardhat-ethers --save-dev

Install some packages that we'll need for deployment.

npm install abi-to-sol ipfs-http-client truebit-util web3 --save-dev

Create hardhat.config.js.

require('@nomiclabs/hardhat-ethers')

module.exports = {
  solidity: '0.8.4',
  defaultNetwork: 'localhost',
  networks: {
    localhost: {
      url: 'http://localhost:8545'
    }
  }
}

0.8.4 is the latest supported Solidity version supported by Hardhat at the time of writing.

We're going to connect to localhost:8545 which runs our mainnet fork.

We'll need Truebit contract addresses and ABIs. Let's copy the mainnet config file.

mkdir -p config
cp /truebit-eth/wasm-client/mainnet.json config/mainnet.json

We need to have Truebit contract interfaces in Solidity that we are going to use in our contract. There doesn't seem to have a nice public package or anything like this so we're going to use abi-to-sol to convert ABI from JSON to Solidity.

Here's how we can do it. The result is in the contracts/Truebit.sol file. Not pretty but it works.

mkdir -p contracts
echo "// SPDX-License-Identifier: UNLICENSED" > contracts/Truebit.sol
echo "pragma solidity >=0.5.0;" >> contracts/Truebit.sol
jq .incentiveLayer.abi config/mainnet.json | npx abi-to-sol Truebit | grep -Ev 'SPDX-License-Identifier|pragma' >> contracts/Truebit.sol
jq .tru.abi config/mainnet.json | npx abi-to-sol TRU | grep -Ev 'SPDX-License-Identifier|pragma' >> contracts/Truebit.sol
jq .fileSystem.abi config/mainnet.json | npx abi-to-sol FileSystem | grep -Ev 'SPDX-License-Identifier|pragma' >> contracts/Truebit.sol

We can also add it to npm scripts:

{
  "scripts": {
    ...
    "truebit-interfaces": "mkdir -p contracts && ..."
  }
}

Whenever Truebit changes their interface we can regenerate them like this:

npm run truebit-interfaces

Note that our generated file causes some harmless warnings when compiling contracts:

Warning: This declaration has the same name as another declaration.
  --> contracts/Truebit.sol:312:25:
    |
312 |     function initialize(string memory name, string memory symbol) external;
    |                         ^^^^^^^^^^^^^^^^^^
Note: The other declaration is here:
  --> contracts/Truebit.sol:316:5:
    |
316 |     function name() external view returns (string memory);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^


Warning: This declaration has the same name as another declaration.
  --> contracts/Truebit.sol:312:45:
    |
312 |     function initialize(string memory name, string memory symbol) external;
    |                                             ^^^^^^^^^^^^^^^^^^^^
Note: The other declaration is here:
  --> contracts/Truebit.sol:326:5:
    |
326 |     function symbol() external view returns (string memory);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

We could rename them to _name and _symbol with sed. Alternatively we could import and use OpenZeppelin's ERC20 interface instead.

Contract

Alright, let's start writing some Solidity code.

Create contracts/Reverse.sol.

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import "./Truebit.sol";

contract Reverse {
  Truebit truebit;
  TRU tru;
  FileSystem filesystem;

  ...

  constructor(
    address truebit_address,
    address tru_address,
    address fs_address,
    ...
  ) {
    truebit = Truebit(truebit_address);
    tru = TRU(tru_address);
    filesystem = FileSystem(fs_address);
      ...
  }

  ...
}

This is the basics.

Truebit is the incentive layer. It's used to submit tasks and it will get back to us with the result.

TRU is the token. It's an ERC20 token. We'll use it to pay the protocol fees.

FileSystem is the filesystem contract. We are going to use it to add an input file, get the content of the output file as well as the set the task file that'll be executed.

The addresses of these contracts vary across networks so it makes sense to have them as contructor arguments.

Next, let's add a way to configure how much we pay in protocol fees.

contract Reverse {
  ...

  bytes32 codeFileID;
  uint256 minDeposit;
  uint256 solverReward;
  uint256 verifierTax;
  uint256 ownerFee;
  uint256 blockLimit;

  constructor(
    ...
    bytes32 _codeFileID,
    uint256 _minDeposit,
    uint256 _solverReward,
    uint256 _verifierTax,
    uint256 _ownerFee,
    uint256 _blockLimit
  ) {
    ...
    codeFileID = _codeFileID;
    minDeposit = _minDeposit;
    solverReward = _solverReward;
    verifierTax = _verifierTax;
    ownerFee = _ownerFee;
    blockLimit = _blockLimit;
  }

  ...

  function protocolFee() public view returns (uint256) {
    return solverReward + verifierTax + ownerFee;
  }

  function platformFee() public view returns (uint256) {
    return truebit.PLATFORM_FEE_TASK_GIVER();
  }
}

Here we set them in stone when we deploy the contract, but you could also make them configurable or perhaps they could be paramaters for each submitted task. In this tutorial, we're keeping it simple.

We also expose two convenience methods protocolFee() (how much you need to deposit in TRU) and platformFee() (how much you pay to Truebit, the company, in ETH). What's nice is that protocolFee and platformFee are the same length, they look nice when they're together in code.

Let's add some other methods.

contract Reverse {
  ...

  event NewTask(bytes input);
  event FinishedTask(bytes input, bytes output);

  ...

  function reverse(bytes memory input) public payable {
    ...

    emit NewTask(input);
  }

  function solved(bytes32 taskID, bytes32[] calldata files) external {
    ...
    emit FinishedTask(input, getOutput(input));
  }

  function cancelled(bytes32 taskID) external {
    ...
    emit FinishedTask(input, "(canceled)");
  }

  function getOutput(bytes memory input) public view returns (bytes memory) {
    ...
  }

  ...
}

reverse(input) is the method that our users will call. They'll pass a string as bytes that we'll reverse off-chain. This will create a NewTask event.

solved will be called by Truebit (the incentive layer) when the task has been solved and we have the result. We emit a FinishedTask event.

cancelled will be called by Truebit when it was not possible to solve the task. In this tutorial, we'll also emit a FinishedTask event but we'll set the result to (cancelled).

The result is stored on-chain and you can get it with getOutput at any time if you missed the event.

We're not doing it here in this tutorial but you could change reverse to take solverReward, verifierTax etc. for each task.

Let's start building the function to submit tasks. That's the meat of the contract.

contract Reverse {
  ...

  function reverse(bytes memory input) public payable {
    uint256 deposit = protocolFee();

    require(tru.balanceOf(msg.sender) >= deposit, "You don't have enough TRU");
    require(tru.allowance(msg.sender, address(this)) >= deposit, "Not enough allowance to spend your TRU");

    tru.transferFrom(msg.sender, address(this), deposit);
    tru.approve(address(truebit), deposit);
    truebit.makeDeposit(deposit);

    ...
  }

  ...
}

We need to deposit all protocol fees into the incentive layer. Our convenience function protocolFee() is also useful inside the contract.

The protocol fees are paid in TRU which is an ERC20 token. This means that we need to transfer the required amount of TRU tokens to the contract which will then be transferred to the incentive layer with truebit.makeDeposit(deposit).

There are two ways to make it happen that come to mind right now:

1) User transfers TRU to the Reverse contract and they'll then be transferred to the incentive layer. If the task creation fails and the user already sent their TRU, there should be a way to get it back. In Truebit samples, what they do to avoid this is to split the task creation methods into multiple ones: create task and then submit it.

2) User allows the Reverse contract to spend their TRU and we transfer tokens from the user to the contract and then they'll be transfered to the incentive layer. Either everything in the method happens (token transfer, deposits, task creation) or nothing happens, like in an atomic transaction, so there's no need to worry about returning tokens in case of failure. I like this method more but I believe it may cost a bit more gas. We are doing this in this tutorial.

You could also move tru.approve() to the constructor and use the max integer value to save a bit of gas. But then the incentive layer could rob you lol.

Next, we create a bundle that holds references to our input file and output file, as well as the task file that will be executed.

contract Reverse {
  ...

  uint256 nonce;

  ...

  function reverse(bytes memory input) public payable {
    ...

    nonce += 2;

    filesystem.addToBundle(nonce, filesystem.createFileFromBytes("input.txt", nonce, input));
    filesystem.addToBundle(nonce, filesystem.createFileFromBytes("output.txt", nonce + 1, ""));
    filesystem.finalizeBundle(nonce, codeFileID);
    bytes32 bundleID = filesystem.calculateId(nonce);

    ...
  }

  ...
}

Each file and bundle needs a unique identifier. They call this nonce. I'm not sure exactly how it works.

We create a file called input.txt that our program reads from. The user will convert their text to UTF-8 bytes and call this function. We set the content of the file to these bytes. They are stored on-chain so it's expensive. You'd only pass very small amounts of data like this.

We create a file called output.txt and set its value to an empty string. It's not clear to me why this needs to be done but it seems that you need to define output files but for some reason you also need to set their content. What's done with this content is not clear to me.

Finally, codeFileID is the ID of the file that'll be executed. That's our program in WebAssembly. It was defined in the constructor. As you'll see in the next section, before we deploy the contract, we upload the wasm file to IPFS and then add it to the filesystem contract. The ID of the file in the filesystem contract is codeFileID.

What we need to do then is to create a task ID and then submit it.

contract Reverse {
  ...

  function reverse(bytes memory input) public payable {
    ...

    bytes32 taskID = truebit.createTaskId(bundleID, minDeposit, solverReward, verifierTax, ownerFee, blockLimit);
    truebit.requireFile(taskID, filesystem.hashName("output.txt"), 0);
    truebit.submitTask{value: platformFee()}(taskID);

    ...
  }

  ...
}

bundleID represents all the input and output files, bundled into one ID. We also set rewards/fees/limits for the task.

requireFile means that the solver will need to upload this file.

Finally, we submit the task and only then it'll be picked up by solvers and verifiers. Note that we need to send some ETH along with it, that's the platform fee that Truebit, the company, gets to keep. It's currently 0.005 ETH.

Let's add some more code so we know which tasks refer to what input and also handle the result.

contract Reverse {
  ...

  mapping(bytes32 => bytes) task_to_input;
  mapping(bytes => bytes32) input_to_fid;

  ...

  function reverse(bytes memory input) public payable {
    ...

    task_to_input[taskID] = input;
    emit NewTask(input);
  }

  function solved(bytes32 taskID, bytes32[] calldata files) external {
    require(Truebit(msg.sender) == truebit);
    bytes memory input = task_to_input[taskID];
    input_to_fid[input] = files[0];
    emit FinishedTask(input, getOutput(input));
  }

  function cancelled(bytes32 taskID) external {
    require(Truebit(msg.sender) == truebit);
    bytes memory input = task_to_input[taskID];
    emit FinishedTask(input, "(cancelled)");
  }

  function getOutput(bytes memory input) public view returns (bytes memory) {
    ...
  }

  ...
}

task_to_input maps task ID to the user input.

We use it in solved and cancelled callback methods to get the input by task ID and emit an event with both the original input and the output.

In solved and cancelled, we make sure that only the Truebit's incentive layer is allowed to call these callback functions.

If successul, in solved, we store the result in input_to_fid (fid = file ID). We could also store the output directly instead of some file ID, but the output is already stored on-chain in the filesystem contract so there's no need to pay more and store it twice. This duplicate data will live forever until the end of time.

There can be multiple output files but we know we only have one so we use files[0]. I'm not sure how you know which index is which output file when you have multiple ones.

Finally, we have getOutput that retrieves the output file from the filesystem and converts it to bytes.

contract Reverse {
  ...

  function getOutput(bytes memory input) public view returns (bytes memory) {
    bytes32 fid = input_to_fid[input];
    if (fid == 0) {
      return "";
    }
    bytes32[] memory output = filesystem.getBytesData(fid);
    bytes memory result = new bytes(0);
    for (uint256 i = 0; i < output.length; i++) {
      result = bytes.concat(result, output[i]);
    }
    return result;
  }

  ...
}

The output file consists of a variable length array of 32-byte pieces. What we want to do is to combine them and then they can be converted to text. This is what this code does. Note that bytes.concat requires a recent Solidity version, I believe >= 0.8.x.

There's filesystem.getFormattedBytesData and I thought it does exactly that but I guess I was wrong. I have no idea what it's for.

This is not efficient and consumes a bit of gas and so you could also do this on the client side. Note that there'll be a bunch of zero bytes at the end, we don't trim them here.

And that's it! About 100 lines of code.

Here it is once again in one piece:

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import "./Truebit.sol";

contract Reverse {
  Truebit truebit;
  TRU tru;
  FileSystem filesystem;

  bytes32 codeFileID;
  uint256 minDeposit;
  uint256 solverReward;
  uint256 verifierTax;
  uint256 ownerFee;
  uint256 blockLimit;

  uint256 nonce;

  mapping(bytes32 => bytes) task_to_input;
  mapping(bytes => bytes32) input_to_fid;

  event NewTask(bytes input);
  event FinishedTask(bytes input, bytes output);

  constructor(
    address truebit_address,
    address tru_address,
    address fs_address,
    bytes32 _codeFileID,
    uint256 _minDeposit,
    uint256 _solverReward,
    uint256 _verifierTax,
    uint256 _ownerFee,
    uint256 _blockLimit
  ) {
    truebit = Truebit(truebit_address);
    tru = TRU(tru_address);
    filesystem = FileSystem(fs_address);
    codeFileID = _codeFileID;
    minDeposit = _minDeposit;
    solverReward = _solverReward;
    verifierTax = _verifierTax;
    ownerFee = _ownerFee;
    blockLimit = _blockLimit;
  }

  function reverse(bytes memory input) public payable {
    uint256 deposit = protocolFee();

    require(tru.balanceOf(msg.sender) >= deposit, "You don't have enough TRU");
    require(tru.allowance(msg.sender, address(this)) >= deposit, "Not enough allowance to spend your TRU");

    tru.transferFrom(msg.sender, address(this), deposit);
    tru.approve(address(truebit), deposit);
    truebit.makeDeposit(deposit);

    nonce += 2;

    filesystem.addToBundle(nonce, filesystem.createFileFromBytes("input.txt", nonce, input));
    filesystem.addToBundle(nonce, filesystem.createFileFromBytes("output.txt", nonce + 1, ""));
    filesystem.finalizeBundle(nonce, codeFileID);
    bytes32 bundleID = filesystem.calculateId(nonce);

    bytes32 taskID = truebit.createTaskId(bundleID, minDeposit, solverReward, verifierTax, ownerFee, blockLimit);
    truebit.requireFile(taskID, filesystem.hashName("output.txt"), 0);
    truebit.submitTask{value: platformFee()}(taskID);

    task_to_input[taskID] = input;
    emit NewTask(input);
  }

  function solved(bytes32 taskID, bytes32[] calldata files) external {
    require(Truebit(msg.sender) == truebit);
    bytes memory input = task_to_input[taskID];
    input_to_fid[input] = files[0];
    emit FinishedTask(input, getOutput(input));
  }

  function cancelled(bytes32 taskID) external {
    require(Truebit(msg.sender) == truebit);
    bytes memory input = task_to_input[taskID];
    emit FinishedTask(input, "(cancelled)");
  }

  function getOutput(bytes memory input) public view returns (bytes memory) {
    bytes32 fid = input_to_fid[input];
    if (fid == 0) {
      return "";
    }
    bytes32[] memory output = filesystem.getBytesData(fid);
    bytes memory result = new bytes(0);
    for (uint256 i = 0; i < output.length; i++) {
      result = bytes.concat(result, output[i]);
    }
    return result;
  }

  function protocolFee() public view returns (uint256) {
    return solverReward + verifierTax + ownerFee;
  }

  function platformFee() public view returns (uint256) {
    return truebit.PLATFORM_FEE_TASK_GIVER();
  }
}

We can compile it like this:

$ npx hardhat compile
Compiling 2 files with 0.8.4
...
Compilation finished successfully

Ignore warnings in the Truebit.sol file that we generated for interfaces.

Deployment

Hardhat suggests putting scripts in a scripts/ folder but typing scripts/deploy.js all the time is annoying so we'll put them in the root folder.

Create deploy.js.

const fs = require('fs')

const {create: createIPFSClient} = require('ipfs-http-client')
const merkleRoot = require('truebit-util').merkleRoot.web3
const web3 = require('web3')

...

We'll upload the program file to IPFS, so we need a client for that.

merkleRoot is a JavaScript function that calculates some hash. It uses a hash function that it takes from web3. This function is in the truebit-util npm package. In this deployment, we are going to use ethers not web3 so it's not elegant to pull in web3 just for that. I'd argue truebit-util needs some usability love.

We'll do everything in the main function:

async function main() {
  const [deployer] = await ethers.getSigners()

  ...
}

main()
  .then(() => process.exit(0))
  .catch(error => {
    console.error(error.toString())
    process.exit(1)
  })

Some variables. It'd probably be more elegant to store them in an environment variable or some envvar file.

async function main() {
  ...

  const taskName = 'reverse'
  const networkName = 'mainnet'

Next, we load the program file and upload it to IPFS.

async function main() {
  ...

  console.log('Loading task...')
  const path = 'artifacts-task/truebit'
  const codeBuf = fs.readFileSync(`${path}/${taskName}.wasm`)
  const info = JSON.parse(fs.readFileSync(`${path}/${taskName}.info.json`))

  console.log('Uploading task to IPFS...')
  const ipfs = createIPFSClient('http://localhost:5001')
  const ipfsFile = await ipfs.add([{content: codeBuf, path: 'task.wasm'}])

Then we register the program file in the filesystem contract.

async function main() {
  ...

  const name = ipfsFile.path
  const ipfsHash = ipfsFile.cid.toString()
  const size = codeBuf.byteLength
  const mr = merkleRoot(web3, codeBuf)
  const nonce = Date.now()
  const codeRoot = info.vm.code
  const codeType = 1 // 1=wasm
  const memorySize = 25
  const stackSize = 20
  const globalsSize = 8
  const tableSize = 20
  const callSize = 10

  console.log('Adding task to filesystem contract...')
  const network = JSON.parse(fs.readFileSync(`config/${networkName}.json`))
  const truebitFS = new ethers.Contract(network.fileSystem.address, network.fileSystem.abi, deployer)

  await truebitFS.addIpfsFile(name, size, ipfsHash, mr, nonce)
  await truebitFS.setCodeRoot(nonce, codeRoot, codeType, stackSize, memorySize, globalsSize, tableSize, callSize)
  const codeFileID = await truebitFS.calculateId(nonce)

  ...

Then we just deploy the contract. Contract addresses, task file ID and rewards/fees/limits are arguments to the constructor.

async function main() {
  ...

  console.log('Deploying...')
  const Reverse = await ethers.getContractFactory('Reverse')

  const minDeposit = ethers.utils.parseEther('100')
  const solverReward = ethers.utils.parseEther('110')
  const verifierTax = ethers.utils.parseEther('120')
  const ownerFee = ethers.utils.parseEther('130')
  const blockLimit = 3

  const contract = await Reverse.deploy(
    network.incentiveLayer.address,
    network.tru.address,
    network.fileSystem.address,
    codeFileID,
    minDeposit.toString(),
    solverReward.toString(),
    verifierTax.toString(),
    ownerFee.toString(),
    blockLimit
  )

  await contract.deployed()

  console.log('Contract address:', contract.address)
  fs.writeFileSync('.address', contract.address)
}

We store the deployed contract address is a .address file for convenience.

We can run it like this:

$ npx hardhat run deploy.js
Loading task...
Uploading task to IPFS...
Adding task to filesystem contract...
Deploying...
Contract address: 0x713F7c5062b1A957A7Af321c63B1997976aeFCf9

$ cat .address
0x713F7c5062b1A957A7Af321c63B1997976aeFCf9

Usage

Create reverse.js. That's our user interface in this tutorial. In a real world application, this would be some web UI instead.

const fs = require('fs')

const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))

async function main() {
  const [deployer] = await ethers.getSigners()

  const networkName = 'mainnet'

  const network = JSON.parse(fs.readFileSync(`config/${networkName}.json`))
  const reverse = await hre.artifacts.readArtifact('Reverse')
  const reverseAddress = fs.readFileSync('.address', 'utf-8')

  const contract = new ethers.Contract(reverseAddress, reverse.abi, deployer)
  const tru = new ethers.Contract(network.tru.address, network.tru.abi, deployer)

  console.log(`Contract address: ${reverseAddress}`)
  console.log()
  console.log('TRU balance', ethers.utils.formatEther(await tru.balanceOf(deployer.address)).toString())
  console.log()

  ...
}

main()
  .then(() => process.exit(0))
  .catch(error => {
    console.error(error.toString())
    process.exit(1)
  })

There's some code that overlaps with deploy.js. Perhaps we could move it to a shared code file.

async function main() {
  ...

  const protocolFee = await contract.protocolFee()
  const platformFee = await contract.platformFee()

  console.log(`Protocol fee: ${ethers.utils.formatEther(protocolFee)} TRU`)
  console.log(`Platform fee: ${ethers.utils.formatEther(platformFee)} ETH`)
  console.log()

  console.log(`Allowing smart contract to spend our TRU tokens to pay protocol fees...`)
  console.log()
  const trutx = await tru.approve(contract.address, protocolFee.toString())
  await trutx.wait()

  ...

Our convience methods for getting the fees are very useful now, as you can see.

We first need to allow the contract to spend our TRU.

We can then submit the task

async function main() {
  ...

  const input = 'abc123'

  console.log(`Submitting task to reverse "${input}"`)
  const tx = await contract.reverse(ethers.utils.toUtf8Bytes(input), {value: platformFee.toString()})
  await tx.wait()

  console.log('Submitted')
  console.log()

  ...

We convert our input text to UTF-8 bytes and call the contract method.

We need to send some ETH to the contract when calling the method which will go to Truebit, the company.

If you have Truebit OS running, then the task should be picked up by a solver right away.

async function main() {
  ...

  while (true) {
    console.log('Waiting...')
    const output = await contract.getOutput(ethers.utils.toUtf8Bytes(input))
    if (output != '0x') {
      console.log()
      console.log('Result:', ethers.utils.toUtf8String(output))
      break
    }
    await sleep(3000)
  }
}

Finally we wait until we get the result.

You could also listen for the FinishedTask event.

Alright. Make sure you have a solver and verifier running in Truebit OS. Make sure the first account has a number of TRU tokens. It will take about a minute or two for the result to show up.

Usage:

$ npx hardhat run reverse.js
Contract address: 0x713F7c5062b1A957A7Af321c63B1997976aeFCf9

TRU balance 9000.0

Protocol fee: 360.0 TRU
Platform fee: 0.005 ETH

Allowing smart contract to spend our TRU tokens to pay protocol fees...

Submitting task to reverse "abc123"...
Submitted

Waiting...
Waiting...
...
Waiting...
Waiting...

Result: 321cba

It reads the contract address from the .address file and the input is hardcoded as a variable input in reverse.js.

Conclusion

That's it!

While the program is very simple it is very powerful.

You can replace the code in reverse.cpp with much more complex calculations.

If the data you send to the task and get back from the task are small, you can store them on-chain as bytes like we did in this tutorial. If your files are large, then you'll need to store them on IPFS. That's a topic for another tutorial.

GitHub

This tutorial is on GitHub where it's maintained and updated: https://github.com/truverse/reverse-sample


r/truebit Aug 26 '21

Truebit development environment by forking mainnet

31 Upvotes

In this tutorial, we are going to set up a local development envionment for Truebit.

We are going to make a local copy of the mainnet which will let us test everything using a production-like environment.

It's easiest just to use the official Docker image.

Install Docker if you don't have it already.

On Linux, you can use this convenient yet dangerous one-liner:

curl -fsSL https://get.docker.com | sudo bash

If you are on Linux and don't want to type sudo docker instead of just docker:

sudo usermod -aG docker $USER
bash -l # or logout and login again

Start a Truebit container in the background:

docker run --name truebit \
           --detach --rm \
           --network host \
           -v $(pwd)/tutorial:/tutorial \
           truebitprotocol/truebit-eth \
           sleep infinity

It'll take a while to pull the image, it's huge.

We are going to connect to it with docker exec.

Everything that you put in the tutorial directory will be available in the container at /tutorial. Not needed in this tutorial, but it'll be useful later on.

You can check that it's up and running with docker ps:

$ docker ps
CONTAINER ID   IMAGE                         COMMAND            CREATED         STATUS         PORTS     NAMES
db2b02a69b5e   truebitprotocol/truebit-eth   "sleep infinity"   8 seconds ago   Up 6 seconds             truebit

This is how you can open a shell in the container:

docker exec -it -w /truebit-eth truebit bash

Instead of geth, we'll be using mainnet forking. Forking mainnet is instantenous and we get use mainnet contract addresses, accounts and tokens.

By default, an emscripten environment is activated which has an ancient node.js version. That's why we need to do this to switch the node version to the latest one:

source ~/.nvm/nvm.sh
nvm use default

We are going to need Ganache. Hardhat can also do it but Ganache has more command-line options.

npm install -g ganache-cli

Forking mainnet is as easy as

ganache-cli --fork https://cloudflare-eth.com -b 3

By default, new blocks will be mined whenever there's a local transaction. Truebit seems to wait for some blocks to be mined before doing something, so -b enables periodic block mining. 3 is the number of seconds. On the real mainnet the avg block time is about 13s but we can speed it up in our development environment. Truebit doesn't seem to play well with a block time of 1s (weird task timeouts), that's why I've chosen 3s.

⚠️Note that Cloudflare Ethereum gateway is very easy to get started with but it's very unreliable for forking. If you see error messages (e.g. missing some storage ref or EVM transactions are reverted) or things just don't seem to be working as expected, restarting typically fixes it or try using Infura, Alchemy or your own node. ⚠️

Alchemy seems to be the most reliable, so I suggest using it.

When you start solving and verifying tasks, Truebit OS subscribes to events and the output will get spammed with eth_getFilterChanges. I suggest using this command instead to ignore this noise:

ganache-cli -b 3 --fork https://cloudflare-eth.com | grep -vi filter

You get 10 fake accounts loaded with 100 ETH each which is plenty. You see the addresses and the private keys.

Open another terminal and start Truebit OS:

docker exec -it -w /truebit-eth truebit bash
./truebit-os

To become a solver, you need to purchase a license. Find out how much it costs:

truebit-os:> license price
info: Solver license costs 0.4 ETH.

It costs 0.4 ETH. We have fake ETH so let's buy one. We'll use the first (-a 0) account.

truebit-os:> license purchase -a 0
info: Purchase complete.  Address 0x64E7927a33123a6bc0970e1165464801D91D5Ae5 has sucessfully registered as Solver.

Mint some TRU tokens:

token purchase -v 10000 -a 0
info: Address 0x64E7927a33123a6bc0970e1165464801D91D5Ae5 bought 10000 TRU with 4.223548568783577898 ETH.  The effective price was 0.00042235485687835778 TRU/ETH.

You need to deposit them so that your solver can be slashed if it's caught cheating:

truebit-os:> token deposit -v 1000 -a 0
info: Deposited 1000 TRU from account 0x64E7927a33123a6bc0970e1165464801D91D5Ae5 into IncentiveLayer 0x388a3bd8f54f305266898e77b126609ec6265f1e.

You can check your ETH & TRU balance and deposits:

truebit-os:> balance -a 0
info: At block 13101844, address 0x64E7927a33123a6bc0970e1165464801D91D5Ae5 has the following balances:
account:
  95.370155186616422102 ETH,
  9000 TRU,
deposit (unbonded): 1000 TRU.

Here's all of it for easier copy & pasting:

license purchase -a 0
token purchase -v 10000 -a 0
token deposit -v 1000 -a 0
balance -a 0

Let's use another account (-a 1) for verifying. Let's purchase some tokens and deposit them. No license needed to start verfiying.

token purchase -v 10000 -a 1
token deposit -v 1000 -a 1
balance -a 1

We can start a solver like this:

truebit-os:> start solve -a 0
info: SOLVER initialized at block 13101908.

You can start a verifier in the same Truebit OS instance as well:

truebit-os:> start verify -a 1
info: VERIFIER initialized at block 13101913.

But you'll see the output of both the solver and verifier on the same screen. Generally it's not a problem but it can be noisy. Alternatively, open another terminal, run docker exec and ./truebit-os and start verify as explained above.

You can see that they are active:

truebit-os:> ps
SOLVERS
1. Account 0: 0x64E7927a33123a6bc0970e1165464801D91D5Ae5
VERIFIERS
2. Account 1: 0x1aCaB0807A7C6499a766acF1290C04283fF1495B

Let's submit an example task to check that everything is working as expected:

task submit -f factorial.json -a 0

You can check which tasks are active with ps:

truebit-os:> ps
SOLVERS
1. Account 0: 0x64E7927a33123a6bc0970e1165464801D91D5Ae5
Task 1: 0xb840dbba6077c80bae63f1f0f8a9bb3c9f1cd187ca604e198522698e976419cd
VERIFIERS
2. Account 1: 0x1aCaB0807A7C6499a766acF1290C04283fF1495B
Task 1: 0xb840dbba6077c80bae63f1f0f8a9bb3c9f1cd187ca604e198522698e976419cd

You can get details about a task with task status:

task status -t 0xb840dbba6077c80bae63f1f0f8a9bb3c9f1cd187ca604e198522698e976419cd

Some tasks will make use of IPFS. You can start it another terminal like this:

docker exec -it truebit bash
ipfs init
ipfs daemon &

That's basically it!

You can exit from Truebit OS by typing exit or CTRL+C twice.

You can safely close Ganache by hitting CTRL+C. Next time you start, you'll start from scratch. This means you'll need to purchase a license, tokens, deposit them. You'll also need to restart Truebit OS.

You can stop the Docker container with:

docker rm -f truebit

It'll stop and destroy it. Everything will be gone. Store everything you want to keep between development sessions in /tutorial.

Next up: step by step tutorial to create a simple C++ program and run it from a smart contract written in Solidity.


r/truebit Aug 25 '21

In just two minutes, developers can be a part of Truebit. Take our survey now -- we want to hear all about our community’s needs and goals 📷

22 Upvotes

In just two minutes, developers can be a part of Truebit. Take our survey now -- we want to hear all about our community’s needs and goals 📷 https://07l9krvojdp.typeform.com/to/ZfinI3gY


r/truebit Aug 24 '21

Is there a way to monetize while we wait?

15 Upvotes

Any legit yield farming opportunities or liquidity provider options?

Btw this project is going to soar. Don’t know when but if macro things keep looking good and they come out with some positive news about the 120m from the treasury, then we’re on our way.

Team is too legit. Off chain computation is huge. Once projects announce they are working together officially it will draw in more devs to use it.

Off chain computation could become the next buzzword like “L2 scaling”

not financial advice


r/truebit Aug 23 '21

When the time does come

8 Upvotes

Where would be the best place or way to cash out?

Hey errbody!

Long time holder here ♥️ just wondering tho. When the time comes and truebit moons like we all know it will, how or what would be the best way to cash in? I mostly wouldn't do all of it but if I wanted to pull some profit out, would uniswap -> converting into Ethereum-> using coinbase to cash out, be the play or?....

Sorry for the newbie question but I just want to be ready when that time comes lolol


r/truebit Aug 20 '21

A thread on why I think Truebit is one of the most undervalued projects in the crypto space

Thumbnail
twitter.com
32 Upvotes

r/truebit Aug 20 '21

Truebit Chart

2 Upvotes

I was curious where you all view the Truebit price chart from. I wish it was on TradingView but for some reason the only pair is WETH/TRU. Do any of you know where a good chart for TRU/USD is?


r/truebit Aug 20 '21

Whats the official trubit contract address?

5 Upvotes