数据专栏

智能大数据搬运工,你想要的我们都有

科技资讯:

科技学院:

科技百科:

科技书籍:

网站大全:

软件大全:

「深度学习福利」大神带你进阶工程师,立即查看>>>
区块链
2018-05-01 14:32:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
问题
1、Error: Cannot find module 'fabric-client'
解决:npm install
==========================================
2、Failed to query successfully :: Error: Failed to get user1.... run registerUser.js
解决:JiuChiDingPaMac:fabcar shijun$ node registerUser.js
新问题:Failed to register: Error: Failed to get admin.... run enrollAdmin.js
解决:步骤1:JiuChiDingPaMac:fabcar shijun$ node enrollAdmin.js
Store path:/Users/shijun/Desktop/openSourceProject/hyperledger/fabric-examples/fabric-samples/fabcar/hfc-key-store
Successfully enrolled admin user "admin"
Assigned the admin user to the fabric client ::{"name":"admin","mspid":"Org1MSP","roles":null,"affiliation":"","enrollmentSecret":"","enrollment":{"signingIdentity":"7200f4ddad2e0b7df90c315c8ce3810d6720ce67c41533f0b12e91cd03be984f","identity":{"certificate":"-----BEGIN CERTIFICATE-----\nMIICAjCCAaigAwIBAgIUfEl2J5DudnqmXsCrlnPUMkvoyrkwCgYIKoZIzj0EAwIw\nczELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNh\nbiBGcmFuY2lzY28xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHDAaBgNVBAMT\nE2NhLm9yZzEuZXhhbXBsZS5jb20wHhcNMTgwNDMwMTQ0NDAwWhcNMTkwNDMwMTQ0\nOTAwWjAhMQ8wDQYDVQQLEwZjbGllbnQxDjAMBgNVBAMTBWFkbWluMFkwEwYHKoZI\nzj0CAQYIKoZIzj0DAQcDQgAEAZzYLggmT0AEnv0yfhHnEA1ZRLa29rAgyO+aUZeY\nYVRJo28NuPSVxP0PgqDVeRBpQWjQpwt8Fskhqz19U2RnoaNsMGowDgYDVR0PAQH/\nBAQDAgeAMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFPwJSeBRw4LM1iyRAmrKvtXu\nkQ2zMCsGA1UdIwQkMCKAIEI5qg3NdtruuLoM2nAYUdFFBNMarRst3dusalc2Xkl8\nMAoGCCqGSM49BAMCA0gAMEUCIQDkLDn5GnQxpSFtM+VHIubCxXUulZeCL9jaRNTW\n8vNMxQIgfEEuE1u+rKIXHgRcdMr6mwvoTO6OlQ3Jhl8lq0QGhmE=\n-----END CERTIFICATE-----\n"}}}
步骤2:JiuChiDingPaMac:fabcar shijun$ node registerUser.js
Store path:/Users/shijun/Desktop/openSourceProject/hyperledger/fabric-examples/fabric-samples/fabcar/hfc-key-store
Successfully loaded admin from persistence
Successfully registered user1 - secret:IWVvPoKavkLP
Successfully enrolled member user "user1"
User1 was successfully registered and enrolled and is ready to intreact with the fabric network
步骤3:JiuChiDingPaMac:fabcar shijun$ node query.js
Store path:/Users/shijun/Desktop/openSourceProject/hyperledger/fabric-examples/fabric-samples/fabcar/hfc-key-store
Successfully loaded user1 from persistence
Query has completed, checking results
Response is [{"Key":"CAR0", "Record":{"colour":"blue","make":"Toyota","model":"Prius","owner":"Tomoko"}},{"Key":"CAR1", "Record":{"colour":"red","make":"Ford","model":"Mustang","owner":"Brad"}},{"Key":"CAR2", "Record":{"colour":"green","make":"Hyundai","model":"Tucson","owner":"Jin Soo"}},{"Key":"CAR3", "Record":{"colour":"yellow","make":"Volkswagen","model":"Passat","owner":"Max"}},{"Key":"CAR4", "Record":{"colour":"black","make":"Tesla","model":"S","owner":"Adriana"}},{"Key":"CAR5", "Record":{"colour":"purple","make":"Peugeot","model":"205","owner":"Michel"}},{"Key":"CAR6", "Record":{"colour":"white","make":"Chery","model":"S22L","owner":"Aarav"}},{"Key":"CAR7", "Record":{"colour":"violet","make":"Fiat","model":"Punto","owner":"Pari"}},{"Key":"CAR8", "Record":{"colour":"indigo","make":"Tata","model":"Nano","owner":"Valeria"}},{"Key":"CAR9", "Record":{"colour":"brown","make":"Holden","model":"Barina","owner":"Shotaro"}}]
==========================================
3、Creating orderer.example.com ... error
ERROR: for orderer.example.com Cannot create container for service orderer.example.com: b'Conflict. The container name "/orderer.example.com" is already in use by container "75c09c
解决:docker rm -f $(docker ps -aq)
==========================================
4、require('fabric-client'); ----Uncaught ReferenceError: require is not defined
解决:这是服务端的代码,不能运行在客户端
==========================================
5、$ node
> Web3 = require('web3')
Error: Cannot find module 'web3'
at Function.Module._resolveFilename (module.js:547:15)
at Function.Module._load (module.js:474:25)
at Module.require (module.js:596:17)
at require (internal/module.js:11:18)
解决:$ cd /Users/shijun/Desktop/truffleWorkspace/voteShiJun
$ npm install web3
==========================================
6、$ node
> solc = require('solc')
Error: Cannot find module 'solc'
at Function.Module._resolveFilename (module.js:547:15)
at Function.Module._load (module.js:474:25)
at Module.require (module.js:596:17)
at require (internal/module.js:11:18)
解决:$ cd /Users/shijun/Desktop/truffleWorkspace/voteShiJun
$ npm install solc
==========================================
7、问题:
ERROR in ./app/javascripts/app.js
Module not found: Error: Can't resolve 'ipfs-api' in......
解决:
$ cd /Users/shijun/Desktop/truffleWorkspace/project
$ npm install --save ipfs-api
区块链
2018-04-30 23:25:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
常见问题
基础问题
Solidity是什么? Solidity是受Javascript启发的编程语言,可以被用来在以太坊区块链上创建智能合约。还有其它编程语言(LLL,Serpent等)也可以创建智能合约。Solidity更被开发者喜爱的主要原因是它是静态类型语言,提供许多高级特性,例如继承、函数库、用户定义的复杂类型和字节码优化。 Solidity合约能够以不同方式被编译(见下文),输出结果可以被剪贴/复制到一个geth控制台,然后被部署到以太坊区块链。
This list was originally compiled by fivedogit gmail.com) ).
Fivedogit写了一些 合约例子 ,Solidity的每个特性都应该有对应的 测试合约 。
怎么编译合约?
最快的方式可能是使用 线上编译器 。
你也可以使用cpp-ethereum自带的solc binary编译合约,或者使用集成开发环境Mix。
创建和发布最基础的合约
一个非常简单的合约是 greeter 。
在特定区块上做一些事情 (例如发布一个合约或者执行一笔交易)是可能的吗?
不能保证交易在下一个区块或未来特定区块中发生,因为这取决于打包交易的矿工,而不是交易的提交者。 如果你想计划将来的合约调用,你可以使用 alarm clock 。
交易“负载”(payload)是什么?
它只是与请求一起发送的字节码“数据”。
现在有可用的反编译器吗?
There is no decompiler to Solidity. This is in principle possible to some degree, but for example variable names will be lost and great effort will be necessary to make it look similar to the original source code.
Bytecode can be decompiled to opcodes, a service that is provided by several blockchain explorers.
Contracts on the blockchain should have their original source code published if they are to be used by third parties.
Does selfdestruct() free up space in the blockchain?
It removes the contract bytecode and storage from the current block into the future, but since the blockchain stores every single block (i.e. all history), this will not actually free up space on full/achive nodes.
Create a contract that can be killed and return funds
First, a word of warning: Killing contracts sounds like a good idea, because “cleaning up” is always good, but as seen above, it does not really clean up. Furthermore, if Ether is sent to removed contracts, the Ether will be forever lost.
If you want to deactivate your contracts, rather disable them by changing some internal state which causes all functions to throw. This will make it impossible to use the contract and ether sent to the contract will be returned automatically.
Now to answering the question: Inside a constructor, msg.sender is the creator. Save it. Thenselfdestruct(creator); to kill and return funds.
example
Note that if you import “mortal” at the top of your contracts and declare contract SomeContract is mortal { ... and compile with a compiler that already has it (which includes browser-solidity ), thenkill() is taken care of for you. Once a contract is “mortal”, then you cancontractname.kill.sendTransaction({from:eth.coinbase}), just the same as my examples.
Store Ether in a contract
The trick is to create the contract with {from:someaddress, value: web3.toWei(3,”ether”)...}
See endowment_retriever.sol .
Use a non-constant function (req sendTransaction) to increment a variable in a contract
See value_incrementer.sol .
Get contract address in Solidity
Short answer: The global variable this is the contract address.
See basic_info_getter .
Long answer: this is a variable representing the current contract. Its type is the type of the contract. Since any contract type basically inherits from the address type, this is always convertible to addressand in this case contains its own address.
What is the difference between a function marked constant and one that is not?
constant functions can perform some action and return a value, but cannot change state (this is not yet enforced by the compiler). In other words, a constant function cannot save or update any variables within the contract or wider blockchain. These functions are called usingc.someFunction(...) from geth or any other web3.js environment.
“non-constant” functions (those lacking the constant specifier) must be called withc.someMethod.sendTransaction({from:eth.accounts[x], gas: 1000000}); That is, because they can change state, they have to have a gas payment sent along to get the work done.
Get a contract to return its funds to you (not using selfdestruct(...)).
This example demonstrates how to send funds from a contract to an address.
See endowment_retriever .
What is a mapping and how do we use them?
A mapping is very similar to a K->V hashmap. If you have a state variable of type mapping (string -> uint) x;, then you can access the value by x[“somekeystring”].
How can I get the length of a mapping?
Mappings are a rather low-level data structure. It does not store the keys and it is not possible to know which or how many values are “set”. Actually, all values to all possible keys are set by default, they are just initialised with the zero value.
In this sense, the attribute length for a mapping does not really apply.
If you want to have a “sized mapping”, you can use the iterable mapping (see below) or just a dynamically-sized array of structs.
Are mappings iterable?
Mappings themselves are not iterable, but you can use a higher-level datastructure on top of it, for example the iterable mapping .
Can you return an array or a string from a solidity function call?
Yes. See array_receiver_and_returner.sol .
What is problematic, though, is returning any variably-sized data (e.g. a variably-sized array likeuint[]) from a fuction called from within Solidity . This is a limitation of the EVM and will be solved with the next protocol update.
Returning variably-sized data as part of an external transaction or call is fine.
How do you represent double/float in Solidity?
This is not yet possible.
Is it possible to in-line initialize an array like so: string32[] myarray = [“a”, “b”];
This is not yet possible.
What are events and why do we need them?
Let us suppose that you need a contract to alert the outside world when something happens. The contract can fire an event, which can be listened to from web3 (inside geth or a web application). The main advantage of events is that they are stored in a special way on the blockchain so that it is very easy to search for them.
What are the different function visibilities?
The visibility specifiers do not only change the visibility but also the way functions can be called. In general, functions in the same contract can also be called internally (which is cheaper and allows for memory types to be passed by reference). This is done if you just use f(1,2). If you use this.f(1,2) orotherContract.f(1,2), the function is called externally.
Internal function calls have the advantage that you can use all Solidity types as parameters, but you have to stick to the simpler ABI types for external calls. external: all, only externally public: all (this is the default), externally and internally internal: only this contract and contracts deriving from it, only internally private: only this contract, only internally
Do contract constructors have to be publicly visible?
You can use the visibility specifiers, but they do not yet have any effect. The constructor is removed from the contract code once it is deployed,
Can a contract have multiple constructors?
No, a contract can have only one constructor.
More specifically, it can only have one function whose name matches that of the constructor.
Having multiple constructors with different number of arguments or argument types, as it is possible in other languages is not allowed in Solidity.
Is a constructor required?
No. If there is no constructor, a generic one without arguments and no actions will be used.
Are timestamps (now, block.timestamp) reliable?
This depends on what you mean by “reliable”. In general, they are supplied by miners and are therefore vulnerable.
Unless someone really messes up the blockchain or the clock on your computer, you can make the following assumptions:
You publish a transaction at a time X, this transaction contains same code that calls now and is included in a block whose timestamp is Y and this block is included into the canonical chain (published) at a time Z.
The value of now will be identical to Y and X <= Y <= Z.
Never use now or block.hash as a source of randomness, unless you know what you are doing!
Can a contract function return a struct?
Yes, but only in “internal” function calls.
If I return an enum, I only get integer values in web3.js. How to get the named values?
Enums are not supported by the ABI, they are just supported by Solidity. You have to do the mapping yourself for now, we might provide some help later.
What is the deal with “function () { ... }” inside Solidity contracts? How can a function not have a name?
This function is called “fallback function” and it is called when someone just sent Ether to the contract without providing any data or if someone messed up the types so that they tried to call a function that does not exist.
The default behaviour (if no fallback function is explicitly given) in these situations is to just accept the call and do nothing. This is desireable in many cases, but should only be used if there is a way to pull out Ether from a contract.
If the contract is not meant to receive Ether with simple transfers, you should implement the fallback function as
function() { throw; }
this will cause all transactions to this contract that do not call an existing function to be reverted, so that all Ether is sent back.
Another use of the fallback function is to e.g. register that your contract received ether by using an event.
Attention : If you implement the fallback function take care that it uses as little gas as possible, because send() will only supply a limited amount.
Is it possible to pass arguments to the fallback function?
The fallback function cannot take parameters.
Under special circumstances, you can send data. If you take care that none of the other functions is invoked, you can access the data by msg.data.
Can state variables be initialized in-line?
Yes, this is possible for all types (even for structs). However, for arrays it should be noted that you must declare them as static memory arrays. Examples: contract C { struct S { uint a; uint b; } S public x = S(1, 2); string name = "Ada"; string[4] memory AdaArr = ["This", "is", "an", "array"];}contract D { C c = new C();}
What is the “modifier” keyword?
Modifiers are a way to prepend or append code to a function in order to add guards, initialisation or cleanup functionality in a concise way.
For examples, see the features.sol .
How do structs work?
See struct_and_for_loop_tester.sol .
How do for loops work?
Very similar to JavaScript. There is one point to watch out for, though:
If you use for (var i = 0; i < a.length; i ++) { a[i] = i; }, then the type of i will be inferred only from 0, whose type is uint8. This means that if a has more than 255 elements, your loop will not terminate because i can only hold values up to 255.
Better use for (uint i = 0; i < a.length...
See struct_and_for_loop_tester.sol .
What character set does Solidity use?
Solidity is character set agnostic concerning strings in the source code, although utf-8 is recommended. Identifiers (variables, functions, ...) can only use ASCII.
What are some examples of basic string manipulation (substring, indexOf, charAt, etc)?
There are some string utility functions at stringUtils.sol which will be extended in the future.
For now, if you want to modify a string (even when you only want to know its length), you should always convert it to a bytes first: contract C { string s; function append(byte c) { bytes(s).push(c); } function set(uint i, byte c) { bytes(s)[i] = c; }}
Can I concatenate two strings?
You have to do it manually for now.
Why is the low-level function .call() less favorable than instantiating a contract with a variable (ContractB b;) and executing its functions (b.doSomething();)?
If you use actual functions, the compiler will tell you if the types or your arguments do not match, if the function does not exist or is not visible and it will do the packing of the arguments for you.
See ping.sol and pong.sol .
Is unused gas automatically refunded?
Yes and it is immediate, i.e. done as part of the transaction.
When returning a value of say “uint” type, is it possible to return an “undefined” or “null”-like value?
This is not possible, because all types use up the full value range.
You have the option to throw on error, which will also revert the whole transaction, which might be a good idea if you ran into an unexpected situation.
If you do not want to throw, you can return a pair: contract C { uint[] counters; function getCounter(uint index) returns (uint counter, bool error) { if (index >= counters.length) return (0, true); else return (counters[index], false); } function checkCounter(uint index) { var (counter, error) = getCounter(index); if (error) { ... } else { ... } }}
Are comments included with deployed contracts and do they increase deployment gas?
No, everything that is not needed for execution is removed during compilation. This includes, among others, comments, variable names and type names.
What happens if you send ether along with a function call to a contract?
It gets added to the total balance of the contract, just like when you send ether when creating a contract.
Is it possible to get a tx receipt for a transaction executed contract-to-contract?
No, a function call from one contract to another does not create its own transaction, you have to look in the overall transaction. This is also the reason why several block explorer do not show Ether sent between contracts correctly.
What is the memory keyword? What does it do?
The Ethereum Virtual Machine has three areas where it can store items.
The first is “storage”, where all the contract state variables reside. Every contract has its own storage and it is persistent between function calls and quite expensive to use.
The second is “memory”, this is used to hold temporary values. It is erased between (external) function calls and is cheaper to use.
The third one is the stack, which is used to hold small local variables. It is almost free to use, but can only hold a limited amount of values.
For almost all types, you cannot specify where they should be stored, because they are copied everytime they are used.
The types where the so-called storage location is important are structs and arrays. If you e.g. pass such variables in function calls, their data is not copied if it can stay in memory or stay in storage. This means that you can modify their content in the called function and these modifications will still be visible in the caller.
There are defaults for the storage location depending on which type of variable it concerns: state variables are always in storage function arguments are always in memory local variables always reference storage
Example: contract C { uint[] data1; uint[] data2; function appendOne() { append(data1); } function appendTwo() { append(data2); } function append(uint[] storage d) { d.push(1); }}
The function append can work both on data1 and data2 and its modifications will be stored permanently. If you remove the storage keyword, the default is to use memory for function arguments. This has the effect that at the point where append(data1) or append(data2) is called, an independent copy of the state variable is created in memory and append operates on this copy (which does not support .push - but that is another issue). The modifications to this independent copy do not carry back to data1 or data2.
A common mistake is to declare a local variable and assume that it will be created in memory, although it will be created in storage: /// THIS CONTRACT CONTAINS AN ERRORcontract C { uint someVariable; uint[] data; function f() { uint[] x; x.push(2); data = x; }}
The type of the local variable x is uint[] storage, but since storage is not dynamically allocated, it has to be assigned from a state variable before it can be used. So no space in storage will be allocated forx, but instead it functions only as an alias for a pre-existing variable in storage.
What will happen is that the compiler interprets x as a storage pointer and will make it point to the storage slot 0 by default. This has the effect that someVariable (which resides at storage slot 0) is modified by x.push(2).
The correct way to do this is the following: contract C { uint someVariable; uint[] data; function f() { uint[] x = data; x.push(2); }}
Can a regular (i.e. non-contract) ethereum account be closed permanently like a contract can?
No. Non-contract accounts “exist” as long as the private key is known by someone or can be generated in some way.
What is the difference between bytes and byte[]?
bytes is usually more efficient: When used as arguments to functions (i.e. in CALLDATA) or in memory, every single element of a byte[] is padded to 32 bytes which wastes 31 bytes per element.
Is it possible to send a value while calling an overloaded function?
It’s a known missing feature. https://www.pivotaltracker.com/story/show/92020468 as part of https://www.pivotaltracker.com/n/projects/1189488
Best solution currently see is to introduce a special case for gas and value and just re-check whether they are present at the point of overload resolution.
Advanced Questions
How do you get a random number in a contract? (Implement a self-returning gambling contract.)
Getting randomness right is often the crucial part in a crypto project and most failures result from bad random number generators.
If you do not want it to be safe, you build something similar to the coin flipper but otherwise, rather use a contract that supplies randomness, like the RANDAO .
Get return value from non-constant function from another contract
The key point is that the calling contract needs to know about the function it intends to call.
See ping.sol and pong.sol .
Get contract to do something when it is first mined
Use the constructor. Anything inside it will be executed when the contract is first mined.
See replicator.sol .
Can a contract create another contract?
Yes, see replicator.sol .
Note that the full code of the created contract has to be included in the creator contract. This also means that cyclic creations are not possible (because the contract would have to contain its own code) - at least not in a general way.
How do you create 2-dimensional arrays?
See 2D_array.sol .
Note that filling a 10x10 square of uint8 + contract creation took more than 800,000 gas at the time of this writing. 17x17 took 2,000,000 gas. With the limit at 3.14 million... well, there’s a pretty low ceiling for what you can create right now.
Note that merely “creating” the array is free, the costs are in filling it.
Note2: Optimizing storage access can pull the gas costs down considerably, because 32 uint8 values can be stored in a single slot. The problem is that these optimizations currently do not work across loops and also have a problem with bounds checking. You might get much better results in the future, though.
What does p.recipient.call.value(p.amount)(p.data) do?
Every external function call in Solidity can be modified in two ways: You can add Ether together with the call You can limit the amount of gas available to the call
This is done by “calling a function on the function”:
f.gas(2).value(20)() calls the modified function f and thereby sending 20 Wei and limiting the gas to 2 (so this function call will most likely go out of gas and return your 20 Wei).
In the above example, the low-level function call is used to invoke another contract with p.data as payload and p.amount Wei is sent with that call.
Can a contract function accept a two-dimensional array?
This is not yet implemented for external calls and dynamic arrays - you can only use one level of dynamic arrays.
What is the relationship between bytes32 and string? Why is it that ‘bytes32 somevar = “stringliteral”;’ works and what does the saved 32-byte hex value mean?
The type bytes32 can hold 32 (raw) bytes. In the assignment bytes32 samevar = “stringliteral”;, the string literal is interpreted in its raw byte form and if you inspect somevar and see a 32-byte hex value, this is just “stringliteral” in hex.
The type bytes is similar, only that it can change its length.
Finally, string is basically identical to bytes only that it is assumed to hold the utf-8 encoding of a real string. Since string stores the data in utf-8 encoding it is quite expensive to compute the number of characters in the string (the encoding of some characters takes more than a single byte). Because of that, string s; s.length is not yet supported and not even index access s[2]. But if you want to access the low-level byte encoding of the string, you can use bytes(s).length and bytes(s)[2] which will result in the number of bytes in the utf-8 encoding of the string (not the number of characters) and the second byte (not character) of the utf-8 encoded string, respectively.
Can a contract pass an array (static size) or string or bytes (dynamic size) to another contract?
Sure. Take care that if you cross the memory / storage boundary, independent copies will be created: contract C { uint[20] x; function f() { g(x); h(x); } function g(uint[20] y) { y[2] = 3; } function h(uint[20] storage y) { y[3] = 4; }
The call to g(x) will not have an effect on x because it needs to create an independent copy of the storage value in memory (the default storage location is memory). On the other hand, h(x)successfully modifies x because only a reference and not a copy is passed.
Sometimes, when I try to change the length of an array with ex: “arrayname.length = 7;” I get a compiler error “Value must be an lvalue”. Why?
You can resize a dynamic array in storage (i.e. an array declared at the contract level) witharrayname.length = ;. If you get the “lvalue” error, you are probably doing one of two things wrong. You might be trying to resize an array in “memory”, or You might be trying to resize a non-dynamic array.
int8 [] memory memArr; // Case 1 memArr.length**++ ; // illegal int8**[5] storageArr; // Case 2 somearray.length**++ ; // legal int8**[5] storage storageArr2; // Explicit case 2 somearray2.length**++**; // legal
Important note: In Solidity, array dimensions are declared backwards from the way you might be used to declaring them in C or Java, but they are access as in C or Java.
For example, int8[][5] somearray; are 5 dynamic int8 arrays.
The reason for this is that T[5] is always an array of 5 T s, no matter whether T itself is an array or not (this is not the case in C or Java).
Is it possible to return an array of strings ( string[] ) from a Solidity function?
Not yet, as this requires two levels of dynamic arrays (string is a dynamic array itself).
If you issue a call for an array, it is possible to retrieve the whole array? Or must you write a helper function for that?
The automatic accessor function for a public state variable of array type only returns individual elements. If you want to return the complete array, you have to manually write a function to do that.
What could have happened if an account has storage value/s but no code? Example: http://test.ether.camp/account/5f740b3a43fbb99724ce93a879805f4dc89178b5
The last thing a constructor does is returning the code of the contract. The gas costs for this depend on the length of the code and it might be that the supplied gas is not enough. This situation is the only one where an “out of gas” exception does not revert changes to the state, i.e. in this case the initialisation of the state variables.
https://github.com/ethereum/wiki/wiki/Subtleties
After a successful CREATE operation’s sub-execution, if the operation returns x, 5 * len(x) gas is subtracted from the remaining gas before the contract is created. If the remaining gas is less than 5 * len(x), then no gas is subtracted, the code of the created contract becomes the empty string, but this is not treated as an exceptional condition - no reverts happen.
How do I use .send()?
If you want to send 20 Ether from a contract to the address x, you use x.send(20 ether);. Here, x can be a plain address or a contract. If the contract already explicitly defines a function send (and thus overwrites the special function), you can use address(x).send(20 ether);.
What does the following strange check do in the Custom Token contract?
if (balanceOf[_to] + _value < balanceOf[_to]) throw ;
Integers in Solidity (and most other machine-related programming languages) are restricted to a certain range. For uint256, this is 0 up to 2**256 - 1. If the result of some operation on those numbers does not fit inside this range, it is truncated. These truncations can have serious consequences , so code like the one above is necessary to avoid certain attacks.
More Questions?
If you have more questions or your question is not answered here, please talk to us on gitter or file an issue .
转自: https://blog.csdn.net/mongo_node/article/details/80152099 如果你希望 高效的 学习以太坊DApp开发,可以访问汇智网提供的 最热门 在线互动教程: 适合区块链新手的以太坊DApp实战入门教程 区块链+IPFS+Node.js+MongoDB+Express去中心化以太坊电商应用开发实战
其他更多内容也可以访问 这个以太坊博客 。
区块链
2018-04-30 22:12:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
Solidity 编程开发实例
接下来的智能合约教程非常复杂,但展示了很多Solidity的编程开发特性。它实现了一个入门的投票合约。当然,电子选举的主要问题是如何赋予投票权给准确的人,并防止操纵。我们不能解决所有的问题,但至少我们会展示如何委托投票可以同时做到投票统计是自动和完全透明。
Voting 投票
思路是为每张选票创建一个合约,每个投票选项提供一个短名称。合约创建者作为会长将会给每个投票参与人各自的地址投票权。
地址后面的人们可以选择自己投票或者委托信任的代表人替他们投票。在投票结束后,winningProposal()将会返回获得票数最多的提案。 /// @title Voting with delegation. /// @title 授权投票 contract Ballot { // 这里声明了复杂类型 // 将会在被后面的参数使用 // 代表一个独立的投票人。 struct Voter { uint weight; // 累积的权重。 bool voted; // 如果为真,则表示该投票人已经投票。 address delegate; // 委托的投票代表 uint vote; // 投票选择的提案索引号 } // 这是一个独立提案的类型 struct Proposal { bytes32 name; // 短名称(32字节) uint voteCount; // 累计获得的票数 } address public chairperson; //这里声明一个状态变量,保存每个独立地址的`Voter` 结构 mapping(address => Voter) public voters; //一个存储`Proposal`结构的动态数组 Proposal[] public proposals; // 创建一个新的投票用于选出一个提案名`proposalNames`. function Ballot(bytes32[] proposalNames) { chairperson = msg.sender; voters[chairperson].weight = 1; //对提供的每一个提案名称,创建一个新的提案 //对象添加到数组末尾 for (uint i = 0; i < proposalNames.length; i++) //`Proposal({...})` 创建了一个临时的提案对象, //`proposal.push(...)`添加到了提案数组`proposals`末尾。 proposals.push(Proposal({ name: proposalNames[i], voteCount: 0 })); } //给投票人`voter`参加投票的投票权, //只能由投票主持人`chairperson`调用。 function giveRightToVote(address voter) { if (msg.sender != chairperson || voters[voter].voted) //`throw`会终止和撤销所有的状态和以太改变。 //如果函数调用无效,这通常是一个好的选择。 //但是需要注意,这会消耗提供的所有gas。 throw; voters[voter].weight = 1; } // 委托你的投票权到一个投票代表 `to`。 function delegate(address to) { // 指定引用 Voter sender = voters[msg.sender]; if (sender.voted) throw; //当投票代表`to`也委托给别人时,寻找到最终的投票代表 while (voters[to].delegate != address(0) && voters[to].delegate != msg.sender) to = voters[to].delegate; // 当最终投票代表等于调用者,是不被允许的。 if (to == msg.sender) throw; //因为`sender`是一个引用, //这里实际修改了`voters[msg.sender].voted` sender.voted = true; sender.delegate = to; Voter delegate = voters[to]; if (delegate.voted) //如果委托的投票代表已经投票了,直接修改票数 proposals[delegate.vote].voteCount += sender.weight; else //如果投票代表还没有投票,则修改其投票权重。 delegate.weight += sender.weight; } ///投出你的选票(包括委托给你的选票) ///给 `proposals[proposal].name`。 function vote(uint proposal) { Voter sender = voters[msg.sender]; if (sender.voted) throw; sender.voted = true; sender.vote = proposal; //如果`proposal`索引超出了给定的提案数组范围 //将会自动抛出异常,并撤销所有的改变。 proposals[proposal].voteCount += sender.weight; } ///@dev 根据当前所有的投票计算出当前的胜出提案 function winningProposal() constant 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; } } } }
可能的改进
现在,指派投票权到所有的投票参加者需要许多的交易。你能想出更好的方法么?
盲拍
这一节,我们将展示在以太上创建一个完整的盲拍合约是多么简单。我们从一个所有人都能看到出价的公开拍卖开始,接着扩展合约成为一个在拍卖结束以前不能看到实际出价的盲拍。
简单的公开拍卖
通常简单的公开拍卖合约,是每个人可以在拍卖期间发送他们的竞拍出价。为了实现绑定竞拍人的到他们的拍卖,竞拍包括发送金额/ether。如果产生了新的最高竞拍价,前一个最高价竞拍人将会拿回他的钱。在竞拍阶段结束后,受益人人需要手动调用合约收取他的钱 — — 合约不会激活自己。 contract SimpleAuction { // 拍卖的参数。 // 时间要么为unix绝对时间戳(自1970-01-01以来的秒数), // 或者是以秒为单位的出块时间 address public beneficiary; uint public auctionStart; uint public biddingTime; //当前的拍卖状态 address public highestBidder; uint public highestBid; //在结束时设置为true来拒绝任何改变 bool ended; //当改变时将会触发的Event event HighestBidIncreased(address bidder, uint amount); event AuctionEnded(address winner, uint amount); //下面是一个叫做natspec的特殊注释, //由3个连续的斜杠标记,当询问用户确认交易事务时将显示。 ///创建一个简单的合约使用`_biddingTime`表示的竞拍时间, /// 地址`_beneficiary`.代表实际的拍卖者 function SimpleAuction(uint _biddingTime, address _beneficiary) { beneficiary = _beneficiary; auctionStart = now; biddingTime = _biddingTime; } ///对拍卖的竞拍保证金会随着交易事务一起发送, ///只有在竞拍失败的时候才会退回 function bid() { //不需要任何参数,所有的信息已经是交易事务的一部分 if (now > auctionStart + biddingTime) //当竞拍结束时撤销此调用 throw; if (msg.value <= highestBid) //如果出价不是最高的,发回竞拍保证金。 throw; if (highestBidder != 0) highestBidder.send(highestBid); highestBidder = msg.sender; highestBid = msg.value; HighestBidIncreased(msg.sender, msg.value); } ///拍卖结束后发送最高的竞价到拍卖人 function auctionEnd() { if (now <= auctionStart + biddingTime) throw; //拍卖还没有结束 if (ended) throw; //这个收款函数已经被调用了 AuctionEnded(highestBidder, highestBid); //发送合约拥有所有的钱,因为有一些保证金可能退回失败了。 beneficiary.send(this.balance); ended = true; } function () { //这个函数将会在发送到合约的交易事务包含无效数据 //或无数据的时执行,这里撤销所有的发送, //所以没有人会在使用合约时因为意外而丢钱。 throw; } }
Blind Auction 盲拍
接下来扩展前面的公开拍卖成为一个盲拍。盲拍的特点是拍卖结束以前没有时间压力。在一个透明的计算平台上创建盲拍系统听起来可能有些矛盾,但是加密算法能让你脱离困境。
在拍卖阶段, 竞拍人不需要发送实际的出价,仅仅只需要发送一个它的散列值。因为目前几乎不可能找到两个值(足够长)的散列值相等,竞拍者提交他们的出价散列值。在拍卖结束后,竞拍人重新发送未加密的竞拍出价,合约将检查其散列值是否和拍卖阶段发送的一样。 另一个挑战是如何让拍卖同时实现绑定和致盲 :防止竞拍人竞拍成功后不付钱的唯一的办法是,在竞拍出价的同时发送保证金。但是在Ethereum上发送保证金是无法致盲,所有人都能看到保证金。下面的合约通过接受任何尽量大的出价来解决这个问题。当然这可以在最后的揭拍阶段进行复核,一些竞拍出价可能是无效的,这样做的目的是(它提供一个显式的标志指出是无效的竞拍,同时包含高额保证金):竞拍人可以通过放置几个无效的高价和低价竞拍来混淆竞争对手。 contract BlindAuction { struct Bid { bytes32 blindedBid; uint deposit; } address public beneficiary; uint public auctionStart; uint public biddingEnd; uint public revealEnd; bool public ended; mapping(address => Bid[]) public bids; address public highestBidder; uint public highestBid; event AuctionEnded(address winner, uint highestBid); ///修饰器(Modifier)是一个简便的途径用来验证函数输入的有效性。 ///`onlyBefore` 应用于下面的 `bid`函数,其旧的函数体替换修饰器主体中 `_`后就是其新的函数体 modifier onlyBefore(uint _time) { if (now >= _time) throw; _ } modifier onlyAfter(uint _time) { if (now <= _time) throw; _ } function BlindAuction(uint _biddingTime, uint _revealTime, address _beneficiary) { beneficiary = _beneficiary; auctionStart = now; biddingEnd = now + _biddingTime; revealEnd = biddingEnd + _revealTime; } ///放置一个盲拍出价使用`_blindedBid`=sha3(value,fake,secret). ///仅仅在竞拍结束正常揭拍后退还发送的以太。当随同发送的以太至少 ///等于 "value"指定的保证金并且 "fake"不为true的时候才是有效的竞拍 ///出价。设置 "fake"为true或发送不合适的金额将会掩没真正的竞拍出 ///价,但是仍然需要抵押保证金。同一个地址可以放置多个竞拍。 function bid(bytes32 _blindedBid) onlyBefore(biddingEnd) { bids[msg.sender].push(Bid({ blindedBid: _blindedBid, deposit: msg.value })); } ///揭开你的盲拍竞价。你将会拿回除了最高出价外的所有竞拍保证金 ///以及正常的无效盲拍保证金。 function reveal(uint[] _values, bool[] _fake, bytes32[] _secret) onlyAfter(biddingEnd) onlyBefore(revealEnd) { uint length = bids[msg.sender].length; if (_values.length != length || _fake.length != length || _secret.length != length) throw; uint refund; for (uint i = 0; i < length; i++) { var bid = bids[msg.sender][i]; var (value, fake, secret) = (_values[i], _fake[i], _secret[i]); if (bid.blindedBid != sha3(value, fake, secret)) //出价未被正常揭拍,不能取回保证金。 continue; refund += bid.deposit; if (!fake && bid.deposit >= value) if (placeBid(msg.sender, value)) refund -= value; //保证发送者绝不可能重复取回保证金 bid.blindedBid = 0; } msg.sender.send(refund); } //这是一个内部 (internal)函数, //意味着仅仅只有合约(或者从其继承的合约)可以调用 function placeBid(address bidder, uint value) internal returns (bool success) { if (value <= highestBid) return false; if (highestBidder != 0) //退还前一个最高竞拍出价 highestBidder.send(highestBid); highestBid = value; highestBidder = bidder; return true; } ///竞拍结束后发送最高出价到竞拍人 function auctionEnd() onlyAfter(revealEnd) { if (ended) throw; AuctionEnded(highestBidder, highestBid); //发送合约拥有所有的钱,因为有一些保证金退回可能失败了。 beneficiary.send(this.balance); ended = true; } function () { throw; } } Safe Remote Purchase 安全的远程购物 contract Purchase { uint public value; address public seller; address public buyer; enum State { Created, Locked, Inactive } State public state; function Purchase() { seller = msg.sender; value = msg.value / 2; if (2 * value != msg.value) throw; } modifier require(bool _condition) { if (!_condition) throw; _ } modifier onlyBuyer() { if (msg.sender != buyer) throw; _ } modifier onlySeller() { if (msg.sender != seller) throw; _ } modifier inState(State _state) { if (state != _state) throw; _ } event aborted(); event purchaseConfirmed(); event itemReceived(); ///终止购物并收回以太。仅仅可以在合约未锁定时被卖家调用。 function abort() onlySeller inState(State.Created) { aborted(); seller.send(this.balance); state = State.Inactive; } ///买家确认购买。交易包含两倍价值的(`2 * value`)以太。 ///这些以太会一直锁定到收货确认(confirmReceived)被调用。 function confirmPurchase() inState(State.Created) require(msg.value == 2 * value) { purchaseConfirmed(); buyer = msg.sender; state = State.Locked; } ///确认你(买家)收到了货物,这将释放锁定的以太。 function confirmReceived() onlyBuyer inState(State.Locked) { itemReceived(); buyer.send(value);//我们有意忽略了返回值。 seller.send(this.balance); state = State.Inactive; } function() { throw; } }
小额支付通道
待补
转自: https://blog.csdn.net/mongo_node/article/details/80151986 如果你希望 高效的 学习以太坊DApp开发,可以访问汇智网提供的 最热门 在线互动教程: 适合区块链新手的以太坊DApp实战入门教程 区块链+IPFS+Node.js+MongoDB+Express去中心化以太坊电商应用开发实战
其他更多内容也可以访问 这个以太坊博客 。
区块链
2018-04-30 22:08:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
安装browser-solidity
基于浏览器的Solidity
如果你只是想尝试一个使用Solidity的小合约,你不需要安装任何东西,只要访问 基于浏览器的Solidity 。
如果你想离线使用,你可以保存页面到本地,或者从 http://github.com/chriseth/browser-solidity 克隆一个。
NPM / node.js
这可能安装Solidity到本地最轻便最省事的方法。
在基于浏览器的Solidity上,Emscripten提供了一个跨平台JavaScript库,把C++源码编译为JavaScript,同时也提供NPM安装包。
去安装它就可以简单使用。, npm install solc
如何使用nodejs包的详细信息可以在 代码库 中找到.
二进制安装包
Ethereum.
包括Mix IDE的二进制Solidity安装包在Ethereum网站 C++ bundle 中下载。
从源码构建
在MacOS X、Ubuntu和其它类Unix系统中编译安装Solidity非常相似。这个指南开始讲解如何在每个平台下安装相关的依赖软件,然后构建Solidity。
MacOS X
系统需求: OS X Yosemite (10.10.5) Homebrew Xcode
安装Homebrew: brew update brew install boost --c++11 # 这需要等待一段时间 brew install cmake cryptopp miniupnpc leveldb gmp libmicrohttpd libjson-rpc-cpp # 仅仅安装Mix IDE和Alethzero brew install xz d-bus brew install llvm --HEAD --with-clang brew install qt5 --with-d-bus # 如果长时间的等待让你发疯,那么添加--verbose输出信息会让你感觉更好。
Ubuntu 系统
下面是在最新版Ubuntu系统上编译安装Solidity的指南。最佳的支持平台是2014年11月发布的64位Ubuntu 14.04,至少需要2GB内存。我们所有的测试都是基于此版本,当然我们也欢迎其它版本的测试贡献者。
安装依赖软件:
在你从源码编译之前,你需要准备一些工具和依赖软件。
首先,升级你的代码库。Ubuntu主代码库不提供所有的包,你需要从Ethereum PPA和LLVM获取。
注意
Ubuntu 14.04的用户需要使用: sudo apt-add-repository ppa:george-edison55/cmake-3.x 获取最新版本的cmake。
现在加入其它的包: sudo apt-get -y update sudo apt-get -y install language-pack-en-base sudo dpkg-reconfigure locales sudo apt-get -y install software-properties-common sudo add-apt-repository -y ppa:ethereum/ethereum sudo add-apt-repository -y ppa:ethereum/ethereum-dev sudo apt-get -y update sudo apt-get -y upgrade
对于Ubbuntu 15.04(Vivid Vervet)或者更老版本,使用下面的命令获取开发相关的包: sudo apt-get -y install build-essential git cmake libboost-all-dev libgmp-dev libleveldb-dev libminiupnpc-dev libreadline-dev libncurses5-dev libcurl4-openssl-dev libcryptopp-dev libjson-rpc-cpp-dev libmicrohttpd-dev libjsoncpp-dev libedit-dev libz-dev
对于Ubbuntu 15.10(Wily Werewolf)或者更新版本,使用下面的命令获取开发相关的包: sudo apt-get -y install build-essential git cmake libboost-all-dev libgmp-dev libleveldb-dev libminiupnpc-dev libreadline-dev libncurses5-dev libcurl4-openssl-dev libcryptopp-dev libjsonrpccpp-dev libmicrohttpd-dev libjsoncpp-dev libedit-dev libz-dev
不同版本使用不同获取命令的原因是,libjsonrpccpp-dev已经在最新版的Ubuntu的通用代码仓库中。
编译
如果你只准备安装solidity,忽略末尾Alethzero和Mix的错误。 git clone --recursive https://github.com/ethereum/webthree-umbrella.git cd webthree-umbrella ./webthree-helpers/scripts/ethupdate.sh --no-push --simple-pull --project solidity # 更新Solidity库 ./webthree-helpers/scripts/ethbuild.sh --no-git --project solidity --all --cores 4 -DEVMJIT=0 # 编译Solidity及其它 # 在OS X系统加上DEVMJIT将不能编译,在Linux系统上则没问题
如果你选择安装Alethzero和Mix: git clone --recursive https://github.com/ethereum/webthree-umbrella.git cd webthree-umbrella && mkdir -p build && cd build cmake ..
如果你想帮助Solidity的开发,你需要分支(fork)Solidity并添加到你的私人远端分支: cd webthree-umbrella/solidity git remote add personal git@github.com:username/solidity.git
注意webthree-umbrella使用的子模块,所以solidity是其自己的git代码库,但是他的设置不是保存在 .git/config , 而是在 webthree-umbrella/.git/modules/solidity/config . 如果你希望 高效的 学习以太坊DApp开发,可以访问汇智网提供的 最热门 在线互动教程: 适合区块链新手的以太坊DApp实战入门教程 区块链+IPFS+Node.js+MongoDB+Express去中心化以太坊电商应用开发实战
其他更多内容也可以访问 这个以太坊博客 。
转自: https://blog.csdn.net/mongo_node/article/details/80151933
区块链
2018-04-30 22:05:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
一个简单的智能合约
在Solidity中,一个智能合约由一组代码(合约的函数)和数据(合约的状态)组成。智能合约位于以太坊区块链上的一个特殊地址。 uint storedData *;* 这行代码声明了一个状态变量,变量名为 storedData ,类型为 uint (256bits无符号整数)。你可以认为它就像数据库里面的一个存储单元,跟管理数据库一样,可以通过调用函数查询和修改它。在以太坊中,通常只有合约的拥有者才能这样做。在这个例子中,函数 set 和 get 分别用于修改和查询变量的值。
跟很多其他语言一样,访问状态变量时,不需要在前面增加 this. 这样的前缀。
这个合约还无法做很多事情(受限于以太坊的基础设施),仅仅是允许任何人储存一个数字。而且世界上任何一个人都可以来存取这个数字,缺少一个(可靠的)方式来保护你发布的数字。任何人都可以调用set方法设置一个不同的数字覆盖你发布的数字。但是你的数字将会留存在区块链的历史上。稍后我们会学习如何增加一个存取限制,使得只有你才能修改这个数字。
先从一个非常基础的例子开始,不用担心你现在还一点都不了解,我们将逐步了解到更多的细节。
Storage contract SimpleStorage { uint storedData; function set(uint x) { storedData = x; } function get() constant returns (uint retVal) { return storedData; } }
代币的例子
接下来的合约将实现一个形式最简单的加密货币。空中取币不再是一个魔术,当然只有创建合约的人才能做这件事情(想用其他货币发行模式也很简单,只是实现细节上的差异)。而且任何人都可以发送货币给其他人,不需要注册用户名和密码,只要有一对以太坊的公私钥即可。
Note
对于在线solidity环境来说,这不是一个好的例子。如果你使用 在线solidity环境 来尝试这个例子。调用函数时,将无法改变from的地址。所以你只能扮演铸币者的角色,可以铸造货币并发送给其他人,而无法扮演其他人的角色。这点在线solidity环境将来会做改进。 contract Coin { //关键字“public”使变量能从合约外部访问。 address public minter; mapping (address => uint) public balances; //事件让轻客户端能高效的对变化做出反应。 event Sent(address from, address to, uint amount); //这个构造函数的代码仅仅只在合约创建的时候被运行。 function Coin() { minter = msg.sender; } function mint(address receiver, uint amount) { if (msg.sender != minter) return; balances[receiver] += amount; } function send(address receiver, uint amount) { if (balances[msg.sender] < amount) return; balances[msg.sender] -= amount; balances[receiver] += amount; Sent(msg.sender, receiver, amount); } }
这个合约引入了一些新的概念,让我们一个一个来看一下。
address public minter; 这行代码声明了一个可公开访问的状态变量,类型为address。address类型的值大小为160 bits,不支持任何算术操作。适用于存储合约的地址或其他人的公私钥。public关键字会自动为其修饰的状态变量生成访问函数。没有public关键字的变量将无法被其他合约访问。另外只有本合约内的代码才能写入。自动生成的函数如下: function minter() returns (address) { return minter; }
当然我们自己增加一个这样的访问函数是行不通的。编译器会报错,指出这个函数与一个状态变量重名。
下一行代码 mapping (address => uint) public balances; 创建了一个public的状态变量,但是其类型更加的复杂。该类型将一些address映射到无符号整数。mapping可以被认为是一个哈希表,每一个可能的key对应的value被虚拟的初始化为全0.这个类比不是很严谨,对于一个mapping,无法获取一个包含其所有key或者value的链表。所以我们得自己记着添加了哪些东西到mapping中。更好的方式是维护一个这样的链表,或者使用其他更高级的数据类型。或者只在不受这个缺陷影响的场景中使用mapping,就像这个例子。在这个例子中由public关键字生成的访问函数将会更加复杂,其代码大致如下: function balances(address _account) returns (uint balance) { return balances[_account]; }
我们可以很方便的通过这个函数查询某个特定账号的余额。
event Sent(address from, address to, uint value); 这行代码声明了一个“事件”。由send函数的最后一行代码触发。客户端(服务端应用也适用)可以以很低的开销来监听这些由区块链触发的事件。事件触发时,监听者会同时接收到from,to,value这些参数值,可以方便的用于跟踪交易。为了监听这个事件,你可以使用如下代码: 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)); } }
注意在客户端中是如何调用自动生成的 balances 函数的。
这里有个比较特殊的函数 Coin。它是一个构造函数,会在合约创建的时候运行,之后就无法被调用。它会永久得存储合约创建者的地址。msg(以及tx和block)是一个神奇的全局变量,它包含了一些可以被合约代码访问的属于区块链的属性。msg.sender 总是存放着当前函数的外部调用者的地址。
最后,真正被用户或者其他合约调用,用来完成本合约功能的函数是mint和send。如果合约创建者之外的其他人调用mint,什么都不会发生。而send可以被任何人(拥有一定数量的代币)调用,发送一些币给其他人。注意,当你通过该合约发送一些代币到某个地址,在区块链浏览器中查询该地址将什么也看不到。因为发送代币导致的余额变化只存储在该代币合约的数据存储中。通过事件我们可以很容易创建一个可以追踪你的新币交易和余额的“区块链浏览器”。
区块链基础
对于程序员来说,区块链这个概念其实不难理解。因为最难懂的一些东西(挖矿,哈希,椭圆曲线加密,点对点网络等等)只是为了提供一系列的特性和保障。你只需要接受这些既有的特性,不需要关心其底层的技术。就像你如果仅仅是为了使用亚马逊的AWS,并不需要了解其内部工作原理。
交易/事务
区块链是一个全局共享的,事务性的数据库。这意味着参与这个网络的每一个人都可以读取其中的记录。如果你想修改这个数据库中的东西,就必须创建一个事务,并得到其他所有人的确认。事务这个词意味着你要做的修改(假如你想同时修改两个值)只能被完完全全的实施或者一点都没有进行。
此外,当你的事务被应用到这个数据库的时候,其他事务不能修改该数据库。
举个例子,想象一张表,里面列出了某个电子货币所有账号的余额。当从一个账户到另外一个账户的转账请求发生时,这个数据库的事务特性确保从一个账户中减掉的金额会被加到另一个账户上。如果因为某种原因,往目标账户上增加金额无法进行,那么源账户的金额也不会发生任何变化。
此外,一个事务会被发送者(创建者)进行密码学签名。这项措施非常直观的为数据库的特定修改增加了访问保护。在电子货币的例子中,一个简单的检查就可以确保只有持有账户密钥的人,才能从该账户向外转账。
区块
区块链要解决的一个主要难题,在比特币中被称为“双花攻击”。当网络上出现了两笔交易,都要花光一个账户中的钱时,会发生什么?一个冲突?
简单的回答是你不需要关心这个问题。这些交易会被排序并打包成“区块”,然后被所有参与的节点执行和分发。如果两笔交易相互冲突,排序靠后的交易会被拒绝并剔除出区块。
这些区块按时间排成一个线性序列。这也正是“区块链”这个词的由来。区块以一个相当规律的时间间隔加入到链上。对于以太坊,这个间隔大致是17秒。
作为“顺序选择机制”(通常称为“挖矿”)的一部分,一段区块链可能会时不时被回滚。但这种情况只会发生在整条链的末端。回滚涉及的区块越多,其发生的概率越小。所以你的交易可能会被回滚,甚至会被从区块链中删除。但是你等待的越久,这种情况发生的概率就越小。
以太坊虚拟机
总览
以太坊虚拟机(EVM)是以太坊中智能合约的运行环境。它不仅被沙箱封装起来,事实上它被完全隔离,也就是说运行在EVM内部的代码不能接触到网络、文件系统或者其它进程。甚至智能合约与其它智能合约只有有限的接触。
账户
以太坊中有两类账户,它们共用同一个地址空间。外部账户,该类账户被公钥-私钥对控制(人类)。合约账户,该类账户被存储在账户中的代码控制。
外部账户的地址是由公钥决定的,合约账户的地址是在创建该合约时确定的(这个地址由合约创建者的地址和该地址发出过的交易数量计算得到,地址发出过的交易数量也被称作"nonce")
合约账户存储了代码,外部账户则没有,除了这点以外,这两类账户对于EVM来说是一样的。
每个账户有一个key-value形式的持久化存储。其中key和value的长度都是256比特,名字叫做storage.
另外,每个账户都有一个以太币余额(单位是“Wei"),该账户余额可以通过向它发送带有以太币的交易来改变。
交易
一笔交易是一条消息,从一个账户发送到另一个账户(可能是相同的账户或者零账户,见下文)。交易可以包含二进制数据(payload)和以太币。
如果目标账户包含代码,该代码会执行,payload就是输入数据。
如果目标账户是零账户(账户地址是0),交易将创建一个新合约。正如上文所讲,这个合约地址不是零地址,而是由合约创建者的地址和该地址发出过的交易数量(被称为nonce)计算得到。创建合约交易的payload被当作EVM字节码执行。执行的输出做为合约代码被永久存储。这意味着,为了创建一个合约,你不需要向合约发送真正的合约代码,而是发送能够返回真正代码的代码。
Gas
以太坊上的每笔交易都会被收取一定数量的gas,gas的目的是限制执行交易所需的工作量,同时为执行支付费用。当EVM执行交易时,gas将按照特定规则被逐渐消耗。
gas price(以太币计)是由交易创建者设置的,发送账户需要预付的交易费用 = gas price * gas amount。 如果执行结束还有gas剩余,这些gas将被返还给发送账户。
无论执行到什么位置,一旦gas被耗尽(比如降为负值),将会触发一个out-of-gas异常。当前调用帧所做的所有状态修改都将被回滚。
存储,主存和栈
每个账户有一块持久化内存区域被称为存储。其形式为key-value,key和value的长度均为256比特。在合约里,不能遍历账户的存储。相对于另外两种,存储的读操作相对来说开销较大,修改存储更甚。一个合约只能对它自己的存储进行读写。
第二个内存区被称为主存。合约执行每次消息调用时,都有一块新的,被清除过的主存。主存可以以字节粒度寻址,但是读写粒度为32字节(256比特)。操作主存的开销随着其增长而变大(平方级别)。
EVM不是基于寄存器,而是基于栈的虚拟机。因此所有的计算都在一个被称为栈的区域执行。栈最大有1024个元素,每个元素256比特。对栈的访问只限于其顶端,方式为:允许拷贝最顶端的16个元素中的一个到栈顶,或者是交换栈顶元素和下面16个元素中的一个。所有其他操作都只能取最顶的两个(或一个,或更多,取决于具体的操作)元素,并把结果压在栈顶。当然可以把栈上的元素放到存储或者主存中。但是无法只访问栈上指定深度的那个元素,在那之前必须要把指定深度之上的所有元素都从栈中移除才行。
指令集
EVM的指令集被刻意保持在最小规模,以尽可能避免可能导致共识问题的错误实现。所有的指令都是针对256比特这个基本的数据类型的操作。具备常用的算术,位,逻辑和比较操作。也可以做到条件和无条件跳转。此外,合约可以访问当前区块的相关属性,比如它的编号和时间戳。
消息调用
合约可以通过消息调用的方式来调用其它合约或者发送以太币到非合约账户。消息调用和交易非常类似,它们都有一个源,一个目标,数据负载,以太币,gas和返回数据。事实上每个交易都可以被认为是一个顶层消息调用,这个消息调用会依次产生更多的消息调用。
一个合约可以决定剩余gas的分配。比如内部消息调用时使用多少gas,或者期望保留多少gas。如果在内部消息调用时发生了out-of-gas异常(或者其他异常),合约将会得到通知,一个错误码被压在栈上。这种情况只是内部消息调用的gas耗尽。在solidity中,这种情况下发起调用的合约默认会触发一个人工异常。这个异常会打印出调用栈。就像之前说过的,被调用的合约(发起调用的合约也一样)会拥有崭新的主存并能够访问调用的负载。调用负载被存储在一个单独的被称为calldata的区域。调用执行结束后,返回数据将被存放在调用方预先分配好的一块内存中。
调用层数被限制为1024,因此对于更加复杂的操作,我们应该使用循环而不是递归。
代码调用和库
存在一种特殊类型的消息调用,被称为callcode。它跟消息调用几乎完全一样,只是加载自目标地址的代码将在发起调用的合约上下文中运行。
这意味着一个合约可以在运行时从另外一个地址动态加载代码。存储,当前地址和余额都指向发起调用的合约,只有代码是从被调用地址获取的。
这使得Solidity可以实现”库“。可复用的库代码可以应用在一个合约的存储上,可以用来实现复杂的数据结构。
日志
在区块层面,可以用一种特殊的可索引的数据结构来存储数据。这个特性被称为日志,Solidity用它来实现事件。合约创建之后就无法访问日志数据,但是这些数据可以从区块链外高效的访问。因为部分日志数据被存储在布隆过滤器(Bloom filter) 中,我们可以高效并且安全的搜索日志,所以那些没有下载整个区块链的网络节点(轻客户端)也可以找到这些日志。
创建
合约甚至可以通过一个特殊的指令来创建其他合约(不是简单的向零地址发起调用)。创建合约的调用跟普通的消息调用的区别在于,负载数据执行的结果被当作代码,调用者/创建者在栈上得到新合约的地址。
自毁
只有在某个地址上的合约执行自毁操作时,合约代码才会从区块链上移除。合约地址上剩余的以太币会发送给指定的目标,然后其存储和代码被移除。
注意,即使一个合约的代码不包含自毁指令,依然可以通过代码调用(callcode)来执行这个操作。 如果你希望 高效的 学习以太坊DApp开发,可以访问汇智网提供的 最热门 在线互动教程: 适合区块链新手的以太坊DApp实战入门教程 区块链+IPFS+Node.js+MongoDB+Express去中心化以太坊电商应用开发实战
其他更多内容也可以访问 这个以太坊博客 。
转自: https://blog.csdn.net/mongo_node/article/details/80151896
区块链
2018-04-30 22:02:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
1.备份 cd /etc/apt sudo cp sources.list sources.list.bak sudo su gedit sources.list
修改内容 deb http://mirrors.ustc.edu.cn/ubuntu/ xenial main restricted universe multiverse deb http://mirrors.ustc.edu.cn/ubuntu/ xenial-security main restricted universe multiverse deb http://mirrors.ustc.edu.cn/ubuntu/ xenial-updates main restricted universe multiverse deb http://mirrors.ustc.edu.cn/ubuntu/ xenial-proposed main restricted universe multiverse deb http://mirrors.ustc.edu.cn/ubuntu/ xenial-backports main restricted universe multiverse deb-src http://mirrors.ustc.edu.cn/ubuntu/ xenial main restricted universe multiverse deb-src http://mirrors.ustc.edu.cn/ubuntu/ xenial-security main restricted universe multiverse deb-src http://mirrors.ustc.edu.cn/ubuntu/ xenial-updates main restricted universe multiverse deb-src http://mirrors.ustc.edu.cn/ubuntu/ xenial-proposed main restricted universe multiverse deb-src http://mirrors.ustc.edu.cn/ubuntu/ xenial-backports main restricted universe multiverse
3.最后 sudo apt-get update sudo apt-get upgrade
区块链
2018-04-28 15:40:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
使用web3.js获取账户余额
可以使用JavaScript API来获取以太坊账户余额,例如在JavaScript代码中: web3.fromWei(web3.eth.getBalance(web3.eth.coinbase));
如果你在geth控制台里,可以将 web3.eth 使用其 eth 别名代替: > web3.fromWei(eth.getBalance(eth.coinbase));
合约中获取账户余额
在合约中更简单,Solidity为每个 address 对象都提供了一个 balance 属性,它返回以 wei 为单位的账户余额。例如: contract ownerbalancereturner { address owner; function ownerbalancereturner() public { owner = msg.sender; } function getOwnerBalance() constant returns (uint) { return owner.balance; } }
推荐两个以太坊相关的实战教程: 区块链初学者 : 以太坊 DApp 实战开发入门 区块链开发进阶: 去中心化以太坊 DApp 电商平台实战开发
更多的内容可以访问博客:
http://blog.hubwiz.com/2018/01/10/how-to-check-ethereum-balance/
区块链
2018-04-28 15:39:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
虽然有北大博士讲,95%的区块链项目都没有前途,但我们知道区块链还是有它的优势,比如数据的不可篡改性对于版权保护有相当大的意义,而地址的匿名性则有其他潜在的用途。那么,如何将任意数据,比如图像或文本写入以太坊区块链呢?本文将讲解如何使用web3.js实现这一功能并给出相应的实现代码。
实现任意数据上链的核心是 web3.eth.sendTransaction() 方法的使用,我们将借助一个转账交易来完成任意数据上链的任务。在要发送的交易对象中,使用 data 字段就可以传入任意的16进制字符串。
将数据转换为16进制字符串
我们可以使用 web3.toHex() 方法将一个字符串转换为16进制字符串: let data = web3.toHex('你可以将任意数据写入以太坊区块链')
得到的data值为: 0x4f6053ef4ee55c064efb610f6570636e519951654ee5592a574a533a575794fe 。
当然不一定需要使用 web3.toHex() 方法,可以使用任何能够得到16进制串的方法,例如在NodeJS中使用 Buffer : let data = '0x' + Buffer.from('使用Buffer更好处理图像数据').toString('hex')
得到的data值为: 0xe4bdbfe794a8427566666572e69bb4e5a5bde5a484e79086e59bbee5838fe695b0e68dae 。
声明交易对象
接下来然后设置要发送的交易对象,我们需要借助一个转账交易来实现数据上链,因此设置的主要字段是转出账户from,转入账户to,转账金额value,当然,少不了data,我们就是为了它才要搞一个交易: let txo = { from: web3.eth.accounts[0], to: web3.eth.accounts[1], value:'0x00', data: data }
如果你只有一个账户,也可以自己转给自己:)
发送交易
最后调用 web3.eth.sendTransaction() 方法即可: web3.eth.sendTransaction(txo, (error, hash) => console.log(hash));
当交易成功后,你可以使用 etherscan.io 来查看交易信息中的 input data 。
推荐两个以太坊相关的实战教程: 区块链初学者 : 以太坊 DApp 实战开发入门 区块链开发进阶: 去中心化以太坊 DApp 电商平台实战开发
更多的内容可以访问博客:
http://blog.hubwiz.com/2018/04/05/save-data-in-ethereum/
区块链
2018-04-28 15:32:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
在使用solidity开发以太坊智能合约时,可以为变量声明memory和storage关键字。那么,它们有什么不同之处?如果在声明合约变量时没有使用memory关键字,Solidity会尝试在storage中存储这个变量。
storage是什么
根据Solidity首席工程师Chriseth的说法: “你可以把storage想像成一个大数组,它有自己的结构,这个结构是由你的合约中的状态变量所决定的,因此在运行时不能改变” 。
这就是说,storage的结构是在合约部署创建时,根据你的合约中状态变量的声明,就固定下来了,并且不能在将来的合约方法调用中改变这个结构。但是,storage中的内容是可以通过交易来改变的。这些交易调用因此将修改合约的状态,这也是为什么合约中的变量被称为状态变量的原因。因此在合约层面声明的一个uint8类型的storage变量, 它的值可以修改为任何0-255之间的有效uint8值,但是该变量在storage结构中的位置始终不会变化。
函数中的变量
如果你在合约函数中声明变量时没有使用memory关键字,那么solidity将会尝试使用storage结构,目前来讲,这样做可以通过编译,但是可能导致不可预期的结果。memory关键字告诉solidity应当在该函数运行时为变量创建一块空间,使其大小和结构满足函数运行的需要。
在合约层面你不能为变量应用memory关键字。
推荐两个以太坊相关的实战教程: 区块链初学者 : 以太坊 DApp 实战开发入门 区块链开发进阶: 去中心化以太坊 DApp 电商平台实战开发
更多的内容可以访问博客:
http://blog.hubwiz.com/2018/04/03/solidity-memory-storage/
区块链
2018-04-28 15:36:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
在以太坊上发送的交易,最多能包含多少字节的数据?有没有上限?
理论上在以太坊中,对交易大小或者块大小都没有直接或固定的上限,这也是 以太坊的一个优势。
不过这并不意味着交易能携带数据量大小没有上限,因为一个块可以使用的 gas是有上限的。 在写这篇文章时, ethstats 显示 这个值是7,984,452,大约700万。
因此,理论上我们可以创建一个交易,让它消耗掉一个块能用的全部gas,这就 决定了一个交易理论上可以包含的最多数据。
决定数据大小的另一个因素是数据内容,因为不同的数据消耗的gas也不同: 0字节消耗4个gas 非0的字节消耗68个gas 每个交易要支付的21000个gas
利用块的gas上限,并结合你的数据内容,就可以计算出一个交易能发送的数据大小了。
可以试着用mist发送256kb的随机数据:
这大约会消耗900万gas,mist会尝试创建交易,但不会成功。
让我们试着接近块gas上限,这次使用44,444个随机字节:
这个交易可以成功,你可以点 这里 查看交易数据。 Value: 60 Finney (0.06 Ether) Gas: 3131800 Gas Price: 50 Gwei (0.00000005 Ether) Gas Used By Transaction: 3031800 Actual Transaction Cost: 151.59 Finney (0.15159 Ether) Cumulative Gas Used: 3031800
你看,我们成功地在块967163上写入了44k字节的数据。
接下来,以太坊的可扩展性开始展示它的力量了。
推荐两个以太坊相关的实战教程: 区块链初学者 : 以太坊 DApp 实战开发入门 区块链开发进阶: 去中心化以太坊 DApp 电商平台实战开发
更多的内容可以访问博客:
http://blog.hubwiz.com/2018/04/23/ethereum-transaction-size/
区块链
2018-04-28 15:28:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
infura提供了托管的以太坊节点,如何将智能合约部署到infura提供的托管节点?本教程介绍配置truffle将智能合约通过infura发布到以太坊网络上。
Infura是一个托管的以太坊节点集群,可以将你开发的以太坊智能合约部署到infura提供的节点上,而无需搭建自己的以太坊节点。
可能你还不了解Infura,但如果你使用过MetaMask,那么就已经接触过Infura了,因为它是MetaMask背后的以太坊供应商。
出于安全原因,Infura不管理你的私钥,这意味着Infura不能代表你签署交易。
但是,Infura可以通过使用 HDWalletProvider 来签署交易。 该服务可以处理事务签名以及与以太坊网络的连接。 点击 这里 了解更多关于HDWalletProvider的信息 。
本教程将向你展示如何使用Infura将现有的dapp迁移到Infura支持的以太坊网络。 在这个特定的例子中,我们将迁移到Ropsten测试网络。 我们假设你已经有了一个dapp, 如果需要一个测试dapp,可以使用Truffle提供的 宠物店 dapp。
安装HDWalletProvider
Infura的HDWalletProvider是一个独立的npm软件包,如下安装: npm install truffle-hdwallet-provider 注意 :如果你在Windows上安装并且遇到MSBUILD错误,则可能需要安装Windows构建工具。 在具有管理员权限的控制台中,运行 npm install -g windows-build-tools 然后再次尝试安装。
注册Infura
在使用Infura之前,需要 注册Infura访问令牌 。
填写并提交表格后你就可以收到访问令牌。 相关信息将显示在屏幕上并发送到你提供的电子邮件。 需要记录下来这个访问令牌并确保它不被别人看到!
配置Truffle项目
下一步是编辑你的truffle.js文件来启用HDWalletProvider并为部署到Ropsten进行必要的配置。
STEP 1:首先,在配置文件中定义HDWalletProvider对象。 在truffle.js文件的顶部添加以下代码: var HDWalletProvider = require("truffle-hdwallet-provider");
STEP 2:接下来,提供助记词( mnemonic )来生成你的账户。 var mnemonic = "orange apple banana ... "; 警告 :在此过程中,我们强烈建议将助记符存储在另一个(秘密)文件中,以降低助记符泄漏风险。 如果有人知道你的助记符,他们将拥有你所有的地址和私钥!
STEP 3:添加Ropsten网络定义: module.exports = { networks: { ropsten: { provider: function() { return new HDWalletProvider(mnemonic, "https://ropsten.infura.io/") }, network_id: 3 } } };
注意事项: 虽然该示例仅定义了单个网络,但你可以像往常一样定义多个网络。 ropsten网络定义中的provider将使用实例化的HDWalletProvider 。 HDWalletProvider以助记符和期望的网络为参数。 Infura主页 上提供Infura支持的 网络列表。 确保使用前面拿到的Infura访问令牌替换 。 provider值被封装在一个函数中,这可以确保它在需要之前不会被初始化。 如果连接到多个网络,这一点尤为重要。 关于该主题的更多信息,请参阅Truffle文档的 网络配置 部分。 默认情况下,由助记符产生的第一个账户将负责执行合约迁移任务。 但如果需要的话,你可以传入参数以指定要使用的帐户。 例如,要使用第三个帐户: new HDWalletProvider(mnemonic, "https://ropsten.infura.io/", 2);
账户索引是从零开始的,所以2表示第三个地址。
使用Faucet获取ether
确保你的帐户有足够的账户余额来进行部署。 可以通过称Faucet的服务在Ropsten网络上获取Ether。 虽然在那里有多个Faucet网站,我们推荐的一个服务是在 EthTools 上托管的 。 导航至EthTools的 Ether Faucet 。 输入你的助记符,并选择你想要多少ether(最多5个)。 Faucet将链接到你的第一个帐户。 点击“Request Ether”提交请求。 很快,你的账户将获得请求的ether。 注意 :也可以通过MetaMask申请ether。 在Ropsten上连接你的帐户,然后点击“Buy”按钮,该按钮将提供MetaMask的Ropsten测试Faucet的链接,它的工作方式与上述类似。
我们现在可以开始将合约部署到Ropsten上了!
部署合约
STEP 1: 编译项目: truffle compile
STEP 2:部署到Ropsten网络: truffle migrate --network ropsten
如果一切顺利,应该可以看到类似于以下内容的输出: Using network 'ropsten'. Running migration: 1_initial_migration.js Deploying Migrations... ... 0xd79bc3c5a7d338a7f85db9f86febbee738ebdec9494f49bda8f9f4c90b649db7 Migrations: 0x0c6c4fc8831755595eda4b5724a61ff989e2f8b9 Saving successful migration to network... ... 0xc37320561d0004dc149ea42d839375c3fc53752bae5776e4e7543ad16c1b06f0 Saving artifacts... Running migration: 2_deploy_contracts.js Deploying MyContract... ... 0x7efbb3e4f028aa8834d0078293e0db7ff8aff88e72f33960fc806a618a6ce4d3 MyContract: 0xda05d7bfa5b6af7feab7bd156e812b4e564ef2b1 Saving successful migration to network... ... 0x6257dd237eb8b120c8038b066e257baee03b9c447c3ba43f843d1856de1fe132 Saving artifacts...
需要提醒的是,你的交易ID将与上面的不同。
注意 :如果收到错误 Error: Exceeds block gas limit ,你可能需要为合约手动设置油量上限( gas limit )。 有关详细信息,请参阅 Truffle配置文档 。
STEP 3:如果想验证合约是否已成功部署,可以在Etherscan的Ropsten部分进行检查。 在搜索字段中,输入部署交易ID。 在上面的例子中,交易ID是: 0x7efbb3e4f028aa8834d0078293e0db7ff8aff88e72f33960fc806a618a6ce4d3
你应该可以看到有关交易的详细信息,包括交易受到保护的区块号。
恭喜! 你已经使用Infura和truffle的组合将合约部署到Ropsten上了!
推荐两个以太坊相关的实战教程: 区块链初学者 : 以太坊 DApp 实战开发入门 区块链开发进阶: 去中心化以太坊 DApp 电商平台实战开发
更多的内容可以访问博客: http://blog.hubwiz.com/2018/04/01/truffle-infura-deploy-contract/
区块链
2018-04-28 15:21:00
「深度学习福利」大神带你进阶工程师,立即查看>>> /* * @param {org.example.mynetwork.SetupDemo} setupDemo * @transaction */ async function initializationFun(setupDemo) { const NS = "org.example.mynetwork"; const factory = getFactory(); //创建三个参与者 var zhangsan = factory.newResource(NS, "Person", 'zhangsan001') zhangsan.name = "张三"; zhangsan.balance = 100; var lisi = factory.newResource(NS, "Person", 'lisi') lisi.name = "李四"; lisi.balance = 100; var wangwu = factory.newResource(NS, "Person", 'wangwu') wangwu.name = "王五"; wangwu.balance = 100; const partRegistry = await getParticipantRegistry(NS+'.Person') await partRegistry.add(zhangsan); await partRegistry.add(lisi); await partRegistry.add(wangwu); //创建资产 var car = factory.newResource(NS, "Car", '川A0001') car.personId = zhangsan.id; car.color = "red" const assetRegistry = await getAssetRegistry(NS+'.Car'); await assetRegistry.add(car) } /** * Sample transaction transaction TransactionCar{ * @param {org.example.mynetwork.TransactionCar} transactionCar * @transaction */ async function transactionCarFun(transactionCar){ const NS = "org.example.mynetwork"; const from = transactionCar.from; const to = transactionCar.to; const price = transactionCar.price; from.balance = from.balance + price; to.balance = to.balance - price; const partRegistry = await getParticipantRegistry(NS+'.Person') await partRegistry.update(from); await partRegistry.update(to); debugger const car = transactionCar.car; car.personId = to.id; if(!car.transactionCars){ car.transactionCars = [] } car.transactionCars.push(transactionCar) const assetRegistry = await getAssetRegistry(NS+".Car"); await assetRegistry.update(car); }

/** * Write your model definitions here */ namespace org.example.mynetwork participant SampleParticipant identified by participantId { o String participantId o String firstName o String lastName } asset SampleAsset identified by assetId { o String assetId --> SampleParticipant owner o String value } transaction SampleTransaction { --> SampleAsset asset o String newValue } event SampleEvent { --> SampleAsset asset o String oldValue o String newValue } participant Person identified by id { o String id o String name o Double balance } abstract asset Vehicle identified by licensePlate { o String licensePlate o String color } asset Car extends Vehicle { o String personId o TransactionCar[] transactionCars optional } transaction SetupDemo {} transaction TransactionCar{ o Double price o String car o String from o String to }

/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Sample access control list. */ rule Default { description: "Allow all participants access to all resources" participant: "ANY" operation: ALL resource: "org.example.mynetwork.*" action: ALLOW } rule SystemACL { description: "System ACL to permit all access" participant: "org.hyperledger.composer.system.Participant" operation: ALL resource: "org.hyperledger.composer.system.**" action: ALLOW } rule NetworkAdminUser { description: "Grant business network administrators full access to user resources" participant: "org.hyperledger.composer.system.NetworkAdmin" operation: ALL resource: "**" action: ALLOW } rule NetworkAdminSystem { description: "Grant business network administrators full access to system resources" participant: "org.hyperledger.composer.system.NetworkAdmin" operation: ALL resource: "org.hyperledger.composer.system.**" action: ALLOW } { "$class": "org.example.mynetwork.TransactionCar", "price": 10, "car": "川A0001", "from": "zhangsan001", "to": "lisi" }
区块链
2018-04-28 09:42:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
区块链无疑是现在最火热的技术,自从比特币火了,其背后的技术——区块链也得到了史无前例的追捧,这些年,各个行业都在实践落地以抢占先机。
今天推荐一本区块链技术的学习书籍—— <区块链技术进阶与实战> ,除了原理之外知识之外,更多的是如何实战把区块链技术真正落地应用,也能帮助打开区块链误区,拓展知识面。
本书介绍
• 国内区块链技术顶尖研究者和一线开发者出品
• 中国工程院院士陈纯等业内专业人士推荐
• 专注介绍区块链核心原理(干货)和应用技术(实战)
• 详细解读区块链平台 以太坊 和 HyperLedger
• 注重实战,全书包含 5 个完整实际项目案例
• 参考本书实例即可快速开发自己的第一个区块链应用
本书从实战的角度出发,结合实际应用开发场景,对区块链技术进行了全面介绍和剖析。
作者背景
蔡亮,博士,副教授,浙江大学计算机学院软件工程系主任,浙江省重大科技专项专家。
李启雷,博士,讲师,杭州趣链科技有限公司首席技术官。
梁秀波,博士,副研究员,杭州趣链科技有限公司副总经理。
内容简介
第一部分介绍区块链的基础知识,使读者快速对区块链技术有一个整体认识。
第二部分对知名开源区块链平台以太坊和Hyperledger进行详细解读,并介绍如何基于这两个平台进行区块链应用开发。
第三部分以自主可控联盟区块链Hyperchain为例对企业级区块链平台的核心技术进行分析,并介绍了基于Hyperchain的企业级区块链应用开发技术。
第四部分介绍了多个区块链实际应用项目案例,并对其开发过程和关键代码进行了详细分析。
免费送书
好消息,本次我们联合了著名的 图灵教育出版社 ,并带来了 8 本 <区块链技术进阶与实战> 重量级书籍免费抽奖赠送给大家,请扫描以下微信公众号参加!
最后,祝大家51劳动节和54青年节快乐!
区块链
2018-04-28 09:08:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
对数字货币的崛起感到新奇的我们,并且想知道其背后的技术——区块链是怎样实现的。
但是完全搞懂区块链并非易事,我喜欢在实践中学习,通过写代码来学习技术会掌握得更牢固。通过构建一个区块链可以加深对区块链的理解。
准备工作
本文要求读者对Python有基本的理解,能读写基本的Python,并且需要对HTTP请求有基本的了解。
我们知道区块链是由区块的记录构成的不可变、有序的链结构,记录可以是交易、文件或任何你想要的数据,重要的是它们是通过哈希值(hashes)链接起来的。
环境准备
环境准备,确保已经安装Python3.5, pip , django, requests,urllib,json,hashlib
安装方法: pip install django requests
同时还需要一个HTTP客户端,比如Postman,cURL或其它客户端,本文以Postman为例。
开始创建Blockchain
通过django-admin startproject block创建一个block的项目,在项目中创建一个demo项目django-admin startproject demo ,目录结构:
Blockchain类
在views中创建一个Blockchain类,在构造函数中创建了两个列表,一个用于储存区块链,一个用于储存交易。
以下是Blockchain类的框架: class Blockchain(object): def __init__(self): self.chain = [] self.current_transactions = [] def new_block(self): # Creates a new Block and adds it to the chain pass def new_transaction(self): # Adds a new transaction to the list of transactions pass @staticmethod def hash(block): # Hashes a Block pass @property def last_block(self): # Returns the last Block in the chain pass
Blockchain类用来管理链条,它能存储交易,加入新块等,下面我们来进一步完善这些方法。
块结构
每个区块包含属性:索引(index),Unix时间戳(timestamp),交易列表(transactions),工作量证明(稍后解释)以及前一个区块的Hash值。
以下是一个区块的结构: block = { 'index': 1, 'timestamp': 1506057125.900785, 'transactions': [ { 'sender': "8527147fe1f5426f9dd545de4b27ee00", 'recipient': "a77f5cdfa2934df3954a5c7c7da5df1f", 'amount': 5, } ], 'proof': 324984774000, 'previous_hash': "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824" }
到这里,区块链的概念就清楚了,每个新的区块都包含上一个区块的Hash,这是关键的一点,它保障了区块链不可变性。如果攻击者破坏了前面的某个区块,那么后面所有区块的Hash都会变得不正确。不理解的话,慢慢消化,可参考区块链记账原理
加入交易
接下来我们需要添加一个交易,来完善下new_transaction方法 class Blockchain(object): ... def new_transaction(self, sender, recipient, amount): """ 生成新交易信息,信息将加入到下一个待挖的区块中 :param sender: Address of the Sender :param recipient: Address of the Recipient :param amount: Amount :return: The index of the Block that will hold this transaction """ self.current_transactions.append({ 'sender': sender, 'recipient': recipient, 'amount': amount, }) return self.last_block['index'] + 1
方法向列表中添加一个交易记录,并返回该记录将被添加到的区块(下一个待挖掘的区块)的索引,等下在用户提交交易时会有用。
创建新块
当Blockchain实例化后,我们需要构造一个创世块(没有前区块的第一个区块),并且给它加上一个工作量证明。
每个区块都需要经过工作量证明,俗称挖矿,稍后会继续讲解。
为了构造创世块,我们还需要完善 new_block(), new_transaction() 和hash() 方法: class Blockchain(object): def __init__(self): self.chain = [] self.current_transactions = [] self.new_block(previous_hash=1, proof=100) self.nodes = set() def new_block(self,proof,previous_hash= None): block = { 'index': len(self.chain) + 1, 'timestamp': time(), 'transactions': self.current_transactions, 'proof':proof , 'previous_hash': previous_hash or self.hash(self.chain[-1]), } self.current_transactions = [] self.chain.append(block) return block def new_transaction(self,sender,recipient,amount): self.current_transactions.append({ 'sender': sender, 'recipient': recipient, 'amount': amount, }) return self.last_block['index']+1 @staticmethod def hash(block): block_string = json.dumps(block, sort_keys=True).encode() return hashlib.sha256(block_string).hexdigest()
通过上面的代码和注释可以对区块链有直观的了解,接下来我们看看区块是怎么挖出来的。
理解工作量证明
新的区块依赖工作量证明算法(PoW)来构造。PoW的目标是找出一个符合特定条件的数字, 这个数字很难计算出来,但容易验证 。这就是工作量证明的核心思想。
为了方便理解,举个例子:
假设一个整数 x 乘以另一个整数 y 的积的 Hash 值必须以 0 结尾,即 hash(x * y) = ac23dc…0。设变量 x = 5,求 y 的值?
用Python实现如下: from hashlib import sha256 x = 5 y = 0 while sha256(str(x*y).encode()).hexdigest()[:4] != "0000": y += 1 print(y,sha256(str(x*y).encode()).hexdigest()[:4]) print(y)
在比特币中,使用称为Hashcash的工作量证明算法,它和上面的问题很类似。矿工们为了争夺创建区块的权利而争相计算结果。通常,计算难度与目标字符串需要满足的特定字符的数量成正比,矿工算出结果后,会获得比特币奖励。
当然,在网络上非常容易验证这个结果。
实现工作量证明
让我们来实现一个相似PoW算法,规则是:寻找一个数 p,使得它与前一个区块的 proof 拼接成的字符串的 Hash 值以 4 个零开头。 import hashlib import json from time import time from uuid import uuid4 class Blockchain(object): ... def last_block(self): return self.chain[-1] def proof_of_work(self, last_proof): proof = 0 while self.valid_proof(last_proof, proof) is False: proof += 1 return proof @staticmethod def valid_proof(last_proof, proof): guess = str(last_proof*proof).encode() guess_hash = hashlib.sha256(guess).hexdigest() return guess_hash[:5] == "00000"
衡量算法复杂度的办法是修改零开头的个数。使用4个来用于演示,你会发现多一个零都会大大增加计算出结果所需的时间。
现在Blockchain类基本已经完成了,接下来使用HTTP requests来进行交互。
Blockchain作为API接口
我们将使用Python django框架,这是一个轻量Web应用框架,它方便将网络请求映射到 Python函数,现在我们来让来试一下:
我们将创建三个接口: /transactions/new 创建一个交易并添加到区块 /mine 告诉服务器去挖掘新的区块 /chain 返回整个区块链
创建节点
我们的“django web服务器”将扮演区块链网络中的一个节点。我们先添加一些框架代码: node_identifier = str(uuid4()).replace('-', '') # Instantiate the Blockchain blockchain = Blockchain() def mine(request): last_block = blockchain.last_block last_proof = last_block['proof'] proof = blockchain.proof_of_work(last_proof) print(proof) blockchain.new_transaction( sender="0", recipient=node_identifier, amount=1, ) # Forge the new Block by adding it to the chain block = blockchain.new_block(proof) response = { 'message': "New Block Forged", 'index': block['index'], 'transactions': block['transactions'], 'proof': block['proof'], 'previous_hash': block['previous_hash'], } print(response) return HttpResponse(json.dumps(response)) def new_transaction(request): values = json.loads(request.body.decode('utf-8')) required = ['sender', 'recipient', 'amount'] if not all(k in values for k in required): return 'Missing values' index = blockchain.new_transaction(values['sender'], values['recipient'], values['amount']) print(index) response = {'message': 'Transaction will be added to Block %s'%index} return HttpResponse(json.dumps(response)) def full_chain(request): response = { 'chain': blockchain.chain, 'length': len(blockchain.chain), } return HttpResponse(json.dumps(response))
添加url路由节点:运行服务 from demo import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^mine', views.mine), url(r'^transactions/new/', views.new_transaction), url(r'^chain/', views.full_chain), url(r'^register', views.register_nodes), url(r'^resolve', views.consensus), ]
运行服务 python manage.py runserver 127.0.0.1:8000
发送交易
发送到节点的交易数据,结构如下: { "sender": "my address", "recipient": "someone else's address", "amount": 5 }
向服务添加一个交易
挖矿
挖矿正是神奇所在,它很简单,做了一下三件事:
计算工作量证明PoW
通过新增一个交易授予矿工(自己)一个币
构造新区块并将其添加到链中 def proof_of_work(self, last_proof): proof = 0 while self.valid_proof(last_proof, proof) is False: proof += 1 return proof @staticmethod def valid_proof(last_proof, proof): guess = str(last_proof*proof).encode() guess_hash = hashlib.sha256(guess).hexdigest() return guess_hash[:5] == "00000"
注意交易的接收者是我们自己的服务器节点,我们做的大部分工作都只是围绕Blockchain类方法进行交互。到此,我们的区块链就算完成了,我们来实际运行下
运行区块链
你可以使用cURL 或Postman 去和API进行交互
让我们通过请求 http://127.0.0.1:8000/mine 来进行挖矿
在挖了两次矿之后,就有3个块了,通过请求 http://localhost:8000/chain 可以得到所有的块信息。 { "chain": [ { "transactions": [], "proof": 100, "timestamp": 1520314374.7261052, "index": 1, "previous_hash": 1 }, { "transactions": [ { "sender": "0", "recipient": "27d4aae55b2848dcae52bc722d86e0c3", "amount": 1 } ], "proof": 1771087, "timestamp": 1520314389.5019505, "index": 2, "previous_hash": "32fa73f48240160257e95fdf8422c6df734b5d7e8ceb69a41a5578643c1d36fb" }, { "transactions": [ { "sender": "d4ee26eee15148ee92c6cd394edd9705", "recipient": "5", "amount": 500 }, { "sender": "0", "recipient": "27d4aae55b2848dcae52bc722d86e0c3", "amount": 1 } ], "proof": 100, "timestamp": 1520314592.4745598, "index": 3, "previous_hash": "e6b1be488e0ed20fe3ec51135e5fafb4dfffaa28a190967106a5dd3e89e4b3aa" } ], "length": 3 }
一致性(共识)
我们已经有了一个基本的区块链可以接受交易和挖矿。但是区块链系统应该是分布式的。既然是分布式的,那么我们究竟拿什么保证所有节点有同样的链呢?这就是一致性问题,我们要想在网络上有多个节点,就必须实现一个一致性的算法。
注册节点
在实现一致性算法之前,我们需要找到一种方式让一个节点知道它相邻的节点。每个节点都需要保存一份包含网络中其它节点的记录。因此让我们新增几个接口: /register 接收URL形式的新节点列表 /resolve 执行一致性算法,解决任何冲突,确保节点拥有正确的链
我们修改下Blockchain的init函数并提供一个注册节点方法: from urllib.parse import urlparse ... class Blockchain(object): def __init__(self): ... self.nodes = set() ... def register_node(self, address): parsed_url = urlparse(address) self.nodes.add(parsed_url.netloc)
我们用 set 来储存节点,这是一种避免重复添加节点的简单方法。
实现共识算法
前面提到,冲突是指不同的节点拥有不同的链,为了解决这个问题,规定最长的、有效的链才是最终的链,换句话说,网络中有效最长链才是实际的链。
我们使用一下的算法,来达到网络中的共识 class Blockchain(object): def __init__(self): ... def valid_chain(self, chain): last_block = chain[0] current_index = 1 while current_index < len(chain): block = chain[current_index] if block['previous_hash'] != self.hash(last_block): return False # Check that the Proof of Work is correct if not self.valid_proof(last_block['proof'], block['proof']): return False last_block = block current_index += 1 return True def resolve_conflicts(self): neighbours = self.nodes new_chain = None max_length = len(self.chain) for node in neighbours: response = requests.get('http://%s/chain' %node) if response.status_code == 200: length = json.loads(response)['length'] chain = json.loads(response)['chain'] # Check if the length is longer and the chain is valid if length > max_length and self.valid_chain(chain): max_length = length new_chain = chain # Replace our chain if we discovered a new, valid chain longer than ours if new_chain: self.chain = new_chain return True return False
第一个方法 valid_chain() 用来检查是否是有效链,遍历每个块验证hash和proof.
第2个方法 resolve_conflicts() 用来解决冲突,遍历所有的邻居节点,并用上一个方法检查链的有效性, 如果发现有效更长链,就替换掉自己的链
在url中添加两个路由,一个用来注册节点,一个用来解决冲突。 from demo import views urlpatterns = [ url(r'^register', views.register_nodes), url(r'^resolve', views.consensus), ]
你可以在不同的机器运行节点,或在一台机机开启不同的网络端口来模拟多节点的网络,这里在同一台机器开启不同的端口演示,在不同的终端运行一下命令,就启动了两个节点: http://127.0.0.1:8000 和 http://127.0.0.1:8100
然后在节点8100节点上挖两个块,确保是更长的链,然后在节点8000节点上访问接口/resolve ,这时节点8100的链会通过共识算法被节点8000节点的链取代。
总结
以上所述是小编给大家介绍的用Django实现一个可运行的区块链应用,希望对大家有所帮助,
区块链
2018-04-28 08:24:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
ipfs(InterPlanetary File System,星际文件系统),本文介绍节点软件ipfs环境搭建和使用方法,学习ipfs-api在nodejs代码中访问ipfs网络。
一、ipfs节点安装与使用
1.1下载节点软件
到官网下载windows版的ipfs节点软件: 32位 , 64位 如果你不能访问官网,可以使用百度云盘镜像: 32位 , 64位
1.2解压节点软件
下载后解压到指定目录,例如d:\go-ipfs,开一个控制台窗口,测试: D:\go-ipfs > ipfs version Ipfs version 0.4.14
可以将该目录加入环境变量PATH, 或者将d:\go-ipfs\ipfs.exe拷贝到windows系统目录,以便在任何目录中可以启动ipfs.exe。
1.3 初始化本地仓库
和git类似,ipfs节点也需要先初始化一个本地仓库。执行init子命令来初始化本地仓库: D:\go-ipfs> ipfs init Initializing IPFS node at C:
区块链
2018-04-29 16:24:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
以太坊p2p原理与实现
区块链技术的去中心依赖于底层组网技术,以太坊的底层实现了p2pServer,大约可以分为这样三层。 底层路由表。封装了kad路由,节点的数据结构以及计算记录,节点搜索,验证等功能。 中层peer抽象,message开放发送接口,server对外提供peer检测,初始化,事件订阅,peer状态查询,启动,停止等功能 以太坊最上层peer,peerset再封装,通过协议的Run函数,在中层启动peer时,获取peer,最终通过一个循环截取稳定peer,包装在peerset中使用。
底层路由表
这里简化问题仅讨论Node Discovery Protocol。 这一层维护了一个buckets桶,总共有17个桶,每个桶有16个节点和10个替换节点。 Node放入时先要计算hash和localNode的距离。再按距离选择一个桶放进去,取的时候逐个计算target和每个桶中对象的举例,详细参考closest函数,后面会贴出来。
距离公式满足:f(x,y)=256-8*n-map(x[n+1]^y[n+1]) 注:n为相同节点数量 map为一个负相关的映射关系。
简单来说就是相似越多,值越小。细节参考Node.go的logdist函数。 这里需要了解算法Kademlia, . ├── database.go //封装node数据库相关操作 ├── node.go //节点数据结构 ├── ntp.go //同步时间 ├── table.go //路由表 ├── udp.go //网络相关操作
其中最重要的就是table对象,table公共方法有: newTable 实例创建 Self local节点获取 ReadRandomNodes 随机读取几个节点 Close 关闭 Resolve 在周边查找某个节点 Lookup 查找某个节点的邻近节点
逐个来分析这些方法:
newTable 1:生成对象实例(获取数据库客户端,LocalNode etc) // If no node database was given, use an in-memory one db, err := newNodeDB(nodeDBPath, Version, ourID) if err != nil { return nil, err } tab := &Table{ net: t, db: db, self: NewNode(ourID, ourAddr.IP, uint16(ourAddr.Port), uint16(ourAddr.Port)), bonding: make(map[NodeID]*bondproc), bondslots: make(chan struct{}, maxBondingPingPongs), refreshReq: make(chan chan struct{}), initDone: make(chan struct{}), closeReq: make(chan struct{}), closed: make(chan struct{}), rand: mrand.New(mrand.NewSource(0)), ips: netutil.DistinctNetSet{Subnet: tableSubnet, Limit: tableIPLimit}, } 2:载入引导节点,初始化k桶。 if err := tab.setFallbackNodes(bootnodes); err != nil { return nil, err } for i := 0; i < cap(tab.bondslots); i++ { tab.bondslots <- struct{}{} } for i := range tab.buckets { tab.buckets[i] = &bucket{ ips: netutil.DistinctNetSet{Subnet: bucketSubnet, Limit: bucketIPLimit}, } } 3:将节点放入到桶里,生成一条协程用于刷新,验证节点。 tab.seedRand() tab.loadSeedNodes(false) //载入种子节点 // Start the background expiration goroutine after loading seeds so that the search for // seed nodes also considers older nodes that would otherwise be removed by the // expiration. tab.db.ensureExpirer() go tab.loop()
载入种子节点 func (tab *Table) loadSeedNodes(bond bool) { seeds := tab.db.querySeeds(seedCount, seedMaxAge) //数据库中的种子节点和引导节点合并 seeds = append(seeds, tab.nursery...) if bond { seeds = tab.bondall(seeds) //节点验证 } for i := range seeds { seed := seeds[i] age := log.Lazy{Fn: func() interface{} { return time.Since(tab.db.bondTime(seed.ID)) }} log.Debug("Found seed node in database", "id", seed.ID, "addr", seed.addr(), "age", age) tab.add(seed) //节点入桶 } }
节点入桶,同时也要检查ip等限制。 func (tab *Table) add(new *Node) { tab.mutex.Lock() defer tab.mutex.Unlock() b := tab.bucket(new.sha) //获取当前节点对应的桶 if !tab.bumpOrAdd(b, new) { // Node is not in table. Add it to the replacement list. tab.addReplacement(b, new) } }
桶的选择 func (tab *Table) bucket(sha common.Hash) *bucket { d := logdist(tab.self.sha, sha) //计算hash举例 if d <= bucketMinDistance { //这里按算法来看,只要hash前三位相等就会到第一个buckets return tab.buckets[0] } return tab.buckets[d-bucketMinDistance-1] }
Resolve
根据Node的Id查找Node,先在当前的桶里面查找,查找一遍之后没找到就在周边的节点里面搜索一遍再找。 // Resolve searches for a specific node with the given ID. // It returns nil if the node could not be found. func (tab *Table) Resolve(targetID NodeID) *Node { // If the node is present in the local table, no // network interaction is required. hash := crypto.Keccak256Hash(targetID[:]) tab.mutex.Lock() //查找最近节点 cl := tab.closest(hash, 1) tab.mutex.Unlock() if len(cl.entries) > 0 && cl.entries[0].ID == targetID { return cl.entries[0] } // Otherwise, do a network lookup. //不存在 搜索邻居节点 result := tab.Lookup(targetID) for _, n := range result { if n.ID == targetID { return n } } return nil }
这里需要理解的函数是 closest,遍历所有桶的所有节点,查找最近的一个 // closest returns the n nodes in the table that are closest to the // given id. The caller must hold tab.mutex. func (tab *Table) closest(target common.Hash, nresults int) *nodesByDistance { // This is a very wasteful way to find the closest nodes but // obviously correct. I believe that tree-based buckets would make // this easier to implement efficiently. close := &nodesByDistance{target: target} for _, b := range tab.buckets { for _, n := range b.entries { close.push(n, nresults) } } return close } func (h *nodesByDistance) push(n *Node, maxElems int) { ix := sort.Search(len(h.entries), func(i int) bool { return distcmp(h.target, h.entries[i].sha, n.sha) > 0 }) if len(h.entries) < maxElems { h.entries = append(h.entries, n) } if ix == len(h.entries) { // farther away than all nodes we already have. // if there was room for it, the node is now the last element. } else { // slide existing entries down to make room // this will overwrite the entry we just appended. //近的靠前边 copy(h.entries[ix+1:], h.entries[ix:]) h.entries[ix] = n } }
ReadRandomNodes
整体思路是先拷贝出来,再逐个桶的抽最上面的一个,剩下空桶移除,剩下的桶合并后,下一轮再抽桶的第一个节点,直到填满给定数据或者桶全部空掉。最后返回填到数组里面的数量。 // ReadRandomNodes fills the given slice with random nodes from the // table. It will not write the same node more than once. The nodes in // the slice are copies and can be modified by the caller. func (tab *Table) ReadRandomNodes(buf []*Node) (n int) { if !tab.isInitDone() { return 0 } tab.mutex.Lock() defer tab.mutex.Unlock() // Find all non-empty buckets and get a fresh slice of their entries. var buckets [][]*Node //拷贝节点 for _, b := range tab.buckets { if len(b.entries) > 0 { buckets = append(buckets, b.entries[:]) } } if len(buckets) == 0 { return 0 } // Shuffle the buckets. for i := len(buckets) - 1; i > 0; i-- { j := tab.rand.Intn(len(buckets)) buckets[i], buckets[j] = buckets[j], buckets[i] } // Move head of each bucket into buf, removing buckets that become empty. var i, j int for ; i < len(buf); i, j = i+1, (j+1)%len(buckets) { b := buckets[j] buf[i] = &(*b[0]) //取第一个节点 buckets[j] = b[1:] //移除第一个 if len(b) == 1 { //空桶移除 buckets = append(buckets[:j], buckets[j+1:]...) } if len(buckets) == 0 { break } } return i + 1 }
Lookup
lookup会要求已知节点查找邻居节点,查找的邻居节点又递归的找它周边的节点 for { // ask the alpha closest nodes that we haven't asked yet for i := 0; i < len(result.entries) && pendingQueries < alpha; i++ { n := result.entries[i] if !asked[n.ID] { asked[n.ID] = true pendingQueries++ go func() { // Find potential neighbors to bond with r, err := tab.net.findnode(n.ID, n.addr(), targetID) if err != nil { // Bump the failure counter to detect and evacuate non-bonded entries fails := tab.db.findFails(n.ID) + 1 tab.db.updateFindFails(n.ID, fails) log.Trace("Bumping findnode failure counter", "id", n.ID, "failcount", fails) if fails >= maxFindnodeFailures { log.Trace("Too many findnode failures, dropping", "id", n.ID, "failcount", fails) tab.delete(n) } } reply <- tab.bondall(r) }() } } if pendingQueries == 0 { // we have asked all closest nodes, stop the search break } // wait for the next reply for _, n := range <-reply { //此处会阻塞请求 if n != nil && !seen[n.ID] { seen[n.ID] = true result.push(n, bucketSize) } } pendingQueries-- }
桶的维护
桶初始化完成后会进入一个循环逻辑,其中通过三个timer控制调整周期。 验证timer 间隔 10s左右 刷新timer 间隔 30 min 持久化timer 间隔 30s revalidate = time.NewTimer(tab.nextRevalidateTime()) refresh = time.NewTicker(refreshInterval) copyNodes = time.NewTicker(copyNodesInterval)
刷新逻辑:重新加载种子节点,查找周边节点,随机三个节点,并查找这三个节点的周围节点。 func (tab *Table) doRefresh(done chan struct{}) { defer close(done) tab.loadSeedNodes(true) tab.lookup(tab.self.ID, false) for i := 0; i < 3; i++ { var target NodeID crand.Read(target[:]) tab.lookup(target, false) } }
验证逻辑:验证每个桶的最末尾节点,如果该节点通过验证则放到队首(验证过程是本地节点向它发送ping请求,如果回应pong则通过) last, bi := tab.nodeToRevalidate() //取最后一个节点 if last == nil { // No non-empty bucket found. return } // Ping the selected node and wait for a pong. err := tab.ping(last.ID, last.addr()) //通信验证 tab.mutex.Lock() defer tab.mutex.Unlock() b := tab.buckets[bi] if err == nil { // The node responded, move it to the front. log.Debug("Revalidated node", "b", bi, "id", last.ID) b.bump(last) //提到队首 return }
Peer/Server
相关文件 . ├── dial.go //封装一个任务生成处理结构以及三种任务结构中(此处命名不太精确) ├── message.go //定义一些数据的读写接口,以及对外的Send/SendItem函数 ├── peer.go //封装了Peer 包括消息读取 ├── rlpx.go //内部的握手协议 ├── server.go //初始化,维护Peer网络,还有一些对外的接口
这一层会不断的从路由中提取节点,提取出来的节点要经过身份验证,协议检查之后加入到peer里面,紧接着如果没有人使用这个peer,这个peer就会被删除,再重新选择一些节点出来继续这个流程,peer再其中是随生随销,这样做是为了平均的使用所有的节点,而不是仅仅依赖于特定的几个节点。因而这里从Server开始入手分析整个流程 Peers() //peer对象 PeerCount() //peer数量 AddPeer(node *discover.Node) //添加节点 RemovePeer(node *discover.Node) //删除节点 SubscribeEvents(ch chan *PeerEvent) //订阅内部的事件(节点的增加,删除) //以上四个属于对外的接口,不影响内部逻辑 Start() //server开始工作 SetupConn(fd net.Conn, flags connFlag, dialDest *discover.Node) //启动一个连接,经过两次验证之后,如果通过则加入到peer之中。
Start初始化
Start做了三件事,生成路由表于建立底层网络。生成DialState用于驱动维护本地peer的更新与死亡,监听本地接口用于信息应答。这里主要分析peer的维护过程。函数是run函数。 func (srv *Server) Start() (err error) { //**************初始化代码省略 if !srv.NoDiscovery && srv.DiscoveryV5 { unhandled = make(chan discover.ReadPacket, 100) sconn = &sharedUDPConn{conn, unhandled} } // node table if !srv.NoDiscovery { //路由表生成 cfg := discover.Config{ PrivateKey: srv.PrivateKey, AnnounceAddr: realaddr, NodeDBPath: srv.NodeDatabase, NetRestrict: srv.NetRestrict, Bootnodes: srv.BootstrapNodes, Unhandled: unhandled, } ntab, err := discover.ListenUDP(conn, cfg) if err != nil { return err } srv.ntab = ntab } if srv.DiscoveryV5 { //路由表生成 var ( ntab *discv5.Network err error ) if sconn != nil { ntab, err = discv5.ListenUDP(srv.PrivateKey, sconn, realaddr, "", srv.NetRestrict) //srv.NodeDatabase) } else { ntab, err = discv5.ListenUDP(srv.PrivateKey, conn, realaddr, "", srv.NetRestrict) //srv.NodeDatabase) } if err != nil { return err } if err := ntab.SetFallbackNodes(srv.BootstrapNodesV5); err != nil { return err } srv.DiscV5 = ntab } dynPeers := srv.maxDialedConns() //newDialState 对象生成,这个对象包含Peer的实际维护代码 dialer := newDialState(srv.StaticNodes, srv.BootstrapNodes, srv.ntab, dynPeers, srv.NetRestrict) // handshake 协议加载 srv.ourHandshake = &protoHandshake{Version: baseProtocolVersion, Name: srv.Name, ID: discover.PubkeyID(&srv.PrivateKey.PublicKey)} for _, p := range srv.Protocols { srv.ourHandshake.Caps = append(srv.ourHandshake.Caps, p.cap()) } // listen/dial //监听本地端口 if srv.ListenAddr != "" { if err := srv.startListening(); err != nil { return err } } if srv.NoDial && srv.ListenAddr == "" { srv.log.Warn("P2P server will be useless, neither dialing nor listening") } srv.loopWG.Add(1) //重要的一句,开个协程,在其中做peer的维护 go srv.run(dialer) srv.running = true return nil }
run 开始peer的生成
该函数中定义了两个队列 runningTasks []task //正在执行的任务 queuedTasks []task //尚未执行的任务
定义了三个匿名函数 //从正在执行任务中删除任务 delTask := func(t task) { for i := range runningTasks { if runningTasks[i] == t { runningTasks = append(runningTasks[:i], runningTasks[i+1:]...) break } } } //开始一批任务 startTasks := func(ts []task) (rest []task) { i := 0 for ; len(runningTasks) < maxActiveDialTasks && i < len(ts); i++ { t := ts[i] srv.log.Trace("New dial task", "task", t) go func() { t.Do(srv); taskdone <- t }() runningTasks = append(runningTasks, t) } return ts[i:] } //启动开始一批任务再调用dialstate的newTasks函数生成一批任务,加载到任务队列里面 scheduleTasks := func() { // Start from queue first. queuedTasks = append(queuedTasks[:0], startTasks(queuedTasks)...) // Query dialer for new tasks and start as many as possible now. if len(runningTasks) < maxActiveDialTasks { nt := dialstate.newTasks(len(runningTasks)+len(queuedTasks), peers, time.Now()) queuedTasks = append(queuedTasks, startTasks(nt)...) } }
定义了一个循环,分不同的chanel执行对应的逻辑 for { //调度开始找生成任务 scheduleTasks() select { case <-srv.quit://退出 break running case n := <-srv.addstatic: //增加一个节点 该节点最终会生成一个dialTask //并在newTasks的时候加入到读列 srv.log.Debug("Adding static node", "node", n) dialstate.addStatic(n) case n := <-srv.removestatic: //直接删除该节点 节点不再参与维护,很快就会死掉了 dialstate.removeStatic(n) if p, ok := peers[n.ID]; ok { p.Disconnect(DiscRequested) } case op := <-srv.peerOp: // Peers 和 PeerCount 两个外部接口,只是读取peer信息 op(peers) srv.peerOpDone <- struct{}{} case t := <-taskdone: //task完成后会根据不同的任务类型进行相应的处理 srv.log.Trace("Dial task done", "task", t) dialstate.taskDone(t, time.Now()) delTask(t) case c := <-srv.posthandshake: //身份验证通过 if trusted[c.id] { // Ensure that the trusted flag is set before checking against MaxPeers. c.flags |= trustedConn } select { case c.cont <- srv.encHandshakeChecks(peers, inboundCount, c): case <-srv.quit: break running } case c := <-srv.addpeer: //身份协议验证通过 加入队列 err := srv.protoHandshakeChecks(peers, inboundCount, c) if err == nil { // The handshakes are done and it passed all checks. p := newPeer(c, srv.Protocols) // If message events are enabled, pass the peerFeed // to the peer if srv.EnableMsgEvents { p.events = &srv.peerFeed } name := truncateName(c.name) srv.log.Debug("Adding p2p peer", "name", name, "addr", c.fd.RemoteAddr(), "peers", len(peers)+1) go srv.runPeer(p) //触发事件 此处是最上层截取peer的位置,如果此物没有外部影响,那么这个peer很快就被销毁了 peerAdd++ fmt.Printf("--count %d--- add %d-- del %d--\n",len(peers),peerAdd,peerDel) peers[c.id] = p if p.Inbound() { inboundCount++ } } // The dialer logic relies on the assumption that // dial tasks complete after the peer has been added or // discarded. Unblock the task last. select { case c.cont <- err: case <-srv.quit: break running } case pd := <-srv.delpeer: //移除peer d := common.PrettyDuration(mclock.Now() - pd.created) pd.log.Debug("Removing p2p peer", "duration", d, "peers", len(peers)-1, "req", pd.requested, "err", pd.err) delete(peers, pd.ID()) peerDel++ fmt.Printf("--count %d--- add %d-- del %d--\n",len(peers),peerAdd,peerDel) if pd.Inbound() { inboundCount-- } } }
记住上面的代码,再来逐个的看:
scheduleTasks
scheduleTasks调度生成任务,生成的任务中有一种dialTask的任务,该任务结构如下 type dialTask struct { flags connFlag dest *discover.Node lastResolved time.Time resolveDelay time.Duration } func (t *dialTask) Do(srv *Server) { if t.dest.Incomplete() { if !t.resolve(srv) { return } } err := t.dial(srv, t.dest) //此处会调用到setupConn函数 if err != nil { log.Trace("Dial error", "task", t, "err", err) // Try resolving the ID of static nodes if dialing failed. if _, ok := err.(*dialError); ok && t.flags&staticDialedConn != 0 { if t.resolve(srv) { t.dial(srv, t.dest) } } } }
dial最终回调用到setupConn函数,函数只保留重点的几句,篇幅有点长了 func (srv *Server) setupConn(c *conn, flags connFlag, dialDest *discover.Node) error { //身份验证码 获取设备,标识等信息 if c.id, err = c.doEncHandshake(srv.PrivateKey, dialDest); err != //此处会往chanel中添加连接对象,最终触发循环中的posthandshake分支 err = srv.checkpoint(c, srv.posthandshake) //协议验证 phs, err := c.doProtoHandshake(srv.ourHandshake) c.caps, c.name = phs.Caps, phs.Name //此处会往chanel中添加连接对象 最终触发循环中的addpeer分支 err = srv.checkpoint(c, srv.addpeer) }
posthandshake 分支仅仅做了验证,addpeer做的事情就比较多了,重要的就是执行runPeer函数 func (srv *Server) runPeer(p *Peer) { // 广播 peer add srv.peerFeed.Send(&PeerEvent{ Type: PeerEventTypeAdd, Peer: p.ID(), }) // run the protocol remoteRequested, err := p.run() // // 广播 peer drop srv.peerFeed.Send(&PeerEvent{ Type: PeerEventTypeDrop, Peer: p.ID(), Error: err.Error(), }) //移除peer srv.delpeer <- peerDrop{p, err, remoteRequested} } func (p *Peer) run() (remoteRequested bool, err error) { //************* writeStart <- struct{}{} p.startProtocols(writeStart, writeErr) //************* //这一句阻塞性确保了peer的存活 p.wg.Wait() } func (p *Peer) startProtocols(writeStart <-chan struct{}, writeErr chan<- error) { p.wg.Add(len(p.running)) for _, proto := range p.running { proto := proto proto.closed = p.closed proto.wstart = writeStart proto.werr = writeErr var rw MsgReadWriter = proto if p.events != nil { rw = newMsgEventer(rw, p.events, p.ID(), proto.Name) } p.log.Trace(fmt.Sprintf("Starting protocol %s/%d", proto.Name, proto.Version)) go func() { //其他的都是为这一句做准备的,在以太坊中p2p就是靠这一句对上层暴露peer对象 err := proto.Run(p, rw) if err == nil { p.log.Trace(fmt.Sprintf("Protocol %s/%d returned", proto.Name, proto.Version)) err = errProtocolReturned } else if err != io.EOF { p.log.Trace(fmt.Sprintf("Protocol %s/%d failed", proto.Name, proto.Version), "err", err) } p.protoErr <- err p.wg.Done() }() } }
这样就可以可理出一条思路 scheduleTasks执行生成dialTask任务 dialTask任务执行过程中逐个填充posthandshake,addPeer这两个chanel。 addPeer执行时对上层暴露了Peer对象,完成后填充了delpeer,最后删除了Peer。
任务的生成
具体看代码中的注释 func (s *dialstate) newTasks(nRunning int, peers map[discover.NodeID]*Peer, now time.Time) []task { if s.start.IsZero() { s.start = now } var newtasks []task //这里声明了一个添加任务的函数 addDial := func(flag connFlag, n *discover.Node) bool { if err := s.checkDial(n, peers); err != nil { log.Trace("Skipping dial candidate", "id", n.ID, "addr", &net.TCPAddr{IP: n.IP, Port: int(n.TCP)}, "err", err) return false } s.dialing[n.ID] = flag //排除掉已经再测试的 newtasks = append(newtasks, &dialTask{flags: flag, dest: n}) return true } // Compute number of dynamic dials necessary at this point. needDynDials := s.maxDynDials //当前系统中最大连接数目 for _, p := range peers { //扣除已建立链接的peer if p.rw.is(dynDialedConn) { needDynDials-- } } for _, flag := range s.dialing { //扣除已建立链接的peer if flag&dynDialedConn != 0 { needDynDials-- } } //外部命令添加的节点 这种节点不占用needDynDials数目, //是为了保证手动加的节点能够起效 for id, t := range s.static { err := s.checkDial(t.dest, peers) switch err { case errNotWhitelisted, errSelf: log.Warn("Removing static dial candidate", "id", t.dest.ID, "addr", &net.TCPAddr{IP: t.dest.IP, Port: int(t.dest.TCP)}, "err", err) delete(s.static, t.dest.ID) case nil: s.dialing[id] = t.flags newtasks = append(newtasks, t) } } // If we don't have any peers whatsoever, try to dial a random bootnode. This // scenario is useful for the testnet (and private networks) where the discovery // table might be full of mostly bad peers, making it hard to find good ones. if len(peers) == 0 && len(s.bootnodes) > 0 && needDynDials > 0 && //检查引导节点 因为引导节点比搜索到的节点更大概率靠谱 因而比较靠前 now.Sub(s.start) > fallbackInterval { bootnode := s.bootnodes[0] s.bootnodes = append(s.bootnodes[:0], s.bootnodes[1:]...) s.bootnodes = append(s.bootnodes, bootnode) if addDial(dynDialedConn, bootnode) { needDynDials-- } } //随机的从路由中抽取最大节点的二分之一 randomCandidates := needDynDials / 2 if randomCandidates > 0 { n := s.ntab.ReadRandomNodes(s.randomNodes) for i := 0; i < randomCandidates && i < n; i++ { if addDial(dynDialedConn, s.randomNodes[i]) { needDynDials-- } } } // 从lookupbuf中抽取 i := 0 for ; i < len(s.lookupBuf) && needDynDials > 0; i++ { if addDial(dynDialedConn, s.lookupBuf[i]) { needDynDials-- } } s.lookupBuf = s.lookupBuf[:copy(s.lookupBuf, s.lookupBuf[i:])] // 如果还是不够,路由再去搜索节点 if len(s.lookupBuf) < needDynDials && !s.lookupRunning { s.lookupRunning = true newtasks = append(newtasks, &discoverTask{}) } // wait if nRunning == 0 && len(newtasks) == 0 && s.hist.Len() > 0 { t := &waitExpireTask{s.hist.min().exp.Sub(now)} newtasks = append(newtasks, t) } return newtasks }
消息发送
另一个是message中的Send,SendItem函数 实现了MsgWriter的对象都可以调用这个函数写入,觉得这里没什么必要,完全可以封装到peer里面去,不过它上层做广播的时候确实是调用的这两个函数。 func Send(w MsgWriter, msgcode uint64, data interface{}) error { size, r, err := rlp.EncodeToReader(data) if err != nil { return err } return w.WriteMsg(Msg{Code: msgcode, Size: uint32(size), Payload: r}) } func SendItems(w MsgWriter, msgcode uint64, elems ...interface{}) error { return Send(w, msgcode, elems) }
以太坊上层调用
Peer/PeerSet
文件:go-ethereum/eth/peer.go
定义了两个struct,Peer和PeerSet。Peer封装了底层的p2p.Peer,集成了一些和业务相关的方法,比如SendTransactions,SendNewBlock等。PeerSet是Peer的集合 type peer struct { id string *p2p.Peer rw p2p.MsgReadWriter version int // Protocol version negotiated forkDrop *time.Timer // Timed connection dropper if forks aren't validated in time head common.Hash td *big.Int lock sync.RWMutex knownTxs *set.Set // Set of transaction hashes known to be known by this peer knownBlocks *set.Set // Set of block hashes known to be known by this peer } type peerSet struct { peers map[string]*peer lock sync.RWMutex closed bool }
Peer注册/注销
文件:go-ethereum/eth/handler.go manager.handle在检查了peer后会把这个peer注册到peerset中,表示此peer可用,发生错误后peerset注销该peer,返回错误,最后再Server中销毁。 manager.SubProtocols = make([]p2p.Protocol, 0, len(ProtocolVersions)) for i, version := range ProtocolVersions { // Skip protocol version if incompatible with the mode of operation if mode == downloader.FastSync && version < eth63 { continue } // Compatible; initialise the sub-protocol version := version // Closure for the run manager.SubProtocols = append(manager.SubProtocols, p2p.Protocol{ Name: ProtocolName, Version: version, Length: ProtocolLengths[i], Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error { peer := manager.newPeer(int(version), p, rw) select { case manager.newPeerCh <- peer: manager.wg.Add(1) defer manager.wg.Done() //此处如果顺利会进入for循环 如果失败返回错误我会销毁掉这个peer return manager.handle(peer) case <-manager.quitSync: return p2p.DiscQuitting } }, NodeInfo: func() interface{} { return manager.NodeInfo() }, PeerInfo: func(id discover.NodeID) interface{} { if p := manager.peers.Peer(fmt.Sprintf("%x", id[:8])); p != nil { return p.Info() } return nil }, }) }
参考
源码: https://github.com/ethereum/go-ethereum/tree/master/p2p
Kademlia算法: https://en.wikipedia.org/wiki/Kademlia
区块链
2018-04-28 23:30:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
web3.js是开发以太坊去中心化应用(DApp)必备的JavaScript库。那么,web3.js的作用是什么?它的实现原理又是什么?
以太坊网络是由众多彼此平等的节点组成的P2P网络,其中每个节点都有整个区块链的拷贝。当你希望调用链上一个智能合约的方法时,需要连接到其中的某个节点并告知节点智能合约的地址、 你希望调用的方法以及向该方法传入的参数
不过以太坊节点只能理解JSON-PRC,这是一种远程调用协议,这种协议对人而言并不是很友好、易读。你发送给节点的对合约方法的调用请求看起来就像这样: // Yeah... Good luck writing all your function calls this way! {"jsonrpc":"2.0","method":"eth_sendTransaction","params":[{"from":"0xb60e8dd61c5d32be8058bb8eb970870f07233155","to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","gas":"0x76c0","gasPrice":"0x9184e72a000","value":"0x9184e72a","data":"0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"}],"id":1}
看起来不那么爽,对吧?
幸运的是,web3.js把这些复杂的调用请求封装起来,让你只需要通过更加友好的javascript接口来和以太坊节点交互。使用web3.js时,你要发送上面的JSON-PRC请求,只需要类似于如下的以下代码: CryptoZombies.methods.createRandomZombie("Vitalik Nakamoto ") .send({ from: "0xb60e8dd61c5d32be8058bb8eb970870f07233155", gas: "3000000" })
是不是看起来更熟悉一些 ? 如果你希望马上开始学习以太坊DApp开发,可以访问汇智网提供的出色的在线互动教程: 以太坊DApp实战开发入门 去中心化电商DApp实战开发
区块链
2018-04-28 22:03:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
在本文中,我们将介绍以太坊智能合约的开发语言solidity。 Solidity是一种语法类似JavaScript的高级语言。它被设计成以编译的方式生成以太坊虚拟机代码。在后续内容中你将会发现,使用它很容易创建用于投票、众筹、封闭拍卖、多重签名钱包等等的合约。
编写第一个合约
让我们先从一个非常基础的例子开始,不用担心你现在还一点都不了解,我们将逐步了解到更多的细节。 contract SimpleStorage { uint storedData; function set(uint x) { storedData = x; } function get() constant returns (uint retVal) { return storedData; } }
在Solidity中,一个合约由一组代码(合约的函数)和数据(合约的状态)组成。合约位于以太坊区块链上的一个特殊地址。
uint storedData ; 这行代码声明了一个状态变量,变量名为storedData,类型为 uint (256bits无符号整数)。你可以认为它就像数据库里面的一个存储单元,跟管理数据库一样,可以通过调用函数查询和修改它。在以太坊中,通常只有合约的拥有者才能这样做。在这个例子中,函数 set 和 get 分别用于修改和查询变量的值。
跟很多其他语言一样,访问状态变量时,不需要在前面增加 this. 这样的前缀。
这个合约还无法做很多事情(受限于以太坊的基础设施),仅仅是允许任何人储存一个数字。而且世界上任何一个人都可以来存取这个数字,缺少一个(可靠的)方式来保护你发布的数字。任何人都可以调用set方法设置一个不同的数字覆盖你发布的数字。但是你的数字将会留存在区块链的历史上。稍后我们会学习如何增加一个存取限制,使得只有你才能修改这个数字。 如果你希望马上开始学习以太坊DApp开发,可以访问汇智网提供的出色的在线互动教程: 以太坊DApp实战入门教程 以太坊去中心化电商应用开发实战
编写代币合约
接下来的合约将实现一个形式最简单的加密货币。任何人都可以发送货币给其他人,不需要注册用户名和密码,只要有一对以太坊的公私钥即可。 contract Coin { //关键字“public”使变量能从合约外部访问。 address public minter; mapping (address => uint) public balances; //事件让轻客户端能高效的对变化做出反应。 event Sent(address from, address to, uint amount); //这个构造函数的代码仅仅只在合约创建的时候被运行。 function Coin() { minter = msg.sender; } function mint(address receiver, uint amount) { if (msg.sender != minter) return; balances[receiver] += amount; } function send(address receiver, uint amount) { if (balances[msg.sender] < amount) return; balances[msg.sender] -= amount; balances[receiver] += amount; Sent(msg.sender, receiver, amount); } }
这个合约引入了一些新的概念,让我们来逐个介绍。 address public minter;`
这行代码声明了一个可公开访问的状态变量,类型为address。address类型的值大小为160 bits,不支持任何算术操作。适用于存储合约的地址或其他人的公私钥。public关键字会自动为其修饰的状态变量生成访问函数。没有public关键字的变量将无法被其他合约访问。另外只有本合约内的代码才能写入。自动生成的函数如下: function minter() returns (address) { return minter; }
当然我们自己增加一个这样的访问函数是行不通的。编译器会报错,指出这个函数与一个状态变量重名。
下一行代码创建了一个public的状态变量,但是其类型更加复杂: mapping (address => uint) public balances;
该类型将一些address映射到无符号整数。mapping可以被认为是一个哈希表,每一个可能的key对应的value被虚拟的初始化为全0.这个类比不是很严谨,对于一个mapping,无法获取一个包含其所有key或者value的链表。所以我们得自己记着添加了哪些东西到mapping中。更好的方式是维护一个这样的链表,或者使用其他更高级的数据类型。或者只在不受这个缺陷影响的场景中使用mapping,就像这个例子。在这个例子中由public关键字生成的访问函数将会更加复杂,其代码大致如下: function balances(address _account) returns (uint balance) { return balances[_account]; }
我们可以很方便的通过这个函数查询某个特定账号的余额。 event Sent(address from, address to, uint value);
这行代码声明了一个“事件”。由send函数的最后一行代码触发。客户端(服务端应用也适用)可以以很低的开销来监听这些由区块链触发的事件。事件触发时,监听者会同时接收到from,to,value这些参数值,可以方便的用于跟踪交易。为了监听这个事件,你可以使用如下代码: 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)); } }
注意在客户端中是如何调用自动生成的 balances 函数的。
这里有个比较特殊的函数 Coin。它是一个构造函数,会在合约创建的时候运行,之后就无法被调用。它会永久得存储合约创建者的地址。msg(以及tx和block)是一个神奇的全局变量,它包含了一些可以被合约代码访问的属于区块链的属性。msg.sender 总是存放着当前函数的外部调用者的地址。
最后,真正被用户或者其他合约调用,用来完成本合约功能的函数是mint和send。如果合约创建者之外的其他人调用mint,什么都不会发生。而send可以被任何人(拥有一定数量的代币)调用,发送一些币给其他人。注意,当你通过该合约发送一些代币到某个地址,在区块链浏览器中查询该地址将什么也看不到。因为发送代币导致的余额变化只存储在该代币合约的数据存储中。通过事件我们可以很容易创建一个可以追踪你的新币交易和余额的“区块链浏览器”。
免费电子书
如果你希望深入了解solidity的语言特性,可以下载我们制作的免费pdf电子书: Solidity官方文档中文版 。
区块链
2018-04-28 21:07:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
ganache-cli是以太坊节点仿真器软件ganache的命令行版本,可以方便开发者快速进行以太坊DApp的开发与测试。
安装 npm install -g ganache-cli
启动 ~$ ganache-cli
启动选项 -a 或 --accounts: 指定启动时要创建的测试账户数量。 -e 或 --defaultBalanceEther: 分配给每个测试账户的ether数量,默认值为100。 -b 或r --blockTime: 指定自动挖矿的blockTime,以秒为单位。默认值为0,表示不进行自动挖矿。 -d 或 --deterministic: 基于预定的助记词( mnemonic )生成固定的测试账户地址。 -n 或 --secure: 默认锁定所有测试账户,有利于进行第三方交易签名。 -m 或 --mnemonic: 用于生成测试账户地址的助记词。 -p 或 --port: 设置监听端口,默认值为8545。 -h 或 --hostname: 设置监听主机,默认值同NodeJS的 server.listen() 。 -s 或 --seed: 设置生成助记词的种子。. -g 或 --gasPrice: 设定Gas价格,默认值为20000000000。 -l 或 --gasLimit: 设定Gas上限,默认值为90000。 -f 或 --fork: 从一个运行中的以太坊节点客户端软件的指定区块分叉。输入值应当是该节点旳HTTP地址和端口,例如 http://localhost:8545 。 可选使用@标记来指定具体区块,例如: http://localhost:8545@1599200 。 -i 或 --networkId:指定网络id。默认值为当前时间,或使用所分叉链的网络id。 --db: 设置保存链数据的目录。如果该路径中已经有链数据,ganache-cli将用它初始化链而不是重新创建。 --debug:输出VM操作码,用于调试。 --mem:输出ganache-cli内存使用统计信息,这将替代标准的输出信息。 --noVMErrorsOnRPCResponse:不把失败的交易作为RCP错误发送。开启这个标志使错误报告方式兼容其他的节点客户端,例如geth和Parity。
特殊选项 --account: 指定账户私钥和账户余额来创建初始测试账户。可多次设置: $ ganache-cli --account=",balance" [--account=",balance"]
注意私钥长度为64字符,必须使用0x前缀的16进制字符串。账户余额可以是整数,也可以是0x前缀的17进制字符串,单位为wei。
使用--account选项时,不会自动创建HD钱包。 -u 或 --unlock: 解锁指定账户,或解锁指定序号的账户。可以设置多次。当与--secure选项同时使用时,这个选项将改变指定账户的锁定状态: $ ganache-cli --secure --unlock "0x1234..." --unlock "0xabcd..."
也可以指定一个数字,按序号解锁账号: $ ganache-cli --secure -u 0 -u 1 如果你希望马上开始学习以太坊DApp开发,可以访问汇智网提供的出色的在线互动教程: 以太坊DApp实战入门教程 以太坊去中心化电商应用开发实战
区块链
2018-04-28 20:55:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
MetaMask是一个开源的以太坊钱包,能帮助用户方便地管理自己的以太坊数字资产, 但在国内由于网络原因,你可能下载不了。本文将介绍如何解决metamask钱包无法下载的问题。
方法1:本地包下载和安装
你可以按照以下操作步骤完成MetaMask钱包的安装:
STEP 1 点击链接: https://github.com/MetaMask/metamask-extension/releases
STEP 2 点击 “Assets” 列表下的 "metamask-chrome-4.4.0.zip" (或你看到的最新版), 下载并解压此压缩包
STEP 3 用谷歌浏览器 (Chrome) 打开链接: chrome://extensions:
选择 "加载已解压的扩展程序” (Load unpacked extension),在跳出菜单中选择刚才解压的文件包
STEP 4 网页将跳转到新的页面,选择 “Get Chrome Extension"
执行上述步骤后,浏览器右上角将出现一个新图标(MetaMask 狐狸插件图标), 成功安装 MetaMask 钱包。
方法2:在线安装
如果你可以直接访问google,也可按照一下步骤快速安装 MetaMask 钱包:
STEP 1 点击链接:
https://chrome.google.com/webstore/detail/metamask/nkbihfbeogaeaoehlefnkodbefgpgknn
STEP 2 点击窗口右上角 “ADD TO CHROME”(添加至Chrome浏览器)
STEP 3 点击新窗口中 “Add extension”
STEP 4 执行上述步骤后,浏览器右上角将出现一个新图标(MetaMask 狐狸插件图标)
如果你希望马上开始学习以太坊DApp开发,可以访问汇智网提供的出色的在线互动教程: 以太坊DApp实战开发入门 去中心化电商DApp实战开发
区块链
2018-04-28 20:48:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
拉勾网爬取
首先是从拉勾网爬取数据,用的 requests 库。拉勾网的反爬虫做的还是比较好的,毕竟自己也知道这种做招聘信息聚合的网站很容易被爬,而且比起妹子图这种网站,开发的技术水平应该高不少。 一开始爬取几个数据后就会跳转到登陆页面,当时采用的应对策略是:当跳转到登陆页面的时候,就挂起20s,再重新请求一次,发现一般挂起一段时间之后就又可以访问了。这样的问题是比较慢,450条信息花了几十分钟。
后来,我天真地认为:只要登陆了就可以不用挂起等待这么久。于是,添加了模拟登陆地逻辑,主要参考地这篇文章: python -- 拉勾网爬虫模拟登录 - CSDN博客 。拉勾网对密码做了两次md5加密,并会下发动态地Token口令,防止低级地伪造请求,需要仔细分析登陆界面加载的JS文件才能成功登陆,拿到Cookie。想起当年模拟登陆教务处,学号密码都是明文传输,我直接用F12工具能看到。。。成功拿到Cookie后发现,访问过快时又会跳转到首页,并弹出一个“切换城市”的悬浮窗,更准确地说是我的请求被重定向了。
为什么重定向,因为后台能够通过访问频率很容易发现我的请求是爬虫,所以重定向,普通用户可以点击取消悬浮窗,由于 requests 不支持JS运行,所以我就GG了,只能像前面那样挂起一段时间再请求。这样一来,模拟登陆就没有意义了。 针对这种情况,我认为有两种解决办法,一种是使用IP池和多线程,不断变换请求的IP就不会被发现了。另外一种就是用无头浏览器。个人感觉两种都是可行的,下次有需求了再实践一下。
爬取结果分析
将爬取的网页提取信息,本着可视化原则,用 mathplotlib 做些图。其中遇到的主要问题是 mathplotlib 的中文支持,试了网上很多方法都失败了,这里要把自己亲测可行的方法记录下来: from matplotlib import rcParams import matplotlib.font_manager as fm zhfont = matplotlib.font_manager.FontProperties(fname='../test.ttf') font_list = fm.createFontList(fm.findSystemFonts(['/home/zhaoyu/Project/BlockChainAnalysis/font', ])) fm.fontManager.ttflist.extend(font_list) matplotlib.rcParams['font.family'] = 'WenQuanYi Micro Hei'
主要结果如下: 公司规模,小公司居多:
城市分布,北上深最多:
发展阶段:
薪资分布:
(感觉这样表示不是很直观,自己划定几个区间,做一个柱状图可能更好)
最后还用 jieba 和 textrank4zh 做了关键词提取,但由于手法粗糙加上噪音严重(产品经理、技术开发等等的招聘需求应该分类处理),效果好像不是很理想: Building prefix dict from the default dictionary ... Loading model from cache /tmp/jieba.cache Loading model cost 0.854 seconds. Prefix dict has been built succesfully. 关键词: 技术 0.010791270870042516 区块 0.008745674154277546 相关 0.00844148459795554 工作 0.008368189087078589 开发 0.007315529659791802 熟悉 0.006915993478846666 产品 0.0066154024633222315 能力 0.00606941754659388 项目 0.005688665635431383 经验 0.005609155675707272 公司 0.005484150951833748 优先 0.005273082641983991 团队 0.004833977171492051 行业 0.004666562221685026 设计 0.004646942513692475 系统 0.004561171667742128 进行 0.004504888034867325 研究 0.004431023464429216 业务 0.00431705965334352 负责 0.004180396829264431
发现的不足 按照之前的计划,个人能力的构建应该分为两部分,一部分是工具库的积累,一部分是底层原理的掌握。目前来看,两方面做的都不是很好。 beautifulsoup 和 mathplotlib 都很不熟练,遇到反爬虫不能快速解决,拿到数据也不能很好的可视化出来。 平时没有意识去建立个人知识体系,比如常见的反爬虫与绕过方法,数据挖掘(拿到了爬取的数据该怎么办),数据可视化(怎么直观地表示数据)。 写代码不能得心应手,比如对容器地某个操作怎么最优雅,自己目前的水平就是遇到问题百度一下,找个可行地方案就套用上去,离自己理想的状态差太远了。
区块链
2018-04-28 19:55:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
这是 DappRadar 根据其对以太坊交易的监测而自动生成的2018年2月以太坊区块链应用活跃度排行榜,计算的主要依据是应用在一周内的交易量,活跃度越高则意味着应用越有可能成功:
1. EtherCraft
地址: https://dappradar.com/app/17/ethercraft
这是一个RPG游戏,其中包含各种各样的物品,运行着超过100(!!!)个智能合约,是目前最活跃的以太坊游戏,每周有超过102,047次交易。
2. CryptoKitties  
地址: https://dappradar.com/app/3/cryptokitties
我们都知道这个,对吧? 我们怎么能忘了这个以太坊上的游戏先锋,它以每周37,249次交易在十大活跃DApp榜中排在第二位。
3. Etheroll
地址: https://dappradar.com/app/10/etheroll
可能是最受欢迎的去中心化赌场。 它不仅7天交易量突破7,898 ETH ,而且以每周交易次数 18,251 排在十大活跃DApp榜中第三位! 如果你开始对以太坊区块链 应用开发产生兴趣,可以访问汇智网提供的出色的在线互动教程: 以太坊智能合约与应用开发入门 以太坊去中心化电商应用开发实战
没准下一个明星DApp就是你开发的:)
4. Ether Dungeon
地址: https://dappradar.com/app/42/ether-dungeon
这个RPG游戏具有独特的游戏性,以每周17,686次交易排在第四位。
5. Etheremon  
地址: https://dappradar.com/app/23/etheremon
喜欢《口袋妖怪》这一类的游戏? 那你得看看Etheremon! 你可以购买动漫生物,训练它们并在战斗中向敌人发起挑战。 Etheremon 上周有9,630笔交易 。
6. KryptoWar
地址: https://dappradar.com/app/29/kryptowar
在这个游戏中,你可以称为100个国家中的某个领主。 目前游戏中已经有了很多领主, 上周的4210个交易清楚地说明了这一点。
7. DWorld
地址: https://dappradar.com/app/22/dworld
在这个酷炫的世界中你可以有一席之地! 不应该将它视为游戏,但查看所有用户在他们的土地上建造的东西确实很有趣。 DWorld 上周成交2,612笔。
8. EtherBots
地址: https://dappradar.com/app/26/etherbots
这款游戏最近非常受欢迎,交易量很高。 购买一个随机生成的机器人并为战斗做好准备! 上周EtherBots 交易量为2,599笔 。
9. CryptoCountries
地址: https://dappradar.com/app/7/cryptocountries
类似于DWorld,但销售的是国家而不是小块土地。 另外,不可以用图片装饰你的国家。 不过, 每周有2,499笔交易也是不错的良好。
10. CryptoCelebrities
地址: https://dappradar.com/app/5/cryptocelebrities
在这个应用中,你可以收集真实世界中那些名人的卡片,然后和其他人交易! CryptoCelebrities的用户上周达成了2,273笔交易 。
原文: 10 Most Active Ethereum DApps for entertainment
区块链
2018-04-28 16:41:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
以太坊中有两类账户,它们共用同一个地址空间。 外部账户,该类账户被公钥-私钥对控制(人类)。 合约账户,该类账户被存储在账户中的代码控制。
外部账户的地址是由公钥决定的,合约账户的地址是在创建该合约时确定的。
合约账户存储了代码,外部账户则没有,除了这点以外,这两类账户对于EVM来说是一样的。 每个账户有一个key-value形式的持久化存储。其中key和value的长度都是256比特,名字叫做storage.
另外,每个账户都有一个以太币余额(单位是“Wei"),该账户余额可以通过向它发送带有以太币的交易来改变。
web3实现
使用 web3.eth.getCode() 方法可以判断一个给定的地址的账户是外部账户,还是合约账户。这个函数返回指定地址的代码,由于外部账户没有代码,因此仅仅会返回 0x ,而合约账户将会返回 0x 开头的16进制代码字符串。例如: var code = web3.eth.getCode("0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8") if(code === '0x') console.log('外部账户') else console.log('合约账户')

在solidity中实现
在合约内,可以使用EVM汇编代码来获取指定地址处的代码大小,显然,普通账户地址将返回 0 :
contract EzDemo { function isContract(address addr) returns (bool) { uint size; assembly { size := extcodesize(addr) } return size > 0; } }

推荐两个以太坊相关的实战教程: 区块链初学者 : 以太坊 DApp 实战开发入门 区块链开发进阶: 去中心化以太坊 DApp 电商平台实战开发
更多的内容可以访问博客:
http://blog.hubwiz.com/2018/02/12/how-to-check-ethereum-address/
区块链
2018-04-28 15:50:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
交易
一笔交易是一条消息,从一个账户发送到另一个账户(可能是相同的账户或者零账户,见下文)。交易可以包含二进制数据(payload)和以太币。
如果目标账户包含代码,该代码会执行,payload就是输入数据。
如果目标账户是零账户(账户地址是0),交易将创建一个新合约。正如上文所讲,这个合约地址不是零地址,而是由合约创建者的地址和该地址发出过的交易数量(被称为nonce)计算得到。创建合约交易的payload被当作EVM字节码执行。执行的输出做为合约代码被永久存储。这意味着,为了创建一个合约,你不需要向合约发送真正的合约代码,而是发送能够返回真正代码的代码。
油:Gas
以太坊上的每笔交易都会被收取一定数量的gas,gas的目的是限制执行交易所需的工作量,同时为执行支付费用。当EVM执行交易时,gas将按照特定规则被逐渐消耗。
油价:gas price
gas price(以太币计)是由交易创建者设置的,发送账户需要预付的交易费用 = gas price * gas amount。 如果执行结束还有gas剩余,这些gas将被返还给发送账户。
无论执行到什么位置,一旦gas被耗尽(比如降为负值),将会触发一个out-of-gas异常。当前调用帧所做的所有状态修改都将被回滚。
交易费上限: gas limit
每笔交易都被要求包括一个gas limit(有的时候被称为startGas)和一个交易愿为单位gas支付的费用。矿工可以有选择的打包这些交易并收取这些费用。在现实中,今天所有的交易最终都是由矿工选择的,但是用户所选择支付的交易费用多少会影响到该交易被打包所需等待的时长。如果该交易由于计算,包括原始消息和一些触发的其他消息,需要使用的gas数量小于或等于所设置的gas limit,那么这个交易会被处理。如果gas总消耗超过gas limit,那么所有的操作都会被复原,但交易是成立的并且交易费任会被矿工收取。区块链会显示这笔交易完成尝试,但因为没有提供足够的gas导致所有的合约命令都被复原。所以交易里没有被使用的超量gas都会以以太币的形式打回给交易发起者。因为gas消耗一般只是一个大致估算,所以许多用户会超额支付gas来保证他们的交易会被接受。这没什么问题,因为多余的gas会被退回给你。
你可以将gasLimit理解为你汽车油箱的上限。同时将gasPrice理解为油价。
区块交易费上限:block gas limit
区块gas limit是单个区块允许的最多gas总量,以此可以用来决定单个区块中能打包多少笔交易。例如,我们有5笔交易的gas limit分别是10、20、30、40和50.如果区块gas limit是100,那么前4笔交易就能被成功打包进入这个区块。矿工有权决定将哪些交易打包入区块。所以,另一个矿工可以选择打包最后两笔交易进入这个区块(50+40),然后再将第一笔交易打包(10)。如果你尝试将一个会使用超过当前区块gas limit的交易打包,这个交易会被网络拒绝,你的以太坊客户端会反馈错误"交易超过区块gas limit"。以下例子是来自于以太坊StackExhcange的帖子。
目前区块的gas limit是 4,712,357 gas,数据来自于ethstats.net,这表示着大约224笔转账交易(gas limit为21000)可以被塞进一个区块(区块时间大约在15-20秒间波动)。这个协议允许每个区块的矿工调整区块gas limit,任意加减 1/2024(0.0976%)。

推荐两个以太坊相关的实战教程: 区块链初学者 : 以太坊 DApp 实战开发入门 区块链开发进阶: 去中心化以太坊 DApp 电商平台实战开发
区块链
2018-04-28 15:46:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
使用web3.js
web3.eth.getCode() 方法返回指定地址上代码的16进制字符串,由于普通账户地址处没有代码,因此将仅返回16进制前缀 0x 。利用这个我们可以进行判断,例如: var code = web3.eth.getCode("0xbfb2e296d9cf3e593e79981235aed29ab9984c0f") if(code === '0x') console.log('普通账户') else console.log('合约账户')
在solidity中实现
在合约内,可以使用EVM汇编代码来获取指定地址处的代码大小,显然,普通账户地址将返回 0 : contract EzDemo { function isContract(address addr) returns (bool) { uint size; assembly { size := extcodesize(addr) } return size > 0; } }

推荐两个以太坊相关的实战教程: 区块链初学者 : 以太坊 DApp 实战开发入门 区块链开发进阶: 去中心化以太坊 DApp 电商平台实战开发
区块链
2018-04-28 15:44:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
区块链兄弟社区,区块链技术专业问答先行者,中国区块链技术爱好者聚集地
作者:吴寿鹤,《区块链开发实战——以太坊关键技术与案例分析》的第一作者,《区块链开发实战——Hyperledger Fabric关键技术与案例分析》联合作者,IONChain 离子链 首席架构师,hyperLedger核心项目开发人员,区块链技术社区-区块链兄弟联合创始人。github: https://github.com/gcc2ge
来源:区块链兄弟,国内第一家专注区块链技术分享实战的公益性媒体社区
原文链接: http://www.blockchainbrother.com/article/1992
文章发布:请标题作者和来源,版权归区块链兄弟所有
事件回顾:
4月25日早间,火币Pro公告,SMT项目方反馈今日凌晨发现其交易存在异常问题,经初步排查,SMT的以太坊智能合约存在漏洞。火币Pro也同期检测到TXID为https://etherscan.io/tx/0x0775e55c402281e8ff24cf37d6f2079bf2a768cf7254593287b5f8a0f621fb83的异常。受此影响,火币Pro现决定暂停所有币种的充提币业务,随后,火币Pro又发布公告称暂停SMT/USDT、SMT/BTC和SMT/ETH的交易。此外,OKEx,gate.io等交易平台也已经暂停了SMT的充提和交易。截止暂停交易,SMT在火币Pro的价格下跌近20%。

该漏洞代理的直接经济损失高达上亿元人民币,间接产生的负面影响目前无法估量。这到底是怎样一个漏洞呢?下面将详细分析该漏洞的产生和解决方案。
漏洞分析:
SMT与前几天爆出的美图BEC代币都出现类似的安全漏洞—整数溢出,那么什么是整数溢出,整数溢出出现的原因是什么,怎样才能避免整数溢出呢?接下来我们带着这些问题来看下面的内容。
整数溢出
整数溢出分向上溢出和向下溢出,有关智能合约安全的其他关键点作者在《区块链开发实战——以太坊关键技术与案例分析》中有详细介绍,以下是截取本书中关于整数溢出的部分,通过下面文字的阅读大家就可以对:什么是整数溢出,整数溢出出现的原因是什么,怎样才能避免整数溢出呢?这三个问题有个答案了。
以下文字截取于《区块链开发实战——以太坊关键技术与案例分析》 pragma solidity ^ 0.4 .10 ; /** 这是一个测试整数类型上溢和下溢的例子 */ contract Test{ // 整数上溢 //如果uint8 类型的变量达到了它的最大值(255),如果在加上一个大于0的值便会变成0 function test () returns (uint8) { uint8 a = 255 ; uint8 b = 1 ; return a+b; // return 0 } //整数下溢 //如果uint8 类型的变量达到了它的最小值(0),如果在减去一个小于0的值便会变成255(uin8 类型的最大值) function test_1 () returns (uint8) { uint8 a = 0 ; uint8 b = 1 ; return a-b; // return 255 } }
有了上面的理论基础,我们在看一个转账的例子,看在我们的合约中应该如何避免不安全的代码出现: // 存储用户余额信息 mapping (address => uint256) public balanceOf; // 不安全的代码 // 函数功能:转账,这里没有做整数溢出检查 function transfer(address _to , uint256 _value ) { /* 检查发送者是否有足够的余额*/ if (balanceOf[msg.sender] < _value ) throw ; /* 修改发送者和接受者的余额 */ balanceOf[msg.sender] -= _value ; balanceOf[ _to ] += _value ; } // 安全代码 function transfer(address _to , uint256 _value ) { /* 检查发送者是否有足够的余额,同时做溢出检查:balanceOf[_to] + _value < balanceOf[_to] */ if (balanceOf[msg.sender] < _value || balanceOf[ _to ] + _value < balanceOf[ _to ]) throw ; /* 修改发送者和接受者的余额 */ balanceOf[msg.sender] -= _value ; balanceOf[ _to ] += _value ; }
我们在做整数运算的时候要时刻注意上溢,下溢检查,尤其对于较小数字的类型比如uint8、uint16、uint24更加要小心:它们更加容易达到最大值,最小值。
案例分析:
SMT 合约中的整数安全问题简析
SMT的合约地址是:0x55F93985431Fc9304077687a35A1BA103dC1e081,合约代码可以访问etherscan的如下网址进行查看
https://etherscan.io/address/0x55f93985431fc9304077687a35a1ba103dc1e081#code
SMT合约有问题的代码存在于transferProxy()函数,代码如下: function transferProxy(address _from , address _to , uint256 _value , uint256 _feeSmt , uint8 _v ,bytes32 _r , bytes32 _s ) public transferAllowed( _from ) returns (bool){ if (balances[ _from ] < _feeSmt + _value ) revert(); uint256 nonce = nonces[ _from ]; bytes32 h = keccak256( _from , _to , _value , _feeSmt ,nonce); if ( _from != ecrecover(h, _v , _r , _s )) revert(); if (balances[ _to ] + _value < balances[ _to ] || balances[msg.sender] + _feeSmt < balances[msg.sender]) revert(); balances[ _to ] += _value ; Transfer( _from , _to , _value ); balances[msg.sender] += _feeSmt ; Transfer( _from , msg.sender, _feeSmt ); balances[ _from ] -= _value + _feeSmt ; nonces[ _from ] = nonce + 1 ; return true ; }
其中的问题分析如下: function transferProxy(address _from , address _to , uint256 _value , uint256 _feeSmt , uint8 _v ,bytes32 _r , bytes32 _s ) public transferAllowed( _from ) returns (bool){ //错误1:这里没有做整数上溢出检查 //_feeSmt,value都是由外部传入的参数,通过我们之前的理论这里可能会出现整数上溢出的情况 // 例如:_feeSmt = 8fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff , // value = 7000000000000000000000000000000000000000000000000000000000000001 // _feeSmt和value均是uint256无符号整数,相加后最高位舍掉,结果为0。 // 那么_feeSmt + _value = 0 直接溢出,绕过代码检查,导致可以构造巨大数量的smt代币并进行转账 if (balances[ _from ] < _feeSmt + _value ) revert(); uint256 nonce = nonces[ _from ]; bytes32 h = keccak256( _from , _to , _value , _feeSmt ,nonce); if ( _from != ecrecover(h, _v , _r , _s )) revert(); if (balances[ _to ] + _value < balances[ _to ] || balances[msg.sender] + _feeSmt < balances[msg.sender]) revert(); balances[ _to ] += _value ; Transfer( _from , _to , _value ); balances[msg.sender] += _feeSmt ; Transfer( _from , msg.sender, _feeSmt ); balances[ _from ] -= _value + _feeSmt ; nonces[ _from ] = nonce + 1 ; return true ; }
作者修改后的代码如下: function transferProxy(address _from , address _to , uint256 _value , uint256 _feeSmt , uint8 _v ,bytes32 _r , bytes32 _s ) public transferAllowed( _from ) returns (bool){ //错误1:这里没有做整数上溢出检查 //_feeSmt,value都是由外部传入的参数,通过我们之前的理论这里可能会出现整数上溢出的情况 // 例如:_feeSmt = 8fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff , // value = 7000000000000000000000000000000000000000000000000000000000000001 // _feeSmt和value均是uint256无符号整数,相加后最高位舍掉,结果为0。 // 那么_feeSmt + _value = 0 直接溢出,绕过代码检查,导致可以构造巨大数量的smt代币并进行转账 // 在这里做整数上溢出检查 if (balances[ _to ] + _value < balances[ _to ] || balances[msg.sender] + _feeSmt < balances[msg.sender]) revert(); // 在这里做整数上溢出检查 ,防止交易费用 过大 if ( _feeSmt + _value < _value ) revert(); // 在这里做整数上溢出检查 ,防止交易费用 过大 if (balances[ _from ] < _feeSmt + _value ) revert(); uint256 nonce = nonces[ _from ]; bytes32 h = keccak256( _from , _to , _value , _feeSmt ,nonce); if ( _from != ecrecover(h, _v , _r , _s )) revert(); // 条件检查尽量 在开头做 // if(balances[_to] + _value < balances[_to] // || balances[msg.sender] + _feeSmt < balances[msg.sender]) revert(); balances[ _to ] += _value ; Transfer( _from , _to , _value ); balances[msg.sender] += _feeSmt ; Transfer( _from , msg.sender, _feeSmt ); balances[ _from ] -= _value + _feeSmt ; nonces[ _from ] = nonce + 1 ; return true ; }
攻击者发送的交易:
以下是攻击者恶意发送的转账交易地地址:https://etherscan.io/tx/0x1abab4c8db9a30e703114528e31dee129a3a758f7f8abc3b6494aad3d304e43f
(黑客攻击交易日志截图)
BEC 合约中的整数安全问题简析
BEC的合约地址是0xC5d105E63711398aF9bbff092d4B6769C82F793D,合约代码可以访问etherscan的如下网址进行查看https://etherscan.io/address/0xc5d105e63711398af9bbff092d4b6769c82f793d#code
BEC合约有问题的代码存在于batchTransfer()函数,代码如下: function batchTransfer(address[] _receivers, uint256 _value) public whenNotPaused returns ( bool) { uint cnt = _receivers.length ; uint256 amount = uint256(cnt) * _value ; require(cnt > 0 && cnt <= 20 ) ; require(_value > 0 && balances[msg.sender] >= amount) ; balances[msg.sender] = balances[msg.sender].sub(amount); for (uint i = 0 ; i < cnt; i++) { balances[_receivers[i]] = balances[_receivers[i]].add(_value); Transfer(msg.sender, _receivers[i], _value) ; } return true ; } }
其中问题代码分析如下: function batchTransfer(address[] _receivers , uint256 _value ) public whenNotPaused returns (bool) { uint cnt = _receivers .length; // 这里直接使用乘法运算符,可能会导致溢出 // 变量cnt为转账的地址数量,可以通过外界的用户输入_receivers进行控制,_value为单地址转账数额,也可以直接进行控制。 // 外界可以通过调整_receivers和_value的数值,产生乘法运算溢出,得出非预期amount数值,amount溢出后可以为一个很小的数字或者0, uint256 amount = uint256(cnt) * _value ; require(cnt > 0 && cnt <= 20 ); // 这里判断当前用户拥有的代币余额是否大于或等于要转移的amount数量 // 由于之前恶意用户通过调大单地址转账数额_value的数值,使amount溢出后可以为一个很小的数字或者0, // 所以很容易绕过balances[msg.sender] >= amount的检查代码。从而产生巨大_value数额的恶意转账。 require( _value > 0 && balances[msg.sender] >= amount); //调用Safemath库中的安全函数来完成加减操作 balances[msg.sender] = balances[msg.sender].sub(amount); for (uint i = 0 ; i < cnt; i++) { balances[ _receivers [i]] = balances[ _receivers [i]].add( _value ); Transfer(msg.sender, _receivers [i], _value ); } return true ; } }
攻击者发送的交易:
以下是攻击者恶意发送的转账交易:
https://etherscan.io/tx/0xad89ff16fd1ebe3a0a7cf4ed282302c06626c1af33221ebe0d3a470aba4a660f。
(黑客攻击交易日志截图)
合约整数漏洞事件总结
从上面的分析中,大家可以看出针对SMT和BEC的合约恶意攻击都是通过恶意的整数溢出来绕过条件检查。目前以太坊上运行着上千种合约,这上千种合约中可能也存在类似的安全隐患,所以作为合约的开发人员需要投入更多的精力来确保合约的安全性。
下篇我们将详细的介绍如何正确保合约的安全,敬请期待。
文章发布只为分享区块链技术内容,版权归原作者所有,观点仅代表作者本人,绝不代表区块链兄弟赞同其观点或证实其描述
区块链
2018-04-27 13:29:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
本文是对以太坊中可升级智能合约领域的各种实现策略的总结 ,目的是汇总迄今为止的相关资源, 以帮助我们在设计智能合约时,考虑如何对其进行升级和更新。
100%可升级机制
目前有两种主要策略用来实现可升级的智能合约: 使用代理合约 将逻辑和数据分离成不同的合约。
这两种方法要解决的根本问题是如何更新合同的逻辑,同时仍然保留对合同状态的访问。
代理合约
代理合约使用 delegatecall 操作码将函数调用转发到可更新的目标合约。 由于delegatecall 保留了函数调用的状态,因此可以更新目标合约的逻辑,并且状态将保留在代理合约中以供 更新后的目标合约的逻辑使用。 与delegatecall一样,msg.sender将保持代理合约的调用者身份。
由于最近的拜占庭硬分叉,现在可以获取函数调用的返回大小了,因此与 Nick Johnson 首次提出的方法相比,目前这种方法可以通用。 在 Daonomic 的文章中可以 看到一个通用代理合约的例子,你可以更详细地了解这个机制。
分离逻辑和数据合约
这中方法到将智能合约拆分两个合约: 包含数据(变量,结构,映射等)以及getter/setter的数据合约 包含如何更新这些数据的业务逻辑的逻辑合约
逻辑合约通过setter更新数据,而数据合约只允许逻辑合约调用setter。 这允许在保持数据不变 的同时更换实现逻辑,从而实现完全可升级的系统。
通过引导用户使用新的逻辑合约(通过诸如ENS的解析器)并更新数据合约的权限来允许新的逻辑合约 执行setter,就可以实现合约的更新。
查看[ @Thomas Wiesner]( https://github.com/tomw1808 )的视频以更好地了解这一机制。
使用键值对数据模型分离逻辑和数据合约
这种策略的工作原理与上述类似,只是不使用最终期望数据结构(struct,mapping等)来定义合约数据模型, 所有数据都被抽象化并存储在键值对中,然后使用一个标准的命名系统以及sha256散列算法用于查找数据值。
可以查阅 David Rugendyke 的文章以更好地理解这种机制。
部分可升级策略
创建一个完全可升级的合约听起来不错,但需要一个很大的妥协:保证合约的不可变性。 因此在很多情况下 实现部分可升级的合约可能是更合理的选择。
在此策略中,智能合约的核心功能可以保留为不可升级。 其他可能不太完整或更复杂的组件则 采用可升级策略实施。
这方面已经有一些很好的案例: 以太坊名称服务 ENS :ENS核心合约是一个非常简单的合约,不能更改。 域名注册商则可以由管理员升级。 域名注册商是一个合约工厂,如果使用一个新的域管理器,它可以与以前的所有 数据状态重新链接,而不会有太多麻烦。 0xProject :603DEX(去中心化交易所)核心智能合约可以完全升级,而代理合约(每个用户一个)保持不变。 0x“代理”合约(不同于前面介绍的代理策略)包含用户资金和相关设置。 由于这个原因,它不是0x合约系统的可升级部分。
其他挑战 在所有情况下,都需要对是否保持智能合约的不变性进行取舍。 创建可选的可升级智能合约系统对用户来说是可能并且有价值的,但是增加了复杂性。 对Solidity编译器的更改可能会破坏新旧合约之间的兼容性。 制定可升级策略时需要考虑gas开销。
结论
没有一个策略是完美的,选择正确的策略取决于要实施的智能合约。 所有策略都非常复杂,智能合约设计人员应该对 他们所选择的可升级策略非常了解,以避免安全漏洞。 为了创建一个可升级的智能合约,代理机制似乎是最好的全面策略,因为它允许程序员将可升级机制 与合约设计区分开来,这使得一切变得更容易,并且会产生更少的错误,而错误是我们需要升级合约的主要原因。 在最简单的核心逻辑不变的情况下,采用部分升级策略也是保持用户信任的好主意。 首先设计不可升级的智能合约系统,然后制定可升级的策略,这是一种实用且理想的方式。
参考资源
思路与观点 2018-02-01 Zeppelin Solution : Zeppelin与智能合约开发的演变 2016-2017: Stackexchange关于可升级的智能合约的问答 ConsenSys : 以太坊智能合约最佳实践 Evoluchain : Evoluchain
代理合约 2018-02-22 Jorge Izquierdo : ERC DelegateProxy #897 2018-02-15 经济学 : 可升级的以太坊智能合约 , Github项目 2018-01-11 B9lab团队 : upgradable - Github项目 2018-01-10 Manuel Araoz : olidity-proxy - Github项目 2017-06-02 @Ownage : Ether-routher - Github项目 2017-05-24 Nick Johnson : 疯狂的区块链科学:100%可升级的合约 , Gist File 2017-03-15 Jorge Izquierdo : Solidity代码部署高级技术 2017-03-07 Manuel Araoz : Solidity中的代理库 2017-02-13 Jorge Izquierdo : Solidity中库驱动的开发 2017-01-21 Tjaden Hess : 可升级的智能合约 2016-06-16 @Martin Swende : 隐式方法代理技巧
分离逻辑和数据合约 2017-12-09 @Thomas Wiesner : 升级链上智能合约 2017-11-13杰克坦纳: 可升级的智能合约 , Github项目 2017-08-21 Lukas K : 可升级的智能合约 。 我们学习了在区块链上建立保险 2016-08-16 @nikolai : Dapp-a-day 6:可升级的通证合约 @monax : Solidity 1:solidity五种合约模型 @monax : Solidity 7:更新Solidity合约 @Z.com-cloud-blockchain : 合约版本升级解决方案
使用键值对数据模型分离逻辑和数据合约 2018-01-20 Hassan Abdel-Rahman : Solidity可升级合约 2017-11-22 David Rugendyke : 可升级Solidity合约设计 , Github项目 2017-06-29 Chandan Gupta : Solidity可升级合约接口设计 , Github项目 2016-06-08 Elena Dimitrova : 在Solidity中编写可升级合约
原文链接 如果你希望马上开始学习以太坊DApp开发,可以访问汇智网提供的出色的在线互动教程: 以太坊DApp实战开发入门 去中心化电商DApp实战开发
区块链
2018-04-27 12:39:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
小白学习以太坊开发时,需要很多资料,这里搜集整理了一些以太坊开发教程: windows以太坊私有链搭建教程 windows下ipfs安装部署与开发环境搭建 以太坊开发资料中文pdf电子书汇编 区块链学习路线及资料索引 Etherscan 使用指南 以太坊DApp开发环境搭建-Ubuntu平台 使用 Infura 和 web3.js 呼叫合约 以太坊DApp开发环境搭建 - Windows ubuntu以太坊私有链搭建教程 如何从零开始学习区块链技术 区块链的10本入门书籍 以太坊区块链学习资料汇总 小白如何入门区块链技术 以太坊开发资源汇总 12个以太坊区块链最佳开发工具
区块链
2018-04-27 12:28:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
小白如何学习以太坊开发,在以太坊应用编程开发时,除了学习solidity开发智能合约,一个小白还应该补充哪些编程知识?文中给出相关参考学习资源。
前端技能学习
智能合约仅仅是以太坊去中心化应用的一个组成部分,要提供用户操作的界面,前端web开发技能比不可少,这是HTML/CSS/JavaScript的天下: HTML入门与实战 CSS入门与实战 JavaScript入门与实践 jQuery开发手册
如果你希望在前端使用现代框架例例如当红炸子鸡Vue,可以参考以下课程: vue.js 2入门与提高 Vuex 2入门与提高 VueRouter 2入门与提高 vue.js 2工程化实践
后端技能学习
严格的去中心化应用不需要后端,但是很多情况下,基于以太坊的应用需要引入一个后端才更实际。我们推荐使用nodejs来作为后端的核心开发平台。 nodejs入门 express入门 mongoose入门 mongodb入门
以太坊技能学习 以太坊DApp开发入门 以太坊+IPFS电商DApp实战
转自:http://blog.hubwiz.com/2018/04/20/freshman-learn-ethereum/
区块链
2018-04-27 12:22:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
本入门教程面向以太坊初学者,内容涵盖 以太坊开发 基础和相关概念,并教大家如何入门构建一个以太坊的完整去中心化应用-区块链投票系统。
第一节 课程概述
通过本教程的学习,你将掌握: 以太坊区块链的基本知识 开发和部署以太坊合约所需的软件环境 使用高级语言( solidity )编写以太坊合约 使用NodeJS编译、部署合约并与之交互 使用 Truffle 框架开发分布式应用 使用控制台或网页与合约进行交互
前序知识要求
为了顺利完成本课程,最好对以下技术已经有一些基本了解: 一种面向对象的开发语言,例如:Python,Ruby,Java... 前端开发语言:HTML/CSS/JavaScript Linxu命令行的使用 数据库的基本概念
课程的所有代码均已在Ubuntu(Trusty、Xenial)和 macOS 上测试过。
第二节 课程简介
在本课程中,我们将会构建一个去中心化的( Decentralized )投票应用。利用这个投票应用, 用户可以在不可信( trustless )的分布环境中对特定候选人投票,每次投票都会被记录在区块 链上:
所谓去中心化应用( DApp :Dcentralized Application),就是一个不存在中心服务器 的应用。在网络中成百上千的电脑上,都可以运行该应用的副本,这使得它几乎不可能 出现宕机的情况。
基于区块链的投票是完全去中心化的,因此无须任何中心化机构的存在。
第三节 开发迭代
本课程将涵盖应用开发的整个过程,我们将通过三次迭代来渐进地引入区块链应用 开发所涉及的相关概念、语言和工具:
Vanilla:在第一个迭代周期,我们不借助任何开发框架,而仅仅使用NodeJS来进行应用开发, 这有助于我们更好地理解区块链应用的核心理念。 Truffle:在第二个迭代周期,我们将使用最流行的去中心化应用开发框架 Truffle 进行开发。 使用开发框架有助于我们提高开发效率。 Token:在第三个迭代周期,我们将为投票应用引入代币( Token ) —— 现在大家都改口 称之为通证了 —— 都是 ICO 惹的祸。代币是公链上不可或缺的激励机制,也是区块链 应用区别于传统的中心化应用的另一个显著特征。
为什么选择投票应用作为课程项目?
之所以选择投票作为我们的第一个区块链应用,是因为集体决策 —— 尤其是投票机制 —— 是以太坊的 一个核心的价值主张。
另一个原因在于,投票是很多复杂的去中心化应用的基础构件,所以我们选择了投票应用作为学习区块链 应用开发的第一个项目。
第四节 初识区块链
如果你熟悉关系型数据库,就应该知道一张数据表里可以包含很多行数据记录。例如,下面的数据表中 包含了6条交易记录:
本质上,区块链首先就是一个分布式( Distributed )数据库,这个数据库维护了一个不断增长的记录列表。 现在,让我们对数据进行批量( batch )存储,比如每批 100 行,并将各存储批次连接起来,是不是就像一条链?
在区块链里,多个数据记录组成的批次就被称为块( block ),块里的每一行数据记录就被称为交易( transaction ):
最开始的那个块,通常被称为创世块( genesis block ),它不指向任何其他块。
不可篡改性
区块链的一个显著特点是,数据一旦写入链中,就不可篡改重写。
在传统的关系型数据库中,你可以很容易地更新一条数据记录。但是,在区块链中,一旦数据写入就无法 再更新了 —— 因此,区块链是一直增长的。
那么,区块链是如何实现数据的不可篡改特性?
这首先得益于哈希( Hash )函数 —— 如果你还没接触过哈希函数,不妨将它视为一个数字指纹的计算函数: 输入任意长度的内容,输出定长的码流(指纹)。哈希函数的一个重要特性就是,输入的任何一点微小变化,都会 导致输出的改变。因此可以将哈希值作为内容的指纹来使用。 你可以点击 这里 进一步了解哈希函数。
由于区块链里的每个块都存储有前一个块内容的哈希值,因此如果有任何块的内容被篡改,被篡改的块之后 所有块的哈希值也会随之改变,这样我们就很容易检测出区块链的各块是否被篡改了。
去中心化的挑战
一旦完全去中心化,在网络上就会存在大量的区块链副本(即:全节点),很多事情都会变得比之前中心化 应用环境复杂的多,例如: 如何保证所有副本都已同步到最新状态? 如何保证所有交易都被广播到所有运行和维护区块链副本的节点计算机上? 如何防止恶意参与者篡改区块链 ......
在接下来的课程中,通过与经典的C/S架构的对比,我们将逐步理解去中心化应用的核心思路, 并掌握如何构建以太坊上的去中心化应用。
第五节 C/S架构以服务器为中心
理解去中心化应用架构的最好方法,就是将它与熟悉的 Client/Server 架构进行对比。如果你是一个 web 开发者, 应该对下图很了解,这是一个典型的 Client/Server 架构:
一个典型web应用的服务端通常由 Java,Ruby,Python 等等语言实现。前端代码由 HTML/CSS/JavaScript 实现。 然后将整个应用托管在云端,比如 AWS、Google Cloud Platform、Heroku....,或者放在你租用的一个 VPS 主机上。
用户通过客户端( Client )与 web 应用( Server )进行交互。典型的客户端包括浏览器、命令行工具( curl 、 wget 等)、 或者是 API 访问代码。注意在这种架构中,总是存在一个(或一组)中心化的 web 服务器,所有的客户端都需要 与这一(组)服务器进行交互。当一个客户端向服务器发出请求时,服务器处理该请求,与数据库/缓存进行交互, 读/写/更新数据库,然后向客户端返回响应。
这是我们熟悉的中心化架构。在下一节,我们将会看到基于区块链的去中心化架构的一些显著区别。
第六节 去中心化架构——彼此平等的节点
下图给出了基于以太坊的去中心化应用架构:
你应该已经注意到,每个客户端(浏览器)都是与各自的节点应用实例进行交互,而不是向 一个中心化的服务器请求服务。
在一个理想的去中心化环境中,每个想要跟DApp交互的人,都需要在他们的计算机或手机上面运行 一个的完整区块链节点 —— 简言之,每个人都运行一个全节点。这意味着,在能够真正使用一个 去中心化应用之前,用户不得不下载整个区块链。
不过我们并非生活在一个乌托邦里,期待每个用户都先运行一个全节点,然后再使用你的应用是不现实的。 但是去中心化背后的核心思想,就是不依赖于中心化的服务器。所以,区块链社区已经出现了 一些解决方案,例如提供公共区块链节点的 Infura , 以及浏览器插件 Metamask 等。通过这些方案, 你就不需要花费大量的硬盘、内存和时间去下载并运行完整的区块链节点,同时也可以利用去中心化 的优点。我们将会以后的课程中对这些解决方案分别进行评测。
第七节 以太坊——世界计算机
以太坊是一种区块链的实现。在以太坊网络中,众多的节点彼此连接,构成了以太坊网络:
以太坊节点软件提供两个核心功能:数据存储、合约代码执行。
在每个以太坊全节点中,都保存有完整的区块链数据。以太坊不仅将交易数据保存在链上,编译后 的合约代码同样也保存在链上。
以太坊全节点中,同时还提供了一个虚拟机来执行合约代码。
交易数据
以太坊中每笔交易都存储在区块链上。当你部署合约时,一次部署就是一笔交易。当你为候选者投票时,一次投票 又是另一笔交易。所有的这些交易都是公开的,每个人都可以看到并进行验证。这个数据永远也无法篡改。
为了确保网络中的所有节点都有着同一份数据拷贝,并且没有向数据库中写入任何无效数据,以太坊 目前使用 工作量证明 ( POW:Proof Of Work )算法来保证网络安全,即通过矿工挖矿( Mining )来达成共识( Consensus )—— 将数据同步到所有节点。
工作量证明不是达成共识的唯一算法,挖矿也不是区块链的唯一选择。现在,我们只需要了解,共识是指各节点 的数据实现了一致, POW 只是众多用于建立共识的算法中的一种,这种算法需要通过矿工的挖矿来实现非可信环境下的 可信交易。共识是目的,POW是手段。
合约代码
以太坊不仅仅在链上存储交易数据,它还可以在链上存储合约代码。
在数据库层面,区块链的作用就是存储交易数据。那么给候选者投票、或者检索投票结果的逻辑放在哪儿呢? 在以太坊的世界里,你可以使用 Solidity 语言来编写业务逻辑/应用代码(也就是合约: Contract ), 然后将合约代码编译为以太坊字节码,并将字节码部署到区块链上:
编写合约代码也可以使用其他的语言,不过 Solidity 是到目前为止最流行的选择。
以太坊虚拟机
以太坊区块链不仅存储数据和代码,每个节点中还包含一个虚拟机(EVM:Ethereum Virtual Machine)来执行 合约代码 —— 听起来就像计算机操作系统。
事实上,这一点是以太坊区别于比特币( Bitcoin )的最核心的一点:虚拟机的存在使区块链迈入了2.0 时代,也让区块链第一次成为应用开发者友好的平台。
JS开发库
为了便于构建基于web的DApp,以太坊还提供了一个非常方便的JavaScript库 web3.js ,它封装了以太坊节点的API 协议,从而让开发者可以轻松地连接到区块链节点而不必编写繁琐的 RPC 协议包。所以,我们可以在常用的JS框架 (比如 reactjs、angularjs 等)中直接引入该库来构建去中心化应用:
课程地址: http://xc.hubwiz.com/course/5a952991adb3847553d205d1?affid=oschinaw
如果你是java和Android App开发程序员可以看这个web3j开发以太坊详解: http://xc.hubwiz.com/course/5b2b6e82c02e6b6a59171de2?affid=76oschina
如果你是PHP开发程序员可以看这个PHP开发以太坊详解: http://xc.hubwiz.com/course/5b36629bc02e6b6a59171de3?affid=76oschina
区块链
2018-04-27 11:55:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
区块链技术,以太坊教程,以太坊开发学习资料如以太坊白皮书,官方文档;web3.js,Solidity,Truffle,geth中文文档;IPFS开发环境搭建,私有链搭建,开发部署等学习资源汇总整理如下:
1. 适合区块链新手的以太坊DApp开发实战入门
2. 区块链,星际文件系统IPFS,Nodejs和MongoDB构建以太坊DApp电商平台
收集了一些免费区块链、以太坊技术开发相关的文件,有需要的可以下载,文件链接: web3.js API官方文档中文版: https://pan.baidu.com/s/1hOV9hEzi7hFxJCL4LTvC6g 以太坊官方文档中文版 : https://pan.baidu.com/s/1ktODJKLMBmkOsi8MPrpIJA 以太坊白皮书中文版 : https://pan.baidu.com/s/1bzAFnzJ35hlQxJ2J4Oj-Ow Solidity的官方文档中文版 : https://pan.baidu.com/s/18yp9XjEqAHpiFm2ZSCygHw Truffle的官方文档中文版 : https://pan.baidu.com/s/1y6SVd7lSLUHK21YF5FzIUQ C#区块链编程指南 : https://pan.baidu.com/s/1sJPLqp1eQqkG7jmxqwn3EA 区块链技术指南: : https://pan.baidu.com/s/13cJxAa80I6iMCczA04CZhg 精通比特币中文版: : https://pan.baidu.com/s/1lz6te3wcQuNJm28rFvBfxg Node.js区块链开发 : https://pan.baidu.com/s/1Ldpn0DvJ5LgLqwix6eWgyg geth使用指南文档中文版 : https://pan.baidu.com/s/1M0WxhmumF_fRqzt_cegnag 以太坊DApp开发环境搭建-Ubuntu : https://pan.baidu.com/s/10qL4q-uKooMehv9X2R1qSA 以太坊DApp开发环境搭建-windows : https://pan.baidu.com/s/1cyYkhIJIFuI2oyxM9Ut0eA 以太坊DApp开发私链搭建-Ubuntu : https://pan.baidu.com/s/1aBOFZT2bCjD2o0EILBWs-g 以太坊DApp开发私链搭建-windows : https://pan.baidu.com/s/10Y6F1cqUltZNN99aJv9kAA 以太坊ganache CLI命令行参数详解: https://pan.baidu.com/s/1lnknFkwenacaeM4asOcBdg 使用truflle和infura部署以太坊合约: https://pan.baidu.com/s/1PTxSVff2vHSVUihYczRRqw IPFS安装部署与开发环境搭建-windows: https://pan.baidu.com/s/1bnhDvqCoOgAqEBZXMtVbRg
区块链
2018-04-27 11:39:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
虽然有北大博士讲,95%的区块链项目都没有前途,但我们知道区块链还是有它的优势,比如数据的不可篡改性对于版权保护有相当大的意义,而地址的匿名性则有其他潜在的用途。那么,如何将任意数据,比如图像或文本写入以太坊区块链呢?本文将讲解如何使用web3.js实现这一功能并给出相应的实现代码。
实现任意数据上链的核心是 web3.eth.sendTransaction() 方法的使用,我们将借助一个转账交易来完成任意数据上链的任务。在要发送的交易对象中,使用 data 字段就可以传入任意的16进制字符串。
将数据转换为16进制字符串
我们可以使用 web3.toHex() 方法将一个字符串转换为16进制字符串: let data = web3.toHex('你可以将任意数据写入以太坊区块链')
得到的data值为: 0x4f6053ef4ee55c064efb610f6570636e519951654ee5592a574a533a575794fe 。
当然不一定需要使用 web3.toHex() 方法,可以使用任何能够得到16进制串的方法,例如在NodeJS中使用 Buffer : let data = '0x' + Buffer.from('使用Buffer更好处理图像数据').toString('hex')
得到的data值为: 0xe4bdbfe794a8427566666572e69bb4e5a5bde5a484e79086e59bbee5838fe695b0e68dae 。
声明交易对象
接下来然后设置要发送的交易对象,我们需要借助一个转账交易来实现数据上链,因此设置的主要字段是转出账户from,转入账户to,转账金额value,当然,少不了data,我们就是为了它才要搞一个交易: let txo = { from: web3.eth.accounts[0], to: web3.eth.accounts[1], value:'0x00', data: data }
如果你只有一个账户,也可以自己转给自己:)
发送交易
最后调用 web3.eth.sendTransaction() 方法即可: web3.eth.sendTransaction(txo, (error, hash) => console.log(hash));
当交易成功后,你可以使用 etherscan.io 来查看交易信息中的 input data 。 如果你希望马上开始学习以太坊DApp开发,可以访问汇智网提供的出色的在线互动教程: 以太坊DApp实战入门教程 以太坊去中心化电商应用开发实战
区块链
2018-04-27 02:03:00
「深度学习福利」大神带你进阶工程师,立即查看>>> 如果你希望马上开始学习以太坊DApp开发,可以访问汇智网提供的出色的在线互动教程: 以太坊DApp实战入门教程 以太坊去中心化电商应用开发实战
返回指定区块的指定序号的交易。
调用: getTransactionFromBlock(hashStringOrNumber, indexNumber [, callback])
参数: hashStringOrNumber : String - 区块号或哈希。或者是earliest,latest或pending。查看web3.eth.defaultBlock了解可选值。 indexNumber : Number - 交易的序号。 callback : Function - 回调函数,用于支持异步的方式执行7。
返回值: Object - 交易对象,详见web3.eth.getTransaction
示例: var transaction = web3.eth.getTransactionFromBlock('0x4534534534', 2); console.log(transaction); // see web3.eth.getTransaction
区块链
2018-04-26 23:58:00
「深度学习福利」大神带你进阶工程师,立即查看>>> 如果你希望马上开始学习以太坊DApp开发,可以访问汇智网提供的出色的在线互动教程: 以太坊DApp实战入门教程 以太坊去中心化电商应用开发实战
指定一个交易哈希,返回一个交易的收据。需要指出的是,处于pending状态的交易,收据是不可用的。
调用: web3.eth.getTransactionReceipt(hashString [, callback])
参数: hashString : String - 交易的哈希 callback : Function - 回调函数,用于支持异步的方式执行7。
返回值: Object - 交易的收据对象,如果找不到返回null blockHash: String - 32字节,这个交易所在区块的哈希。 blockNumber: Number - 交易所在区块的块号。 transactionHash: String - 32字节,交易的哈希值。 transactionIndex: Number - 交易在区块里面的序号,整数。 from: String - 20字节,交易发送者的地址。 to: String - 20字节,交易接收者的地址。如果是一个合约创建的交易,返回null。 cumulativeGasUsed: Number - 当前交易执行后累计花费的gas总值10。 gasUsed: Number - 执行当前这个交易单独花费的gas。 contractAddress: String - 20字节,创建的合约地址。如果是一个合约创建交易,返回合约地址,其它情况返回null。 logs: Array - 这个交易产生的日志对象数组。
示例: var receipt = web3.eth.getTransactionReceipt('0x9fc76417374aa880d4449a1f7f31ec597f00b1f6f3dd2d66f4c9c6c445836d8b'); console.log(receipt); { "transactionHash": "0x9fc76417374aa880d4449a1f7f31ec597f00b1f6f3dd2d66f4c9c6c445836d8b", "transactionIndex": 0, "blockHash": "0xef95f2f1ed3ca60b048b4bf67cde2195961e0bba6f70bcbea9a2c4e133e34b46", "blockNumber": 3, "contractAddress": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", "cumulativeGasUsed": 314159, "gasUsed": 30234, "logs": [{ // logs as returned by getFilterLogs, etc. }, ...] }
区块链
2018-04-26 23:57:00
「深度学习福利」大神带你进阶工程师,立即查看>>> 如果你希望马上开始学习以太坊DApp开发,可以访问汇智网提供的出色的在线互动教程: 以太坊DApp实战入门教程 以太坊去中心化电商应用开发实战
使用指定帐户签名要发送的数据,帐户需要处于解锁状态。
调用: web3.eth.sign(address, dataToSign, [, callback])
参数: address : String - 签名使用的地址 dataToSign : String - 要签名的数据 callback : Function -(可选)回调函数,用于支持异步的方式执行7。
返回值: String - 签名后的数据。
返回的值对应的是ECDSA(Elliptic Curve Digital Signature Algorithm)签名后的字符串。 r = signature[0:64] s = signature[64:128] v = signature[128:130]
需要注意的是,如果你使用ecrecover,这里的v值是00或01,所以如果你想使用他们,你需要把这里的v值转成整数,再加上27。最终你要用的值将是27或2813。
示例: var result = web3.eth.sign("0x135a7de83802408321b74c322f8558db1679ac20", "0x9dd2c369a187b4e6b9c402f030e50743e619301ea62aa4c0737d4ef7e10a3d49"); // second argument is web3.sha3("xyz") console.log(result); // "0x30755ed65396facf86c53e6217c52b4daebe72aa4941d89635409de4c9c7f9466d4e9aaec7977f05e923889b33c0d0dd27d7226b6e6f56ce737465c5cfd04be400"
区块链
2018-04-26 23:57:00
「深度学习福利」大神带你进阶工程师,立即查看>>> 如果你希望马上开始学习以太坊DApp开发,可以访问汇智网提供的出色的在线互动教程: 以太坊DApp实战入门教程 以太坊去中心化电商应用开发实战
返回指定区块的交易数量。
调用: web3.eth.getBlockTransactionCount(hashStringOrBlockNumber [, callback])
参数: hashStringBlockNumber : Number|String -(可选)如果未传递参数,默认使用web3.eth.defaultBlock定义的块,否则使用指定区块。 callback : Function - 回调函数,用于支持异步的方式执行7。
返回值: Nubmer - 给定区块的交易数量。
示例: var number = web3.eth.getBlockTransactionCount("0x407d73d8a49eeb85d32cf465507dd71d507100c1"); console.log(number); // 1
区块链
2018-04-26 23:56:00
「深度学习福利」大神带你进阶工程师,立即查看>>> 如果你希望马上开始学习以太坊DApp开发,可以访问汇智网提供的出色的在线互动教程: 以太坊DApp实战入门教程 以太坊去中心化电商应用开发实战
返回块号或区块哈希值所对应的区块
调用: web3.eth.getBlock(blockHashOrBlockNumber [, returnTransactionObjects] [, callback])
参数: blockHashOrBlockNumber : Number|String -(可选)如果未传递参数,默认使用web3.eth.defaultBlock定义的块,否则使用指定区块。 returnTransactionObjects : Boolean -(可选)默认值为false。true会将区块包含的所有交易作为对象返回。否则只返回交易的哈希。 callback : Function - 回调函数,用于支持异步的方式执行7。
返回值 - 区块对象: Number - 区块号。当这个区块处于pending将会返回null。 hash - 字符串,区块的哈希串。当这个区块处于pending将会返回null。 parentHash - 字符串,32字节的父区块的哈希值。 nonce - 字符串,8字节。POW生成的哈希。当这个区块处于pending将会返回null。 sha3Uncles - 字符串,32字节。叔区块的哈希值。 logsBloom - 字符串,区块日志的布隆过滤器9。当这个区块处于pending将会返回null。 transactionsRoot - 字符串,32字节,区块的交易前缀树的根。 stateRoot - 字符串,32字节。区块的最终状态前缀树的根。 miner - 字符串,20字节。这个区块获得奖励的矿工。 difficulty - BigNumber类型。当前块的难度,整数。 totalDifficulty - BigNumber类型。区块链到当前块的总难度,整数。 extraData - 字符串。当前块的extra data字段。 size - Number。当前这个块的字节大小。 gasLimit - Number,当前区块允许使用的最大gas。 gasUsed - 当前区块累计使用的总的gas。 timestamp - Number。区块打包时的unix时间戳。 transactions - 数组。交易对象。或者是32字节的交易哈希。 uncles - 数组。叔哈希的数组。
示例: var info = web3.eth.getBlock(3150); console.log(info); /* { "number": 3, "hash": "0xef95f2f1ed3ca60b048b4bf67cde2195961e0bba6f70bcbea9a2c4e133e34b46", "parentHash": "0x2302e1c0b972d00932deb5dab9eb2982f570597d9d42504c05d9c2147eaf9c88", "nonce": "0xfb6e1a62d119228b", "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "transactionsRoot": "0x3a1b03875115b79539e5bd33fb00d8f7b7cd61929d5a3c574f507b8acf415bee", "stateRoot": "0xf1133199d44695dfa8fd1bcfe424d82854b5cebef75bddd7e40ea94cda515bcb", "miner": "0x8888f1f195afa192cfee860698584c030f4c9db1", "difficulty": BigNumber, "totalDifficulty": BigNumber, "size": 616, "extraData": "0x", "gasLimit": 3141592, "gasUsed": 21662, "timestamp": 1429287689, "transactions": [ "0x9fc76417374aa880d4449a1f7f31ec597f00b1f6f3dd2d66f4c9c6c445836d8b" ], "uncles": [] } */
区块链
2018-04-26 23:55:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
以太坊的开发包括智能合约的开发和去中心化应用( DApp )开发,这个教程将介绍如何 根据不同的开发场景在本地windows或linux下安装、搭建并配置四种不同的以太坊开发环境: 轻量级开发环境、仿真器开发环境、私有链开发环境和公链开发环境。
轻量级开发环境
如果你只需要开发智能合约(例如开发一个ERC20代币)而不是完整的 去中心化应用( DApp ),那么使用remix + metamask的轻量级组合就够了。
remix 是以太坊官方提供的solidity在线集成开发环境, 你不需要在本地安装任何软件,就可以开发、编译并测试智能合约了。
metamask是一个轻量级钱包,它是chrome浏览器的插件,remix搭配上metamask 以后,你就可以直接在浏览器里部署以太坊智能合约了。
进一步阅读: 使用Remix编译和部署以太坊智能合约 。 metamask下载和安装方法
DApp快速开发环境
如果要开发完整的去中心化应用,你就需要部署额外的开发工具了,这主要包括: Ganache:以太坊仿真器 Truffle:以太坊DApp开发框架 Node.js:后端应用开发平台 Express:node.js的web开发库
这一组合可以让你在一台计算机上模拟以太坊环境,进行DApp的快速开发、部署与测试。 快速的意思是,由于使用了以太坊仿真器,你不需要挖矿、不需要等待交易完成,一切 就像传统的应用开发那么迅速。
进一步阅读: windows以太坊DApp开发环境搭建 linux以太坊DApp开发环境搭建
DApp私有链开发环境
除了使用以太坊仿真器,你也可以部署一个私有链来开发以太坊应用,这里主要的变化 是采用标准的以太坊节点软件Geth来代替Ganache仿真器。
在私有链开发环境下,你提交到以太坊节点的每一笔交易,需要挖矿才能完成,这意味着 你在调试代码时可能需要额外的等待时间,这会拖慢开发的进度。因此,我们建议在可能 的情况下,首先使用仿真器先调通你的整个应用,再迁移到私有链环境下。
进一步阅读: windows以太坊私有链DApp开发环境安装 linux以太坊私有链DApp开发环境安装
DApp公链开发环境
最终你的开发需要将智能合约部署到公链,例如主链或测试链。这有两种方案: 使用自己的geth全节点 使用公开的托管节点
如果使用自己的geth节点,就和私有链开发环境没有什么区别,你只需要让Geth启动连接 到公链而不是你搭建的私有链即可。
但更多的情况是,你并不需要使用自己的全节点,可以利用Infura提供的托管以太坊节点。 除非特别需要,我们推荐你首先考虑使用托管节点。
进一步阅读: truffle+infura部署以太坊智能合约 如果你希望马上开始学习以太坊DApp开发,可以访问我们的在线互动教程: 以太坊DApp实战开发入门 去中心化电商DApp实战开发
我的博客即将搬运同步至博客云+社区,邀请大家一同入驻: https://cloud.tencent.com/developer/support-plan
区块链
2018-04-28 02:09:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
这个指南希望能帮助新用户了解目前有哪些流行的以太坊钱包,哪个钱包更加安全, 哪个钱包更加好用,我们会根据不同的需求来进行推荐,同时也提供了五种钱包的官方下载地址。
1#:Jaxx
Jaxx是一款功能强大的以太坊钱包,设计很美观,用起来体验非常好。
Jaxx的主要特性包括: 单一主种子备份 可以轻松切换比特币和以太币 单屏幕操作 支持使用原生相机扫描 多币种 可用余额显示 生成自定义金额的QR码
使用Jaxx钱包的一个主要好处是除了比特币和以太坊之外还支持其他代币。除此之外, Jaxx还可以与Shapeshift集成,允许用户在Jaxx支持的所有货币之间进行货币兑换。 出于这个原因,如果你更加关注数字货币的交易,我们强烈推荐Jaxx。
官方下载地址
2#:MyEtherWallet
MyEtherWallet(MEW)是以太坊社区最受欢迎的钱包之一,在手机或台式机上都可以使用。
MEW是一个开放源代码的钱包,可以让你发送、创建和接收以太币,而不会影响你的私钥。 MEW不会储存你的密码或钥匙,你只需要使用Keystore文件生成账户然后使用私钥来访问它。 正是这种双重认证机制让我们非常欣赏MEW。
此外,MEW还支持多种方式访问钱包:硬件钱包、助记符短语等等。 你可以在他们的wiki 上找到访问方法的完整列表。
注意:请务必检查你访问的是否是MEW的正确网址,因为MEW经常发生恶意网络钓鱼诈骗事件。
官方下载地址
3#:MetaMask
MetaMask是另一个值得推荐的、高度安全的钱包。 MetaMask是一个桥梁,允许你使用现有 的浏览器来访问未来的去中心化web。 它允许你在浏览器中运行以太坊DApp,而无需自己运行 完整的以太坊节点。
MetaMask有一个仪表板,可以让你管理不同站点上的身份并进行区块链交易的签署。
MetaMask是谷歌浏览器插件,如果这是你的首选浏览器,那用它非常合适。国内由于网络环境 的问题无法在谷歌浏览器里直接安装,请参考文章 metamask下载和安装方法 解决这一问题。
官方下载地址
4#:Parity
像MetaMask一样,Parity是另一个轻量级的基于浏览器的钱包,可让用户访问以太坊上的 去中心化应用程序( DApp )和数字货币。
Parity内置了以太坊钱包和DApp环境,这使得它非常适合于开发人员使用。Parity的特性包括: 帐户、地址簿和多重签名管理。 密钥创建、导入和导出管理。 Web3 Dapp浏览器。 硬件和电子冷钱包支持。 名称注册支持。 合约开发、部署和交互环境。
官方下载地址
5#:Mist
毫无疑问,Mist是最常用和最值得信赖的以太坊钱包之一。 它作为一个开源项目托管在GitHub上。
Mist是一款采用Web技术开发的混合式桌面应用,基于Electron框架开发,这使得它能够快速部署重要更新。
官方下载地址 如果你开始对以太坊上去中心化应用的开发产生兴趣,希望马上可以动手实践,可以访问在线互动教程: 以太坊DApp实战入门教程 以太坊去中心化电商应用开发实战
原文链接
区块链
2018-04-28 00:43:00