Akawa

ETY001的博客

前言

ETH的多签是依靠智能合约实现的,也就是说持币的是智能合约,通过在智能合约里设置条件,来使转账之类的权限通过多人签名来集体控制。

从网上找到了一段简单的多签智能合约,代码如下:

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
pragma solidity ^0.4.21;

contract MultiSigWallet{
address private owner;
mapping (address => uint8) private managers;

modifier isOwner{
require(owner == msg.sender);
_;
}

modifier isManager{
require(
msg.sender == owner || managers[msg.sender] == 1);
_;
}

uint constant MIN_SIGNATURES = 3;
uint private transactionIdx;

struct Transaction {
address from;
address to;
uint amount;
uint8 signatureCount;
mapping (address => uint8) signatures;
}

mapping (uint => Transaction) private transactions;
uint[] private pendingTransactions;

function MultiSigWallet() public{
owner = msg.sender;
}

event DepositFunds(address from, uint amount);
event TransferFunds(address to, uint amount);
event TransactionCreated(
address from,
address to,
uint amount,
uint transactionId
);

function addManager(address manager) public isOwner{
managers[manager] = 1;
}

function removeManager(address manager) public isOwner{
managers[manager] = 0;
}

function () public payable{
emit DepositFunds(msg.sender, msg.value);
}

function withdraw(uint amount) isManager public{
transferTo(msg.sender, amount);
}
function transferTo(address to, uint amount) isManager public{
require(address(this).balance >= amount);
uint transactionId = transactionIdx++;

Transaction memory transaction;
transaction.from = msg.sender;
transaction.to = to;
transaction.amount = amount;
transaction.signatureCount = 0;
transactions[transactionId] = transaction;
pendingTransactions.push(transactionId);
emit TransactionCreated(msg.sender, to, amount, transactionId);
}

function getPendingTransactions() public isManager view returns(uint[]){
return pendingTransactions;
}

function signTransaction(uint transactionId) public isManager{
Transaction storage transaction = transactions[transactionId];
require(0x0 != transaction.from);
require(msg.sender != transaction.from);
require(transaction.signatures[msg.sender]!=1);
transaction.signatures[msg.sender] = 1;
transaction.signatureCount++;

if(transaction.signatureCount >= MIN_SIGNATURES){
require(address(this).balance >= transaction.amount);
transaction.to.transfer(transaction.amount);
emit TransferFunds(transaction.to, transaction.amount);
deleteTransactions(transactionId);
}
}

function deleteTransactions(uint transacionId) public isManager{
uint8 replace = 0;
for(uint i = 0; i< pendingTransactions.length; i++){
if(1==replace){
pendingTransactions[i-1] = pendingTransactions[i];
}else if(transacionId == pendingTransactions[i]){
replace = 1;
}
}
delete pendingTransactions[pendingTransactions.length - 1];
pendingTransactions.length--;
delete transactions[transacionId];
}

function walletBalance() public isManager view returns(uint){
return address(this).balance;
}
}

部署合约

我本地的测试网络是使用 Ganache 搭建的,如下图

我修改了我的 Ganache 的端口为 8545

然后打开 Remix,在右边栏选择 Run,在 Environment 中选择 Web3 Provider,如下图

在弹出的层中,输入本地地址和刚才配置的8545端口,就可以连接到 Ganache 的环境了,如下图

连接成功后,会看到在 Environment 下面的 Account 栏中会列出所有 Ganache 中的测试账号。

我们选择第一个账号作为部署合约的主账号,即合约的 owner

在编辑器中创建一个新的合约文件,把上面的合约代码复制进编辑器,右边栏选择 Compile
Select new compiler version 中选择 0.4.21 版本的编译器,等待编译完成。
完成后,最下面出现绿色的提示信息,如下图

再切换回 Run 标签,点击 Deploy 就可以部署了。
部署成功后,在 Deployed Contracts 中就能看到新部署的合约了,下拉打开可以看到合约的各个接口,如图

增加合约管理员

在右边栏找到合约的 addManager 接口,下拉打开输入一个以太钱包地址,然后点击 transact 即完成添加。
这里我把 Ganache 的第 2 到 4 的地址加入进管理员中。

