从ERC20 到 ERC721

ERC20的问题点

我们熟悉的基于以太坊的ICO,通常使用ERC20标准发行Token。

ERC20标准的Token主要有两个问题。

第一个问题是,如果错误地将ERC20的Token发送到了非用户地址(比如合约地址)上,大多数情况下,该合约不具备处理相应Token的能力,所以这笔Token就看永久性丧失了。

第二个问题,基于ERC20发行的Token,相同的Token都具有可替代性(fungible), 拿简单的例子来说,我拥有的5ETH和别人拥有的5ETH并没有本质上的区别,相同条件下都可以在交易所换成相同数量的法币。这个性质本身没有什么问题,但是就Token的使用来说,其功能就会受到很大限制。

ERC223 解决了第一个问题。当发送者错误发送到不支持相应Token的合约地址上的是时候,错误发送的Token可以允许被召回。ERC223的细节就此不展开了。我们主要集中讨论第二个问题。

ERC721 以及 Non-fungible

ERC721是Non-fungible Token的标准。著名的以太坊上的游戏CryptoKitties就是基于ERC721的Token。现实生活中,有很多价值是Non-fungible的,最简单的例子就是你在游戏里抽的扭蛋。

ERC721与ERC20的不同之处,主要有两处。

第一,任何一个Token都有相应的一个id,合约本身会用一个mapping记录token的id以及相应的所有者。

为了满足Non-fungible的特性,任何一个Token都会有相应的MetaData。直接将每一个Token本身的特性记录在Blokchain上通常代价很昂贵,所以,我们通常的做法是保持一个Token id和ipfs或者外部https链接的mapping。例如下面的例子。

1
2
3
4
5
6
contract MyNFT {
mapping(uint256 => string) tokenLinks;
function tokenMetadata(uint256 _tokenId) constant returns (string infoUrl) {
return tokenLinks[_tokenId];
}
}

参考资料

更加进一步的说明可能比较多余。建议直接看下面的参考资料。

参考1

参考2

参考3

使用Truflle开发基于oraclize的应用

Oraclize 是什么

Oraclize是将外部的变化引入Blockchain的一套工具。Oraclize已经部署到了很多Blockchain当中,
最常见的,在Ethererum上,Oraclize是一个智能合约。
直接安例子比较直观。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
pragma solidity ^0.4.11;
import "github.com/oraclize/ethereum-api/oraclizeAPI.sol";

contract ExampleContract is usingOraclize {

string public EURGBP;
event LogPriceUpdated(string price);
event LogNewOraclizeQuery(string description);

function ExampleContract() payable {
updatePrice();
}

function __callback(bytes32 myid, string result) {
if (msg.sender != oraclize_cbAddress()) throw;
EURGBP = result;
LogPriceUpdated(result);
}

function updatePrice() payable {
if (oraclize_getPrice("URL") > this.balance) {
LogNewOraclizeQuery("Oraclize query was NOT sent, please add some ETH to cover for the query fee");
} else {
LogNewOraclizeQuery("Oraclize query was sent, standing by for the answer..");
oraclize_query("URL", "json(https://api.fixer.io/latest?symbols=USD,GBP).rates.GBP");
}
}
}

上面的智能合约调用了oraclize_query这个oraclize提供的方法。oraclize会监听整个Ethereum Blockchain,在区块链外调用这个query,获取到结果之后,调用目标合约的__callback方法并把query的结果作为参数传入。

有了oraclize,我们想要在blockchain内部调用外部api的时候,只需要写好相应的callback函数就行了。

用Truffle将上例的智能合约部署到Rinkeby TestNet

其实下面的内容和oraclize关系不大。不过既然尝试了解oraclize,不如将其部署之后熟悉一下中间的步骤。

首先是需要的东西。

  • Mist 钱包以及testnet的账号。注意同步最新的内容。

  • Geth

  • Rinkeby Testnet的Eth。网上所以下faucet一下就出来了。

用Geth 跑起local 节点

Mist自带的Geth已经同步了最新的链上的内容,我们需要关掉mist,然后在local用Geth跑Rinkeby的节点。

1
geth --rinkeby --rpc --rpcapi db,eth,net,web3,personal --unlock="0x889a183da0d5450CCbC4604a8200eEb47020f719" --rpccorsdomain https://localhost:3000

注意要输入解锁账号的phrase。

建立truffle项目进行配置

1
2
3
$ mkdir truffle-test
$ cd truffle-test
$ truffle init

需要修改truffle.js的内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
module.exports = {
networks: {
development: {
host: "localhost",
port: 8545,
network_id: "*", // Match any network id
},
rinkeby: {
host: "localhost", // Connect to geth on the specified
port: 8545,
from: "0x889a183da0d5450CCbC4604a8200eEb47020f719", // default address to use for any transaction Truffle makes during migrations
network_id: 4,
gas: 4612388 // Gas limit used for deploys
}
}
};

然后书写我们的合约。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# contracts/Example.sol

