The post Deploy Contract With Custom Metadata Hash appeared first on Justin Silver.
]]>When you deploy a Solidity contract the compile has removed the comments from the bytecode, but the last 32 bytes of the bytecode is actually a hash of:
When you verify a contract on EtherScan, or SonicScan, this hash is also checked to make sure the verified contract has the correct license, etc. even if the bytecode itself matches. So… can we deploy a contract with a custom hash that will let us verify contracts with customize comments?
This contract will take a custom metadata hash and update the bytecode of our contract before deploying a new instance of it.
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {YourContract} from './YourContract.sol'; contract CustomFactory { event ContractDeployed(address indexed contractAddress, bytes32 metadataHash); error BytecodeTooShort(); /** * @notice Deploys a new contract with a modified metadata hash. * @param newMetadataHash The new metadata hash to append to the bytecode. */ function deployWithCustomMetadata(bytes32 newMetadataHash) external returns (address) { // Get the creation code of the contract bytes memory bytecode = abi.encodePacked(type(YourContract).creationCode); require(bytecode.length > 32, BytecodeTooShort()); // Replace the last 32 bytes of the bytecode with the new metadata hash for (uint256 i = 0; i < 32; i++) { bytecode[bytecode.length - 32 + i] = newMetadataHash[i]; } // Deploy the contract with the modified bytecode address deployedContract; assembly { deployedContract := create(0, add(bytecode, 0x20), mload(bytecode)) if iszero(deployedContract) { // if there wsa an error, revert and provide the bytecode revert(add(0x20, bytecode), mload(bytecode)) } } emit ContractDeployed(deployedContract, newMetadataHash); return deployedContract; } }
This is a sample contract with a comment /* YourContract */
we can use to replace with our custom comment.
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /* YourContract */ contract YourContract { /** * @notice Constructor for the contract. */ constructor() {} /** * @notice Returns the stored message. */ function getMessage() external view returns (string memory) { return 'hello world'; } }
This is a Hardhat test that loads necessary information from build info and artifacts provided by Hardhat, but you can also create this manually.
// Hardhat test for CustomFactory and YourContract import * as fs from 'fs/promises'; import * as path from 'path'; import { expect } from 'chai'; import { ethers } from 'hardhat'; import { encode as cborEncode } from 'cbor'; import { loadFixture } from '@nomicfoundation/hardhat-network-helpers'; import { AddressLike, ContractTransactionResponse } from 'ethers'; import { YourContract } from 'sdk/types'; function calculateMetadataHash(sourceCode: string, comment: string, originalMetadata: any): string { const keccak256 = ethers.keccak256; const toUtf8Bytes = ethers.toUtf8Bytes; // Inject the comment const modifiedSourceCode = sourceCode.replace(/\/\* YourContract \*\//gs, comment); // console.log('modifiedSourceCode', modifiedSourceCode); // Calculate keccak256 of the modified source code const sourceCodeHash = keccak256(toUtf8Bytes(modifiedSourceCode)); // Use original metadata as a base const metadata = { ...originalMetadata, sources: { 'contracts/YourContract.sol': { ...originalMetadata.sources['contracts/YourContract.sol'], keccak256: sourceCodeHash, }, }, }; // CBOR encode the metadata and calculate the hash const cborData = cborEncode(metadata); const metadataHash = keccak256(cborData); return metadataHash; } export const fixture = async () => { const Factory = await ethers.getContractFactory('CustomFactory'); const factory = await Factory.deploy(); await factory.waitForDeployment(); const [owner] = await ethers.getSigners(); return { factory, owner }; }; export async function getDeployedContractAddress( tx: Promise<ContractTransactionResponse> | ContractTransactionResponse, ): Promise<AddressLike> { const _tx = tx instanceof Promise ? await tx : tx; const _receipt = await _tx.wait(); const _interface = new ethers.Interface([ 'event ContractDeployed(address indexed contractAddress, bytes32 metadataHash)', ]); const _data = _receipt?.logs[0].data; const _topics = _receipt?.logs[0].topics; const _event = _interface.decodeEventLog('ContractDeployed', _data || ethers.ZeroHash, _topics); return _event.contractAddress; } describe('CustomFactory and YourContract', function () { it('should deploy a contract with a custom metadata hash', async function () { const { factory } = await loadFixture(fixture); // Load source code of YourContract from file const sourceCodePath = path.join(__dirname, '../contracts/YourContract.sol'); const sourceCode = await fs.readFile(sourceCodePath, 'utf8'); const comment = ('/* My Contract */'); // Load original metadata from build-info const buildInfoPath = path.join(__dirname, `../artifacts/build-info`); const buildInfoFiles = await fs.readdir(buildInfoPath); let buildInfo: any; for (const file of buildInfoFiles) { const fullPath = path.join(buildInfoPath, file); const currentBuildInfo = JSON.parse(await fs.readFile(fullPath, 'utf8')); if (currentBuildInfo.output.contracts['contracts/YourContract.sol']) { buildInfo = currentBuildInfo; break; } } if (!buildInfo) { throw new Error('Build info for YourContract not found.'); } const originalMetadata = buildInfo.output.contracts['contracts/YourContract.sol'].YourContract.metadata; // Calculate the metadata hash const newMetadataHash = calculateMetadataHash(sourceCode, comment, JSON.parse(originalMetadata)); // Deploy the contract via the Factory const tx = await factory.deployWithCustomMetadata(newMetadataHash); const deployedAddress = await getDeployedContractAddress(tx); // console.log('deployedAddress', deployedAddress); // Validate the deployment expect(deployedAddress).to.be.properAddress; // Interact with the deployed contract const YourContract = await ethers.getContractFactory('YourContract'); const yourContract = YourContract.attach(deployedAddress) as YourContract; // Ensure it was initialized properly const message = await yourContract.message(); expect(message).to.equal('hello world'); // Get the bytecode of the deployed contract const deployedBytecode = await ethers.provider.getCode(deployedAddress); // Get the last 32 bytes of the deployed bytecode const deployedBytecodeHash = deployedBytecode.slice(-64); // Validate that the hash matches our new metadata hash expect('0x' + deployedBytecodeHash).to.equal(newMetadataHash); }); });
The post Deploy Contract With Custom Metadata Hash appeared first on Justin Silver.
]]>The post Slither & Echidna + Remappings appeared first on Justin Silver.
]]>While testing a project using hardhat and Echidna I was able to run all tests in the project with echidna-test .
but was not able to run tests in a specific contract that imported contracts using NPM and the node_modules directory, such as @openzeppelin. When running echidna-test
the following error would be returned
> echidna-test path/to/my/Contract.sol --contract Contract echidna-test: Couldn't compile given file stdout: stderr: ERROR:CryticCompile:Invalid solc compilation Error: Source "@openzeppelin/contracts/utils/Address.sol" not found: File not found. Searched the following locations: "". --> path/to/my/Contract.sol:4:1: | 4 | import {Address} from '@openzeppelin/contracts/utils/Address.sol'; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
To fix this, I added Solc Remappings for Slither and Echidna.
Make sure that you have Slither and Echidna installed. Follow the install instructions on their site, or on OSX with Homebrew run brew install echidna
Create a Slither JSON config file – named slither.config.json
– to use filter_paths
to exclude some directories and provide remappings for node_modules
to solc
.
{ "filter_paths": "(mocks/|test/|@openzeppelin/)", "solc_remaps": "@=node_modules/@" }
Slither will pick up the config file automatically.
slither path/to/my/Contract.sol
For multiple remappings using an array of strings for solc_remaps
.
{ "filter_paths": "(mocks/|test/|@openzeppelin/)", "solc_remaps": [ "@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/", "@openzeppelin/contracts-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable/" ] }
Note that if you are using Hardhat or similar for your projects, slither will use it for the compile if a configuration can be found.
slither .
To force a particular compiler, specify it with the command.
slither --compile-force-framework solc ./contracts
For Echidna we can create a YAML config file and pass the solc
remappings to crytic-compile
via cryticArgs
.
# provide solc remappings to crytic-compile cryticArgs: ['--solc-remaps', '@=node_modules/@']
When running echidna-test
we can use the --config
option to specify the YAML config file and pick up our remappings (and other settings).
echidna-test --config echidna.yaml path/to/my/Contract.sol --contract Contract
{ "remappings": ["@openzeppelin/=node_modules/@openzeppelin/"] }
myth analyze --solc-json mythril.solc.json path/to/my/Contract.sol
The post Slither & Echidna + Remappings appeared first on Justin Silver.
]]>The post UnsafeMath for Solidity 0.8.0+ appeared first on Justin Silver.
]]>UPDATE: Check out the
@0xdoublesharp/unsafe-math
module available on NPM for an easy to use, prepackaged, and tested version of this library!
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 – 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 = _to.length; i != 0; ) { unchecked{ --i; } uint256 _random = uint256(keccak256(abi.encodePacked(index, msg.sender, block.timestamp, blockhash(block.number-1)))); _mint(_to[i], _pickRandomUniqueId(_random)); } } function _pickRandomUniqueId(uint256 random) private returns (uint256 id) { unchecked{ ++index; } 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 = _to.length; i != 0; ) { unchecked{ --i; } (_randomness, _random) = _rng.getRandom(_randomness); _mint(_to[i], _pickRandomUniqueId(_random)); } } function _pickRandomUniqueId(uint256 random) private returns (uint256 id) { unchecked{ ++index; } 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 = _to.length; i != 0; ) { unchecked{ --i; } (_randomness, _random) = _rng.getRandom(_randomness); _mint(_to[i], _pickRandomUniqueId(_random)); } } function _pickRandomUniqueId(uint256 random) private returns (uint256 id) { unchecked{ ++index; } 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 Syscoin + ZMQ + Node.js = Realtime Blockchain Updates! appeared first on Justin Silver.
]]>You can use the ZMQ topic message queues in Syscoin to receive realtime updates for your application. Using in conjunction with syscoin-core
to blockchain-enable your applications in no time.
Make sure to enable the ZMQ listeners in your syscoin.conf
file and restart syscoind
or Syscoin Core Qt.
# server server=1 daemon=1 # indexes addressindex=1 txindex=1 litemode=0 # rpc rpcuser=u rpcpassword=p rpcport=8370 rpcallowip=127.0.0.1 # zmq listener config zmqpubaliasrecord=tcp://127.0.0.1:3030 zmqpubaliashistory=tcp://127.0.0.1:3030 zmqpubaliastxhistory=tcp://127.0.0.1:3030 zmqpubassetrecord=tcp://127.0.0.1:3030 zmqpubassetallocation=tcp://127.0.0.1:3030 zmqpubassethistory=tcp://127.0.0.1:3030 zmqpubcertrecord=tcp://127.0.0.1:3030 zmqpubcerthistory=tcp://127.0.0.1:3030 zmqpubescrowrecord=tcp://127.0.0.1:3030 zmqpubescrowbid=tcp://127.0.0.1:3030 zmqpubescrowfeedback=tcp://127.0.0.1:3030 zmqpubofferrecord=tcp://127.0.0.1:3030 zmqpubofferhistory=tcp://127.0.0.1:3030 zmqpubhashblock=tcp://127.0.0.1:3030 zmqpubhashtx=tcp://127.0.0.1:3030 zmqpubhashtxlock=tcp://127.0.0.1:3030 zmqpubrawblock=tcp://127.0.0.1:3030 zmqpubrawtx=tcp://127.0.0.1:3030 zmqpubrawtxlock=tcp://127.0.0.1:3030
You will need to npm install
the module zeromq
.
const zeromq = require('zeromq'); const subscriber = zeromq.socket('sub'); subscriber.on('message', async (topic, message) => { topic = topic.toString('utf8'); message = message.toString('utf8') const alias = JSON.parse(message); console.log(JSON.stringify(alias, null, 2)); }); // connect to message producer subscriber.connect('tcp://127.0.0.1:3030'); subscriber.subscribe('aliasrecord'); console.log('subscribed to syscoin topic aliasrecord');
Run your script with the following:
> node zmq-client.js subscribed to syscoin topic aliasrecord { "_id": "gldm1", "address": "SRxK2GjfzTrm8z5PgCtLKzheN5ebd5kN8f", "expires_on": 1590601936, "encryption_privatekey": "", "encryption_publickey": "" } { "_id": "elatte", "address": "Sd8JMHxtuFVSVJN2V51M27S6MkBBMjgjHY", "expires_on": 1559077278, "encryption_privatekey": "", "encryption_publickey": "" } { "_id": "primitive7", "address": "Sk7q3kZcttBNVkUwpMXU59yQf9Pco4sAAJ", "expires_on": 1558656041, "encryption_privatekey": "", "encryption_publickey": "" } { "_id": "primitive9", "address": "SYKff6VzkrzmSn9tL3zZE7FmV2dGFSKfxs", "expires_on": 1558656041, "encryption_privatekey": "", "encryption_publickey": "" } // ....
The post Syscoin + ZMQ + Node.js = Realtime Blockchain Updates! appeared first on Justin Silver.
]]>The post Syscoin + Node.js = Blockchain Apps! appeared first on Justin Silver.
]]>You will need to have syscoind
or Syscoin Core Qt running on your system and have node
/npm
installed. If you don’t have Node.js I recommend installing it via nvm
.
First make sure that syscoind
is configured so that you can connect to the RPC server locally. Use the example below, choosing a secret rcpuser
, rpcpassword
, and optionally changing the rpcport
. You will need to use these values later to connect to query the blockchain using Node.js later.
You don’t really need the ZMQ config for this example, but it shows how to tell Syscoin to listen on those ports should you want to implement a Node.js ZMQ client in your application :).
Before making changes be sure to stop Syscoin by running syscoin-cli stop
, update the configuration, then run syscoind
to start the process again. If you change the config file before stopping Syscoin it will prevent syscoin-cli
from being able to communicate with the RPC server properly. Changing the index values may require you to restart with syscoind -reindex
.
# server server=1 daemon=1 # indexes addressindex=1 txindex=1 litemode=0 # rpc rpcuser=u rpcpassword=p rpcport=8370 rpcallowip=127.0.0.1 # zmq listener config zmqpubaliasrecord=tcp://127.0.0.1:3030 zmqpubaliashistory=tcp://127.0.0.1:3030 zmqpubaliastxhistory=tcp://127.0.0.1:3030 zmqpubassetrecord=tcp://127.0.0.1:3030 zmqpubassetallocation=tcp://127.0.0.1:3030 zmqpubassethistory=tcp://127.0.0.1:3030 zmqpubcertrecord=tcp://127.0.0.1:3030 zmqpubcerthistory=tcp://127.0.0.1:3030 zmqpubescrowrecord=tcp://127.0.0.1:3030 zmqpubescrowbid=tcp://127.0.0.1:3030 zmqpubescrowfeedback=tcp://127.0.0.1:3030 zmqpubofferrecord=tcp://127.0.0.1:3030 zmqpubofferhistory=tcp://127.0.0.1:3030 zmqpubhashblock=tcp://127.0.0.1:3030 zmqpubhashtx=tcp://127.0.0.1:3030 zmqpubhashtxlock=tcp://127.0.0.1:3030 zmqpubrawblock=tcp://127.0.0.1:3030 zmqpubrawtx=tcp://127.0.0.1:3030 zmqpubrawtxlock=tcp://127.0.0.1:3030
Next create a new folder for your project and create a Node project by running npm init
and answering the questions as you see fit. The only module that is required is syscoin-core
, a cutting edge version can be found at my fork.
mkdir -p my-syscoin-app && cd my-syscoin-app npm init # answer init questions npm install -S https://github.com/doublesharp/syscoin-core.git#dev-3.0
Once syscoin-core
is installed you can use the following example to get started. Make sure that the port
, username
, and password
match the values set in your syscoin.conf
file.
const SyscoinClient = require('@syscoin/syscoin-core'); const syscoin = new SyscoinClient({ host: process.env.SYSCOIND_HOST || 'localhost', port: process.env.SYSCOIND_PORT || 8370, username: process.env.SYSCOIND_USER || 'u', password: process.env.SYSCOIND_PASS || 'p', timeout: 30000, }); async function run() { // prune expired data! const pruneStart = Date.now(); const prune = await syscoin.pruneSyscoinServices(); console.log('pruned', prune, 'in', Date.now()-pruneStart, 'ms'); // get all alias records const aliasStart = Date.now(); const list = await syscoin.listAliases(0); console.log('fetched', list.length, 'aliases in', Date.now()-aliasStart, 'ms'); // get just an array of alias names const aliases = list.map((alias) => alias._id); // ...etc process.exit(0); } run();
> node index.js pruned { services_cleaned: 0 } in 341 ms fetched 15162 aliases in 1685 ms
You can take this same basic structure and turn it into an Express app, etc, depending on the needs of your application. It’s surprisingly easy to get started building blockchain applications with Syscoin!
The post Syscoin + Node.js = Blockchain Apps! appeared first on Justin Silver.
]]>The post Syscoin 3 Alias Creation appeared first on Justin Silver.
]]>Before we start – please read this post fully! It will only take a few minutes, and you will feel much more comfortable creating your alias. Don’t worry about all the text, I included a lot of detail but these are the only commands you actually need to run – it’s mostly cutting and pasting.
aliasnew YOUR_ALIAS "" 3 EXPIRATION YOUR_ALIAS_ADDRESS "" "" "" syscointxfund ALIAS_HEX '{"addresses": ["YOUR_FUNDING_ADDRESS"]}' signrawtransaction RAW_HEX syscoinsendrawtransaction SIGNED_HEX
To use any Syscoin Alias service you will first need to enable the address index in the Syscoin Core by adding addressindex=1
to your syscoin.conf
file, then restarting Syscoin Core Qt and reindexing (it will prompt you when you restart). If you are using syscoind
directly you will need to run syscoin-cli stop
followed by syscoind -reindex
to trigger the reindexing process.
The process to create a Syscoin Alias is just 4 commands. You will need to run them once to reserve your alias, then a second time after waiting for a block confirmation to finalize your alias registration. You will need a small amount of SYS available, and either an existing address (funded or unfunded) or a new address to assign to the alias.
When selecting an expiration timestamp you will need to use Unix Epoch Time. Determine your expiration time by using a site such as https://www.epochconverter.com/ and make sure to only select 1 or 2 years for your expiration – aliases become exponentially more expensive and be quite pricey to reserve for a very long time.
If you’re wallet is encrypted (and it should be!) then you will need to unlock you wallet with `walletpassphrase ‘YOUR_PASSSPHRASE’ 1800` to unlock it for 30 minutes (1800 seconds).
It is recommended to create a new address to use to fund your alias creation transaction – you can do this by sending 1 SYS to a new address in your wallet and noting the address for later use when funding your alias creation transaction.
First, get familiar with the aliasnew command. You can read the help below or run syscoin-cli help aliasnew
to see it on your system.
aliasnew [aliasname] [public value] [accept_transfers_flags=3] [expire_timestamp] [address] [encryption_privatekey] [encryption_publickey] [witness] <aliasname> alias name. <public value> alias public profile data, 256 characters max. <accept_transfers_flags> 0 for none, 1 for accepting certificate transfers, 2 for accepting asset transfers and 3 for all. Default is 3. <expire_timestamp> Time in seconds. Future time when to expire alias. It is exponentially more expensive per year, calculation is FEERATE*(2.88^years). FEERATE is the dynamic satoshi per byte fee set in the rate peg alias used for this alias. Defaults to 1 hour. <address> Address for this alias. <encryption_privatekey> Encrypted private key used for encryption/decryption of private data related to this alias. Should be encrypted to publickey. <encryption_publickey> Public key used for encryption/decryption of private data related to this alias. <witness> Witness alias name that will sign for web-of-trust notarization of this transaction.
If you are running these commands from the command line and not the Syscoin Core Qt Console, just add “syscoin-cli” before each command, otherwise follow them filling in your details where there are capital letters.
In these examples YOUR_ALIAS
is the alias name you want to register, in lower case (ironically). Replace YOUR_ALIAS_ADDRESS
with the address you want to associate with your alias (possibly your masternode staking address?). For the EXPIRATION
use https://www.epochconverter.com/ to generate the expiration in Unix Epoch time – no more than a year or two is strongly recommend due to exponentially increasing fees.
aliasnew YOUR_ALIAS "" 3 EXPIRATION YOUR_ALIAS_ADDRESS "" "" ""
[ "00740000010ae45894ba90b24c5120db9354d44fc93f17b86dcc269eddd8111f2454c629f20200000000feffffff02f1020000000000005e515140313463626631663365313533636633656166643736346530396262303532373239336134343935353934333936363536323665353436656533633531316364646d7576a91464c63b4b3398452a32a8d9f47b42ee63d2a59cac88ac85c2e59a000000001976a914c1da47e228637af983e1c75172506eabd77962c188acf4040000", "TKA43ch9qBVh7H8LmfSNFN5GqPsXfLyNqJ" ]
Copy the hex string result from the aliasnew
command (the top part), which we will call ALIAS_HEX
below to keep it readable. Replace YOUR_FUNDING_ADDRESS
with the address you want to fund the transaction with – note that the change from this transaction will be sent to a new address, so it’s best to fund with a different address than you want to have your SYS associated with.
ALIAS_HEX
=output from aliasnew, “0074000….” in the example aboveYOUR_FUNDING_ADDRESS
=address to fund txsyscointxfund ALIAS_HEX '{"addresses": ["YOUR_FUNDING_ADDRESS"]}'
[ "007400000179347c7191687ee50469bb8cb8a50a978db51651f482e114d24a6ba926a027ed010000001976a914572b8c4219a138984742c46b231f63afaad6688c88acffffffff02b80b0000000000005e515140373137383831646139346264353934343335383837363766373739343538386134373966313064613163373862313235373333623664373165386262623666326d7576a9149ec5c7b6544d752575ed7672d7984b5aceb14cd588ac534c0609000000001976a9147dc19cec3f574a85d94f2b05186bf3fec39f170588ac00000000" ]
The result of this command is the raw transaction hex, which we will call RAW_HEX
from now on, again for brevity. Next you need to sign this raw transaction with your private key before it can be submitted.
RAW_HEX
=output from syscointxfundsignrawtransaction RAW_HEX
{ "hex": "00740000010ae45894ba90b24c5120db9354d44fc93f17b86dcc269eddd8111f2454c629f2020000006a47304402200be0e27776d44cb9ce69dc683c5d1fc41aceca4131392957181f25d40eae186b02201ac64cdbd219f6e587f347d87f5f72f36a22a85050a8c4d79e0d9e3adb29eff9012103d0f289fbb510b953fa5cecc20cf70f95a249c6e30cc8fec292b45a0637120a19feffffff02f1020000000000005e515140313463626631663365313533636633656166643736346530396262303532373239336134343935353934333936363536323665353436656533633531316364646d7576a91464c63b4b3398452a32a8d9f47b42ee63d2a59cac88ac85c2e59a000000001976a914c1da47e228637af983e1c75172506eabd77962c188acf4040000", "complete": true }
This gives us a signed transaction hex that we can submit to the network which we will call SIGNED_HEX
. Now, submit it to the network to reserve your alias.
SIGNED_HEX
=output from signrawtransactionsyscoinsendrawtransaction SIGNED_HEX
{ "txid": "d4a5ecdd111978082862f2b2ebc199f29158dd5f397df3117dd68e791ed9f446" }
The result of this is the transaction id that was submitted to the network. Now we need to wait for a couple of blocks before we can confirm the alias reservation to complete the registration. Check the current block height so you know where to start.
> getinfo
{ "version": "3.0.4.1", "dashversion": "0.12.3", "protocolversion": 70221, "walletversion": 3000000, "balance": 26.42070085, "privatesend_balance": 0.00000000, "blocks": 36817, "timeoffset": 0, "connections": 8, "proxy": "", "difficulty": 137061451496.0324, "testnet": false, "keypoololdest": 1525736009, "keypoolsize": 1999, "unlocked_until": 0, "paytxfee": 0.00000000, "relayfee": 0.00010000, "errors": "" }
In the results you can see "blocks": 36817,
though it will be higher when you run these commands :). Wait for a minute or two, then run it again to check the height. After a couple of blocks you are ready to continue. Enter the same four commands again entering exactly the same information for aliasnew
but updating each with the response of the commands the second time you run them.
aliasnew YOUR_ALIAS "" 3 EXPIRATION YOUR_ALIAS_ADDRESS "" "" "" syscointxfund ALIAS_HEX '{"addresses": ["YOUR_FUNDING_ADDRESS"]}' signrawtransaction RAW_HEX syscoinsendrawtransaction SIGNED_HEX
Now you’re done! You can check the status of your alias with the following commands. Fund your alias by sending to either your alias name or the underlying address.
aliasinfo YOUR_ALIAS aliasbalance YOUR_ALIAS
The post Syscoin 3 Alias Creation appeared first on Justin Silver.
]]>The post Syscoin Core 3.0 Build Scripts appeared first on Justin Silver.
]]>Use the following commands to compile Syscoin 3.0 on a variety of operating systems. These scripts will prepare your system with the proper build tools and install the necessary libraries and source code to compile binaries on and for your system.
Invalid URL
Invalid URL
Invalid URL
Invalid URL
Invalid URL
Invalid URL
The post Syscoin Core 3.0 Build Scripts appeared first on Justin Silver.
]]>