给合约内转账

切换回 Ganache,在 Transactions 中找到我们刚才部署的合约地址,0x08ea5409922D4204C0E03d17243E77c3810C0CCe

使用任意的其他账号转账任意个 ETH 进合约里。
我用的是 MyEtherWallet进行的转账,只需要把节点设置成本地网络,
然后用私钥模式登陆转账即可,这里就略过不说了。

转完账后,在 RemixRun 标签下,找到合约的 walletBalance 接口,
点击调用即可显示当前合约的余额,注意单位!

多签从合约内转出金额

回到 Remix,在右边栏的 Run 标签下,先确认 Account 选择的是合约的 owner
这个例子里就是第一个测试地址了。

在合约中找到 transferTo,下拉打开,输入一个本地测试网络中的以太钱包地址,输入待转账的金额,点击 transact。这里注意单位,下图为转 5 ETH,

成功执行后,就会有一个等待多签的 transactionpendingTransactions 这个数组里。
点击 getPendingTransactions 可以得到当前等待多签的 transaction 在数组中的索引值。
下图则代表当前等待多签的 transaction 的索引值是 0 。

这时,在 Run 标签页的顶部,把 Account 依次切换为其他几个管理员,分别执行合约的 signTransaction 接口即可完成多签,其中 transactionId 就是上一步中的索引值,如下图

目前这个合约代码中,是当签名管理员的数量大于等于3个,就正式完成多签并发送交易。
所以当第三个管理执行完 signTransaction 后,查一下合约的余额就发现已经转出去 5ETH了。

总结

目前这个例子中的合约代码还是比较简单的,没有各个用户的权重关系。这个就可以根据自己的需要来写合约了。

Over!

这是这个系列的第二篇,主要讲一下各种设备接入配置。先放一张目前我已经配置好的管理界面图片

再放一张配置好的 HomeKit 截图

开始这篇之前,首先你要确保你已经安装了上一篇中提到的 SSH & Web Terminal 插件。

如何修改配置文件

如果已经安装好了 SSH & Web Terminal 插件并启动了,那么直接在 Terminal 中执行下面的命令就可以连接到树莓派上了。

1
ssh hassio@hassio.local

连接后 ,用 ls 查看目录

各个目录的作用如下:

  • addons 存储自定义插件
  • backup 备份目录
  • config 配置文件目录已经自定义组件
  • share 共享目录
  • ssl 证书目录

我们配置的时候,主要就是修改 config 目录下的 configuration.yaml 文件。每次修改保存后,要先在后台的“配置”=>“通用”中“检查配置”,配置检查通过后,再重载配置或者重启服务(有些配置的修改需要重启服务才能生效)。

接入小米多功能网关

打开 config/configuration.yaml,加入下面的配置(根据你的情况选择是多网关还是单网关配置):

1
2
3
4
5
6
7
8
9
10
11
12
# 使用单网关前提下,可不填 mac, mac 不带冒号
xiaomi_aqara:
gateways:
- mac:
key: xxxxxxxxxxxxxxxx
# 多个网关必须填入 mac,mac 不带冒号
xiaomi_aqara:
gateways:
- mac: xxxxxxxxxxxx
key: xxxxxxxxxxxxxxxx
- mac: xxxxxxxxxxxx
key: xxxxxxxxxxxxxxxx

其中 key 的获取,需要打开米家App,打开网关页面,点击右上角的三个点,打开菜单后,选择“关于”,之后“玩法教程”下面的空白处点击5次,就会出现新的列表选项,点击“局域网通信协议”,里面的密码就是 key 了,另外要把最上面的那个“局域网通信协议”的开关打开。

保存重启,接入网关成功后,通过网关接入的各种 ZigBee 设备都会被 HomeAssistant 识别出来。
目前的支持情况参见这里:https://home-assistant.cc/component/xiaomi/zigbee/

接入小米Wifi设备

插座类的配置如下:

1
2
3
4
5
switch:
- platform: xiaomi_miio
name: Original Xiaomi Mi Smart WiFi Socket
host: 192.168.130.59
token: YOUR_TOKEN

其中 name 可以自定义,host 是插座的IP地址,token则是通信用的凭证。

