LeanCloud 又在  招募新的小伙伴  了,来和精子一起工作吧。
标签 #Bitcoin

我对比特币的信心来自哪里

这是我写的第三篇评论比特币的文章,目前比特币的价格在 18000 人民币,前两篇分别是在 2013 年初(600 人民币)和 2013 年末(6000 人民币),在那之后的三年多时间里,我一直保留着大约 1 到 2 个比特币。后来在 2016 年初我准备做一个关于区块链的演讲,于是为了给幻灯片截图,还买入了几十个以太币(Ether),当时以太币可能还没有进入到大众视野。

之前我也说过,我最早了解到比特币是在 2012 年初,那时我对去中心化系统非常感兴趣,当时比特币的价格是 40 元钱,说起来最近「我当初知道比特币的时候才 XX 元钱」已经成了和朋友聊起比特币时的标准开头。从那时起我一直都是比特币的持有者,中间还有一段时间的工作和比特币相关,对技术层面的了解当然是必不可少的,但时间长了也会去想那个终极问题 —— 比特币的价值是来自哪里。

但我们先来解释另外一个问题:为什么最近两个月比特币涨了这么多(从 10000 到 20000)?可能有的人会说因为外汇管制、因为「隔离见证」、因为 WannaCry,但我觉得可以简单地归结为两个字「信心」,价格之所以上涨是因为有更多的人买入,而他们之所以买入是因为他们对比特币有信心,或者说他们对比特币的信心增加了。这种信心的来源是多方面的,可能是你看到了比特币一路上涨、可能是你认识到了比特币是一个可能改变世界的发明,当然更大的可能是看到了身边其他人对比特币的信心,你的信心来自于其他更多对比特币有信心的人,因为有他们的支持,所以你可以放心地买入比特币而不必担心它会一文不值。

如果你认可了比特币的价格来自于信心的话,其实我觉得它就更加符合我们对货币的一般理解了。人民币作为一种法币,即使它只是一张纸钞或者银行中的一串数字,大家对它的信心如此之强,以至于全国十三亿人没有人不收人民币。而且大家甚至认为人民币是唯一可靠的货币,任何其他的货币都会受到这些人的质疑,这种信心来自于几十年里人民币的购买力相对稳定、来自国家政府的背书,当然更多的还是来自于其他人对人民币的信心,你知道你拿着这张纸可以从任何一个中国人那里买到东西。

那么为什么比特币的波动如此之大呢,我觉得一方面是因为它的体量太少,即使按照现在 18000 的价格,总市值大概也只是人民币的千分之一,更小的市值意味着它更容易被操纵。另一方面比特币通过世界各地的交易所可以自由、快速地进行 P2P 的兑换,一旦有影响大家的信心的新闻产生,大家就会第一时间进行买入或卖出操作,更及时地反映到市场价格上;而法币之间的兑换往往要通过银行,再加上一些对于法币兑换的管制,导致信心对价格的影响速度被人为地拖慢了,汇率并不准确地反映当前大家的信心。如果一个法币有着和比特币差不多的市值,并且允许自由兑换,我并不觉得仅仅因为它是法币,它就会比特币的价格更稳定。

比特币的出现和崛起的确让我非常激动,这是一项非常伟大的实验 —— 一种并非政府发行的、去中心化的、基于计算机和互联网的货币能否最终被大家认可。这项实验还远没有结束,包括我在内的无数人用真金白银,以及显卡在继续着这项实验。比特币带动了数百种其他的电子货币,带起了大家对区块链和去中心化技术的兴趣,某种程度上来说可能也带动了显卡和专用芯片的发展,这是比特币在「价格」之外的意义。

1

精子生于 1995.11.25, 21 岁,英文 ID jysperm.

订阅推送

通过邮件订阅精子的博客日志、产品和项目的最新动态,精子承诺每一封邮件都会认真撰写(历史邮件),有想和精子说的话也可以直接回复邮件。

该博客使用基于  Hexo  的  simpleblock  主题。博客内容使用  CC BY-NC-SA 3.0  授权发布。最后生成于 2017-09-17.

