Skip to content

Commit

Permalink
Merge pull request #638 from iotexproject/minter
Browse files Browse the repository at this point in the history
smart contract
  • Loading branch information
huangzhiran authored Oct 10, 2024
2 parents 8d7c93c + 8d84519 commit 946e78b
Show file tree
Hide file tree
Showing 18 changed files with 1,479 additions and 950 deletions.
31 changes: 31 additions & 0 deletions smartcontracts/contracts/Scrypt.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

contract Scrypt {
function hash(
bytes calldata password,
bytes calldata salt,
uint64 N,
uint32 r,
uint32 p,
uint32 keyLen,
uint32 mode
) external view returns (bytes memory retval) {
bytes memory input = abi.encode(password, salt, N, r, p, keyLen, mode);
uint length = input.length;
assembly {
if iszero(staticcall(gas(), 0x8002, add(input, 0x20), length, 0, 0)) {
revert(0x0, 0x0)
}
if iszero(eq(returndatasize(), keyLen)) {
revert(0x0, 0x0)
}
mstore(retval, returndatasize()) // Store the length.
let o := add(retval, 0x20)
returndatacopy(o, 0x00, returndatasize()) // Copy the returndata.
mstore(0x40, add(o, returndatasize())) // Allocate the memory.
}

return retval;
}
}
128 changes: 128 additions & 0 deletions smartcontracts/contracts/W3bstreamBlockHeaderValidator.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {BlockHeader, IBlockHeaderValidator, IScrypt} from "./interfaces/IBlockHeaderValidator.sol";

contract W3bstreamBlockHeaderValidator is IBlockHeaderValidator, Ownable {
event OperatorSet(address operator);
event TargetDurationSet(uint256 duration);
event NBitsSet(uint32 nbits);

uint32 public constant MAX_EXPONENT = 0x1c;
uint32 public constant UPPER_BOUND = 0xffff00;
uint32 public constant LOWER_BOUND = 0x8000;
// uint256 public constant MAX_TARGET = 0x00000000ffff0000000000000000000000000000000000000000000000000000;

address public operator;
IScrypt public scrypt;
uint256 public targetDuration;
uint32 public currentNBits;
bool public useAdhocNBits;

uint256 private _currentTarget;
uint256[10] private _durations;
uint256 private _durationSum;
uint256 private _durationNum;
uint256 private _durationIndex;

constructor(IScrypt _scrypt) Ownable() {
scrypt = _scrypt;
_setTargetDuration(12);
_setAdhocNBits(0x1c7fffff);
}

function validate(BlockHeader calldata header) public view returns (bytes memory) {
require(header.nbits == currentNBits, "invalid nbits");
bytes memory encodedHeader = abi.encodePacked(header.meta, header.prevhash, header.merkleRoot, header.nbits, header.nonce);
bytes memory headerHash = scrypt.hash(encodedHeader, encodedHeader, 1024, 1, 1, 32, 224);
require(headerHash.length == 32, "invalid header hash length");
require(uint256(bytes32(headerHash)) <= _currentTarget, "invalid proof of work");
return encodedHeader;
}

function setOperator(address _operator) public onlyOwner {
operator = _operator;
emit OperatorSet(_operator);
}

function setTargetDuration(uint256 duration) public onlyOwner {
_setTargetDuration(duration);
}

function _setTargetDuration(uint256 duration) internal {
targetDuration = duration;
emit TargetDurationSet(duration);
}

function setAdhocNBits(uint32 nbits) public onlyOwner {
_setAdhocNBits(nbits);
}

function _setAdhocNBits(uint32 nbits) internal {
if (nbits == 0) {
useAdhocNBits = false;
return;
}
_setNBits(nbits);
useAdhocNBits = true;
}

function updateDuration(uint256 duration) public {
require(msg.sender == operator, "not operator");
_durationSum += duration - _durations[_durationIndex];
_durations[_durationIndex] = duration;
_durationIndex = (_durationIndex + 1) % _durations.length;
if (_durationNum < _durations.length) {
_durationNum++;
return;
}
if (useAdhocNBits) {
return;
}
uint32 nbits = uint32(currentNBits);
uint32 next = _nextNBits(nbits, targetDuration * _durationNum, _durationSum);
if (next != nbits) {
_setNBits(next);
}
}

function _nextNBits(uint32 nbits, uint256 expectedSum, uint256 sum) internal pure returns (uint32) {
if (sum * 5 > expectedSum * 6) {
(uint32 exponent, uint32 coefficient) = decodeNBits(nbits);
if (coefficient < UPPER_BOUND) {
return (exponent << 24) | uint32(coefficient + 1);
}
if (exponent < MAX_EXPONENT) {
return ((exponent + 1) << 24) | LOWER_BOUND;
}
} else if (expectedSum * 4 > sum * 5) {
(uint32 exponent, uint32 coefficient) = decodeNBits(nbits);
if (coefficient > LOWER_BOUND) {
return (exponent << 24) | uint32(coefficient - 1);
}
if (exponent > 0) {
return ((exponent - 1) << 24) | UPPER_BOUND;
}
}
return nbits;
}

function _setNBits(uint32 nbits) internal {
uint256 target = nbitsToTarget(nbits);
currentNBits = nbits;
_currentTarget = target;
emit NBitsSet(nbits);
}

function decodeNBits(uint32 nbits) internal pure returns (uint32, uint32) {
return (nbits >> 24, nbits & 0x00ffffff);
}

function nbitsToTarget(uint32 nbits) public pure returns (uint256) {
(uint32 exponent, uint256 coefficient) = decodeNBits(nbits);
require(exponent <= MAX_EXPONENT, "invalid nbits");
require(coefficient >= LOWER_BOUND && coefficient <= UPPER_BOUND, "invalid nbits");
return coefficient << (8 * (exponent - 3));
}
}
82 changes: 82 additions & 0 deletions smartcontracts/contracts/W3bstreamBlockMinter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {BlockHeader, IBlockHeaderValidator} from "./interfaces/IBlockHeaderValidator.sol";
import {ITaskManager, TaskAssignment} from "./interfaces/ITaskManager.sol";