pragma solidity ^0.4.11;
import "./oraclizeAPI.sol";

contract ExampleContract is usingOraclize {

string public EURGBP;
event LogPriceUpdated(string price);
event LogNewOraclizeQuery(string description);

function ExampleContract() payable {
updatePrice();
}

function __callback(bytes32 myid, string result) {
if (msg.sender != oraclize_cbAddress()) throw;
EURGBP = result;
LogPriceUpdated(result);
}

function updatePrice() payable {
if (oraclize_getPrice("URL") > this.balance) {
LogNewOraclizeQuery("Oraclize query was NOT sent, please add some ETH to cover for the query fee");
} else {
LogNewOraclizeQuery("Oraclize query was sent, standing by for the answer..");
oraclize_query("URL", "json(https://api.fixer.io/latest?symbols=USD,GBP).rates.GBP");
}
}
}

我们需要在local应用oraclize。

1
2
3
$ cd contracts
$ wget https://raw.githubusercontent.com/oraclize/ethereum-api/master/oraclizeAPI_0.5.sol
$ mv oraclizeAPI_0.5.sol oraclizeAPI.sol

接下来在migrations下面添加2_deploy_contracts.js

1
2
3
4
5
var Example = artifacts.require("ExampleContract");

module.exports = function(deployer) {
deployer.deploy(Example);
};

这样智能合约的内容就完成了。
rk rinkeby

a 译truffle compile, 部署 truffle migrate --network rinkeby

大功告成。

用Truffle console 调试

1
2
3
4
5
6
$ truffle console --network rinkeby
truffle(rinkeby)> ExampleContract.deployed().then(function(instance){return instance.EURGBP.call();}).then(function(value){return value});
'0.87523'
truffle(rinkeby)> ExampleContract.deployed().then(function(instance){return instance.updatePrice({value: web3.toWei(0.01, "ether")});}).then(function(value){return value.logs[0].
args});
{ description: 'Oraclize query was sent, standing by for the answer..' }

最后如果要监听回掉函数里面的Event,可以用下面的方法。

1
ExampleContract.deployed().then(meta => { const allEvents = meta.allEvents({ fromBlock: 0, toBlock: 'latest' });allEvents.watch((err, res) => { console.log(err, res);});});

以上流水账仅供备忘。

Logstash的多行输入以及自定义Pattern

前言

通常我们仅仅用logstash处理access log。这个时候logstash的配置如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
input {
stdin {}
}

filter {
grok {
match => { "message" => "%{HTTPD_COMMONLOG}" }
}
}

output {
stdout { codec => rubydebug }
}

我们知道,logstash的config内容分为input,filter和output三个部分。
这三个部分都集成了很多插件。我们可以从不同的地方进行input,通过filter对data进行整理,然后output。

在实际问题中,我遇到几个问题。

  • log的单位并不是一行,而是多行。

  • 我可以自定义match的pattern吗?

  • grok的match里面的message能省略掉吗?

接受以多行为单位输入的Log

我们知道,apache access log 和 nginx access log 以及大多数log都是以单行为单位输出的。

比如

1
[Fri Sep 09 10:42:29.902022 2011] [core:error] [pid 35708:tid 4328636416] [client 72.15.99.187] File does not exist: /usr/local/apache2/htdocs/favicon.ico

input的大多数插件默认是接受单行单位log输入。我们可以用multiline这个codec plugin 来接受多行输入。

1
2
3
4
5
6
7
8
input {
stdin {
codec => multiline {
pattern => "^\s"
what => "previous"
}
}
}

上面的配置的意思是,当以空格开头的行,我们把它和上面一行归并到一个log中。具体的其他用法可以查看multiline plugin的文档。

自定义grok的pattern

grok里面的match的message,其实是定义在这里的各种pattern。
我们可以自定义pattern。

形式如下

1
2
3
4
5
USERNAME [a-zA-Z0-9._-]+
USER %{USERNAME}
EMAILLOCALPART [a-zA-Z][a-zA-Z0-9_.+-=:]+
EMAILADDRESS %{EMAILLOCALPART}@%{HOSTNAME}
INT (?:[+-]?(?:[0-9]+))

将这些pattern保存在文件中,然后在logstash的config中制定读取pattern的目录即可。

1
2
3
4
5
6
filter {
grok {
patterns_dir => ["/usr/local/logstash/patterns"]
match => { "message" => "%{USER: user} %{INT: age} %{EMAILADDRESS: email}"}
}
}

这样,我们输出的时候可以获得到message,user,age,email这几个field。

省略grok解析出的一些field

可以用mutate这个plugin来移除一些field。

1
2
3
mutate {
remove_field => [ "message" ]
}

这样output的结果中就不会有message这个field了。

感想

logstash提供了丰富的插件。在遇到问题的时候除了善于搜索还要有耐心阅读插件文档,必要时候需要写demo进行测试。

Use Nem Blockchain to Store Your Hexo Blog

Hexo is a static blog generator which has large amount of handy plugins and themes.
I really enjoyed to write my blog using hexo. However I also have trouble sometimes.

