add erc20 and modify basic chain folder

pull/3/head
yjjnls 2019-05-21 17:07:33 +08:00
parent e93887165b
commit d7120b7b87
33 changed files with 480 additions and 2 deletions

3
src/.gitignore vendored
View File

@ -1,2 +1,3 @@
package-lock.json
node_modules
node_modules
build

View File

@ -0,0 +1,23 @@
pragma solidity >=0.4.21 <0.6.0;
contract Migrations {
address public owner;
uint public last_completed_migration;
constructor() public {
owner = msg.sender;
}
modifier restricted() {
if (msg.sender == owner) _;
}
function setCompleted(uint completed) public restricted {
last_completed_migration = completed;
}
function upgrade(address new_address) public restricted {
Migrations upgraded = Migrations(new_address);
upgraded.setCompleted(last_completed_migration);
}
}

View File

@ -0,0 +1,73 @@
/*
Implements EIP20 token standard: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md
.*/
pragma solidity ^0.5.0;
import "./ERC20Interface.sol";
contract ERC20 is ERC20Interface {
uint256 constant private MAX_UINT256 = 2**256 - 1;
mapping (address => uint256) public balances;
mapping (address => mapping (address => uint256)) public allowed;
/*
NOTE:
The following variables are OPTIONAL vanities. One does not have to include them.
They allow one to customise the token contract & in no way influences the core functionality.
Some wallets/interfaces might not even bother to look at this information.
*/
string public name; //fancy name: eg Simon Bucks
uint8 public decimals; //How many decimals to show.
string public symbol; //An identifier: eg SBX
uint256 public totalSupply;
constructor(
uint256 _initialAmount,
string memory _tokenName,
uint8 _decimalUnits,
string memory _tokenSymbol
) public {
balances[msg.sender] = _initialAmount; // Give the creator all initial tokens
totalSupply = _initialAmount; // Update total supply
name = _tokenName; // Set the name for display purposes
decimals = _decimalUnits; // Amount of decimals for display purposes
symbol = _tokenSymbol; // Set the symbol for display purposes
}
function transfer(address _to, uint256 _value) public returns (bool success) {
require(balances[msg.sender] >= _value);
balances[msg.sender] -= _value;
balances[_to] += _value;
emit Transfer(msg.sender, _to, _value); //solhint-disable-line indent, no-unused-vars
return true;
}
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
uint256 allowance = allowed[_from][msg.sender];
require(balances[_from] >= _value && allowance >= _value);
balances[_to] += _value;
balances[_from] -= _value;
if (allowance < MAX_UINT256) {
allowed[_from][msg.sender] -= _value;
}
emit Transfer(_from, _to, _value); //solhint-disable-line indent, no-unused-vars
return true;
}
function balanceOf(address _owner) public view returns (uint256 balance) {
return balances[_owner];
}
function approve(address _spender, uint256 _value) public returns (bool success) {
allowed[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value); //solhint-disable-line indent, no-unused-vars
return true;
}
function allowance(address _owner, address _spender) public view returns (uint256 remaining) {
return allowed[_owner][_spender];
}
}

View File

@ -0,0 +1,50 @@
// Abstract contract for the full ERC 20 Token standard
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md
pragma solidity ^0.5.0;
contract ERC20Interface {
/* This is a slight change to the ERC20 base standard.
function totalSupply() constant returns (uint256 supply);
is replaced with:
uint256 public totalSupply;
This automatically creates a getter function for the totalSupply.
This is moved to the base contract since public getter functions are not
currently recognised as an implementation of the matching abstract
function by the compiler.
*/
/// total amount of tokens
uint256 public totalSupply;
/// @param _owner The address from which the balance will be retrieved
/// @return The balance
function balanceOf(address _owner) public view returns (uint256 balance);
/// @notice send `_value` token to `_to` from `msg.sender`
/// @param _to The address of the recipient
/// @param _value The amount of token to be transferred
/// @return Whether the transfer was successful or not
function transfer(address _to, uint256 _value) public returns (bool success);
/// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from`
/// @param _from The address of the sender
/// @param _to The address of the recipient
/// @param _value The amount of token to be transferred
/// @return Whether the transfer was successful or not
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);
/// @notice `msg.sender` approves `_spender` to spend `_value` tokens
/// @param _spender The address of the account able to transfer the tokens
/// @param _value The amount of tokens to be approved for transfer
/// @return Whether the approval was successful or not
function approve(address _spender, uint256 _value) public returns (bool success);
/// @param _owner The address of the account owning tokens
/// @param _spender The address of the account able to transfer the tokens
/// @return Amount of remaining tokens allowed to spent
function allowance(address _owner, address _spender) public view returns (uint256 remaining);
// solhint-disable-next-line no-simple-event-func-name
event Transfer(address indexed _from, address indexed _to, uint256 _value);
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
}

