机械境

这里只是我的后花园,随性而写

缘起

区块链是作为一种创新技术,具有三大特点,即 “去中心化”、“集体维护”、“防篡改”。采用区块链技术构建的平台具有极高的数据安全性、集体信任以及数据资产溯源等特性和能力。这些特性和能力为供应链金融搭建可持续运行的安全运营平台提供了可靠的技术支撑环境。

当前区块链技术已经形成了三种类型,即 “公有链”、“私有链” 以及 “联盟链”。

  • 以比特币、以太坊为代表的就是典型的 “公有链” 平台。在公有链平台上,最为典型的特点就是 “匿名性”,在匿名环境下如何确保所有成员的 “诚实”,比特币和以太坊都采用了 PoW 的共识模式。PoW 的本质是你付出足够多的计算资源(也就是算力,或者成本),证明你是诚实的。为了确保 PoW 能正常工作,公有链上的节点需要能够查看所有交易的信息

  • 私有链就是企业内部使用的区块链平台,与当前的企业信息系统一样与外界保持着高度的安全隔离机制

  • 联盟链与公有链最显著的区别是联盟链不采用匿名机制,相反具有严格的成员准入机制。也就是说,所有参与平台的计算节点、业务主体都全部是具名的,并有唯一的身份证书

商业的本质是信息不对称,商业机密是一个企业确立竞争优势的基础。也就是说任何企业不期望自身的商业信息被其它无关企业甚至竞争对手掌握。我们以比特币为例来看,通过区块链浏览器就能查询到区块中交易的一些信息。尽管比特币具有匿名性,但这些信息假如在商业环境中出现,竞争对手有可能分析出来一些商业行为的蛛丝马迹甚至能部分推测出竞争对手的身份和商业策略。区块链技术公开、透明以及分布式存储的特性与商业企业信息保密这一刚性需求产生了矛盾。

Read more »

最近在回顾 2020 的计划的时候,发现有不少任务进展不尽如人意。抛开 COVID-19 的影响,事实上这方面的影响确实也很小。经过反思,比较大的差别,在 2020 年的计划中有几件事情是之前很少涉猎的。但是在处理这些问题时,并没能摆正自己的心态,缺少对行业的敬畏之心。

这让我想起之前很火的 邓宁 - 克鲁格心理效应,其描述的是一种认知偏差。貌似我就属于了这种情况,在新的领域错误地预估了自己的实力,按照过往经验做了一些错误的决策,导致了最终的失败。在反思的过程中,我觉得我就是回到了绝望之谷。

有意思的是,在 《指月录》,卷二十八中有 “见山是山,见山不是山,见山还是山。” 的说法。我觉得这两者有异曲同工之妙。

原文如下:

吉州青原惟信禅师

上堂。老僧三十年前。未参禅时。见山是山。见水是水。及至后来亲见知识。有个入处。见山不是山。见水不是水。而今得个休歇处。依前见山只是山。见水只是水。大众。这三般见解。是同是别。有人缁素得出。许汝亲见老僧 (更参三十年迥无入处在)。

当然了我也不是什么禅宗高手,也没有多深入的研究,姑妄言之:

  • 人生第一重界:看山是山,看水是水。对看到事物的初步感受,因为这时候人没有太多复杂的观念在里面,可谓初识世界,纯洁无瑕。眼睛看见什么就是什么,山就是山,水就是水,没有 “弱水三千,只取一瓢饮” 之说,山水虫鱼,万事万物都是它自己本身。

  • 人生第二重界:看山不是山,看水不是是水。看到世间万物之后,开始对他们进行思考,我们不再停留于简单的第一印象,我们开始探寻事情背后更深层的东西。于是我们说 “白马非马”,“陋室不陋”、“花非花,雾非雾”,山水虫鱼,万事万物都不是它自己本身。经历过见山不是山的阶段,就是经过困惑、怀疑,好像对又好像不对的阶段,要仔细推敲,思考,心中似乎已找到答案,却又不敢肯定的过程。

  • 人生第三重界:看山是山,看水是水。放下一切,万法随缘。这里所说的看山是山,看水是水,已经不是简单的重复,而是经过初识,迷惑之后的大悟,是我们留下好的文化认知,把不好的观念摒除之后的山水,是更有韵味的山水。