子块。但在 Ethereum 中,一个新产生的块可以有一个父块和若干个叔块。回到上面的例子,如果在 A 挖到新的块但其他人尚未收到广播的时间中,如果有人挖出了一个新的块,但因为广播较晚没有被大家接受,那么这个块有可能成为下个块的「叔块」—— 这个块所代表的工作量证明会被认为是下一个块的一部分(即这个人挖出下一个块的难度降低了),叔块也仅仅提供工作量证明,其中所包含的交易是无效的。这样一来便补偿了较晚收到广播的客户端在低出块间隔情况下的劣势,具体来讲,直接的叔块提供 50% 的工作量证明、二代叔块提供 25% 的工作量证明以此类推,最多会承认最多五代的叔块。

blockchain-ethereum-uncles

图片来自 https://blog.ethereum.org/2014/07/11/toward-a-12-second-block-time

尚未解决的问题

接下来这个部分我向大家介绍一下 Ethereum 目前尚未解决的几个问题。

首先就是 Ethereum 目前达成共识的方式依然和 Bitcoin 一样是通过 POW(工作量证明)来担保的,只有完成了特定工作量的节点才能够参与 Block 生成的工作,工作量证明的问题就在于会浪费大量的计算力去保证网络的安全性,虽然者也是基于我们前面提到的「经济激励」思想,但其实是可以改进的。Ehtereum 认为更好的方式是用 POS(所有权证明)去代替工作量证明,这样可以极大地提高这个网络的效率 —— 不需要再去进行无意义的计算了。

既然 Ether 本身就是有价值的,那么为什么不用它本身来进行经济激励呢?所谓 POS 就是说大家用所拥有的 Ether 去做担保,即每一个希望参与 Block 生成(传统意义上的挖矿)的节点(被称为验证人)都需要向系统(这里说的系统是指在协议上做规定,所有节点都认为这笔保证金被「冻结」了)缴纳一笔保证金,然后大家用自己的保证金来对可能成为下一个 Block 的 Block 下注(所谓「可能」的一个重要前提就是这个 Block 必须是符合协议规定的),如果这个块真的成为下一个 Block,那么所有下注的节点将会得到奖励,否则保证金将会被罚没。

这个模式其实和 POW 非常类似,在 POW 中,矿工用自己的计算力来「下注」,而且如果一旦有一个链更长,就有必要切换到这个链上继续挖矿 —— 因为参与的人越多的链越有可能成为正确的链,最终大家达成一个共识。而在 POS 中,大家使用自己的保证金下注,大家同样倾向于选择已经被很多其他人下注的块(如果它是合法的话),最后达成一个共识。

POS 势必会增加整个网络的吞吐量 —— 大家不再需要通过进行大量无意义的计算来达成共识了,每个节点的运算量将趋近于执行 Contract 中代码和进行数据验证的计算量。

当然 POS 之所以目前还未被采用,是因为还存在一些尚未解决的问题,其中之一就是和 POW 一样的 51% 攻击问题,在 POW 中集中全网 51% 的计算力是有一定物理限制的 —— 因为计算力需要计算设备来提供;而相比之下在 POS 中收集全网 51% 的 Ether 则相比之下容易一些 —— 只要你有足够的钱。POS 天然地比 POW 更非复杂,要实现上述的工作逻辑,需要处理例如维护有效的验证人列表、保证金的冻结、罚没和返还、提议区块和投注区块、防止验证人之间的结盟攻击、网络分区之后的恢复等等。

另外一个话题是「分片」,无论是 Bitcoin 还是 Ethereum, 目前都是在同一个 Blockchain 上完成所有的交易确认,这极大地限制了一个分布式网络的计算能力 —— 每个节点都需要接收、存储、验算每一笔交易,整个网络的处理能力其实等于一个节点的处理能力。

因此 Ethereum 希望在未来引入一个「分片」的机制,来将整个网络分为若干个部分,之间独立地进行交易验证。但分片之间会通过指针的结构去引用其他分片的数据、通过异步调用的方式去影响其他分片,所以整个网络在用户看来依然是一体的,只不过整个网络的处理能力将会有非常强的可拓展性。目前分片相关的实现还在比较早期的开发阶段,我找到的资料有限,所以就不过多介绍了。

Contract

这一部分我将会给大家展示一些实际的、可以工作的 Contract 的代码。Contract 可以由很多种不同范式的语言来编写,最终它们都会被编译成 opcode 在 EVM 上执行,今天我们选择以 Solidity 这个类 JavaScript 的语言为例,它是目前维护得最好的一个 EVM 语言。