View File

@ -0,0 +1,5 @@
const Migrations = artifacts.require("Migrations");
module.exports = function (deployer) {
deployer.deploy(Migrations);
};

View File

@ -0,0 +1,5 @@
const ERC20 = artifacts.require("ERC20");
module.exports = (deployer) => {
deployer.deploy(ERC20, 10000, 'Simon Bucks', 1, 'SBX');
};

221
src/ERC20/test/erc20.js Normal file
View File

@ -0,0 +1,221 @@
"use strict";
let assertRevert = async (promise) => {
try {
await promise;
} catch (error) {
const revertFound = error.message.search('revert') >= 0;
assert(revertFound, `Expected "revert", got ${error} instead`);
return;
}
assert.fail('Expected revert not received');
};
const ERC20Abstraction = artifacts.require('ERC20');
let HST;
contract('ERC20', (accounts) => {
beforeEach(async () => {
HST = await ERC20Abstraction.new(10000, 'Simon Bucks', 1, 'SBX', { from: accounts[0] });
});
it('creation: should create an initial balance of 10000 for the creator', async () => {
const balance = await HST.balanceOf.call(accounts[0]);
assert.strictEqual(balance.toNumber(), 10000);
});
it('creation: test correct setting of vanity information', async () => {
const name = await HST.name.call();
assert.strictEqual(name, 'Simon Bucks');
const decimals = await HST.decimals.call();
assert.strictEqual(decimals.toNumber(), 1);
const symbol = await HST.symbol.call();
assert.strictEqual(symbol, 'SBX');
});
it.skip('creation: should succeed in creating over 2^256 - 1 (max) tokens', async () => {
// 2^256 - 1
const HST2 = await ERC20Abstraction.new('115792089237316195423570985008687907853269984665640564039457584007913129639935', 'Simon Bucks', 1, 'SBX', { from: accounts[0] });
const totalSupply = await HST2.totalSupply();
const match = totalSupply.equals('1.15792089237316195423570985008687907853269984665640564039457584007913129639935e+77');
assert(match, 'result is not correct');
});
// TRANSERS
// normal transfers without approvals
it('transfers: ether transfer should be reversed.', async () => {
const balanceBefore = await HST.balanceOf.call(accounts[0]);
assert.strictEqual(balanceBefore.toNumber(), 10000);
await assertRevert(new Promise((resolve, reject) => {
web3.eth.sendTransaction({ from: accounts[0], to: HST.address, value: web3.utils.toWei('10', 'Ether') }, (err, res) => {
if (err) { reject(err); }
resolve(res);
});
}));
const balanceAfter = await HST.balanceOf.call(accounts[0]);
assert.strictEqual(balanceAfter.toNumber(), 10000);
});
it('transfers: should transfer 10000 to accounts[1] with accounts[0] having 10000', async () => {
await HST.transfer(accounts[1], 10000, { from: accounts[0] });
const balance = await HST.balanceOf.call(accounts[1]);
assert.strictEqual(balance.toNumber(), 10000);
});
it('transfers: should fail when trying to transfer 10001 to accounts[1] with accounts[0] having 10000', async () => {
await assertRevert(HST.transfer.call(accounts[1], 10001, { from: accounts[0] }));
});
it('transfers: should handle zero-transfers normally', async () => {
assert(await HST.transfer.call(accounts[1], 0, { from: accounts[0] }), 'zero-transfer has failed');
});
// NOTE: testing uint256 wrapping is impossible since you can't supply > 2^256 -1
// todo: transfer max amounts
// APPROVALS
it('approvals: msg.sender should approve 100 to accounts[1]', async () => {
await HST.approve(accounts[1], 100, { from: accounts[0] });
const allowance = await HST.allowance.call(accounts[0], accounts[1]);
assert.strictEqual(allowance.toNumber(), 100);
});
// bit overkill. But is for testing a bug
it('approvals: msg.sender approves accounts[1] of 100 & withdraws 20 once.', async () => {
const balance0 = await HST.balanceOf.call(accounts[0]);
assert.strictEqual(balance0.toNumber(), 10000);
await HST.approve(accounts[1], 100, { from: accounts[0] }); // 100
const balance2 = await HST.balanceOf.call(accounts[2]);
assert.strictEqual(balance2.toNumber(), 0, 'balance2 not correct');
await HST.transferFrom.call(accounts[0], accounts[2], 20, { from: accounts[1] });
await HST.allowance.call(accounts[0], accounts[1]);
await HST.transferFrom(accounts[0], accounts[2], 20, { from: accounts[1] }); // -20
const allowance01 = await HST.allowance.call(accounts[0], accounts[1]);
assert.strictEqual(allowance01.toNumber(), 80); // =80
const balance22 = await HST.balanceOf.call(accounts[2]);
assert.strictEqual(balance22.toNumber(), 20);
const balance02 = await HST.balanceOf.call(accounts[0]);
assert.strictEqual(balance02.toNumber(), 9980);
});
// should approve 100 of msg.sender & withdraw 50, twice. (should succeed)
it('approvals: msg.sender approves accounts[1] of 100 & withdraws 20 twice.', async () => {
await HST.approve(accounts[1], 100, { from: accounts[0] });
const allowance01 = await HST.allowance.call(accounts[0], accounts[1]);
assert.strictEqual(allowance01.toNumber(), 100);
await HST.transferFrom(accounts[0], accounts[2], 20, { from: accounts[1] });
const allowance012 = await HST.allowance.call(accounts[0], accounts[1]);
assert.strictEqual(allowance012.toNumber(), 80);
const balance2 = await HST.balanceOf.call(accounts[2]);
assert.strictEqual(balance2.toNumber(), 20);
const balance0 = await HST.balanceOf.call(accounts[0]);
assert.strictEqual(balance0.toNumber(), 9980);
// FIRST tx done.
// onto next.
await HST.transferFrom(accounts[0], accounts[2], 20, { from: accounts[1] });
const allowance013 = await HST.allowance.call(accounts[0], accounts[1]);
assert.strictEqual(allowance013.toNumber(), 60);
const balance22 = await HST.balanceOf.call(accounts[2]);
assert.strictEqual(balance22.toNumber(), 40);
const balance02 = await HST.balanceOf.call(accounts[0]);
assert.strictEqual(balance02.toNumber(), 9960);
});
// should approve 100 of msg.sender & withdraw 50 & 60 (should fail).
it('approvals: msg.sender approves accounts[1] of 100 & withdraws 50 & 60 (2nd tx should fail)', async () => {
await HST.approve(accounts[1], 100, { from: accounts[0] });
const allowance01 = await HST.allowance.call(accounts[0], accounts[1]);
assert.strictEqual(allowance01.toNumber(), 100);
await HST.transferFrom(accounts[0], accounts[2], 50, { from: accounts[1] });
const allowance012 = await HST.allowance.call(accounts[0], accounts[1]);
assert.strictEqual(allowance012.toNumber(), 50);
const balance2 = await HST.balanceOf.call(accounts[2]);
assert.strictEqual(balance2.toNumber(), 50);
const balance0 = await HST.balanceOf.call(accounts[0]);
assert.strictEqual(balance0.toNumber(), 9950);
// FIRST tx done.
// onto next.
await assertRevert(HST.transferFrom.call(accounts[0], accounts[2], 60, { from: accounts[1] }));
});
it('approvals: attempt withdrawal from account with no allowance (should fail)', async () => {
await assertRevert(HST.transferFrom.call(accounts[0], accounts[2], 60, { from: accounts[1] }));
});
it('approvals: allow accounts[1] 100 to withdraw from accounts[0]. Withdraw 60 and then approve 0 & attempt transfer.', async () => {
await HST.approve(accounts[1], 100, { from: accounts[0] });
await HST.transferFrom(accounts[0], accounts[2], 60, { from: accounts[1] });
await HST.approve(accounts[1], 0, { from: accounts[0] });
await assertRevert(HST.transferFrom.call(accounts[0], accounts[2], 10, { from: accounts[1] }));
});
it.skip('approvals: approve max (2^256 - 1)', async () => {
await HST.approve(accounts[1], '115792089237316195423570985008687907853269984665640564039457584007913129639935', { from: accounts[0] });
const allowance = await HST.allowance(accounts[0], accounts[1]);
assert(allowance.equals('1.15792089237316195423570985008687907853269984665640564039457584007913129639935e+77'));
});
// should approve max of msg.sender & withdraw 20 without changing allowance (should succeed).
it.skip('approvals: msg.sender approves accounts[1] of max (2^256 - 1) & withdraws 20', async () => {
const balance0 = await HST.balanceOf.call(accounts[0]);
assert.strictEqual(balance0.toNumber(), 10000);
const max = '1.15792089237316195423570985008687907853269984665640564039457584007913129639935e+77';
await HST.approve(accounts[1], max, { from: accounts[0] });
const balance2 = await HST.balanceOf.call(accounts[2]);
assert.strictEqual(balance2.toNumber(), 0, 'balance2 not correct');
await HST.transferFrom(accounts[0], accounts[2], 20, { from: accounts[1] });
const allowance01 = await HST.allowance.call(accounts[0], accounts[1]);
assert(allowance01.equals(max));
const balance22 = await HST.balanceOf.call(accounts[2]);
assert.strictEqual(balance22.toNumber(), 20);
const balance02 = await HST.balanceOf.call(accounts[0]);
assert.strictEqual(balance02.toNumber(), 9980);
});
/* eslint-disable no-underscore-dangle */
it('events: should fire Transfer event properly', async () => {
const res = await HST.transfer(accounts[1], '2666', { from: accounts[0] });
const transferLog = res.logs.find(element => element.event.match('Transfer'));
assert.strictEqual(transferLog.args._from, accounts[0]);
assert.strictEqual(transferLog.args._to, accounts[1]);
assert.strictEqual(transferLog.args._value.toString(), '2666');
});
it('events: should fire Transfer event normally on a zero transfer', async () => {
const res = await HST.transfer(accounts[1], '0', { from: accounts[0] });
const transferLog = res.logs.find(element => element.event.match('Transfer'));
assert.strictEqual(transferLog.args._from, accounts[0]);
assert.strictEqual(transferLog.args._to, accounts[1]);
assert.strictEqual(transferLog.args._value.toString(), '0');
});
it('events: should fire Approval event properly', async () => {
const res = await HST.approve(accounts[1], '2666', { from: accounts[0] });
const approvalLog = res.logs.find(element => element.event.match('Approval'));
assert.strictEqual(approvalLog.args._owner, accounts[0]);
assert.strictEqual(approvalLog.args._spender, accounts[1]);
assert.strictEqual(approvalLog.args._value.toString(), '2666');
});
});