其实不管是说开悟之坡,还是第三层的看山是山,看水是水,都不是那么容易到达的境界。对应到编程的阶段,也就是第一阶段,熟练使用某一门语言,会用一些别人写的框架,了解基本的套路和架式。第二阶段,深入理解底层的原理,知其然并知其所以然,能写一些框架 / 库之类的给别人用,第三阶段,不在局限语言本身,能全局把握整个系统的设计,把自己世界观映射到系统,一花一世界吧。一、二阶段应该都属于术,第三阶段才是道。应该大多数人都到不了第三阶段吧,但是如何从第二阶段到第三阶段呢?自觉就是经验和练习,参与更多地实践,但真的如此么?比如软件有设计模式,前人根据经验总结出各种套路,供新手快速入门。看起来就是经验非常重要,很多时候也是高级工程师和初级工程师的差别。在现代医学上,在临床决策的时候,有通过 循证医学(ESB) 辅助决策,其和与传统医学的不同处是,它并不依赖于经验主义或过往案例,提倡使用正式且明确的方法来分析证据,并提供给决策者。详细的细节,可以查看 科普视频。当然了不同的行业有很多差别,不能教条地死搬硬套,这套方法论的核心应该就是把不确定性的东西转化成可回答的问题,然后逐步论证。

一点胡思乱想,记录下,等有新想法了再更新。

---EOF---

缘起

虽然我日常的工作都是在 MacBook Pro (13-inch, 2018, Four Thunderbolt 3 Ports) 上完成的,也用得还算比较顺手。但是还是有偶尔需要 Windows Only 的需求,比如小米的解锁 Bootloader 的工具就只有 Win 版。本来还有一台 DELL XPS13 作为备用机,由于某些原因主板进水了,在官方修了一次,回来没用几次之后又再次挂了,懒得再修了,就一直在吃灰。鉴于我之前有个 256 G 的 Windows To Go 的 USB 盘,想这再废物利用一下。

主要诉求如下,把原来的 WTG 中的 Windows 10 升级到最新的版本可以用 WLS2,可以作为单独的 WTG 运行,也可以通过虚拟机的方式挂载到 macOS 下应个急。思路如下,在 Windows 中安装 Boot Camp 驱动,在 macOS 中可以 VMware Fusion 中可以作为 Boot Camp 分区引导。

准备

Windows 10 镜像

可以在 MSDN I tell you 下载,这个是完全是合法的,不涉及任何盗版的问题。

VMware Fusion

  • 注册 VMware Fusion,个人免费使用
  • brew install --cask vmware-fusion 安装 VMware Fusion
  • 用刚注册的序列号激活 VMware Fusion

Boot Camp

  • Make sure that your Mac is connected to the Internet.
  • Open Boot Camp Assistant, which is in the Utilities folder of your Applications folder.
  • From the menu bar at the top of your screen, choose Action > Download Windows Support Software, then choose your USB flash drive as the save destination. When the download completes, quit Boot Camp Assistant.

制作 WTG

  • Windows

在 Windows 下制作 WTG 有一堆工具了,WinToUSB 算是比较傻瓜式的。安装之后,按照向导即可。

  • macOS

可以通过 VMware Fusion 安装一个 Windows 虚拟机,然后在虚拟机里面安装 WinToUSB 来制作 WTG 的盘。

安装

WTG 制作完成后,只安装 Boot Camp 下载的驱动。如果出现 “Boot Camp is unsupported on this computer model” 错误,可参考 How to fix the ‘Boot Camp is unsupported on this computer model’ error on Mac 解决。

在 macOS 中启动 WTG

  • 在 System Preferences->Security & Privacy->Full Disk Access 中启用 com.vmware.DiskHelper

  • 在 VMware Fusion 中启动,具体步骤可以参考 Launching your Boot Camp partition in VMware Fusion

  • 启用 CPU 虚拟化

Tips

  • WTG 不能升级?
    • HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\PortableOperatingSystem 的值修改为 0
    • 在虚拟机中启动 WTG,就可以升级