小米Wifi设备由于各个生产厂商的原因,并不像智能网关那么人性给提供设备的 token。获取 wifi 设备的 token 需要折腾一下。网上给出了几种方案,我使用的是用 Root 过的安卓手机。在手机上安装 5.0.19 版的米家(这是最后一个版本支持通过本地sqlite数据库查询 token 的版本),登陆米家,然后手机上打开文件管理器(比如ES文件管理器),把 /data/data/com.xiaomi.smarthome/databases/miio2.db 复制到电脑可访问的目录(比如SD卡),然后通过USB 把文件从手机转移到电脑上,用 sqlite 软件查询 devicerecord 表,其中有个 token 字段,那个就是了。

其他的一些Wifi设备参考这里:https://home-assistant.cc/component/xiaomi/wifi/

接入苹果HomeKit

配置这个的好处是,可以使用 Siri 来控制你的家庭智能场景和设备。

增加如下配置:

1
homekit:

保存重启服务后,会在主面板看到一个PIN码,这时候打开iPhone,找到 Home ,“添加配件”,“没有代码或无法扫描”,“输入代码”,把刚才的PIN码输入进去即可。

其他配置可参考这里:https://home-assistant.cc/homekit/builtin/

接入路由器

我的是网件路由刷的华硕的固件,因此找到华硕的配置如下:

1
2
3
4
5
6
7
8
asuswrt:
host: 路由器IP
username: 路由器账号
password: 路由器密码
protocol: 协议 ssh 或者 telnet
port: 端口,默认 22,telnet 模式为 23
mode: 模式,默认 router,可选 ap
ssh_key: ssh 秘钥路径,可选

其他路由参考:https://home-assistant.cc/component/router/

自定义组件

有时候,有些设备可能自带的组件并不支持,那么需要去搜索安装下第三方组件,比如小米直流电风扇,就需要去下载 https://github.com/syssi/xiaomi_fanconfig 目录下面,

然后按照第三方插件给的说明增加配置即可。

位置和天气设置

查询下经纬度,可以把经纬度设置好,之后就能显示当地的日落日出时间了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
homeassistant:
# 名称,可为中文
name: 家
# 纬度
latitude: !secret latitude
# 经度
longitude: !secret longitude
# 海拔
elevation: 11
# 度量单位,这里选择“米”
unit_system: metric
# 时区
time_zone: Asia/Shanghai
# 设备个性化
customize:

我使用的是yahoo天气的数据,配置如下:

1
2
3
4
weather:
- platform: ywether
woeid: 2168257
name: Zibo

其中 woeid 需要自己去 https://www.yahoo.com/news/weather/ 查询城市天气,URL中的id即为 woeid

个性化设置

个性化设置在 “配置” -> “自定义” 里面,在这里可以修改每个接入设备的名字等参数,并且可以设置是否隐藏(这个还是挺有用的,尤其是接入了路由器的情况下,会有很多的 device_tracker 类的设备,都显示在面板上显然是很乱的)。

一些可以参考的参数:https://home-assistant.cc/system/customize/

智能语言

配置这个的目的是为了能通过语音合成技术进行结果输出(播报)。

直接参考这里即可: https://home-assistant.cc/component/nlp/

结语

基本的配置就这些,目前家里所有的智能设备都加入到了 Home Assistant 的管理中了,之后就是配置各种智能逻辑了,等我试验过后,会在下一篇写一下。

Over!

本来今天应该是写(二)的,但是昨晚配置的过程中发现了很多问题,比如声音、蓝牙之类的配置,以及最关键的是发现原来 Hass.io 已经不被官方推荐了,现在官方推荐他们自己做的 HassOS,WTF,这都是啥跟啥?!所以今天就是把昨天写的(一)推翻掉,重新写。

先来说说刚才提到的 Hass.ioHassOS

