Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/pow support #161

Merged
merged 14 commits into from
Jun 14, 2024
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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please rename to deployPow

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
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
14 changes: 14 additions & 0 deletions hardhat/utils/web3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
LilypadOnChainJobCreator,
LilypadUsers,
ExampleClient,
LilypadPow,
} from '../typechain-types'

/*
Expand Down Expand Up @@ -247,3 +248,16 @@ export async function connectController() {
export async function getControllerAddress() {
return getContractAddress('LilypadController')
}

/*

pow

*/
export async function connectPow() {
return connectContract<LilypadPow>('LilypadPow')
}

export async function getPoWAddress() {
return getContractAddress('LilypadPow')
}
Loading