参考链接

  • Download and install Windows support software on your Mac
  • [教程] Windows To Go 系统升级(2018.8.27 更新)

---EOF---

以太坊 Parity 客户端是除 Geth 之外使用量最高的一款以太坊客户端,使用的是 Rust 语言,现在改名为 openethereum 了。

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
version: "3"

services:
openethereum:
image: openethereum/openethereum:v3.0.1
container_name: openethereum
restart: unless-stopped
ports:
- "30303:30303"
- "30303:30303/udp"
- "127.0.0.1:8545:8545"
- "127.0.0.1:8546:8546"
user: openethereum
volumes:
- ./chaindata:/chaindata
stop_signal: SIGINT
stop_grace_period: 2m
command:
- --no-download
- --base-path=/chaindata
- --jsonrpc-interface=all
- --jsonrpc-cors=all
- --ws-interface=all
- --ws-origins=all
- --no-ipc
- --scale-verifiers
- --num-verifiers=4
- --pruning=fast
- --pruning-memory=512
- --cache-size-db=1024
- --cache-size-blocks=128
- --cache-size-queue=512
- --cache-size-state=256
logging:
driver: "json-file"
options:
max-size: "2m"
max-file: "10"
Read more »

缘起

在 GitHub 建一个和用户名同名的仓库,仓库中 README.md 的内容就会显示在账户首页。如果 README.md 中的内容是动态更新的,那么首页的内容也就是动态更新的。

根据此思路,只要通过 GitHub Actions 去动态更新 README.md 即可。

配置

这里以我仓库的内容为例,自动同步博客的内容;统计 GitHub 的贡献记录。

  • 同步博客标题,只需要抓取 RSS 转换成 README.md 即可
  • GitHub 的统计,可以通过 anuraghazra/github-readme-stats 完成,这个本身就是动态的了,配置一下即可

更新脚本

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
#!/usr/bin/python
# -*- coding: utf-8 -*-

import requests
import xml.etree.ElementTree as ET

feed = requests.get('https://gythialy.github.io/atom.xml').text
root = ET.fromstring(feed)
nsfeed = {'nsfeed': 'http://www.w3.org/2005/Atom'}
with open('README.md', 'w') as f:
f.write(r'''
## Hi there 👋

- 🔭 I’m currently working on [@QLC Chain](https://github.com/qlcchain)
- 🌱 I’m currently learning [Rust](https://github.com/rust-lang/rust)

## Latest blog posts
''')
for entry in root.findall('nsfeed:entry', nsfeed)[:5]:
text = entry.find('nsfeed:title', nsfeed).text
url = entry.find('nsfeed:link', nsfeed).attrib['href']
published = entry.find('nsfeed:published', nsfeed).text[:10]
f.write('- {} [{}]({})\n'.format(published, text, url))

f.write('''
[>>> More blog posts](https://gythialy.github.io/)
## Statistics
![Goren's github stats](https://github-readme-stats.vercel.app/api?username=gythialy&count_private=true&show_icons=true)
''')

GitHub Actions

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
name: Build README

on:
push:
workflow_dispatch:
schedule:
- cron: '0 10 * * *'

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Check out repo
uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.8
- uses: actions/cache@v2
name: Configure pip caching
with:
path: ~/.cache/pip
key: ${{runner.os}}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{runner.os}}-pip-
- name: Install Python dependencies
run: |
python -m pip install -r requirements.txt
- name: Update README
run: |-
python build.py
cat README.md
- name: Commit and push if changed
run: |-
git diff
git config --global user.email "readme-bot@example.com"
git config --global user.name "README-bot"
git add -A
git commit -m "🤖 Updated README.md" || exit 0
git push

参考链接

  • Building a self-updating profile README for GitHub
  • simonw

---EOF---

背景

由于 WebAssembly Interface Types 还处于草案阶段,目前 WASM 只支持四种基本类型,本质上来说没什么大用。