contract Test {
  uint storedData; // State variable

  struct Voter { // Struct
    uint weight;
    bool voted;
    address delegate;
    uint vote;
  }

  event HighestBidIncreased(address bidder, uint amount); // Event

  function func() { // Function
    if (msg.sender.balance < 10 finney) {
        msg.sender.send(10 finney);
    }

    sha256("...");

    address nameServer = 0x72ba7d8e73fe8eb666ea66babc8116a41bfb10e2;
    nameServer.delegatecall("isAvailable", "MyName");
    nameServer.call("register", "MyName");
  }
}

以上是一些核心语法的展示,在 Solidity 中你可以声明状态变量(uint storedData;),这些变量的值会永远被保存在 Blockchain 上;可以用 struct 去声明复杂的数据结构;也可以定义函数,这些函数会在收到交易时被执行,交易的发起者可以选择执行哪些函数,所以一个 Contract 可以提供若干个函数,在函数内可以进行逻辑判断、循环、修改变量的值。

语言内置一些很方便的小功能,例如常见的密码学算法(sha256)、单位换算(10 finney)、直接书写钱包地址(0x72ba7d8e73fe8eb666ea66babc8116a41bfb10e2)等。msg 是内置的全局变量,可以从上面读取与此次交易有关的信息,如发起者、金额等。Contract 可以通过两种方式去调用其他 Contract 的代码,delegatecall 相当于将另一个 Contract 的代码放到当前上下文执行,就好像引入了一个库函数;而 call 则是发起一笔新的交易去触发另一个 Contract 的逻辑。

那么 Contract 如何从 blockchain 上读取和写入数据呢?这个复杂的工作被抽象为了「状态变量」,上面的 storedData 就是一个状态变量。其实 Contract 执行过程中对状态变量的修改并不会保存到 blockchain 中,因为 Contract 执行的都是确定性的计算 —— Contract 的执行由交易触发,执行过程中只能读取 blockchain 上已有的数据,因此只要我们知道历史上每一笔与这个 Contract 有关的交易,我们就可以随时推算出一个 Contract 在某个时间点上各个状态变量的值。

接下来我来展示一个真正可用的 Contract —— 在 Ethereum 网络的基础上发行一个属于自己的代币:

contract Coin {
    // The keyword "public" makes those variables
    // readable from outside.
    address public minter;
    mapping (address => uint) public balances;

    // Events allow light clients to react on
    // changes efficiently.
    event Sent(address from, address to, uint amount);

    // This is the constructor whose code is
    // run only when the contract is created.
    function Coin() {
        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);
    }
}

代码来自 http://solidity.readthedocs.io/en/latest/introduction-to-smart-contracts.html#subcurrency-example (MIT)

这个名为 Coin 的 Contract 声明了两个状态变量,minter 用来存储这个代币的创建者,在构造函数(function Coin())中将第一笔用于创建 Contract 的交易的发起者赋值给了这个变量;还声明了一个钱包地址到数字的映射表 balances, 用来表示每个持有该代币的地址的余额。

mint 这个函数中先判断了交易的发起者是否是该代币的创建者,如果是的话就按照函数参数,将一定数量的代币加给指定的地址。send 这个函数可以被所有人调用,会从交易发起者的地址扣除一定量的余额(如果有足够的余额的话),加到目标地址上,相当于一个转账的功能。

我们还声明了一个名为 Sent 的事件,事件其实并不会有什么实际的作用,只是便于调试时打印关键性事件,未来也会方便轻量级客户端的实现(轻量级客户端只接受事件而不实际执行 Contract)。

blockchain-ethereum-mix

Ethereum 提供了一个叫 Mix 的 IDE 来调试这段代码,在 Mix 的右侧你可以虚构一些 Block 和账户来测试你的 Contract,也可以看到在执行过程中每个状态变量的值的变化情况。值得一提的是 Contract 一旦发布便无法修改,此后的运行完全靠其他人的交易触发,对于每天都在写 Bug 的程序员来讲这一点会令人非常不爽,但是 Contract 的语义本来就是「合约」,一旦你发布了一个合约自然不能去修改它,否则谁还会信任你的合约呢。当然你可以在 Contract 中给自己一些特权(就像前面的 Coin 中那样,只有创建者可以凭空创造代币),但这些代码也存在于 Blockchain 上,其他使用者也是知晓的。

