Skip to content

Commit

Permalink
Feat/pow support (#161)
Browse files Browse the repository at this point in the history
* feat: power implementation

* feat: simplly code

* chore: add a explain comment for target difficulty

* fix typo

* fix: blocked error channel

* chore: update dev toml sc address

* feat: change wrong log and add toml tag

* fix: upgrade cowsay to v0.0.4

* fix: update devent controller address

* fix: Add POW config processor

* feat: add pow address for testnet

* chore: update to latest controller address

* chore: update contract addresses

* build: add hardhat testnet config

---------

Co-authored-by: root <[email protected]>
Co-authored-by: hunjixin <[email protected]>
Co-authored-by: Brian Ginsburg <[email protected]>
Co-authored-by: Gorka <[email protected]>
Co-authored-by: Arsen Yeremin <[email protected]>
  • Loading branch information
6 people authored Jun 14, 2024
1 parent 6ca4289 commit a08786c
Show file tree
Hide file tree
Showing 35 changed files with 2,647 additions and 40 deletions.
51 changes: 51 additions & 0 deletions cmd/lilypad/pow_signal.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package lilypad

import (
"github.com/lilypad-tech/lilypad/pkg/options"
optionsfactory "github.com/lilypad-tech/lilypad/pkg/options"
"github.com/lilypad-tech/lilypad/pkg/system"
"github.com/lilypad-tech/lilypad/pkg/web3"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
)

func newPowSignalCmd() *cobra.Command {
options := optionsfactory.NewPowSignalOptions()

powSignalCmd := &cobra.Command{
Use: "pow-signal",
Short: "Send a pow signal to smart contract.",
Long: "Send a pow signal to smart contract.",
Example: "",
RunE: func(cmd *cobra.Command, _ []string) error {
network, _ := cmd.Flags().GetString("network")

options, err := optionsfactory.ProcessPowSignalOptions(options, network)
if err != nil {
return err
}
return runPowSignal(cmd, options)
},
}

optionsfactory.AddPowSignalCliFlags(powSignalCmd, &options)

return powSignalCmd
}

func runPowSignal(cmd *cobra.Command, options options.PowSignalOptions) error {
commandCtx := system.NewCommandContext(cmd)
defer commandCtx.Cleanup()

web3SDK, err := web3.NewContractSDK(options.Web3)
if err != nil {
return err
}

_, err = web3SDK.SendPowSignal(commandCtx.Ctx)
if err != nil {
return err
}
log.Info().Msgf("send pow signal successful.")
return nil
}
1 change: 1 addition & 0 deletions cmd/lilypad/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func NewRootCmd() *cobra.Command {

RootCmd.AddCommand(newSolverCmd())
RootCmd.AddCommand(newResourceProviderCmd())
RootCmd.AddCommand(newPowSignalCmd())
RootCmd.AddCommand(newRunCmd())
RootCmd.AddCommand(newMediatorCmd())
RootCmd.AddCommand(newJobCreatorCmd())
Expand Down
5 changes: 3 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@ require (
github.com/ethereum/go-ethereum v1.13.4
github.com/fatih/color v1.15.0
github.com/go-git/go-git/v5 v5.10.0
github.com/google/uuid v1.3.0
github.com/gorilla/mux v1.8.0
github.com/gorilla/websocket v1.5.0
github.com/hashicorp/go-retryablehttp v0.7.4
github.com/hashicorp/golang-lru/v2 v2.0.7
github.com/holiman/uint256 v1.2.4
github.com/ipfs/go-merkledag v0.11.0
github.com/rs/zerolog v1.31.0
github.com/spf13/cobra v1.7.0
Expand Down Expand Up @@ -45,10 +48,8 @@ require (
github.com/go-stack/stack v1.8.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/holiman/uint256 v1.2.3 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/ipfs/bbloom v0.0.4 // indirect
github.com/ipfs/go-block-format v0.0.3 // indirect
Expand Down
6 changes: 4 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -122,10 +122,12 @@ github.com/hashicorp/go-retryablehttp v0.7.4 h1:ZQgVdpTdAL7WpMIwLzCfbalOcSUdkDZn
github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 h1:3JQNjnMRil1yD0IfZKHF9GxxWKDJGj8I0IqOUol//sw=
github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao=
github.com/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o=
github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw=
github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU=
github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
Expand Down
15 changes: 14 additions & 1 deletion hardhat/contracts/LilypadController.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ contract LilypadController is ILilypadController, Ownable, Initializable {
address private paymentsAddress;
address private mediationAddress;
address private jobCreatorAddress;
address private powAddress;

ILilypadStorage private storageContract;
ILilypadPayments private paymentsContract;
Expand All @@ -35,13 +36,15 @@ contract LilypadController is ILilypadController, Ownable, Initializable {
address _usersAddress,
address _paymentsAddress,
address _mediationAddress,
address _jobCreatorAddress
address _jobCreatorAddress,
address _powAddress
) public initializer {
setStorageAddress(_storageAddress);
setUsersAddress(_usersAddress);
setPaymentsAddress(_paymentsAddress);
setMediationAddress(_mediationAddress);
setJobCreatorAddress(_jobCreatorAddress);
setPowAddress(_powAddress);
}

function setStorageAddress(address _storageAddress) public onlyOwner {
Expand Down Expand Up @@ -92,6 +95,16 @@ contract LilypadController is ILilypadController, Ownable, Initializable {
return jobCreatorAddress;
}

function setPowAddress(address _powAddress) public onlyOwner {
require(_powAddress != address(0), "Users address");
powAddress = _powAddress;
}

function getPowAddress() public view returns(address) {
return powAddress;
}


/**
* Agreements
*/
Expand Down
153 changes: 153 additions & 0 deletions hardhat/contracts/LilypadPow.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.6;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

contract LilypadPow is Ownable, Initializable {
struct POWSubmission {
address walletAddress;
string nodeId;
uint256 nonce;
uint256 start_timestap;
uint256 complete_timestap; //used to estimate hashrate of this submission
bytes32 challenge; //record this to provent user never change challenge
uint256 difficulty;
}

struct Challenge {
bytes32 challenge;
uint256 difficulty;
string nodeId;
uint256 timestamp;
}

// todo difficulty may need to adjust in test
// this difficulty was calculate with this tool https://github.com/hunjixin/pow-tool/tree/main/difficulty
// Theoretically A machine with a hash rate of 2M has a probability of no more than 0.01% of not finding a nonce that meets the difficulty within 20 blocks.
// However, this issue has not been well validated in practice. it can solve nonce within one minute most of the time.
uint256 public immutable TARGET_DIFFICULTY =
2221842798488549893930113429797694032668256326301844165995655665287168;
mapping(address => POWSubmission[]) public powSubmissions;
mapping(address => uint256) public minerSubmissionCount; //used for loop powsubmission
address[] public miners;

mapping(address => Challenge) public lastChallenges;
uint256 public validProofs;
uint256 public startTime;

uint256 public window_start;
uint256 public window_end;
/**
* Init
*/

// https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable
function initialize() public initializer {}

function getMiners() external view returns (address[] memory) {
return miners;
}

// generateChallenge gen a byte32 value as challenge value, Sc store this one for verify
function generateChallenge(string calldata nodeId) external {
checkTimeWindow();

bytes32 challenge = keccak256(
abi.encodePacked(block.timestamp, window_start, msg.sender, nodeId)
);

uint256 difficulty = calculate_difficulty();
lastChallenges[msg.sender] = Challenge(
challenge,
difficulty,
nodeId,
block.timestamp
);
emit GenerateChallenge(challenge, difficulty);
}

function calculate_difficulty() public view returns (uint256) {
uint256 percentChange = 90 + (block.prevrandao % 21);
return (TARGET_DIFFICULTY * percentChange) / 100;
}

// submitWork miner submint a nonce value, sc check the difficulty and emit a valid pow event when success
function submitWork(uint256 nonce, string calldata nodeId) external {
checkTimeWindow();

Challenge memory lastChallenge = lastChallenges[msg.sender];
bytes32 challenge = keccak256(
abi.encodePacked(
lastChallenge.timestamp,
window_start,
msg.sender,
nodeId
)
);

require(
lastChallenge.challenge == challenge,
"Work submit not compatable with challenge"
);

bytes32 hash = keccak256(abi.encodePacked(challenge, nonce));
require(
uint256(hash) < lastChallenge.difficulty,
"Work does not meet difficulty target"
);

validProofs++;

if (minerSubmissionCount[msg.sender] == 0) {
//first submit, append to miners
miners.push(msg.sender);
}

minerSubmissionCount[msg.sender]++; //increase miner's valid proofs
POWSubmission[] storage posSubmissions = powSubmissions[msg.sender];
posSubmissions.push(
POWSubmission(
msg.sender,
nodeId,
nonce,
lastChallenge.timestamp,
block.timestamp,
lastChallenge.challenge,
lastChallenge.difficulty
)
);

//clean last challenge to submit the same proof
lastChallenges[msg.sender] = Challenge(0, 0, "", 0);
emit ValidPOWSubmitted(
msg.sender,
nodeId,
nonce,
block.timestamp,
lastChallenge.challenge,
lastChallenge.difficulty
);
}

function triggerNewPowRound() external onlyOwner {
window_start = block.number;
window_end = block.number + 30; //todo arbitary value , need to discuss
emit NewPowRound();
}

function checkTimeWindow() public view {
require(block.number < window_end, "proof windows has closed");
}

event ValidPOWSubmitted(
address indexed walletAddress,
string nodeId,
uint256 nonce,
uint256 timestamp,
bytes32 challenge,
uint256 difficulty
);
event GenerateChallenge(bytes32 challenge, uint256 difficulty);
event NewPowRound();
}
28 changes: 28 additions & 0 deletions hardhat/deploy/007_deploy_pow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { HardhatRuntimeEnvironment } from 'hardhat/types'
import { DeployFunction } from 'hardhat-deploy/types'

const deployUsers: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const { deployments, getNamedAccounts } = hre
const { deploy, execute } = deployments
const {
admin,
} = await getNamedAccounts()
await deploy("LilypadPow", {
from: admin,
args: [],
log: true,
})
await execute(
'LilypadPow',
{
from: admin,
log: true,
},
'initialize'
)
return true
}

deployUsers.id = 'deployPow'

export default deployUsers
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const deployController: DeployFunction = async function (hre: HardhatRuntimeEnvi
const mediationContract = await deployments.get('LilypadMediationRandom')
const paymentsContract = await deployments.get('LilypadPayments')
const jobCreatorContract = await deployments.get('LilypadOnChainJobCreator')
const powContract = await deployments.get('LilypadPow')

await execute(
'LilypadController',
Expand All @@ -31,7 +32,8 @@ const deployController: DeployFunction = async function (hre: HardhatRuntimeEnvi
usersContract.address,
paymentsContract.address,
mediationContract.address,
jobCreatorContract.address
jobCreatorContract.address,
powContract.address
)

await execute(
Expand Down
5 changes: 5 additions & 0 deletions hardhat/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ const config: HardhatUserConfig = {
chainId: 412346,
accounts: [getAccount('admin').privateKey],
},
testnet:{
url: 'https://sepolia-rollup.arbitrum.io/rpc',
chainId: 421614,
accounts: [getAccount('admin').privateKey],
},
},
etherscan: {
apiKey: process.env.ETHERSCAN_API_KEY,
Expand Down
7 changes: 5 additions & 2 deletions hardhat/scripts/print-contract-env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import {
getJobManagerAddress,
getPaymentsAddress,
getStorageAddress,
getUsersAddress
getUsersAddress,
getPoWAddress
} from '../utils/web3'

async function main() {
Expand All @@ -16,7 +17,8 @@ async function main() {
const paymentsAddress = await getPaymentsAddress()
const storageAddress = await getStorageAddress()
const usersAddress = await getUsersAddress()

const powAddress = await getPoWAddress()

console.log(`export WEB3_RPC_URL=ws://localhost:8546`)
console.log(`export WEB3_CONTROLLER_ADDRESS=${controllerAddress}`)
console.log(`export WEB3_TOKEN_ADDRESS=${tokenAddress}`)
Expand All @@ -25,6 +27,7 @@ async function main() {
console.log(`export WEB3_PAYMENTS_ADDRESS=${paymentsAddress}`)
console.log(`export WEB3_STORAGE_ADDRESS=${storageAddress}`)
console.log(`export WEB3_USERS_ADDRESS=${usersAddress}`)
console.log(`export WEB3_POW_ADDRESS=${powAddress}`)
}

main().catch((error) => {
Expand Down
Loading

0 comments on commit a08786c

Please sign in to comment.