在实际的开发中,数据结构肯定是远比 i32,i64,f32,f64 这四种基本类型来得复杂的,至少可能是一个复杂的结构体。比如,我需要把一组 key/value 存起来,然后可以通过 key 查询到对应的 value。按照目前的 WASM 的规范,这个实现都是比较麻烦的。key/value 可能是字符串或者其他结构,但是不管什么类型,在内存都是按照字节序的方式存储,所以只需要把类型编码成字节序放到内存中,这样这个结构就可以转化成内存的起始地址(指针)和数据长度,这两个值就是 i32/i64,并且都是 WASM 支持的类型。

函数声明

  • 对外 API

    为了对调用者友好,调用这不需要处理 slice 的地址

1
2
3
4
5
  // 把 key/value 写入存储
pub fn storage_write(key: &[u8], val: &[u8])

// 根据 key 查询对应的 value
pub fn storage_read(key: &[u8]) -> Option<Vec<u8>>
  • 内部接口

在虚拟机中注入这两个 host function 来实现具体的逻辑

1
2
3
4
5
6
7
8
9
10
extern "C" {
pub fn qlcchain_storage_read(
key: *const u8,
klen: u32,
val: *mut u8,
vlen: u32,
offset: u32,
) -> u32;
pub fn qlcchain_storage_write(key: *const u8, klen: u32, val: *const u8, vlen: u32);
}
Read more »

为了能够让人类阅读和编辑 WebAssembly,wasm 二进制格式提供了相应的文本表示。这是一种用来在文本编辑器、浏览器开发者工具等工具中显示的中间形式。本文用基本语法的方式解释了这种文本表示是如何工作的,以及它是如何与它表示的底层字节码,及在 JavaScript 中表示 wasm 的封装对象关联起来的。

本质上,这种文本形式更类似于处理器的汇编指令。

S - 表达式

不论是二进制还是文本格式,WebAssembly 代码中的基本单元是一个模块。在文本格式中,一个模块被表示为一个大的 S - 表达式。

S - 表达式是一个非常古老和非常简单的用来表示树的文本格式。因此,我们可以把一个模块想象为一棵由描述了模块结构和代码的节点组成的树。不过,与一门编程语言的抽象语法树不同的是,WebAssembly 的树是相当平的,也就是大部分包含了指令列表。

首先,让我们看下 S - 表达式长什么样。树上的每个一个节点都有一对括号 ——(…)—— 包围。括号内的第一个标签告诉你该节点的类型,其后跟随的是由空格分隔的属性或孩子节点列表。

S - 表达式如下:

1
(module (memory 1) (func))

这条表达式,表示一棵根节点为 “模块(module)” 的树,该树有两个孩子节点,分别是 属性为 1 的 “内存(memory)” 节点 和 一个 “函数(func)” 节点。

Read more »

WebAssembly 是一种运行在现代网络浏览器中的新型代码,并且提供新的性能特性和效果。它设计的目的不是为了手写代码而是为诸如 C、C++ 和 Rust 等低级源语言提供一个高效的编译目标。对于网络平台而言,这具有巨大的意义 —— 为客户端 app 提供了一种在网络平台以接近本地速度的方式运行多种语言编写的代码的方式。JavaScript 框架不但可以使用 WebAssembly 获得巨大性能优势和新特性,而且还能使得各种功能保持对网络开发者的易用性。简而言之,WebAssembly 可以为 Javascript 提供由 C/C++ 等更高效的语言编写的库。

前世今生

问题

业务需求越来越复杂,前端的开发逻辑越来越复杂,相应的代码量随之变的越来越多,整个项目的起步的时间越来越长。在性能不好的电脑上,启动一个前端的项目甚至要花上十多秒。除了逻辑复杂、代码量大,还有另一个原因是 JavaScript 这门语言本身的缺陷,JavaScript 没有静态变量类型。这门解释型编程语言的作者 Brendan Eich,仓促的创造了这门如果被广泛使用的语言,以至于 JavaScript 的发展史甚至在某种层面上变成了填坑史。

asm.js

为了解决这个问题,WebAssembly 的前身,asm.js 诞生了。asm.js 是一个 Javascript 的严格子集,合理合法的 asm.js 代码一定是合理合法的 JavaScript 代码,但是反之就不成立。同 WebAssembly 一样,asm.js 不是用来给各位用手一行一行撸的代码,asm.js 是一个编译目标。它的可读性、可读性虽然比 WebAssembly 好,但是对于开发者来说,仍然是无法接受的。