按照目前我的理解,这个项目的演化历程是这样的,最早的 Home Assistant 是一堆脚本构成的,系统是基于 Respbian 的,所以起名叫做 Hassbian,之后基于 ResinOS(一个面向嵌入式系统的docker系统平台),把整个的核心代码封装进了两个镜像中,一个是 **homeassistant/raspberrypi3-homeassistant**,一个是 **homeassistant/armhf-hassio-supervisor**,其余的插件都各自独立成为一个镜像,这样整个系统运行起来就是一个 ResinOS 加上一堆容器,对于整体的稳定性和可维护性提升了不少,这也就是 Hass.io。再之后不知道什么原因,又基于 buildroot 系统(另外一个面向嵌入式的系统平台)做了 HassOS,这款比起上一款来说,感觉加入了一些新的驱动,比如声卡和蓝牙,另外就是宿主机做的更加的封闭了,可能是处于安全考虑,也可能是处于以后的商业考虑。

由于不想覆盖掉昨天的那篇,所以开始这篇作为第一篇,然后开始我们的重做系统之旅吧。。。。。

目前 HassOS 的稳定版本是 1.13,最新版本是 2.3,这个在 HassOS 项目的 Release 页面可以看到,https://github.com/home-assistant/hassos/releases 。我选择了 2.3 这个版本,由于我是树莓派3B,所以下载了 hassos_rpi3-2.3.img.gz 这个包(这里之所以不下载64位的是因为树莓派官方推出64位的系统时间也不长,稳定性有待观察)。

下载后解压得到 img 文件,依旧用 etcher 来写入到 TF 卡中。

这里需要注意,如果你是 Mac 电脑,那么写完后,会提示 TF 卡无法识别,你直接推出就好。这是因为 HassOS 使用的 GPT 分区格式,而 Mac 默认不支持这个格式。

那么这里就有意思了,往常都是 boot 分区是 FAT 格式,然后挂载,放入各种位置文件,比如wifi联网的配置,然后把卡插回树莓派,启动就能用了,但现在 TF 卡的 boot 分区无法挂载,这咋整?

官方给出了方案:https://github.com/home-assistant/hassos/blob/dev/Documentation/configuration.md ,思路就是再找一个U盘,格式化成 FAT32 / EXT4 / NTFS 任意格式,格式化的时候把U盘命名为 CONFIG(全大写)。

之后需要加入的配置文件,按照文档要求的格式放在U盘根目录下即可。启动树莓派的时候,先把U盘插好,系统会自动读取U盘的配置文件并生效。

另外注意就是配置network的时候,文件名必须是my-network。

除了网络配置以外,timesyncd.conf也要配置,否则系统启动起来时间不对,然后更新就会卡住,因为更新访问的是 HTTPS 加密链接,时间不对,签名就有问题,进而就无法访问更新镜像的链接(下面的截图就是这个坑了)。timesyncd.conf中的NTP服务器网上找下亚洲区的就好。

另外建议把 authorized_keys 也配置了吧,这样可以远程直接SSH到宿主机。authorized_keys 文件里的内容其实就是你电脑 ~/.ssh/id_rsa.pub 文件的内容,如果没有这个目录,就执行下 ssh-keygen 生成下。

还有就是一定记住,初始化结束后,要在UI界面上把U盘的配置再导入一次,这个之后会再提到,切记切记啊!!!

以上满满的都是坑!

所有工作做完,启动树莓派,来看看显示器最后输出的内容,默认登陆用户是root,不需要密码。

恩,你没有看错,登陆后是官方实现的一个 cli,用于便捷管理各种服务,屏蔽掉操作系统的存在。虽然现在允许你使用 login 命令进入宿主机,但是我觉得早晚会屏蔽掉这个命令,做的跟路由器似的。

这时候,你执行 login 进入宿主机,然后执行 docker ps,看看是不是一个 supervisor 的容器,一个 homeassistant/raspberrypi3-homeassistant 生成的容器。

如果没问题,那么在浏览器里访问 http://hassio.local:8123 就能看到创建新用户的界面了(如果你路由器不支持mDNS,那么就只能用 http://树莓派IP:8123 的形式访问了。

登陆之后,就能看到主界面了。去左边栏 Hass.io 中,选择 ADD-ON STORE,然后找到 SSH & Web Terminal,点进去安装,如下图

这个插件其实是让你 SSH 登陆进一个容器,然后把一些配置文件从宿主机映射进容器,这样相对安全一些。

