搭建自己的以太坊区块链

一、基本概念

以太坊简单来说就是区块链与智能合约的结合,是基于solidity语言实现的。在以太坊中,智能合约也有一个帐户地址。

1.1、 EVM

  以太坊虚拟机(EVM)是以太坊中智能合约的运行环境。它不仅被沙箱封装起来,事实上它被完全隔离,运行在EVM内部的代码不能接触到网络、文件系统或者其它进程。甚至智能合约之间也只有有限的调用。

  以太坊支持两种智能合约的编程语言:Solidity 和 Serpent。Serpent 语言面临一些安全问题,现在已经不推荐使用了。Solidity 语法类似 JavaScript,它编译器 solc 可以把智能合约源码编译成以太坊虚拟机 EVM 可以执行的二进制码。
现在以太坊提供更方便的在线 IDE —— Remix https://remix.ethereum.org 使用 Remix,免去了安装 solc 和编译过程,它可以直接提供部署合约所需的二进制码和 ABI。

1.2、 Accounts

  以太坊中有两类账户,它们共用同一个地址空间。外部账户,该类账户被公钥-私钥对控制。合约账户,该类账户被存储在账户中的代码控制。 外部账户的地址是由公钥决定的,合约账户的地址是在创建合约时确定的

  每个账户都有一个以太币余额(单位是“Wei”),该账户余额可以通过向它发送带有以太币的交易来改变。

1.3、 Transactions

  每一笔交易都是一条信息,可以通过交易,将余额从一个帐户发至另一个帐户。

1.4、 Gas

  每一笔交易需要支付一定的gas。gas price是由创建者设置的,调用合约的发送账户需要交易费用 = gas price * gas amount。

1.5、 以太坊客户端

  以太坊客户端用于接入以太坊网络,进行账户管理、交易、挖矿、智能合约相关的操作。目前有多种语言实现的客户端,常用的有 Go 语言实现的 go-ethereum 客户端 Geth,支持接入以太坊网络并成为一个完整节点,也可作为一个 HTTP-RPC 服务器对外提供 JSON-RPC 接口。
  

二、搭建go-ethereum

两种安装方式:

  • ppa安装
  • 源码编译

2.1、ppa安装

2.2、源码编译

编译安装时 make geth 过程中可能会报错,可以通过下面方案进行修复

三、安装 Solidity 编译器

Solidity 编译器也有多种方法安装,参照 http://solidity.readthedocs.io/en/latest/installing-solidity.html 这里介绍最简单快捷的安装方式:PPA 直接安装。

3.1、ppa安装

四、搭建以太坊

4.1、创世区块

要运行以太坊私有链,需要定义自己的创世区块,创世区块信息写在一个 JSON 格式的配置文件中。首先将下面的内容保存到一个 JSON 文件中,例如 genesis.json

配置简单说明:

chainId指定了独立的区块链网络 ID。网络 ID 在连接到其他节点的时候会用到,以太坊公网的网络 ID 是 1,为了不与公有链网络冲突,运行私有链节点的时候要指定自己的网络 ID。不同 ID 网络的节点无法相互连接。

difficulty挖矿难度。

gasLimit区块 Gas 消耗限制。

4.2、初始化创世区块

目录结构

其中 geth/chaindata 中存放的是区块数据,keystore 中存放的是账户数据。

4.3、 启动以太坊区块链,并进入控制台模式

上面命令的主体是 geth console,表示启动节点并进入交互式控制台。
各选项含义如下:

  • datadir:指定区块链数据的存储位置;
  • identity:指定节点 ID
  • rpc:表示开启 HTTP-RPC 服务;
  • rpcport:指定 HTTP-RPC 服务监听端口号(默认为 8545);
  • port:指定和其他节点连接所用的端口号(默认为 30303);
  • nodiscover:关闭节点发现机制,防止加入有同样初始配置的陌生节点。

geth console进入一个交互式的 JavaScript 执行环境,在这里面可以执行 JavaScript 代码,其中 > 是命令提示符。在这个环境里也内置了一些用来操作以太坊的 JavaScript 对象,可以直接使用这些对象。这些对象主要包括:

  • eth:包含一些跟操作区块链相关的方法;
  • net:包含一些查看p2p网络状态的方法;
  • admin:包含一些与管理节点相关的方法;
  • miner:包含启动&停止挖矿的一些方法;
  • personal:主要包含一些管理账户的方法;
  • txpool:包含一些查看交易内存池的方法;
  • web3:包含了以上对象,还包含一些单位换算的方法。
