Idea taken from @Shrugs' image, in turn taken from 0xBitcoin:

I'd make it generic, i.e not tied to token minting, as in the image, and maybe then add a MinedToken that uses the generic mining validation contracts
interesting! how would a miner interact with this contract? you need to pay gas for each submission?
This implementation is vulnerable to front-running. But you can easily fix that with something like
bytes32 digest = keccak256(lastNonce, nonce, msg.sender);.
This way the proof of work is tied to whoever did it, and no front-running is possible.
Updated contract should be
contract MineableToken is MintableToken {
public uint256 blockHeight = 0;
public uint256 lastNonce = 0;
function getDifficulty() public returns (uint256) {
return <some-difficulty-function>
}
function mint(uint256 nonce) public returns (bool success) {
bytes32 digest = keccak256(msg.sender, lastNonce, nonce);
require(uint256(digest) < getDifficulty());
lastNonce = nonce
blockHeight += 1
return super.mint(msg.sender, 1);
}
}
Mining would look like
const coinbase = web3.eth.accounts[0]
const lastNonce = await mineableToken.lastNonce()
const difficulty = await mineableToken.getDifficulty()
const randomNonce = () => BigNumber.random(78).mul(10 ** 78) // this probably works, idk
let isValid = false
let nonce
while (!isValid) {
nonce = randomNonce()
const digest = web3.utils.keccack256(coinbase, lastNonce, nonce)
// ^ make sure the bytes workout here, idk
isValid = digest < difficulty
}
await mineableToken.mint(nonce)
// yay
Super interesting !! want to see it in zeppelin
It can also have a getReward() function, that returns the reward of the block number mined, it can be deflationary.
Also it can have a fee per transfer, like take 1 wei for each transfer and claim all the feeBalance every time a block is mined, this way when you reach a block reward of cero you still have the fee reward.
The difficulty can be calculated knowing only how much time it took to mine the last block ? smth like:
if (lastBlockTime < blockTime)
increaseDifficulty( blockTime.sub(lastBlockTime) )
else
decreaseDifficulty( lastBlockTime.sub(blockTime) )
I like the idea of validating PoW in a smart contract, but I wouldn't couple its implementation to minting a token.
While miners do PoW for earning cryptocurrencies, that's not the purpose of the PoW. Block rewards are just a way of incentivize people to secure a blockchain. If obtaining new tokens is the entire purpose of the PoW it would only model the distribution of the tokens. The same distribution can probably be obtained in a more environmentally friendly way.
It inhibit other (IMO more interesting) uses of it. The first uses that come to my mind: making spam and sibyl attacks more expensive, and hopefully impractical.
Yeah totally agreed, glad you brought that up!
So now we've got a Mineable interface that implements PoW checking and exports a verifyProofOfWork(nonce, difficulty) function.
Then MineableToken is Mineable, MintableToken and mint looks like
function mint(_to, uint256 nonce) external returns (bool success) {
require(verifyProofOfWork(nonce, getDifficulty()));
adjustDifficulty();
return mint(msg.sender, getReward();
}
It inhibit other (IMO more interesting) uses of it. The first uses that come to my mind: making spam and sibyl attacks more expensive, and hopefully impractical.
I really liked the whole PoW idea, but had never come up with a use case that went beyond toy projects such as minting tokens. This, however, seems super neat.
After discussing this further with @alcuadrado, we came to the conclusion that placing PoW requirements for prevent spam wouldn't really be a great solution, since it is not very expensive to purchase computing power (much) more powerful than the equivalent of running JavaScript inside a browser.
I think many of us like this idea because it sounds cool, but it lacks a real use-case. Closing until one comes up.
Most helpful comment
This implementation is vulnerable to front-running. But you can easily fix that with something like
bytes32 digest = keccak256(lastNonce, nonce, msg.sender);.This way the proof of work is tied to whoever did it, and no front-running is possible.