安装完,在下面的配置中,把你的登陆用户名和密码加入进去,另外配置下 Web Terminal 的证书,证书是放在 /root/ssl 目录下的。你可以先关闭 Web Terminal,启动 SSH 成功后,用 SCP 把证书传上去,或者去插件中心安装 Samba Share 插件,用共享的方法传上去。

最关键的一步来了,在 Hass.io 中,选择 SYSTEM ,点击 IMPORT FROM USB 完成配置写入。

OK,这一篇就先到这里,下一篇真的要讲讲其他的配置了。

最近想要在树莓派上折腾下 Home Assistant。

Home Assistant 于 2017年7月26日发布的 Hass.io 集成系统,全可视化安装配置,基于 Docker 和 ResinOS。

Docker 的引入使得 Hass.io 管理功能插件就像你在手机上安装 App 一样简单(事实上 iOS 的底层确实采用了类似机制),再不用通过命令行和代码来管理你的 Home Assistant。同时,通过 Docker 来封装插件,使得插件的稳定性得到了极大提高,用户能够把精力集中在个性化定制 Home Assistant 及自动化上来。

可以预见 Hass.io 是 Home Assistant 的发展方向,如果说它有什么缺点的话,那么也在于它的封闭性上。

下载针对树莓派3的 hass.io 镜像

https://github.com/home-assistant/hassio-build/releases/

使用 etcher 写入镜像到 TF 卡

配置Wifi

如果树莓派采用 WiFi 连接,在烧录完成后使用文本编译器打开 TF 卡目录下 system-connections/resin-sample 文件,修改填写你的 WiFi 信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[connection]
id=resin-wifi
type=wifi

[wifi]
hidden=true
mode=infrastructure
ssid=你的 WiFi SSID

[ipv4]
method=auto

[ipv6]
addr-gen-mode=stable-privacy
method=auto

[wifi-security]
auth-alg=open
key-mgmt=wpa-psk
psk=你的 WiFi 密码

启动初始化

将 TF 卡插入树莓派中,上电,几分钟后,在浏览器(推荐 Chrome)地址栏输入 http://hassio.local:8123 即可看到如下界面了

由于没有国内的镜像站,所以且等着就好了,需要很久,除非你在路由上开了科学上网。

完成后,大致是这样

接下来

在下一篇将会继续安装相关的插件和配置,敬请期待。

跑路前还发个公告

重要公告
首先感谢大中华地区的BlockCDN爱好者和参与者,在这一年多来对BCDN项目的支持和鼓励。伴随BCDN走过了融资到退币,从94到成长。没有您们就没有BCDN的成长!为了更好的让分享型CDN能尽快的落地造福大众,区块链全球发展基金会(Blockchain Global Development Foundation)拟定同全球CDN巨头合作技术研发和加快BCDN的全球应用落地。具体洽谈合作商包括:Akamai 、 Amazon 、CloudFront、EdgeCast、CloudFlare。介于大中华地区对CDN的准入条件和对区块链行业的政策性风险,基金会艰难的决定暂停中国地区的BCDN业务和停止同原中国合作商(成都区块链公司)的开发和大中华区的推广合作。并于2018年12月1日起暂停原大中华区的BCDN挖矿,快照等一切活动。大中华区业务的从新开启时间待定。原大中华区平台内的BCDN资产请于12月15日前提现到个人ETH 钱包,官方将于12月15日24:00后将停止所有大中华区平台内BCDN的提现、维护等活动,未提现的按自愿放弃其所有权。望相互告知。再次感谢您的支持!

区块链全球发展基金会(Blockchain Global Development Foundation)
2018/11/22

之前写过一篇用 Portainer 图形界面工具来搭建 Docker 化的LNMP。但是每次开一台新的VPS,按照那个流程走下来都要累死,于是按照那个思路,整理了一份命令行版本的部署方案。

创建数据目录

1
mkdir -p /data/wwwroot && mkdir -p /data/logs && mkdir -p /data/mysql

这三个目录分别用来存储网站文件,网站日志,数据库文件。

