Introduction to Smart Contracts
A simple smart contract
pragma solidity ^0.4.0;
contract SimpleStorage {
uint storedData;
function set(uint x) public {
storedData = x;
}
function get() public constant returns (uint) {
return storedData;
}
}
1st line: the source code is written for Solidity version 0.4.0 or anything newer that does not break functionality (up to, but not including , version 0.5.0). This is to ensure that the contract does not suddenly behave differently with a new compiler version. The keyword pragma
is called that way because, in general, pragmas are instructions for the compiler about how to treat the source code (e.g. pragma once).
Solidity所代表的智能合约是一个代码(函数)、数据(state)的集合,它存在Ethereum区块链的一个特定的address(地址)。unit storedData 声明了一个状态变量叫做storedData,类型为uint(unsigned integer of 256 bit)。可以认为它是一个在数据库中的一个single slot,并且可以用函数查询、更改。在这种情况下,set和get函数可以被用来更改或获取变量的值。
The following contract will implement the simplest form of a cryptocurrency. It is possible to generate coins out of thin air, but only the person that created the contract will be able to do that (it is trivial to implement a different issuance scheme). Furthermore, anyone can send coins to each other without any need for registering with username and password - all you need is an Ethereum keypair.
pragma solidity ^0.4.0;
contract Coin {
// The keyword "public" makes those variables
// readable from outside.
address public minter;
mapping (address => uint) public balances;
// Events allow light clients to react on
// changes efficiently.
event Sent(address from, address to, uint amount);
// This is the constructor whose code is
// run only when the contract is created.
function Coin() public {
minter = msg.sender;
}
function mint(address receiver, uint amount) public {
if (msg.sender != minter) return;
balances[receiver] += amount;
}
function send(address receiver, uint amount) public {
if (balances[msg.sender] < amount) return;
balances[msg.sender] -= amount;
balances[receiver] += amount;
Sent(msg.sender, receiver, amount);
}
}
address public minter声明了一个状态变量类型为address,可以被公开访问。address类型是一个160bit的值,不接受任何算数运算。适用于存储交易、键值对的地址。关键字public自动生成一个函数允许外部访问当前状态值。如果没有这个关键字public,其他contract无法访问这个变量。这个函数类似这个样子:
function minter() returns (address) { return minter; }
The next line, mapping (address => uint) public balances;
also creates a public state variable, but it is a more complex datatype. The type maps addresses to unsigned integers. Mappings can be seen as hash tables which are virtually initialized such that every possible key exists and is mapped to a value whose byte-representation is all zeros. This analogy does not go too far, though, as it is neither possible to obtain a list of all keys of a mapping, nor a list of all values. So either keep in mind (or better, keep a list or use a more advanced data type) what you added to the mapping or use it in a context where this is not needed, like this one. The getter function created by the public
keyword is a bit more complex in this case. It roughly looks like the following:
function balances(address _account) public view returns (uint) {
return balances[_account];
}
The line event Sent(address from, address to, uint amount); declares a so-called “event” which is fired in the last line of the function send. User interfaces (as well as server applications of course) can listen for those events being fired on the blockchain without much cost. As soon as it is fired, the listener will also receive the arguments from, to and amount, which makes it easy to track transactions. In order to listen for this event, you would use
Coin.Sent().watch({}, '', function(error, result) {
if (!error) {
console.log("Coin transfer: " + result.args.amount +
" coins were sent from " + result.args.from +
" to " + result.args.to + ".");
console.log("Balances now:\n" +
"Sender: " + Coin.balances.call(result.args.from) +
"Receiver: " + Coin.balances.call(result.args.to));
}
})
Note how the automatically generated function balances is called from the user interface.
The special function Coin is the constructor which is run during creation of the contract and cannot be called afterwards. It permanently stores the address of the person creating the contract: msg (together with tx and block) is a magic global variable that contains some properties which allow access to the blockchain. msg.sender is always the address where the current (external) function call came from.
Finally, the functions that will actually end up with the contract and can be called by users and contracts alike are mint and send. If mint is called by anyone except the account that created the contract, nothing will happen. On the other hand, send can be used by anyone (who already has some of these coins) to send coins to anyone else. Note that if you use this contract to send coins to an address, you will not see anything when you look at that address on a blockchain explorer, because the fact that you sent coins and the changed balances are only stored in the data storage of this particular coin contract. By the use of events it is relatively easy to create a “blockchain explorer” that tracks transactions and balances of your new coin.
交易
区块链是被全局共享的事务性数据库。意味着所有人只要参与到网络当中都可以读取对应数据库中的entries。如果想更改数据库中的值,需要创建一个所谓的‘交易’,这个交易需要被别人所接受。交易(事务)意味着做出的改变是原子性的(假设想要同时更改2个值,要么这次更改没有完成,要么两个值都被更改)。一旦交易被写入到数据库,没有其他的交易可以更改它。
区块
Bitcoin所要攻克的最大问题是double-spend attack:两笔交易都想要从一个账户转出钱。交易顺序将会被选择二,交易将被捆绑到块中,然后他们将在所有参与节点中执行和分配。如果两笔交易相互抵触,最后一笔交易将被拒绝,而不成为该块的一部分。
The Ethereum Virtual Machine
EVM是一个在ethereum智能合约下的运行环境。它不只是沙盒,确切的说是完全被孤立的。运行其中的代码无权访问网络、文件系统或是其他进程。智能合约甚至限制访问其他智能合约。
Accounts
There are two kinds of accounts in Ethereum which share the same address space: External accounts that are controlled by public-private key pairs (i.e. humans) and contract accounts which are controlled by the code stored together with the account.
The address of an external account is determined from the public key while the address of a contract is determined at the time the contract is created (it is derived from the creator address and the number of transactions sent from that address, the so-called “nonce”).
Regardless of whether or not the account stores code, the two types are treated equally by the EVM.
Every account has a persistent key-value store mapping 256-bit words to 256-bit words called storage.
Furthermore, every account has a balance in Ether (in “Wei” to be exact) which can be modified by sending transactions that include Ether.
Transactions
A transaction is a message that is sent from one account to another account (which might be the same or the special zero-account, see below). It can include binary data (its payload) and Ether.
If the target account contains code, that code is executed and the payload is provided as input data.
If the target account is the zero-account (the account with the address 0), the transaction creates a new contract. As already mentioned, the address of that contract is not the zero address but an address derived from the sender and its number of transactions sent (the “nonce”). The payload of such a contract creation transaction is taken to be EVM bytecode and executed. The output of this execution is permanently stored as the code of the contract. This means that in order to create a contract, you do not send the actual code of the contract, but in fact code that returns that code.
Gas
Upon creation, each transaction is charged with a certain amount of gas, whose purpose is to limit the amount of work that is needed to execute the transaction and to pay for this execution. While the EVM executes the transaction, the gas is gradually depleted according to specific rules.
The gas price is a value set by the creator of the transaction, who has to pay gas_price * gas up front from the sending account. If some gas is left after the execution, it is refunded in the same way.
If the gas is used up at any point (i.e. it is negative), an out-of-gas exception is triggered, which reverts all modifications made to the state in the current call frame.
Solidity by Example
Voting
电子投票的主要核心:如何将投票权分配给正确的人,如何防止操纵。实现自动计票,完全透明。
main idea:为每个选票创建一个contract,为每个选项提供一个简短的名字。contract的创建者(chairpaerson)会给予每个地址投票权。地址背后的人可以选择自己投票,也可以把投票委托给他们信任的人。最后winningProposal()会返回投票最多的proposal。
pragma solidity ^0.4.16;
/// @title Voting with delegation.
contract Ballot {
// This declares a new complex type which will
// be used for variables later.
// It will represent a single voter.
struct Voter {
uint weight; // weight is accumulated by delegation
bool voted; // if true, that person already voted
address delegate; // person delegated to
uint vote; // index of the voted proposal
}
// This is a type for a single proposal.
struct Proposal {
bytes32 name; // short name (up to 32 bytes)
uint voteCount; // number of accumulated votes
}
address public chairperson;
// This declares a state variable that
// stores a `Voter` struct for each possible address.
mapping(address => Voter) public voters;
// A dynamically-sized array of `Proposal` structs.
Proposal[] public proposals;
/// Create a new ballot to choose one of `proposalNames`.
function Ballot(bytes32[] proposalNames) public {
chairperson = msg.sender;
voters[chairperson].weight = 1;
// For each of the provided proposal names,
// create a new proposal object and add it
// to the end of the array.
for (uint i = 0; i < proposalNames.length; i++) {
// `Proposal({...})` creates a temporary
// Proposal object and `proposals.push(...)`
// appends it to the end of `proposals`.
proposals.push(Proposal({
name: proposalNames[i],
voteCount: 0
}));
}
}
// Give `voter` the right to vote on this ballot.
// May only be called by `chairperson`.
function giveRightToVote(address voter) public {
// If the argument of `require` evaluates to `false`,
// it terminates and reverts all changes to
// the state and to Ether balances. It is often
// a good idea to use this if functions are
// called incorrectly. But watch out, this
// will currently also consume all provided gas
// (this is planned to change in the future).
require((msg.sender == chairperson) && !voters[voter].voted && (voters[voter].weight == 0));
voters[voter].weight = 1;
}
/// Delegate your vote to the voter `to`.
function delegate(address to) public {
// assigns reference
Voter storage sender = voters[msg.sender];
require(!sender.voted);
// Self-delegation is not allowed.
require(to != msg.sender);
// Forward the delegation as long as
// `to` also delegated.
// In general, such loops are very dangerous,
// because if they run too long, they might
// need more gas than is available in a block.
// In this case, the delegation will not be executed,
// but in other situations, such loops might
// cause a contract to get "stuck" completely.
while (voters[to].delegate != address(0)) {
to = voters[to].delegate;
// We found a loop in the delegation, not allowed.
require(to != msg.sender);
}
// Since `sender` is a reference, this
// modifies `voters[msg.sender].voted`
sender.voted = true;
sender.delegate = to;
Voter storage delegate = voters[to];
if (delegate.voted) {
// If the delegate already voted,
// directly add to the number of votes
proposals[delegate.vote].voteCount += sender.weight;
} else {
// If the delegate did not vote yet,
// add to her weight.
delegate.weight += sender.weight;
}
}
/// Give your vote (including votes delegated to you)
/// to proposal `proposals[proposal].name`.
function vote(uint proposal) public {
Voter storage sender = voters[msg.sender];
require(!sender.voted);
sender.voted = true;
sender.vote = proposal;
// If `proposal` is out of the range of the array,
// this will throw automatically and revert all
// changes.
proposals[proposal].voteCount += sender.weight;
}
/// @dev Computes the winning proposal taking all
/// previous votes into account.
function winningProposal() public view
returns (uint winningProposal)
{
uint winningVoteCount = 0;
for (uint p = 0; p < proposals.length; p++) {
if (proposals[p].voteCount > winningVoteCount) {
winningVoteCount = proposals[p].voteCount;
winningProposal = p;
}
}
}
// Calls winningProposal() function to get the index
// of the winner contained in the proposals array and then
// returns the name of the winner
function winnerName() public view
returns (bytes32 winnerName)
{
winnerName = proposals[winningProposal()].name;
}
}
网友评论