编写完成后我们就可以用 Ethereum 钱包将这个 Contract 发布到网络上了:

blockchain-ethereum-create-contract

发布之后你可以关注这个 Contract,随时点到 Contract 的详情界面:

blockchain-ethereum-wallet-contract

在左侧可以看到两个状态变量的值,minter 的值就是我自己的地址,balances 因为是一个映射表,所以你可以输入一个地址去查询它的余额。在右侧你可以向这个 Contract 发起新的交易,有一个下拉菜单可以选择 send 或是 mint 函数,你可以填写传递给 Contract 的参数。因为在这里我们发交易的目的是传递一个消息,而非传递 Ether,所以我们不必设置交易的金额。

接下来我要介绍一个很有趣的 Contract,这个 Contract 实现了一个「庞氏骗局」的效果,即你可以向这个 Contract 支付 1 Ether 来加入这个游戏,之后每加入三个人,就会按顺序支付给先加入的人 3 Ether:

contract Pyramid {
    struct Participant {
        address etherAddress;
    }

    Participant[] public participants;

    uint public payoutIdx = 0;

    // events make it easier to interface with the contract
    event NewParticipant(uint indexed idx);

    // fallback function - simple transactions trigger this
    function() {
        enter();
    }

    function enter() {
        if (msg.value < 1 ether) {
            msg.sender.send(msg.value);
            return;
        }

        if (msg.value > 1 ether) {
            msg.sender.send(msg.value - 1 ether);
        }

        uint idx = participants.length;
        participants.length += 1;
        participants[idx].etherAddress = msg.sender;

        NewParticipant(idx);

        // for every three new participants we can
        // pay out to an earlier participant
        if (idx != 0 && idx % 3 == 0) {
            // payout is triple, minus 10 % fee
            uint amount = 3 ether;
            participants[payoutIdx].etherAddress.send(amount);
            payoutIdx += 1;
        }
    }

    function getNumberOfParticipants() constant returns (uint n) {
        return participants.length;
    }
}

代码简化自 https://ethereumpyramid.com/contract.html

代码还算简单,这个 Contract 声明了一个 participants 数组用来按顺序存储所有参与者的钱包地址,还是声明了一个 payoutIdx 用来记录前多少名参与者已经得到了 3 Ether 的返还。enter 实现了这个 Contract 的主要功能,首先是一些参数检查,保证每个参与者都支付了 1 Ether, 然后将新的参与者放到 participants 数组的末尾,最后如果当前参与者的序号刚好是 3 的倍数,就发送 3 Ether 给第 payoutIdx 个参与者,并将 payoutIdx 指向下一个参与者。

参考链接

HashTree:

Bitcoin:

Halting Problem:

Ethereum:

Ethereum Network:

Next of Ethereum:

Contract:

Contract IDE:

利用 Bitcoin 网络进行时间区间证明

有这样一个有趣的话题,如果我在今天,2013 年 12 月 31 日,截取一张截图,那么在事后如何证明这张截图是今天所截取的呢?

同时我希望这个证明足够可靠,关键信息不掌握在少数人手中,任何人无法篡改已经证明的信息,证明不会因为某些机密信息的泄露而失效,且任何人都要可以非常方便地验证这个证明。

乍一看,简单。但细一想,又似乎不可能。

其实这个问题可以分解成两个问题:

「证明这张图必须在某个时间之后才能被截取出来」这个问题简单,只需在截图中包含一些「只有在该时间后才能获取到的信息」,比如当天双色球的中奖号码等等。

这很好理解,如果我在截图中包含了 2013.12.31 的双色球中奖号码,就可以毫无疑问地证明这张图是在 2013.12.31 之后被截取的。

但是,这并不能妨碍我在 2014.1.1 重新截取一张截图,加入 2013.12.31 的双色球号码。所以我们还必须证明「这张图必须在某个时间之前才能被截取出来」。

解决这个问题的关键是要把这张截图的信息,永久地保存到某个可以随时查证的地方,比如我们可以让这张图片登上报纸头条,它就是永远可被查证的了,今后我们可以随时把需要证明的图片与 2013.12.31 的报纸进行对比,证明图片的真伪。