Most of hexo users keep a backup of their blog using git, and however sometimes its forgetable for me to push the contents to remote and when I want to write my blog at another place, I hesitate to do so since I can not render all the blogs I wrote.

When I began to know blockchain technology and nem related development, It seems that you can use the message field of nem transaction to store your blog contents so that you can write your blog at anytime with just a nem transaction with messages of your blog contents.

That’s why I came out with a hexo plugin to fetch your blog contents on nem blockchain. Check Here for more detail.

Basic idea

Nem has provided convenient API for application developers.

When we write our blogs, we launch a transaction using your nem wallet or API, the message field should be your blog contents in markdown format.

When Hexo generate the contents, we first use nem api to fetch the contents from blockchain and save them to local files with the markdown extension. Then those contents get generated and rendered to html just same as your blogs stored on your file system.

One thing to take care is that the message field in nem can support at most 680 bytes of date and with a large message field, you are going to pay more transaction fee.

Usage

This plugin can be configured to use nem mainnet or testnet, Since it’s more easy to get some xem(currency name for nem blockchain), I suggest to use testnet first.

Prepare your testnet account

Use This script to create your testnet private key, public key and address.

You should install nem-sdk first.

Get some testnet XEM

Find a testnet faucet like This one.

Input your account address and get some testnet XEM.

The transaction should be ok in less than 5 minutes.

Create a Hexo blog and Config

Run hexo new your_blog_name to create a new hexo blog.

Go to the blog directory,
run npm install hexo-fetch-nem to install the plugin.

In _config.yaml add the config to use our nem plugin.

1
2
isMainnet: false 
nem_address: your_nem_address

your_nem_address is the nem address you transfer your contents into. You can run the above script again to get an empty account. Don’t mix up your account with XEM inside and the one you are going to write your blog to.

Write your markdown blog and make a transaction

Write down your blog for example like this.

1
2
3
4
### My title

* foo
* bar

and save your blog into a file which can be named for example my_blog.md

Use the following script to launch a tranaction to send the contents of the blog to nem blockchain.

The private key should be the one with XEM inside. You should install nem-ruby first by gem install nem-ruby and then run the script.

1
$ ruby transfer.rb my_blog.md

Generate the blog

Run hexo g to generate the blog and now have a check. Cool. I’s already there.

More

Hexo won’t generate your latest blog unless you run hexo g. To solve the problem, you can have a crontab to run generate repeatedly.

That’s all

I have a demo running. You can have a check. I am ready to write down what I have learned everyday on nem blockchain!

微信小程序IOS真机date的一个坑

给之前做的就职日历小程序添加列表功能,顺便发现了一个巨坑。

1
2
3
4
5
6
7
console.log(new Date('2018-03-01'))

// on android and pc simulator
// =>2018-03-01T00:00:00.000Z

// on ios
//=> null

WTF

原因很简单,三者的JS的引擎不一样。
修正也很简单,把所有的’yyyy-mm-dd’ 格式修正成’yyyy/mm/dd’格式既可。
防止于未然的策略就是,真机测试测试再测试。
以上。

在Terminal上快速预览Markdown

本文的起因

其实,对于Markdown预览这个需求,本来就是伪命题。

试想一下,发明Markdown的目的本来就是一种新的所见即所得的语言。

不过,在书写时候如果能够及时预览,也给我们避免了一些格式方面的错误,能让我们写得更顺手。

在诸如Atom之类的GUI编辑器中,我们可以很方便一遍书写markdown一遍即时预览。那么我们用vim书写Markdown的时候该如何即时预览呢?

几个工具

可以Parse markdown的格式,然后在terminal上输出的工具。Bug还比较多,觉得不顺手的话自己修改两下吧哈哈。

1
2
parsed = TTY::Markdown.parse('example.md')
puts parsed

可以监听文件是否有变化。然后可以处罚一个命令。用法如下。

1
$ filewatcher '**/*.js' 'jshint $FILENAME'
  • tmux

用来分割窗口。

在Termial上即时预览markdown

思路很简单,我们书写markdown的时候,用tmux分割窗口,在另一个窗口中,实用filewatcher 监听文件变化,
然后触发我们用tty-markdown书写的脚本即可,这个脚本的作用就是读取正在编辑的markdown文件,然后渲染输出到terminal上。

1
2
3
4
require 'tty-markdown'
parsed = TTY::Markdown.parse_file(ARGV[0])
puts "\e[H\e[2J"
puts parsed

保存这个脚本到PATH中,然后我们打开我们需要编辑的markdown文件。开始编辑,同时,在tmux的另一个窗口中,输入例如

1
filewatcher README.md 'tty-markdown  README.md'

哈哈,来一张截屏看看效果。

Todo

其实可以做成vim的插件,在vim中直接splitwindow来看渲染的内容。不过至今还没有大神来做这个事情,到底是为什么呢? 原因大概就是我在开头说的那几句话吧。本来就是所见即所得的东西,何必太过挑剔。