4.3.1、创建账户

1)直接输入密码创建账户

2)交互式创建账户

4.3.2、查看账户余额

4.3.3、挖矿

开始挖矿

其中 start 的参数表示挖矿使用的线程数。第一次启动挖矿会先生成挖矿所需的 DAG 文件,这个过程有点慢,等进度达到 100% 后,就会开始挖矿,此时屏幕会被挖矿信息刷屏。

停止挖矿

4.3.4、交易

第一个账户已经有余额了了。第二个账户没有币。

账户解锁,只有先对账户解锁才能转账。

进行交易

此时如果没有挖矿,用 txpool.status 命令可以看到本地交易池中有一个待确认的交易,可以使用 eth.getBlock("pending", true).transactions 查看当前待确认交易。

进行挖矿交易。

新区块挖出后,挖矿结束,查看账户 1 的余额,已经收到了账户 0 的以太币

4.3.5、 查看交易与区块

1) 查看总区块数

2) 查看交易

3)通过区块号查看区块:

五、以太坊区块链网络

我们通过单机的不同端口来模拟不同的网络。

首先需要使用相同的创世区块来初始化一个链出来。

然后在chian2中创建一个账户

使用2个终端分别打开着2个链

目前启动了三个节点,但都默认关闭了发现功能,需要手动添加peer节点。

在每个终端输入admin.nodeInfo.enode, 把输出记录下来到static-nodes.json文件,我这里情况如下:

两个终端退出后重启。输入admin.peers可以查看节点信息

开始挖矿

chain2 链启动后会出现警告同步数据失败,等待的一段时间后就可以了。

增加节点还是使用admin.addPeer来处理。

addPeer() 的参数就是节点二的 enode 信息,注意要把 enode 中的 [::] 替换成节点二的 IP 地址。连接成功后,节点二就会开始同步节点一的区块,同步完成后,任意一个节点开始挖矿,另一个节点会自动同步区块,向任意一个节点发送交易,另一个节点也会收到该笔交易。

通过 admin.peers 可以查看连接到的其他节点信息,通过 net.peerCount 可以查看已连接到的节点数量。

除了上面的方法,也可以在启动节点的时候指定 --bootnodes 选项连接到其他节点。

参考链接:

  • https://mshk.top/2017/11/go-ethereum-1-7-2/
  • https://g2ex.github.io/2017/09/12/ethereum-guidance/
  • https://github.com/xiaoping378/blog/blob/master/posts/%E4%BB%A5%E5%A4%AA%E5%9D%8A-%E7%A7%81%E6%9C%89%E9%93%BE%E6%90%AD%E5%BB%BA%E5%88%9D%E6%AD%A5%E5%AE%9E%E8%B7%B5.md

windows环境管理多版本jvm

1、下载jvms

解压后将jvms.exe放入c:\windows\下。

2、使用jvms

基础用法
  • jvms ls 查看受jvms管理的jdk
  • jvms ls-remote 查看jvms源里提供下载的jdk版本
  • jvms install 从jvms源里安装jdk
  • jvms uninstall 删除受jvms管理的jdk
  • jvms use 使用指定版本的jdk
  • jvms version 可看jvms的版本
安装jvm

1)查看有哪些发行版本

2)选择自己需要的版本进行安装

3) 初始化环境,设置环境变量

4)切换版本

golang new 和 make 区别

new(T) 返回的是 T 的指针

new(T) 为一个 T 类型新值分配空间并将此空间初始化为 T 的零值,返回的是新值的地址,也就是 T 类型的指针 *T,该指针指向 T 的新分配的零值。

上面的代码是等价的,new(int) 将分配的空间初始化为 int 的零值,也就是 0,并返回 int 的指针,这和直接声明指针并初始化的效果是相同的。

make 只能用于 slice,map,channel

make 只能用于 slice,map,channel 三种类型,make(T, args) 返回的是初始化之后的 T 类型的值,这个新值并不是 T 类型的零值,也不是指针 *T,是经过初始化之后的 T 的引用。

slice 的零值是 nil,使用 make 之后 slice 是一个初始化的 slice,即 slice 的长度、容量、底层指向的 array 都被 make 完成初始化,此时 slice 内容被类型 int 的零值填充,形式是 [0 0 0],map 和 channel 也是类似的。

make(T, args) 返回的是 T 的 引用