无论 asm.js 对静态类型的问题做的再好,它始终逃不过要经过 Parser,要经过 ByteCode Compiler,而这两步是 JavaScript 代码在引擎执行过程当中消耗时间最多的两步。而 WebAssembly 不用经过这两步。这就是 WebAssembly 比 asm.js 更快的原因。

WebAssembly 横空出世

在 2015 年,我们迎来了 WebAssembly,是经过编译器编译之后的代码,体积小、起步快。在语法上完全脱离 JavaScript,同时具有沙盒化的执行环境。WebAssembly 同样的强制静态类型,是 C/C++/Rust 的编译目标。

2019 年 12 月 5 日 — 万维网联盟(W3C)宣布 WebAssembly 核心规范 成为正式标准

工具链

  • AssemblyScript
    支持直接将 TypeScript 编译成 WebAssembly
  • Emscripten
    将其 C/C++ 编译成 WebAssembly
  • WABT
    将 WebAssembly 在字节码和文本格式相互转换的一个工具,方便开发者去理解这个 wasm 到底是在做什么事。
  • WebAssembly Studio
    在线 IDE,支持 C/C++/Rust

关键概念

模块

表示一个已经被浏览器编译为可执行机器码的 WebAssembly 二进制代码。一个模块是无状态的,并且像一个二进制大对象(Blob)一样能够被缓存到 IndexedDB 中或者在 windows 和 workers 之间进行共享(通过 postMessage() 函数)。一个模块能够像一个 ES2015 的模块一样声明导入和导出。

内存

ArrayBuffer (浏览器 WASM 虚拟机实现),大小可变。本质上是连续的字节数组,WebAssembly 的低级内存存取指令可以对它进行读写操作。

表格

带类型数组,大小可变。表格中的项存储了不能作为原始字节存储在内存里的对象的引用(为了安全和可移植性的原因)。

实例

一个模块及其在运行时使用的所有状态,包括内存、表格和一系列导入值。一个实例就像一个已经被加载到一个拥有一组特定导入的特定的全局变量的 ES2015 模块。

生命周期

wasm lifecycle

  1. 通过工具链把其他语言 (C++、Go、 Rust) 编译成 WebAssembly 汇编格式 .wasm 文件
  2. 在网页中使用 fetchXMLHttpRequest 等获取 wasm 文件
  3. .wasm 编译为模块,编译过程中进行合法性检查
  4. 实例化,初始化导入对象,创建模块的实例
  5. 执行实例的导出函数,完成所需操作

虚拟机体系

wasm vm system

WebAssembly 模块在运行时由以下几部分组成,如上图所示

  1. 一个全局类型数,与很多语言不同,在 WebAssembly 中 “类型” 指的并非数据类型,而是函数签名,函数签名定义了函数的参数个数参数类型返回值类型;某个函 数签名在类型数组中的下标 (或者说位置) 称为类型索引
  2. 一个全局函数数组,其中容纳了所有的函数,包括导入的函数以及模块内部定 义的函数,某个函数在函数数组中的下标称为函数索引
  3. 一个全局变量数组,其中容纳了所有的全局变量包括导入的全局变量以及 模块内部定义的全局变量,某个全局变量在全局变量数组的下标称为全局变量索引
  4. 一个全局表格对象,表格也是一个数组,其中存储了元素 (目前元素类型只能 为函数) 的引用,某个元素在表格中的下标称为元素索引。
  5. 一个全局内存对象
  6. 一个运行时栈
  7. 函数执行时可以访问一个局部变量数组,其中容纳了函数所有的局部变量,某 个局部变量在局部变量数组中的下标称为局部变量索引

在 WebAssembly 中,操作某个具体的对象(比如读写某个全局变量)都是通过其索引完成。在当前版本中,所有的索引都是 32 位整形数。

参考资料

  • WebAssembly Concepts
  • WebAssembly 标准入门

---EOF---

0%