100
src/ERC20/truffle-config.js Normal file
View File

@ -0,0 +1,100 @@
/**
* Use this file to configure your truffle project. It's seeded with some
* common settings for different networks and features like migrations,
* compilation and testing. Uncomment the ones you need or modify
* them to suit your project as necessary.
*
* More information about configuration can be found at:
*
* truffleframework.com/docs/advanced/configuration
*
* To deploy via Infura you'll need a wallet provider (like truffle-hdwallet-provider)
* to sign your transactions before they're sent to a remote public node. Infura accounts
* are available for free at: infura.io/register.
*
* You'll also need a mnemonic - the twelve word phrase the wallet uses to generate
* public/private key pairs. If you're publishing your code to GitHub make sure you load this
* phrase from a file you've .gitignored so it doesn't accidentally become public.
*
*/
// const HDWalletProvider = require('truffle-hdwallet-provider');
// const infuraKey = "fj4jll3k.....";
//
// const fs = require('fs');
// const mnemonic = fs.readFileSync(".secret").toString().trim();
module.exports = {
/**
* Networks define how you connect to your ethereum client and let you set the
* defaults web3 uses to send transactions. If you don't specify one truffle
* will spin up a development blockchain for you on port 9545 when you
* run `develop` or `test`. You can ask a truffle command to use a specific
* network from the command line, e.g
*
* $ truffle test --network <network-name>
*/
networks: {
// Useful for testing. The `development` name is special - truffle uses it by default
// if it's defined here and no other network is specified at the command line.
// You should run a client (like ganache-cli, geth or parity) in a separate terminal
// tab if you use this network and you must also set the `host`, `port` and `network_id`
// options below to some value.
//
development: {
host: "127.0.0.1", // Localhost (default: none)
port: 8545, // Standard Ethereum port (default: none)
network_id: "*", // Any network (default: none)
},
// Another network with more advanced options...
// advanced: {
// port: 8777, // Custom port
// network_id: 1342, // Custom network
// gas: 8500000, // Gas sent with each transaction (default: ~6700000)
// gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei)
// from: <address>, // Account to send txs from (default: accounts[0])
// websockets: true // Enable EventEmitter interface for web3 (default: false)
// },
// Useful for deploying to a public network.
// NB: It's important to wrap the provider as a function.
// ropsten: {
// provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/v3/YOUR-PROJECT-ID`),
// network_id: 3, // Ropsten's id
// gas: 5500000, // Ropsten has a lower block limit than mainnet
// confirmations: 2, // # of confs to wait between deployments. (default: 0)
// timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50)
// skipDryRun: true // Skip dry run before migrations? (default: false for public nets )
// },
// Useful for private networks
// private: {
// provider: () => new HDWalletProvider(mnemonic, `https://network.io`),
// network_id: 2111, // This network is yours, in the cloud.
// production: true // Treats this network as if it was a public net. (default: false)
// }
},
// Set default mocha options here, use special reporters etc.
mocha: {
// timeout: 100000
},
// Configure your compilers
compilers: {
solc: {
// version: "0.5.1", // Fetch exact version from solc-bin (default: truffle's version)
// docker: true, // Use "0.5.1" you've installed locally with docker (default: false)
// settings: { // See the solidity docs for advice about optimization and evmVersion
// optimizer: {
// enabled: false,
// runs: 200
// },
// evmVersion: "byzantium"
// }
}
}
};

View File

@ -1,6 +1,6 @@
# BlockChain for Node.js
Basic implementation for blockchain in node.js
Basic implementation for blockchain in Node.js
- [x] Block structure
- [x] Blockchain structure