如果不特殊声明,go 的函数默认都是按值穿参,即通过函数传递的参数是值的副本,在函数内部对值修改不影响值的本身,但是 make(T, args) 返回的值通过函数传递参数之后可以直接修改,即 map,slice,channel 通过函数穿参之后在函数内部修改将影响函数外部的值。

这说明 make(T, args) 返回的是引用类型,在函数内部可以直接更改原始值,对 map 和 channel 也是如此。

很少需要使用 new

以下代码演示了 struct 初始化的过程,可以说明不使用 new 一样可以完成 struct 的初始化工作。

foo1 和 foo2 是同样的类型,都是 Foo 类型的值,foo1 是通过 var 声明,Foo 的 filed 自动初始化为每个类型的零值,foo2 是通过字面量的完成初始化。foo3,foo4 和 foo5 是一样的类型,都是 Foo 的指针 Foo。*但是所有 foo 都可以直接使用 Foo 的 filed,读取或修改,为什么?

如果 x 是可寻址的,&x 的 filed 集合包含 m,x.m 和 (&x).m 是等同的,go 自动做转换,也就是 foo1.age 和 foo3.age 调用是等价的,go 在下面自动做了转换。

因而可以直接使用 struct literal 的方式创建对象,能达到和 new 创建是一样的情况而不需要使用 new。

小结

new(T) 返回 T 的指针 *T 并指向 T 的零值。

make(T) 返回的初始化的 T,只能用于 slice,map,channel。

转载自: http://sanyuesha.com/2017/07/26/go-make-and-new/

golang标准库 —— context包

一、背景描述

Golang中我们可以使用sync.WaitGroup进行并发控制。如下,

wg.Wait()会等待2个goroutine完成后继续执行。

在实际的业务中,我们可能会有这么一种场景:需要我们主动的通知某一个goroutine结束。比如我们开启一个后台goroutine一直做事情,比如监控,如果主程序,就需要通知这个监控goroutine结束,不然它会一直跑,就泄漏了。

chan通知

golang一直推崇着不要通过共享内存来通信,而应该通过通信来共享内存,我们可以使用select + chan的方式来实现。

当主程序完成后,向stop chan中推送消息,goroutine接收到消息后推出监控。

context

Context包是Google内部专门用来简化 对于处理单个请求的多个 goroutine 之间与请求域的数据、取消信号、截止时间等相关操作,这些操作可能涉及多个 API 调用。在Golang1.7版本之后开源出来了。

1.6及之前版本可以参考golang.org/x/net/context

我们可以使用context包的功能里替换select + chan的模式。但是其本质还是select + chan。如下,

  • context.Background() 获取一个空的context
  • ctx, cancel := context.WithCancel(context.Background())扩展一个带有取消功能的coontext,同时得到的cancel是以后取消的方法。当需要执行取消操作的时候,cancel()执行一下就可以了。
  • ctx.Done()获取一个chan。当chan里面可以获取到东西的时候,就表示该context已经被取消了。

context也是线程安全的,所以也支持传入多个goroutine

二、源码剖析

核心接口

emptyCtx

Context接口并不需要我们自己来实现,因为context包内已经帮我们实现了一个emptyCtx

emptyCtx是一个不可取消,没有设置截止时间,没有携带任何值的Context

background & todo

context提供了2个方法来获取emptyCtxbackgroudtodo本质上没有区别,一般通过要干什么事情来区分调用哪一个。

  • background,主要用于main函数、初始化以及测试代码中
  • todo,当你不清楚需要使用那个时,就用它

WithCancel、 WithDeadline、 WithTimeout、 WithValue

当有了emptyCtx以后,我们可以使用golang为我们提供的4个扩展方法,对emptyCtx进行扩展。如下,

1、WithCancel

WithCancel函数,传递一个父Context作为参数,返回cancelCtx,以及一个CancelFunc用来取消Context

使用方法正如上面的监控案例。

2、WithDeadline、WithTimeout

WithDeadline它会多传递一个截止时间参数,意味着到了这个时间点,会自动取消Context,当然我们也可以不等到这个时候,可以提前通过取消函数进行取消。

WithTimeout底层调用的WithDeadline 区别就是一个传入的具体是简单,一个传入的是偶多少时间之后。

上面案例,当时间到了之后,就自动停止了。当我么你再去调用cancel方法时,会自动忽略。

3、WithValue

通过Context我们也可以传递一些必须的元数据,这些数据会附加在Context上以供使用。

取值时,key必须对应上,否则返回nil

JSON Web Token

一、简介