综上只需要在截图中加入当日的双色球号码,并且让它登上报纸头条即可。

——等等,你在开玩笑么。

好吧,所以我要提 Bitcoin, Bitcoin 给我们提供了这样的一个机会,它既是双色球,又是报纸。使用下文的方法进行一次证明只需花费你几分钟的时间,和一点点金钱(几分钱).

首先我们准备一张图片(记作 PIC).

pic.png

因为图片本身很大,对整张图片应用各种加密算法很不划算,所以我们要对这张图片进行散列,如果散列算法足够可靠,我们可以认为散列值就代表了这张图片(记作 HASH), 我们选用在今后很长一段时间都足够可靠的 SHA-256 算法,Bitcoin 网络使用的也是该算法。

HASH = sha256(PIC) = 896c53284a04c3df2c5fe81b1fa228d421ed6c87190da60e153562786346af75

然后我们去瞧一眼 Bitcoin 网络最新的区块(Block) 的散列值,Bitcoin 的区块大约每隔十分钟出现一个,除非你控制了世界上几乎全部的(参与 Bitcoin, 下略)的计算机,否则没有人能预测到它。同时新生成的 Block 会马上被同步到世界上所有的计算机上,事后同样没有人能够篡改。它在此起到了「双色球」的作用。

北京时间 2013.12.31 21:32 产生的最新一个区块的散列值如下,记作 BBH(Bitcoin Block Hash):

BBH = 0000000000000001149d2d7b4fcc693095fef279a1300f938e9cbeec1b43c034

然后我们将最新区块的散列值加到图片上,记作 HASH2:

HASH2 = sha256(HASH1 + BBH) = 277e31d0a5495dbeb642f459be2bcb768d728a7c1e9e008c4d6276dc938d4195

以后我们需要用 HASH2 来表示这张图片(而不是HASH), 因为 HASH2 中包含了最新区块的信息。

至此,我们完成了后向证明。

即,我们证明了「HASH2 只有在拥有 HASH(即 PIC) 且晚于北京时间 2013.12.31 21:32 的情况下才能取得」。

然后,我们使用 HASH2 作为钱包私钥,来生成对应的 Bitcoin 收款地址,因为比特币钱包私钥和收款地址本质上是一对公钥加密算法(如 RSA)的私钥和公钥,因此从公钥无法推出私钥。我们得到了这个收款地址,即证明了我们拥有私钥,再进一步证明了我们拥有 HASH2, HASH, 和 PIC.

在这个过程中,Bitcoin 网络起到了「报纸」的作用,(理想情况下)每笔交易会在下一个区块「截稿」的时候被定格,成为区块的一部分,被同步到世界上所有的计算机上,随时可以查证,又无人可以篡改。

在这一步我使用了 blockchain.info 提供的导入私钥的服务,暂时我还未查证 blockchain.info 以何种格式理解该私钥,但这不影响结论。

我由 HASH2 得出了对应的 Bitcoin 收款地址(记作 PUB):

1ALtyqivh8VgnefQ8okroJUFuNqJYFgSac

然后我向该地址汇入了 0.0001 个 Bitcoin, 以证明我在此时就已经获知了该收款地址,同时支付了 0.0001 个 Bitcoin 的交易手续费(其实可以更少一点), 该交易被收录到了于北京时间 2013.12.31 21:49 产生的新区块中。

至此,我们完成前向证明。

即,我们证明了「我在北京时间 2013.12.31 21:49 之前,就已经拥有该图片了」。

也即,我们在北京时间 2013.12.31 21:49 钱通过向 PUB 中汇款的方式,证明了我们在此刻之前已经获知了 PUB, 而获知 PUB 的前提是 获知 HASH2, HASH, 和 PIC.

至此我我们完成了整个证明过程,证明了我在北京时间 2013.12.31 21:32 - 21:49 这 17 分钟的时间段拥有这张截图,而在这之前或者之后,即使我拥有这种图片,和以上全部信息,也无法做出同样的证明,世上只此一份。

当然,你在今后出示这种图片的同时需要出示 HASH2.

或者你也可以通过某种方式把 HASH2 嵌入到原始图片中(这个工作需要你在做前向证明之前完成).