在 CentOS 下安装 Docker

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-selinux \
docker-engine-selinux \
docker-engine \
&& yum install -y yum-utils \
device-mapper-persistent-data \
lvm2 \
&& yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo \
&& yum install -y docker-ce \
&& systemctl enable docker \
&& systemctl start docker

以上是 Docker 官方给出的在 CentOS 下安装 Docker 的文档,Ubuntu 可以参考这里: https://docs.docker.com/install/linux/docker-ce/ubuntu/ 。在边栏还可以看到其他系统的安装方法。

创建子网络

1
docker network create --gateway "172.20.0.1" --subnet "172.20.0.0/16" lnmp

把 LNMP 放在独立的网络里更易管理。

部署 Nginx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
docker run -itd --name nginx nginx \
&& \
docker cp nginx:/etc/nginx /etc \
&& \
docker stop nginx && docker rm nginx \
&& \
docker run -itd --name nginx \
-v /etc/nginx:/etc/nginx \
-v /data/wwwroot:/data/wwwroot \
-v /data/logs/nginx:/var/log/nginx \
-p 80:80 -p 443:443 \
--restart always \
-l "ink.akawa.nginx" \
-v /tmp:/tmp \
--network lnmp --ip "172.20.0.2" nginx

前三行的作用就是启动一个临时的 Nginx 容器,目的是为了把其中的配置文件复制出来,最后再删除掉这个临时容器。最后一行就是真正启动 Nginx 容器的命令了。

部署 MySQL

1
read -s mysql_pass

输入MySQL管理员密码后,继续执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
docker run -itd --name mysql mariadb \
&& \
docker cp mysql:/etc/mysql /etc \
&& \
docker stop mysql && docker rm mysql \
&& \
docker run -itd --name mysql \
-p 3306:3306 \
-v /etc/mysql:/etc/mysql \
-v /data/mysql:/var/lib/mysql \
-v /tmp:/tmp \
-v /data/logs/mysql:/var/log/mysql \
--restart always \
--network lnmp \
--ip "172.20.0.3" \
-e MYSQL_ROOT_PASSWORD=$mysql_pass \
mariadb

前三行依旧是为了复制配置文件出来,第四行会要求你输入 MySQL 的 root 密码。第五行启动 MySQL 容器。

部署 PHP7

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
docker run -itd --name php7 ety001/php:7.2.14 \
&& \
docker cp php7:/etc/php7 /etc \
&& \
docker stop php7 && docker rm php7 \
&& \
docker run -itd --name php7 \
-v /etc/php7:/etc/php7 \
-v /data/wwwroot:/data/wwwroot \
-v /tmp:/tmp \
-v /data/logs/php7:/var/log/php7 \
--restart always \
--network lnmp \
--ip "172.20.0.4" \
ety001/php:7.2.14

前三行复制配置文件出来,第四行启动 PHP 容器。其中这里使用的镜像是我自己封装的,镜像里面写死了数据目录,如果你更改了数据目录,请自行修改 /etc/php7/php-fpm.d/www.conf 中的 chdir 参数值。

修改 Nginx 配置文件并进行测试