JSON Web Token 简称 JWT,是token认证的一种。它是一个开放标准,这个开放标准允许我们使用JWT在用户和服务器之间传递安全可靠的信息。

二、JWT的组成

JWT由三部分组成:
– header(头)
– payload(载体)
– signature(签名)

通常如下所示

xxxxxxx.yyyyyyy.zzzz

2.1、header

header包含2部分信息:令牌的信息,即JWT,以及签名所用的算法,例HMAC SHA256RSA

2.2、payload

payload也称为 JWT Claims,这里放置的是我们需要传输的信息,有多个项目如注册的claim名称,公共claim名称和私有claim名称。

注册claim名称有下面几个部分:

  • iss: token的发行者
  • sub: token的题目
  • aud: token的客户
  • exp: 经常使用的,以数字时间定义失效期,也就是当前时间以后的某个时间本token失效。
  • nbf: 定义在此时间之前,JWT不会接受处理。
  • iat: JWT发布时间,能用于决定JWT年龄
  • jti: JWT唯一标识. 能用于防止 JWT重复使用,一次只用一个token

公共claim名称用于定义我们自己创造的信息,比如用户信息和其他重要信息。

私有claim名称用于发布者和消费者都同意以私有的方式使用claim名称。

下面是JWT的一个案例:

2.3、signature

通过上面两部分信息我们可以得到一个字符串

然后指定的加密算法加上密钥以后可以得到我们的签名。

最后我们将签名凭借到最后我们就可以得到我们需要的JWT

三、适用场景

3.1、信息会暴露

在JWT中,不应该在载荷里面加入任何敏感的数据。像密码这样的内容就不能被放在JWT中了。如果将用户的密码放在了JWT中,那么怀有恶意的第三方通过Base64解码就能很快地知道你的密码了。

3.2、会话凭证

当用户登录成功后,我们将JWT返回给客户端。之后客户端每次请求将JWT传给后端,后端通过解码就可以得到用户的相关信息。

3.3、最适合的应用场景[开票]或者[签字]

1)邮箱确认。用户注册成功后,发送邮件到注册邮箱。然后用户点击链接跳转验证服务器,确认邮箱有效。

2)好友关注。在A用户关注了B用户的时候,系统发信息给B用户,并且附有一个链接“点此关注A用户”。B点击连接后添加关注A。

有上面2个例子,我们会发现一些相似性,如下:

web认证机制

1、 HTTP Basic Authentication认证

Basic 认证是HTTP 中非常简单的认证方式,因为简单,所以不是很安全。

客户端向服务端请求时,如果没有进行认证,会返回401状态码,要求客户端输入用户名密码。

当用户输入完用户名和密码后,用户名和密码会经过BASE64加密附加到请求信息中再次请求HTTP服务器,HTTP服务器会根据请求头携带的认证信息,决定是否认证成功及做出相应的响应。

浏览器访问test.api.com/1.html,请求会pedding住,等待输入用户密码。成功后可以访问内容。

request头信息中可以看到Authorization: Basic cGFuZ291OjEyMzQ1Ng==base64解码后就是pangou:123456。、

参考:https://segmentfault.com/a/1190000004694935

2、Digest Authentication (摘要式身份认证)

摘要式身份认证解决了基础认证的明文传输问题。

实现方案主要通过服务端返回nonce,然后客户端混淆用户名密码进行加密。

服务端可以进行配置nonce的过期时间。

auth_digest模块需要额外安装。

参考:https://www.nginx.com/resources/wiki/modules/auth_digest/

3、OAuth2

oauth2其实是一个授权系统。不过他可以通过扩展openid connect进行认证。

oauth2主要有一下几种流程:

  • 授权码模式(authorization code)最为严谨,用于常见的第三方应用。
  • 简化模式(implicit)常用于单页应用
  • 密码模式(resource owner password credentials)因为需要用户提供账号密码敏感信息,通常用于客户端与服务端(授权系统)为同一公司的情况下。
  • 客户端模式(client credentials)可用于企业内部服务之间的授权。比如A服务是否有权限访问B服务

参考:https://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html

4、Cookie Auth

即传统cookie-session模式

5、Token Auth

