The post UnsafeMath for Solidity 0.8.0+ appeared first on Justin Silver.
]]>UnsafeMath
is a Solidity library used to perform unchecked, or “unsafe”, math operations. Prior to version 0.8.0 all math was unchecked meaning that subtracting 1 from 0 would underflow and result in the max uint256 value. This behavior led many contracts to use the OpenZeppelin SafeMath
library to performed checked math – using the prior example subtracting 1 from 0 would throw an exception as a uint256 is unsigned and therefore cannot be negative. In Solidity 0.8.0+ all math operations became checked, but at a cost of more gas used per operation.
The UnsafeMath
library allows you to perform unchecked math operations where you are confident the result will not be an underflow or an overflow of the uint256 space – saving gas in your contracts where checked math is not needed.
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; // solhint-disable func-name-mixedcase library UnsafeMath { function unsafe_add(uint256 a, uint256 b) internal pure returns (uint256) { unchecked { return a + b; } } function unsafe_sub(uint256 a, uint256 b) internal pure returns (uint256) { unchecked { return a - b; } } function unsafe_div(uint256 a, uint256 b) internal pure returns (uint256) { unchecked { uint256 result; // solhint-disable-next-line no-inline-assembly assembly { result := div(a, b) } return result; } } function unsafe_mul(uint256 a, uint256 b) internal pure returns (uint256) { unchecked { return a * b; } } function unsafe_increment(uint256 a) internal pure returns (uint256) { unchecked { return ++a; } } function unsafe_decrement(uint256 a) internal pure returns (uint256) { unchecked { return --a; } } }
This test contract uses the UnsafeMath.unsafe_decrement()
and Unsafe.unsafe_decrement()
functions alongside their checked counterparts to test the difference in gas used between the different methods.
// SPDX-License-Identifier: MIT pragma solidity ^0.8.15; import './UnsafeMath.sol'; contract TestUnsafeMath { using UnsafeMath for uint256; uint256 private _s_foobar; function safeDecrement(uint256 count) public { for (uint256 i = count; i > 0; --i) { _s_foobar = i; } } function safeIncrement(uint256 count) public { for (uint256 i = 0; i < count; ++i) { _s_foobar = i; } } function unsafeDecrement(uint256 count) public { for (uint256 i = count; i > 0; i = i.unsafe_decrement()) { _s_foobar = i; } } function unsafeIncrement(uint256 count) public { for (uint256 i = 0; i < count; i = i.unsafe_increment()) { _s_foobar = i; } } }
Using a simple Mocha setup, our tests will call each of the contract functions with an argument for 100 iterations.
import { ethers } from 'hardhat'; import { ContractFactory } from '@ethersproject/contracts'; import { TestUnsafeMath } from '../sdk/types'; describe('UnsafeMath', () => { let testUnsafeMathDeploy: ContractFactory, testUnsafeMathContract: TestUnsafeMath; beforeEach(async () => { testUnsafeMathDeploy = await ethers.getContractFactory('TestUnsafeMath', {}); testUnsafeMathContract = (await testUnsafeMathDeploy.deploy()) as TestUnsafeMath; }); describe('Gas Used', async () => { it('safeDecrement gas used', async () => { const tx = await testUnsafeMathContract.safeDecrement(100); // const receipt = await tx.wait(); // console.log(receipt.gasUsed.toString(), 'gasUsed'); }); it('safeIncrement gas used', async () => { const tx = await testUnsafeMathContract.safeIncrement(100); // const receipt = await tx.wait(); // console.log(receipt.gasUsed.toString(), 'gasUsed'); }); it('unsafeDecrement gas used', async () => { const tx = await testUnsafeMathContract.unsafeDecrement(100); // const receipt = await tx.wait(); // console.log(receipt.gasUsed.toString(), 'gasUsed'); }); it('unsafeIncrement gas used', async () => { const tx = await testUnsafeMathContract.unsafeIncrement(100); // const receipt = await tx.wait(); // console.log(receipt.gasUsed.toString(), 'gasUsed'); }); }); });
The results show that a checked incrementing loop used 60276 gas
, checked decrementing used 59424 gas
, unchecked incrementing used 58117 gas
, and unchecked decrementing came in at 57473 gas
. That’s a savings of 2803 gas on a 100 iteration loop, or 4.55% of the total gas used.
UnsafeMath Gas Used ✓ safeDecrement gas used ✓ safeIncrement gas used ✓ unsafeDecrement gas used ✓ unsafeIncrement gas used ·--------------------------------------|---------------------------|----------------|-----------------------------· | Solc version: 0.8.15 · Optimizer enabled: true · Runs: 999999 · Block limit: 30000000 gas │ ·······································|···························|················|······························ | Methods │ ···················|···················|·············|·············|················|···············|·············· | Contract · Method · Min · Max · Avg · # calls · usd (avg) │ ···················|···················|·············|·············|················|···············|·············· | TestUnsafeMath · safeDecrement · - · - · 59424 · 1 · - │ ···················|···················|·············|·············|················|···············|·············· | TestUnsafeMath · safeIncrement · - · - · 60276 · 1 · - │ ···················|···················|·············|·············|················|···············|·············· | TestUnsafeMath · unsafeDecrement · - · - · 57473 · 1 · - │ ···················|···················|·············|·············|················|···············|·············· | TestUnsafeMath · unsafeIncrement · - · - · 58117 · 1 · - │ ···················|···················|·············|·············|················|···············|·············· | Deployments · · % of limit · │ ·······································|·············|·············|················|···············|·············· | TestUnsafeMath · - · - · 188806 · 0.6 % · - │ ·--------------------------------------|-------------|-------------|----------------|---------------|-------------· 4 passing (2s)
The post UnsafeMath for Solidity 0.8.0+ appeared first on Justin Silver.
]]>The post NFT Keyed Ephemeral Counterfactual Minimal Proxy Contracts appeared first on Justin Silver.
]]>These example contracts demonstrate a gas effective way to deploy counterfactual contracts using CREATE2. Using minimal proxy instances that are destroyed between transactions is a secure way to isolate assets in a way that only the key holder can access, Using the token ID of a known NFT contract allows this access to be transferred based on the permissions associated with that NFT.
This is an example of a simple “smart wallet” implementation. In practice allowing it to execute arbitrary calls will mean no other methods are needed, however additional security can be added by implementing explicit calls followed by selfdestruct()
.
// SPDX-License-Identifier: MIT pragma solidity ^0.8.14; contract Implementation { /// @notice A structure to define arbitrary contract calls struct Call { address to; uint256 value; bytes data; } /// @dev reference back to the factory address private immutable _owner; // called in the factory constructor - when immutable! constructor() { _owner = msg.sender; } // only the factory can call functions on the instance modifier onlyFactory() { require(_owner == msg.sender, 'factory only'); _; } /// @dev make call without a return value function doSomething() external payable onlyFactory { // make a call without a return value, maybe payable // ...then selfdestruct the contract selfdestruct(payable(address(0))); } /// @notice Executes calls on behalf of this instance. /// @param calls The array of calls to be executed. /// @return An array of the return values for each of the calls function executeCalls(Call[] calldata calls) external onlyFactory returns (bytes[] memory) { // handle the calls bytes[] memory results = new bytes[](calls.length); for (uint256 i = 0; i < calls.length; i++) { // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory result) = calls[i].to.call{value: calls[i].value}(calls[i].data); require(success, string(result)); results[i] = result; } // NOTE: cleanup() must be called from the factory! return results; } /// @notice Destroys this contract function cleanup() external onlyFactory { // remove the bytecode - mayble handle balances on the factory? selfdestruct(payable(address(0))); } }
This factory contract will create new minimal proxy instances using the token ID as the salt. Only the token owner is allowed to call the function to create the contract, so any assets in the contracts storage location will be safe between calls.
// SPDX-License-Identifier: MIT pragma solidity ^0.8.14; import '@openzeppelin/contracts/interfaces/IERC721Enumerable.sol'; import '@openzeppelin/contracts/proxy/Clones.sol'; import './Implementation.sol'; contract Create2Factory { /// @dev the nft used to calculate the address IERC721Enumerable private _token; /// @dev the address of the implementation for the minimal proxy address private _implementation; /// @dev constructor constructor(address nft) { // nft contract used as key _token = IERC721Enumerable(nft); // pass into constructor, etc. // smart wallet implementation for minimal proxy _implementation = address(new Implementation()); } /// @notice get the address of the instance for the given tokenId /// @param tokenId the tokenId /// @return the address of the instance function getAddressForTokenId(uint256 tokenId) external view returns (address) { return Clones.predictDeterministicAddress(_implementation, _salt(tokenId)); } /// @notice some call without a callback /// @param tokenId the tokenId function doSomething(uint256 tokenId) external payable { // get a minimal proxy instance of the implementation Implementation instance = _createInstance(tokenId); // will auto destruct instance.doSomething{value: msg.value}(); } /// @notice Allows the owner of an ERC721 to execute arbitrary calls on behalf of the associated wallet. /// @dev The wallet will be counterfactually created, calls executed, then the contract destroyed. /// @param tokenId The token ID /// @param calls The array of call structs that define that target, amount of ether, and data. /// @return The array of call return values. function executeCalls(uint256 tokenId, Implementation.Call[] calldata calls) external returns (bytes[] memory) { Implementation instance = _createInstance(tokenId); bytes[] memory result = instance.executeCalls(calls); // manuall cleanup instance.cleanup(); return result; } /// @dev Computes the CREATE2 salt for the given token. /// @param tokenId The token ID /// @return salt bytes32 value that is unique to that token. function _salt(uint256 tokenId) private pure returns (bytes32 salt) { return keccak256(abi.encodePacked(tokenId)); } /// @dev Creates a Implementation for the given token id. /// @param tokenId The token ID /// @return The address of the newly created Implementation. function _createInstance(uint256 tokenId) private returns (Implementation) { require(msg.sender == _token.ownerOf(tokenId), 'not owner'); // get the create2 clone address address payable _address = payable(Clones.cloneDeterministic(_implementation, _salt(tokenId))); // get a minimal proxy instance of the locker Implementation instance = Implementation(_address); // return the clone instance return instance; } }
The post NFT Keyed Ephemeral Counterfactual Minimal Proxy Contracts appeared first on Justin Silver.
]]>The post NFT – Mint Random Token ID appeared first on Justin Silver.
]]>The perceived value of many NFTs is based on that item’s rarity making it ideal to mint them fairly. Rarity snipers and bad actors on a team can scoop up rare items from a collection in an attempt to further profit on the secondary market. How can you both fairly distribute the tokens – both to the community and the project team?
One solution is to hide the metadata until after reveal and mint the token IDs out of order – either using a provable random number, a pseudo random number, as pseudo random number seeded with a provable random beacon, or other options depend on your security needs. For provable random numbers check out Provable Randomness with VDF.
uint16[100] public ids; uint16 private index; function _pickRandomUniqueId(uint256 random) private returns (uint256 id) { uint256 len = ids.length - index++; require(len > 0, 'no ids left'); uint256 randomIndex = random % len; id = ids[randomIndex] != 0 ? ids[randomIndex] : randomIndex; ids[randomIndex] = uint16(ids[len - 1] == 0 ? len - 1 : ids[len - 1]); ids[len - 1] = 0; }
We can efficiently track which IDs have – and have not – been minted by starting with an empty and cheap to create array of empty values with a size that matches your total supply. The array size could suffice in lieu of tracking the index, but this is more gas efficient than pop()
ing the array. For each round it will select a random index, bounded by the remaining supply – we will call this a “roll” as in “roll of the dice” except we will reduce the number of sides by one for each round.
When we start the Data
array will match the supply we want to create (five) and be empty (all zeroes), as well our Results
, which are just empty (this represents the token ids that would be minted).
Index: 0 1 2 3 4 5 -------------------------- Data: [0, 0, 0, 0, 0, 0] Results: []
3
For the first round, let’s say it’s a 3
. We look at the third index, check to see if it is 0
, and if is we return the index – this will make more sense in a moment. Next we look at the last position in the array given our remaining supply and if it is a 0
we move that index to the 3
position we rolled.
Index: 0 1 2 *3* 4 5 --------------------------- Data: [0, 0, 0, 0, 0, 0] Results: [] << before after >> Data: [0, 0, 0, 5, 0] Results: [3]
3
In the previous step when we check an index for a value, if a value was set a that index we would use it rather than the index. To demonstrate this, let’s assume we rolled a 3
again. This time we look at this third position and it contains a 5
, so we return that instead of a three. This is great, because we already selected a 3
and we want these to be unique. Again we look at the last position, and since it is not set we set the index 4
as the value of index 3
.
Index: 0 1 2 *3* 4 5 --------------------------- Data: [0, 0, 0, 5, 0] Results: [3] << before after >> Data: [0, 0, 0, 4] Results: [3, 5]
2
Next, we roll a 2
again. We look at position 2, it’s not set, so we return a 2
, again a number we haven’t selected previously. Next we check the last position which now as a 4
set, so it is moved into index 2
as we have yet to select it.
Index: 0 1 *2* 3 4 5 --------------------------- Data: [0, 0, 0, 4] Results: [3, 5] << before after >> Data: [0, 0, 4] Results: [3, 5, 2]
1
We roll a 1
, and since the first index contains a 4
we move that to our results.
Index: 0 *1* 2 3 4 5 --------------------------- Data: [0, 0, 4] Results: [3, 5, 2] << before after >> Data: [0, 4] Results: [3, 5, 2, 1]
1
We roll a 1
again. This time we return the 4, but since there is nothing to move into its place, we move on.
Index: 0 *1* 2 3 4 5 --------------------------- Data: [0, 4] Results: [3, 5, 2, 1] << before after >> Data: [0] Results: [3, 5, 2, 1, 4]
0
Lastly, we get a 0
, since that’s all that remains. It both contains a 0
and is in that position so we select a 0
.
Index: *0* 1 2 3 4 5 --------------------------- Data: [0] Results: [3, 5, 2, 1, 4] << before after >> Data: [] Results: [3, 5, 2, 1, 4, 0]
Each index of the array tracks an unminted ID. If the position isn’t set, that ID hasn’t been minted. If it is set, it’s because the last position was moved to it when the available indexes shrank and the last index wasn’t the one selected so we want to preserve it. If you want to start minting at 1, add 1.
Uses a pseudo random number to select from a unique set of token IDs.
// SPDX-License-Identifier: MIT pragma solidity ^0.8.11; import '@openzeppelin/contracts/token/ERC721/ERC721.sol'; contract RandomTokenIdv1 is ERC721 { uint16[100] public ids; uint16 private index; constructor() ERC721('RandomIdv1', 'RNDMv1') {} function mint(address[] calldata _to) external { for (uint256 i = 0; i < _to.length; i++) { uint256 _random = uint256(keccak256(abi.encodePacked(index++, msg.sender, block.timestamp, blockhash(block.number-1)))); _safeMint(_to[i], _pickRandomUniqueId(random)); } } function _pickRandomUniqueId(uint256 random) private returns (uint256 id) { uint256 len = ids.length - index++; require(len > 0, 'no ids left'); uint256 randomIndex = random % len; id = ids[randomIndex] != 0 ? ids[randomIndex] : randomIndex; ids[randomIndex] = uint16(ids[len - 1] == 0 ? len - 1 : ids[len - 1]); ids[len - 1] = 0; } }
Uses a provable random number and derivatives to select from a unique set of token IDs.
// SPDX-License-Identifier: MIT pragma solidity ^0.8.11; import '@openzeppelin/contracts/token/ERC721/ERC721.sol'; import './libraries/Randomness.sol'; import './libraries/SlothVDF.sol'; contract RandomTokenIdv2 is ERC721 { using Randomness for Randomness.RNG; Randomness.RNG private _rng; mapping(address => uint256) public seeds; uint256 public prime = 432211379112113246928842014508850435796007; uint256 public iterations = 1000; uint16[100] public ids; uint16 private index; constructor() ERC721('RandomIdv2', 'RNDMv2') {} function createSeed() external payable { seeds[msg.sender] = _rng.getRandom(); } function mint(address[] calldata _to, uint256 proof) external { require(SlothVDF.verify(proof, seeds[msg.sender], prime, iterations), 'Invalid proof'); uint256 _randomness = proof; uint256 _random; for (uint256 i = 0; i < _to.length; i++) { (_randomness, _random) = _rng.getRandom(_randomness); _safeMint(_to[i], _pickRandomUniqueId(_random)); } } function _pickRandomUniqueId(uint256 random) private returns (uint256 id) { uint256 len = ids.length - index++; require(len > 0, 'no ids left'); uint256 randomIndex = random % len; id = ids[randomIndex] != 0 ? ids[randomIndex] : randomIndex; ids[randomIndex] = uint16(ids[len - 1] == 0 ? len - 1 : ids[len - 1]); ids[len - 1] = 0; } }
Uses a provable random number as a beacon which is used as the seed for a pseudo random number and derivatives to select from a unique set of token IDs.
// SPDX-License-Identifier: MIT pragma solidity ^0.8.11; import '@openzeppelin/contracts/access/Ownable.sol'; import '@openzeppelin/contracts/token/ERC721/ERC721.sol'; import './libraries/Randomness.sol'; import './libraries/SlothVDF.sol'; contract RandomTokenIdv3 is ERC721, Ownable { using Randomness for Randomness.RNG; Randomness.RNG private _rng; uint16[100] public ids; uint16 private index; uint256 public prime = 432211379112113246928842014508850435796007; uint256 public iterations = 1000; uint256 public seed; uint256 public beacon; constructor() ERC721('RandomTokenIdv3', 'RNDMv3') {} // create a set - use something interesting for the input. function createSeed() external onlyOwner { (uint256, uint256 _random) = _rng.getRandom(); seed = _random; } // once calclated set the beacon function setBeacon(uint256 proof) external { require(SlothVDF.verify(proof, seeds[msg.sender], prime, iterations), 'Invalid proof'); beacon = proof; } function mint(address[] calldata _to) external { require(beacon > 0, 'Beacon not set'); uint256 _randomness = beacon; uint256 _random; for (uint256 i = 0; i < _to.length; i++) { (_randomness, _random) = _rng.getRandom(_randomness); _safeMint(_to[i], _pickRandomUniqueId(_random)); } } function _pickRandomUniqueId(uint256 random) private returns (uint256 id) { uint256 len = ids.length - index++; require(len > 0, 'no ids left'); uint256 randomIndex = random % len; id = ids[randomIndex] != 0 ? ids[randomIndex] : randomIndex; ids[randomIndex] = uint16(ids[len - 1] == 0 ? len - 1 : ids[len - 1]); ids[len - 1] = 0; } }
The post NFT – Mint Random Token ID appeared first on Justin Silver.
]]>The post Provable Randomness with VDF appeared first on Justin Silver.
]]>A Verifiable Delay Function (VDF) is a linearly computed function that takes a relatively long time to calculate, however the resulting proof can be verified to be the result of this computation in a much shorter period of time. Since the computation can’t be sped up through parallelization or other tricks we can be sure that for a given seed the resulting value can’t be known ahead of time – thus making it a provable random number.
We can apply this to a blockchain to achieve provable randomness without an oracle by having the client compute the VDF. This process takes two transactions – the first to commit to the process and generate a seed for the VDF input, and the second to submit the calculated proof. If the length of time to calculate the VDF proof exceeds the block finality for the chain you are using, then the result of the second transaction can’t be known when the seed is generated and can thus be used as a provable random number. For more secure applications you can use multiple threads to calculate multiple VDF proofs concurrently, or for less strict requirements you can bitshift the value to get “new” random numbers.
The good stuff first – provable random numbers without an oracle. The user first makes a request to createSeed()
typically with a commitment such as payment. This seed value along with the large prime and number of iterations are used to calculate the VDF proof – the larger the prime and the higher the iterations, the longer the proof takes to calculate and can be adjusted as needed. As long as the number of iterations takes longer to compute than the block finality we know it’s random since it’s not possible to know the result before it’s too late to change it. A blockchain like Fantom is ideal for this application with block times of ~1 second and finality after one block – validators cannot reorder blocks once the are minted.
This proof is then passed in to the prove()
function. It uses the previously created seed – which now can’t be changed – and other inputs to verify the proof. If it passes, the value can be used as a random number, or can be passed into another function (as below) to create multiple random numbers by shifting the bits on each request for a random(ish) number.
You can find large primes for your needs using https://bigprimes.org/, potentially even rotating them. Note that the code below is an example and should not be used directly without modifying for your needs.
// SPDX-License-Identifier: MIT pragma solidity ^0.8.11; import './libraries/SlothVDF.sol'; contract RandomVDFv1 { // large prime uint256 public prime = 432211379112113246928842014508850435796007; // adjust for block finality uint256 public iterations = 1000; // increment nonce to increase entropy uint256 private nonce; // address -> vdf seed mapping(address => uint256) public seeds; function createSeed() external payable { // commit funds/tokens/etc here // create a pseudo random seed as the input seeds[msg.sender] = uint256(keccak256(abi.encodePacked(msg.sender, nonce++, block.timestamp, blockhash(block.number - 1)))); } function prove(uint256 proof) external { // see if the proof is valid for the seed associated with the address require(SlothVDF.verify(proof, seeds[msg.sender], prime, iterations), 'Invalid proof'); // use the proof as a provable random number uint256 _random = proof; } }
This code is an example Hardhat script for calling the RandomVDFv1
contract. It shows the delay to calculate a proof and attempts to submit it. In a real implementation this could be an NFT mint, etc.
import { ethers, deployments } from 'hardhat'; import { RandomVDFv1 } from '../sdk/types'; import sloth from './slothVDF'; async function main() { // We get the signer const [signer] = await ethers.getSigners(); // get the contracts const deploy = await deployments.get('RandomVDFv1'); const token = (await ethers.getContractAt('RandomVDFv1', deploy.address, signer)) as RandomVDFv1; // the prime and iterations from the contract const prime = BigInt((await token.prime()).toString()); const iterations = BigInt((await token.iterations()).toNumber()); console.log('prime', prime.toString()); console.log('iterations', iterations.toString()); // create a new seed const tx = await token.createSeed(); await tx.wait(); // get the seed const seed = BigInt((await token.seeds(signer.address)).toString()); console.log('seed', seed.toString()); // compute the proof const start = Date.now(); const proof = sloth.computeBeacon(seed, prime, iterations); console.log('compute time', Date.now() - start, 'ms', 'vdf proof', proof); // this could be a mint function, etc const proofTx = await token.prove(proof); await proofTx.wait(); } main().catch((error) => { console.error(error); process.exit(1); });
This off-chain implementation of Sloth VDF in Typescript will let us compute the proof on the client.
const bexmod = (base: bigint, exponent: bigint, modulus: bigint) => { let result = 1n; for (; exponent > 0n; exponent >>= 1n) { if (exponent & 1n) { result = (result * base) % modulus; } base = (base * base) % modulus; } return result; }; const sloth = { compute(seed: bigint, prime: bigint, iterations: bigint) { const exponent = (prime + 1n) >> 2n; let beacon = seed % prime; for (let i = 0n; i < iterations; ++i) { beacon = bexmod(beacon, exponent, prime); } return beacon; }, verify(beacon: bigint, seed: bigint, prime: bigint, iterations: bigint) { for (let i = 0n; i < iterations; ++i) { beacon = (beacon * beacon) % prime; } seed %= prime; if (seed == beacon) return true; if (prime - seed === beacon) return true; return false; }, }; export default sloth;
Next we need to be able to verify the Sloth VDF proof on chain which is easy using the following library.
// SPDX-License-Identifier: MIT // https://eprint.iacr.org/2015/366.pdf pragma solidity ^0.8.11; library SlothVDF { /// @dev pow(base, exponent, modulus) /// @param base base /// @param exponent exponent /// @param modulus modulus function bexmod( uint256 base, uint256 exponent, uint256 modulus ) internal pure returns (uint256) { uint256 _result = 1; uint256 _base = base; for (; exponent > 0; exponent >>= 1) { if (exponent & 1 == 1) { _result = mulmod(_result, _base, modulus); } _base = mulmod(_base, _base, modulus); } return _result; } /// @dev compute sloth starting from seed, over prime, for iterations /// @param _seed seed /// @param _prime prime /// @param _iterations number of iterations /// @return sloth result function compute( uint256 _seed, uint256 _prime, uint256 _iterations ) internal pure returns (uint256) { uint256 _exponent = (_prime + 1) >> 2; _seed %= _prime; for (uint256 i; i < _iterations; ++i) { _seed = bexmod(_seed, _exponent, _prime); } return _seed; } /// @dev verify sloth result proof, starting from seed, over prime, for iterations /// @param _proof result /// @param _seed seed /// @param _prime prime /// @param _iterations number of iterations /// @return true if y is a quadratic residue modulo p function verify( uint256 _proof, uint256 _seed, uint256 _prime, uint256 _iterations ) internal pure returns (bool) { for (uint256 i; i < _iterations; ++i) { _proof = mulmod(_proof, _proof, _prime); } _seed %= _prime; if (_seed == _proof) return true; if (_prime - _seed == _proof) return true; return false; } }
Instead of using the proof directly as a single random number we can use it as the input to a random number generator for multiple provable random numbers. If we want to save a bit more gas instead of calling for a new number every time we can just shift the bits of the random number to the right and refill it when it empties. This pattern is more efficient if implemented directly your contract, but this library can be reused if you can support the relaxed security.
// SPDX-License-Identifier: MIT pragma solidity ^0.8.11; library Randomness { // memory struct for rand struct RNG { uint256 seed; uint256 nonce; } /// @dev get a random number function getRandom(RNG storage _rng) external returns (uint256 randomness, uint256 random) { return _getRandom(_rng, 0, 2**256 - 1, _rng.seed); } /// @dev get a random number function getRandom(RNG storage _rng, uint256 _randomness) external returns (uint256 randomness, uint256 random) { return _getRandom(_rng, _randomness, 2**256 - 1, _rng.seed); } /// @dev get a random number passing in a custom seed function getRandom( RNG storage _rng, uint256 _randomness, uint256 _seed ) external returns (uint256 randomness, uint256 random) { return _getRandom(_rng, _randomness, 2**256 - 1, _seed); } /// @dev get a random number in range (0, _max) function getRandomRange( RNG storage _rng, uint256 _max ) external returns (uint256 randomness, uint256 random) { return _getRandom(_rng, 0, _max, _rng.seed); } /// @dev get a random number in range (0, _max) function getRandomRange( RNG storage _rng, uint256 _randomness, uint256 _max ) external returns (uint256 randomness, uint256 random) { return _getRandom(_rng, _randomness, _max, _rng.seed); } /// @dev get a random number in range (0, _max) passing in a custom seed function getRandomRange( RNG storage _rng, uint256 _randomness, uint256 _max, uint256 _seed ) external returns (uint256 randomness, uint256 random) { return _getRandom(_rng, _randomness, _max, _seed); } /// @dev fullfill a random number request for the given inputs, incrementing the nonce, and returning the random number function _getRandom( RNG storage _rng, uint256 _randomness, uint256 _max, uint256 _seed ) internal returns (uint256 randomness, uint256 random) { // if the randomness is zero, we need to fill it if (_randomness <= 0) { // increment the nonce in case we roll over _randomness = uint256( keccak256( abi.encodePacked(_seed, _rng.nonce++, block.timestamp, msg.sender, blockhash(block.number - 1)) ) ); } // mod to the requested range random = _randomness % _max; // shift bits to the right to get a new random number randomness = _randomness >>= 4; } }
This example uses the Randomness library to generate multiple random numbers from a single proof in an efficient way. Note that this is a less secure application, though still valid for many use cases.
// SPDX-License-Identifier: MIT pragma solidity ^0.8.11; import './libraries/Randomness.sol'; import './libraries/SlothVDF.sol'; contract RandomVDFv2 { using Randomness for Randomness.RNG; Randomness.RNG private _rng; // large prime uint256 public prime = 432211379112113246928842014508850435796007; // adjust for block finality uint256 public iterations = 1000; // increment nonce to increase entropy uint256 private nonce; // address -> vdf seed mapping(address => uint256) public seeds; // commit funds/tokens/etc here function createSeed() external payable { // create a pseudo random seed as the input seeds[msg.sender] = Randomness.RNG(0, nonce++).getRandom(); } function prove(uint256 proof) external { // see if the proof is valid for the seed associated with the address require(SlothVDF.verify(proof, seeds[msg.sender], prime, iterations), 'Invalid proof'); uint256 _randomness; uint256 _random; (_randomness, _random) = _rng.getRandom(_randomness, proof); (_randomness, _random) = _rng.getRandom(_randomness, proof); (_randomness, _random) = _rng.getRandom(_randomness, proof); } }
The post Provable Randomness with VDF appeared first on Justin Silver.
]]>The post Fantom Lachesis Full Node RPC appeared first on Justin Silver.
]]>Create an Alpine Linux image to run the lachesis
node for the Fantom cryptocurrency.
FROM alpine:latest as build-stage ARG LACHESIS_VERSION=release/1.0.0-rc.0 ENV GOROOT=/usr/lib/go ENV GOPATH=/go ENV PATH=$GOROOT/bin:$GOPATH/bin:/build:$PATH RUN set -xe; \ apk add --no-cache --virtual .build-deps \ # get the build dependencies for go git make musl-dev go linux-headers; \ # install fantom lachesis from github mkdir -p ${GOPATH}; cd ${GOPATH}; \ git clone --single-branch --branch ${LACHESIS_VERSION} https://github.com/Fantom-foundation/go-lachesis.git; \ cd go-lachesis; \ make build -j$(nproc); \ mv build/lachesis /usr/local/bin; \ rm -rf /go; \ # remove our build dependencies apk del .build-deps; FROM alpine:latest as lachesis # copy the binary COPY --from=build-stage /usr/local/bin/lachesis /usr/local/bin/lachesis COPY run.sh /usr/local/bin WORKDIR /root ENV LACHESIS_PORT=5050 ENV LACHESIS_HTTP=18545 ENV LACHESIS_API=eth,ftm,debug,admin,web3,personal,net,txpool ENV LACHESIS_VERBOSITY=2 EXPOSE ${LACHESIS_PORT} EXPOSE ${LACHESIS_HTTP} VOLUME [ "/root/.lachesis" ] CMD ["run.sh"]
The run.sh
just starts the nodes with the ports you set in the environment.
#!/usr/bin/env sh set -xe lachesis \ --port ${LACHESIS_PORT} \ --http \ --http.addr "0.0.0.0" \ --http.port ${LACHESIS_HTTP} \ --http.api "${LACHESIS_API}" \ --nousb \ --verbosity ${LACHESIS_VERBOSITY}
Use docker-compose to define the TCP/UDP ports to expose as well as a data volume to persist the blockchain data.
version: '3.4' services: lachesis: image: doublesharp/fantom-lachesis:latest restart: always ports: - '5050:5050' - '5050:5050/udp' - '18545:18545' volumes: - lachesis:/root/.lachesis environment: LACHESIS_VERBOSITY: 2 volumes: lachesis: {}
The post Fantom Lachesis Full Node RPC appeared first on Justin Silver.
]]>The post Alpine Linux PHP + iconv fix appeared first on Justin Silver.
]]>To use PHP with iconv on Alpine Linux – in a Docker container for example – you need to use the preloadable iconv library, which was previously provided with the gnu-libiconv
package, but was removed after Alpine v3.13. After recently rebuilding an Alpine image and running a PHP script that required iconv, I saw the following error:
Notice: iconv(): Wrong charset, conversion from `UTF-8' to `UTF-8//IGNORE' is not allowed
To work around it I installed the gnu-libiconv package from the v3.13 repo. For my projects I went ahead and exported the preloadable binary once it was built as well so that I could just COPY
it into the image instead of building it – in my case it’s only for Alpine after all.
You can do this by using an Alpine image tag of alpine:3.13
to add gnu-libiconv
and compile /usr/lib/preloadable_libiconv.so
, then copy it to a volume to save the binary once the container exits – the output folder is called ./out
in this example.
% docker run -v $(pwd)/out:/out -it alpine:3.13 \ /bin/sh -c 'apk add --no-cache gnu-libiconv && cp -f /usr/lib/preloadable_libiconv.so /out/preloadable_libiconv.so' fetch https://dl-cdn.alpinelinux.org/alpine/v3.13/main/x86_64/APKINDEX.tar.gz fetch https://dl-cdn.alpinelinux.org/alpine/v3.13/community/x86_64/APKINDEX.tar.gz (1/1) Installing gnu-libiconv (1.15-r3) Executing busybox-1.32.1-r6.trigger OK: 8 MiB in 15 packages % ls -la out/preloadable_libiconv.so -rw-r--r-- 1 justin staff 1005216 Apr 23 14:32 out/preloadable_libiconv.so
Once you have the prebuilt binary you can use COPY
in your Dockerfile to use it without needing to build it.
# copy preloadable_libiconv.so from prebuilt COPY /rootfs/usr/lib/preloadable_libiconv.so /usr/lib/preloadable_libiconv.so ENV LD_PRELOAD /usr/lib/preloadable_libiconv.so php
If you prefer to install the older package that includes the preloadable binary in a different Alpine Dockerfile you can specify an older repository in a RUN
command, like so:
FROM wordpress:5.7.1-php7.4-fpm-alpine # ... some config RUN apk add --no-cache \ --repository http://dl-cdn.alpinelinux.org/alpine/v3.13/community/ \ --allow-untrusted \ gnu-libiconv ENV LD_PRELOAD /usr/lib/preloadable_libiconv.so php
The post Alpine Linux PHP + iconv fix appeared first on Justin Silver.
]]>The post Autodesk Fusion 360 Using eGPU appeared first on Justin Silver.
]]>Open a Finder window showing the latest version of Fusion 360 in the webdeploy folder.
F360_APP=Autodesk\ Fusion\ 360 F360_WEBDEPLOY="~/Applications/$F360_APP.app/Contents/MacOS/$F360_APP" F360_DIR=$(cat ~/Applications/"$F360_APP".app/Contents/MacOS/"$F360_APP" | grep destfolder | head -n 1 | cut -c 12- | tr -d '"') open -R "$F360_DIR/$F360_APP.app"
Press ⌘ + i
to open the Finder Inspector and check “Prefer External GPU”.
The next time you launch Fusion 360 it should use the external GPU instead of the internal options.
The post Autodesk Fusion 360 Using eGPU appeared first on Justin Silver.
]]>The post Chocolate Chip Cookies appeared first on Justin Silver.
]]>
The post Chocolate Chip Cookies appeared first on Justin Silver.
]]>The post Using APK for Alpine Linux with Docker appeared first on Justin Silver.
]]>Some quick tips on how to use apk
for Alpine Linux in a Docker environment. Some common use cases might be to install command line tools you will use in scripts, or to compile a PHP extension. In the former you will often be able to access a binary, and not need to worry about polluting much of your Docker layer with extra files. When you need to compile something however – like a PHP extension – you may need several build tools as well as libraries that you don’t need to keep around after you compile the module.
This first example is common for installing command line tools:
RUN apk add --no-cache --update \ bash curl findutils sed sudo
The next example shows how to compile PHP modules and remove their dependencies after compilation.
RUN set -xe; \ apk add --no-cache --virtual .build-deps $PHPIZE_DEPS \ # build tools autoconf g++ gcc make \ # lib tools bzip2-dev freetype-dev gettext-dev icu-dev imagemagick-dev libintl libjpeg-turbo-dev \ # libmcrypt-dev libpng-dev libxslt-dev libzip-dev \ ; \ docker-php-ext-configure \ gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ --with-png-dir=/usr/include/ \ ; \ docker-php-ext-install -j$(nproc) \ bcmath bz2 calendar exif gettext gd intl mysqli opcache pcntl pdo_mysql soap xsl zip \ ; \ pecl channel-update pecl.php.net && \ pecl install -o -f \ redis \ ; \ docker-php-ext-enable \ redis \ ; \ runDeps="$( \ scanelf --needed --nobanner --format '%n#p' --recursive /usr/local/lib/php/extensions \ | tr ',' '\n' \ | sort -u \ | awk 'system("[ -e /usr/local/lib/" $1 " ]") == 0 { next } { print "so:" $1 }' \ )"; \ apk add --virtual .phpexts-rundeps $runDeps; \ apk del .build-deps
The post Using APK for Alpine Linux with Docker appeared first on Justin Silver.
]]>The post OSX Mojave Disappearing Icons Fixed appeared first on Justin Silver.
]]>Icons on my Macbook Pro running OSX Mojave (10.14.6) started disappearing and being replaced with a generic icon in the Dock, Finder, Applications, task switcher, etc… which is pretty annoying as it makes it hard to determine which icon launches which app.
The standard recommendations of restarting the Dock and Finder did not fix the issue for me, nor did rebuilding the launch services database. Ultimately I needed to clear the icon cache, as well as the former items, and then after waiting a bit (and relaunching the applications) the icons appeared as normal.
find "${HOME}/Library/Application Support/Dock" -name "*-*.db" -maxdepth 1 -delete; sudo rm -rf /Library/Caches/com.apple.iconservices.store; sudo find /private/var/folders/ -name com.apple.dock.iconcache -exec rm -rf {} \;; sudo find /private/var/folders/ -name com.apple.iconservices -exec rm -rf {} \;; sudo touch /Applications/*; defaults write com.apple.dock ResetLaunchPad -bool true; killall Dock; killall Finder;
The post OSX Mojave Disappearing Icons Fixed appeared first on Justin Silver.
]]>