打开 /etc/nginx/conf.d/default.conf 文件,改成下面的样子

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
server {
listen 80;
server_name localhost;

#charset koi8-r;
#access_log /var/log/nginx/host.access.log main;

location / {
root /data/wwwroot/default; # <------ 修改这里为自己的目录
index index.html index.htm;
}

#error_page 404 /404.html;

# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}

# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}

# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
location ~ \.php$ {
root /data/wwwroot/default; # <------ 修改这里为自己的目录
fastcgi_pass 172.20.0.4:9000; # <------ 修改这里为正确的PHP解析地址
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; # <------ 修改这里的路径
include fastcgi_params;
}

# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}

修改好以后,执行 docker exec nginx nginx -s reload 重载 nginx 配置以使其生效。这时可以在 /data/wwwroot/default 目录下创建一个 index.html 的文件,然后浏览器访问下看看是否可以正常显示。再创建一个含有 <?php phpinfo();?> 代码的 PHP 文件测试下 PHP 是否可用。

后续要添加网站

后续如果要增加新的网站,只需要在 /data/wwwroot/ 中新建个文件夹存储网站文件,然后在 /etc/nginx/conf.d/ 中增加新网站的配置文件,最后执行 docker exec nginx nginx -s reload 重载 nginx 配置即可生效。

如果要增加 Let’s encrypted 的 SSL 的话,建议使用 acme.sh 中的 DNS 方法来注册证书,并把证书安装到 /etc/nginx/ssl 目录里,最后使用 docker restart nginx 来重启 Nginx 容器生效。这个后续可以再开篇文章写一下。

完工!

再来一篇,这篇代码是获取指定用户当前的 Voting Power,不废话直接上代码。

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
31
32
33
34
35
<?php
function postData($data) {
$url = 'https://api.steemit.com';
$options = array(
'http' =>
array(
'header' => "Content-Type: application/json\r\n".
"Content-Length: ".strlen($data)."\r\n".
"User-Agent:SteemTools/1.0\r\n",
'method' => 'POST',
'content' => $data,
)
);
$context = stream_context_create($options);
try {
$result = file_get_contents($url, false, $context);
$r = json_decode($result, true);
$result = $r['result'];
} catch (\Exception $e) {
return false;
}
return $result;
}

function getAccountVotingPower($username) {
$data = postData('{"jsonrpc":"2.0", "method":"condenser_api.get_accounts", "params":[["'.trim($username).'"]], "id":1}');
$user = $data[0];
$last_vote_time = $user['last_vote_time'];
var_dump(strtotime($last_vote_time));
$voting_power = $user['voting_power'];
var_dump($voting_power);
$vpow = $voting_power + (10000 * (time() - strtotime($last_vote_time)) / 432000);
return number_format(min($vpow, 10000) / 100, 2);
}
var_dump(getAccountVotingPower('ety001'));

完工!

由于最近开发需要计算指定账号的赞的价值,因此研究了下。

代码如下:

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<?php
function postData($data) {
$url = 'https://api.steemit.com';
$options = array(
'http' =>
array(
'header' => "Content-Type: application/json\r\n".
"Content-Length: ".strlen($data)."\r\n".
"User-Agent:SteemTools/1.0\r\n",
'method' => 'POST',
'content' => $data,
)
);
$context = stream_context_create($options);
try {
$result = file_get_contents($url, false, $context);
$r = json_decode($result, true);
$result = $r['result'];
} catch (\Exception $e) {
return false;
}
return $result;
}

function getRewardFund() {
$result = postData('{"id":0,"jsonrpc":"2.0","method":"call","params":["database_api","get_reward_fund",["post"]]}');
if (isset($result['reward_balance'])) {
$result['reward_balance'] = str_replace(' STEEM', '', $result['reward_balance']);
}
return $result;
}

function getAccountVests($username) {
$result = postData('{"jsonrpc":"2.0", "method":"condenser_api.get_accounts", "params":[["'.trim($username).'"]], "id":1}');
return str_replace(' VESTS', '', $result[0]['vesting_shares']) + str_replace(' VESTS', '', $result[0]['received_vesting_shares']) - str_replace(' VESTS', '', $result[0]['delegated_vesting_shares']);
}

function getCurrentMedianHistoryPrice() {
$result = postData('{"id":1,"jsonrpc":"2.0","method":"call","params":["database_api","get_current_median_history_price",[]]}');
return str_replace(' SBD', '', $result['base']) / str_replace(' STEEM', '', $result['quote']);
}

function getAccountUpvoteValue($username, $vp, $weight) {
$power = (100*$vp * 100*$weight / 1e4 + 49) / 50;
$total_vests = getAccountVests($username);
$final_vests = $total_vests * 1e6;
$rshares = $power * $final_vests / 1e4;
$rewards = getRewardFund();
$sbd_median_price = getCurrentMedianHistoryPrice();
$estimate = $rshares / $rewards['recent_claims'] * $rewards['reward_balance'] * $sbd_median_price;
return $estimate;
}

// var_dump(getRewardFund());
// var_dump(getAccountVests('ety001'));
// var_dump(getCurrentMedianHistoryPrice());
var_dump(getAccountUpvoteValue('ety001', 100, 100));

官网的公式已经很明确了,其中遇到的问题就是 voting powerweight 的精度问题了。由于在官网公式上并没有指明,所以很容易被忽略。

在我的代码中,很清楚的看到我传入的 voting power90weight100,由于 Steem 在百分比上的精度是下面这样子的:

1
100.00%

而存储在区块上的是整数 10000。因此官方给出的代码中,power = (voting_power * weight / 10000) / 50 要看你传入是什么精度的数据了。按照我的传参,我必须要给传入的值再乘以100才是正确结果,也就是我的代码是 $power = (100*$vp * 100*$weight / 1e4 + 49) / 50;。其中的 +49 是挺高精度的一种方式,可以去掉。

完工!

最近有个项目需要使用 eth 的智能合约,虽然我不看好eth,但是为了挣钱也不得不花些时间去学习下了。之所以选择 Docker 是因为我不喜欢删除的时候删不干净,毕竟不熟悉工具会安装哪些东西。使用 Docker 的话,基本上都被封装在容器里,不用了删掉容器和映射出来的目录数据就ok了。

去 eth 的 github 看了下,是有提供 Docker 镜像的,那么我们直接拉取官方做好的镜像包就好了

1
docker pull ethereum/client-go

由于我们是本地开发测试,所以我们启动测试节点

1
docker run -it --rm -v $(pwd)/eth:/root/.ethereum ethereum/client-go console --dev

其中 $(pwd)/eth 是用来存储区块数据的宿主机目录,设置成为自己的一个目录就好。
执行后,就会看到类似下面的样子

最后会出现一个 > 的控制符,这时候就可以输入命令了,比如查看下目前的账户

1
2
> eth.accounts
["0xeeffae8858bee06d1b4f57d7a2fbf3544a593f12"]

获取指定用户的 balance

1
2
> eth.getBalance(eth.accounts[0])
1.15792089237316195423570985008687907853269984665640564039457584007913129639927e+77

创建个密码是“ety001”的新用户来专门测试开发

1
2
> personal.newAccount('ety001')
"0x6ed051356a14c1354a95f3352856f05d622b1658"

转账给新账户,用于测试

1
> eth.sendTransaction({from: '0xb0ebe17ef0e96b5c525709c0a1ede347c66bd391', to: '0xf280facfd60d61f6fd3f88c9dee4fb90d0e11dfc', value: web3.toWei(1, "ether")})

从网上找一段合约代码试试创建合约,把下面的代码复制到 Remix 里,编译器选择 0.4.18 编译,之后点击“detail”

1
2
3
4
5
6
7
8
9
10
11
12
pragma solidity ^0.4.18;
contract hello {
string greeting;

function hello(string _greeting) public {
greeting = _greeting;
}

function say() constant public returns (string) {
return greeting;
}
}

点击“detail”打开一个浮层,里面找到 WEB3DEPLOY 的代码,复制出来,修改一下第一行,比如

1
var _greeting = 'hi, ety001'

修改完后解锁钱包,把代码复制进 console 里,回车,即可创建新的合约了

1
> personal.unlockAccount(eth.accounts[1],"ety001");

console 里输入 hello.say() 回车后,就能看到输出了 hi, ety001 了,这样一个智能合约的开发环境就搭建好了。

完工!

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
31
32
33
34
<?php
function steem_per_mvests() {
$url = 'https://api.steemit.com';

$data = '{"jsonrpc":"2.0", "method":"database_api.get_dynamic_global_properties", "id":1}';
$options = array(
'http' =>
array(
'header' => "Content-Type: application/json\r\n".
"Content-Length: ".strlen($data)."\r\n".
"User-Agent:SteemMention/1.0\r\n",
'method' => 'POST',
'content' => $data,
)
);
$context = stream_context_create($options);
try {
$result = file_get_contents($url, false, $context);
$r = json_decode($result, true);
$result = $r['result'];
} catch (\Exception $e) {
return false;
}
return $result['total_vesting_fund_steem']['amount'] / ($result['total_vesting_shares']['amount'] / 1e6);
}

function vests_to_sp($vests) {
$steem_per_mvests = steem_per_mvests();
if ($steem_per_mvests) {
return $vests / 1e6 * $steem_per_mvests;
} else {
return false;
}
}
0%