Token Auth的优点

  • 支持跨域访问: Cookie是不允许垮域访问的,这一点对Token机制是不存在的,前提是传输的用户认证信息通过HTTP头传输.
  • 无状态:Token机制在服务端不需要存储session信息,因为Token 自身包含了所有登录用户的信息,只需要在客户端的cookie或本地介质存储状态信息.
  • 更适用CDN: 可以通过内容分发网络请求你服务端的所有资料(如:javascript,HTML,图片等),而你的服务端只要提供API即可.
  • 去耦: 不需要绑定到一个特定的身份验证方案。Token可以在任何地方生成,只要在你的API被调用的时候,你可以进行Token生成调用即可.
  • 更适用于移动应用: 当你的客户端是一个原生平台(iOS, Android,Windows 8等)时,Cookie是不被支持的(你需要通过Cookie容器进行处理),这时采用Token认证机制就会简单得多。
  • CSRF:因为不再依赖于Cookie,所以你就不需要考虑对CSRF(跨站请求伪造)的防范。
  • 性能: 一次网络往返时间(通过数据库查询session信息)总比做一次HMACSHA256计算 的Token验证和解析要费时得多.
  • 不需要为登录页面做特殊处理: 如果你使用Protractor 做功能测试的时候,不再需要为登录页面做特殊处理.
  • 基于标准化:你的API可以采用标准化的 JSON Web Token (JWT). 这个标准已经存在多个后端库(.NET, Ruby, Java,Python, PHP)和多家公司的支持(如:Firebase,Google, Microsoft).

参考:https://juejin.im/entry/593522150ce463005728585a

yaml语法

一、简介

基本规则:

  • 大小写敏感
  • 使用缩进表示层级关系
  • 缩进时不允许使用Tab键,只允许使用空格。
  • 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
  • # 表示注释,从这个字符一直到行尾,都会被解析器忽略。

AML 支持的数据结构有三种:

  • 对象:键值对的集合,又称为映射(mapping)/ 哈希(hashes) / 字典(dictionary)
  • 数组:一组按次序排列的值,又称为序列(sequence) / 列表(list)
  • 纯量(scalars):单个的、不可再分的值

二、对象

对象的一组键值对,使用冒号结构表示。

转为 JavaScript 如下。

Yaml 也允许另一种写法,将所有键值对写成一个行内对象。

转为 JavaScript 如下。

三、数组

一组连词线开头的行,构成一个数组。

转为 JavaScript 如下。

数据结构的子成员是一个数组,则可以在该项下面缩进一个空格。

转为 JavaScript 如下。

数组也可以采用行内表示法。

转为 JavaScript 如下。

四、复合结构

对象和数组可以结合使用,形成复合结构。

转为 JavaScript 如下。

五、纯量

纯量是最基本的、不可再分的值。以下数据类型都属于 JavaScript 的纯量。

  • 字符串
  • 布尔值
  • 整数
  • 浮点数
  • Null
  • 时间
  • 日期
  • 数值直接以字面量的形式表示。

转为 JavaScript 如下。

布尔值用true和false表示。

转为 JavaScript 如下。

null用~表示。

转为 JavaScript 如下。

时间采用 ISO8601 格式。

转为 JavaScript 如下。

日期采用复合 iso8601 格式的年、月、日表示。

转为 JavaScript 如下。

YAML 允许使用两个感叹号,强制转换数据类型。

转为 JavaScript 如下。

六、字符串

字符串是最常见,也是最复杂的一种数据类型。

字符串默认不使用引号表示。

转为 JavaScript 如下。

如果字符串之中包含空格或特殊字符,需要放在引号之中。

转为 JavaScript 如下。

单引号和双引号都可以使用,双引号不会对特殊字符转义。

转为 JavaScript 如下。

单引号之中如果还有单引号,必须连续使用两个单引号转义。

转为 JavaScript 如下。

字符串可以写成多行,从第二行开始,必须有一个单空格缩进。换行符会被转为空格。

转为 JavaScript 如下。

多行字符串可以使用|保留换行符,也可以使用>折叠换行。

转为 JavaScript 代码如下。

+表示保留文字块末尾的换行,-表示删除字符串末尾的换行。

转为 JavaScript 代码如下。

字符串之中可以插入 HTML 标记。

转为 JavaScript 如下。

七、引用

锚点&和别名*,可以用来引用。

等同于下面的代码。

&用来建立锚点(defaults),<<表示合并到当前数据,*用来引用锚点。

下面是另一个例子。

转为 JavaScript 代码如下。

八、函数和正则表达式的转换

这是 JS-YAML 库特有的功能,可以把函数和正则表达式转为字符串。

解析上面的 yml 文件的代码如下。

从 JavaScript 对象还原到 yaml 文件的代码如下。

转载:http://www.ruanyifeng.com/blog/2016/07/yaml.html