interface IDAO {
function tip() external view returns (uint256, bytes32, uint256);
function mint(bytes32 hash, uint256 timestamp) external;
}

interface IBlockRewardDistributor {
function distribute(address recipient, uint256 amount) external;
}

struct Sequencer {
address addr;
address operator;
address beneficiary;
}

contract W3bstreamBlockMinter is OwnableUpgradeable {
event BlockRewardSet(uint256 reward);
event TaskAllowanceSet(uint256 allowance);
event TargetDurationSet(uint256 duration);
event NBitsSet(uint32 nbits);

IDAO public dao;
ITaskManager public taskManager;
IBlockRewardDistributor public distributor;
IBlockHeaderValidator public headerValidator;

uint256 public blockReward;
uint256 public taskAllowance;

function initialize(IDAO _dao, ITaskManager _taskManager, IBlockRewardDistributor _distributor, IBlockHeaderValidator _headerValidator) public initializer {
__Ownable_init();
dao = _dao;
taskManager = _taskManager;
distributor = _distributor;
headerValidator = _headerValidator;
_setBlockReward(1000000000000000000);
_setTaskAllowance(720);
}

function mint(
BlockHeader calldata header,
Sequencer calldata coinbase,
TaskAssignment[] calldata assignments
) public {
require(coinbase.operator == msg.sender, "invalid operator");
(, bytes32 tiphash, uint256 tipTimestamp) = dao.tip();
require(tipTimestamp != block.number);
require(header.prevhash == tiphash, "invalid prevhash");
require(header.merkleRoot == keccak256(abi.encode(coinbase.addr, coinbase.operator, coinbase.beneficiary)), "invalid merkle root");
bytes memory encodedHeader = headerValidator.validate(header);
bytes32 blockHash = keccak256(abi.encode(encodedHeader, assignments));
taskManager.assign(assignments, coinbase.beneficiary, block.number + taskAllowance);
headerValidator.updateDuration(block.number - tipTimestamp);
dao.mint(blockHash, block.number);
distributor.distribute(coinbase.beneficiary, blockReward);
}

function setBlockReward(uint256 reward) public onlyOwner {
_setBlockReward(reward);
}

function _setBlockReward(uint256 reward) internal {
blockReward = reward;
emit BlockRewardSet(reward);
}

function setTaskAllowance(uint256 allowance) public onlyOwner {
_setTaskAllowance(allowance);
}

function _setTaskAllowance(uint256 allowance) internal {
taskAllowance = allowance;
emit TaskAllowanceSet(allowance);
}
}
49 changes: 49 additions & 0 deletions smartcontracts/contracts/W3bstreamBlockRewardDistributor.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

contract W3bstreamBlockRewardDistributor is OwnableUpgradeable {
event Distributed(address indexed recipient, uint256 amount);
event Withdrawn(uint256 amount);
event OperatorSet(address indexed operator);
event Topup(uint256 amount);
address public operator;

modifier onlyOperator() {
require(msg.sender == operator, "not operator");
_;
}

receive() external payable {
emit Topup(msg.value);
}

function initialize() public initializer {
__Ownable_init();
}

function setOperator(address _operator) public onlyOwner {
operator = _operator;
emit OperatorSet(_operator);
}

function distribute(address recipient, uint256 amount) public onlyOperator {
if (amount == 0) {
return;
}
if (amount > address(this).balance) {
revert("insufficient balance");
}
(bool success, ) = recipient.call{value: amount}("");
require(success, "transfer failed");
emit Distributed(recipient, amount);
}

function withdraw(uint256 amount) public onlyOwner {
require(amount <= address(this).balance, "insufficient balance");
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "transfer failed");
emit Withdrawn(amount);
}
}
34 changes: 34 additions & 0 deletions smartcontracts/contracts/W3bstreamDAO.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

contract W3bstreamDAO is OwnableUpgradeable {
struct Block {
bytes32 hash;
uint256 timestamp;
}
event BlockAdded(uint256 indexed num, bytes32 hash, uint256 timestamp);

Block[] public blocks;

function initialize(bytes32 genesis) public initializer {
__Ownable_init();
_mint(genesis, block.number);
}

function mint(bytes32 hash, uint256 timestamp) public onlyOwner {
_mint(hash, timestamp);
}

function tip() public view returns (uint256, bytes32, uint256) {
uint256 blocknum = blocks.length - 1;
Block storage tipblock = blocks[blocknum];
return (blocknum, tipblock.hash, tipblock.timestamp);
}

function _mint(bytes32 hash, uint256 timestamp) internal {
blocks.push(Block({timestamp: timestamp, hash: hash}));
emit BlockAdded(blocks.length - 1, hash, timestamp);
}
}
Loading

0 comments on commit 946e78b

Please sign in to comment.