最后的福利:那个钱包里用于证明的 0.0001 Bitcoin 我没有取走,谁要就拿去吧。

重谈 Bitcoin: 只是一种可能性

我是从 2012 年年初开始关注 Bitcoin 的,当时 Bitcoin 还是 Geek 的代名词,而现在再一提起 Bitcoin, 立刻就会被贴上土豪的标签。

最近一年,Bitcoin 的影响力越来越大,我的圈子里,几乎没有人不知道 Bitcoin 了,连我们的班主任都时不时提上一句两句的。

说实话,大部分人都是在瞎起哄,他们一不懂经济学,二不懂 Bitcoin 的原理,只是知道 Bitcoin 是个价格涨得很快,很多人炒,而且还不大靠谱的东西。

有人说,Bitcoin 没有任何价值,只是一个骗局。相应的,还会有人说,法定货币同样没有价值,价值源自信用。

有人说 Bitcoin 浪费了大量的计算力。相应的,还会有人说,维护法定货币的交易秩序,需要更大的代价。

我觉得,Bitcoin 最大的价值在于,为个人发行货币创造了一种可能性。它不见得是一个好的选择,但从此世界上就多了这么一种可能性。

很多发明都是如此,在当时都只是多了一种可能性罢了。

五年前,如果你想自己发行货币,岂不是天方夜谭?

其实也就是五年多之前,腾讯发行的 Q 币,仅仅是作为一种代金券的存在,就遇到了政策方面不少的阻力,要不是腾讯财大气粗,早就夭折了。

而今天,央行又能对 Bitcoin 说什么呢,前两天央行发过一个《关于防范比特币风险的通知》,看似是在打击 Bitcoin, 但细一品,全是废话,更多的是对 Bitcoin 的无奈。

总之,我觉得这是一种进步。

发明 Bitcoin 并不是一件很困难的事情,它只是将业界早就出现的一些技术组合到了一切,事实上两年前我在构思一个类似的东西,所以我才关注到 Bitcoin 的,Bitcoin 的技术细节无外乎:

要知道这三个难点都是在「无中心」的情况下实现的,这在外行人看来是很难理解的,但如果你对密码学稍微有点研究的话,你会发现至少从技术层面来说,Bitcoin 是无懈可击的。

同时 Bitcoin 也给了其他众多 P2P 网络以启发,虽然 Bitcoin 不是第一个,但它让人们看到了希望:原来有那么多东西都可以以去中心化的方式实现。

随着 Bitcoin 的发展,更多的电子货币纷纷出现,也让我们重新思考了什么是货币,我对经济学毫无研究,但我也有些思考。

我觉得货币本身就不应该具有价值,货币必须是抽象的,这样才能用货币去衡量其他商品的价值,要求货币具有价值是很无理取闹的事情。

如果说货币是基于信用的,那你真的信任法定货币的发行者么?还不是因为货币发行的垄断性,让你不得不信任法定货币,如果有另一种足够可靠的货币呢?

比如政府可以随意地发行货币来稀释你手中货币的价值,银行也可以修改你的存款数字,撤销你的交易,的确这种可能性很小,但凡是人来控制的事情,总有变数。

而 Bitcoin 呢,至少当你发起一笔交易后,没有任何人能够修改或撤销这笔交易,这笔交易永远都是可供考证的,而且不需要第三方就可以核实这笔交易的真实性。

你很难阻止拥有权利的人去作恶,所以最好不要让少数人拥有特权,这是人类最近几千年都在做的事情。

以前我们没有这个可能性,但现在我们有了,随着计算机/互联网的普及,更多的东西会被「数字化」,包括货币。

我觉得货币的数字化是大势所趋,总有一天「数字货币」会代替传统货币,但可能不是 Bitcoin.

推荐另一篇不错的文章:http://www.zhihu.com/question/19653494/answer/17411132

12

精子生于 1995.11.25, 21 岁,英文 ID jysperm.

订阅推送

通过邮件订阅精子的博客日志、产品和项目的最新动态,精子承诺每一封邮件都会认真撰写(历史邮件),有想和精子说的话也可以直接回复邮件。

该博客使用基于  Hexo  的  simpleblock  主题。博客内容使用  CC BY-NC-SA 3.0  授权发布。最后生成于 2017-09-17.