太上中的基因设计与Binary | 函数式与区块链(一)

let's Code with Function

关于函数式编程:

函数式编程是有别于传统的面对对象范式的编程范式,函数式方向是目前编程语言发展的大方向,所有新设计的编程语言都或多或少的引入了函数式编程功能。

笔者认为,「下一代计算机学科体系」将基于函数式编程语言。因此,打好函数式编程基础对于具备「长期主义」思维的程序员是必要的。

关于本专栏:

本专栏将通过实战代码分析与经典著作解读,分享作者关于函数式编程与区块链的思考与实践。就目前而言,本专栏将基于两种函数式语言:Rust 和 Elixir,有时候会提及其它语言,作为辅助性参考。

关于太上:

太上是笔者团队近期实践的一个函数式+区块链的项目。

太上炼金炉在不改变原有 NFT 合约的基础上,通过附加「存证合约」,赋予 NFT 组合、拆解、生命周期、权益绑定等能力,锻造 NFT +,创造无限创新玩法与想象空间。

项目地址:

愿景0x01: 助力所有 NFT 及其相关项目,让其具备无限商业想象空间与无限玩法。

愿景0x02: 成为下一代区块链基础设施

太上是本系列用以探讨函数式编程的第一个项目。

Binary 基础知识

什么是 Binary? Binary 中文为二进制类型,是一种各编程语言中都存在的基本数据类型,在部分语言(如Python)中,将其称之为 Bytes。

Binary 可以看做是 0-255 的整型构成列表(List),如:<<255,18,33>>

Binary 和字符串(String)是什么关系? 字符串是 Binary 的子集,字符串能转化为 Binary,但不是所有的 Binary 都能转化为String。

Binary 在区块链中有哪些应用?

在区块链中随处可见 Binary 的身影,如私钥(Privkey)、公钥(Pubkey)、地址(Address)均可以表现为

Binary 的形式;智能合约编码后是 Binary;签名(Signature)也是 Binary。

不同语言中的 Binary

虽然在本质上等价,但是在不同语言中 Binary 会呈现不同的形式。

Python:

>> payload = bytes('你好 世界', encoding='UTF-8')
>> print(payload)
b'\xe4\xbd\xa0\xe5\xa5\xbd \xe4\xb8\x96\xe7\x95\x8c'

Java:

class BytesDemo
{
    public static void main(String[] args)
    {

        byte payload[] = new byte[3];
        payload[0] = (byte) 0x0A;
        payload[1] = (byte) 0xFF;
        payload[2] = (byte) 0x01;

        for (byte theByte : payload){
            System.out.println(Integer.toHexString(theByte));
        }
    }
}

NodeJs:

> var payload = new Buffer('hello world')
> Buffer.prototype.toByteArray = function () {return Array.prototype.slice.call(this, 0)}
> payload.toByteArray()
[ 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]

Rust:

let shift_jis = b"\x82\xe6\x82\xa8\x82\xb1\x82"; // SHIFT-JIS 编码的 "ようこそ"

Elixir:

iex> <<11,22,33,44>>

万变不离其宗。我们要认识到,本质上来讲这些 Binary 都是一样的。

太上中的基因设计

太上中的基因,是对于价值容器(NFT)承载的属性的一种抽象表达。具体渲染出来的实体,不管是渲染出一只猫还是一把屠龙宝刀,这事和基因本身是解耦的。

因此,笔者设计了这样的 Binary 模型:

Gene
<<33, 44, 66, 22, 11, 55, 234, 111>>
  |---|   |---------------------|-----           
    |                                |
前1/4处用以表示二值属性,如男/女      后3/4用以表示数值属性,如魔法值

在处理 Gene 的时候,函数式编程中的 「Pattern Match」(模式匹配)就表现出它的威力了,我们可以通过 Pattern Match 充分玩转 Binary。

下面将以基因分离为例,讲解「变量层面的模式匹配」这个知识点。

目标:实现函数,将基因的二值部分与其余部分分离。

Elixir 实现:

@spec split_gene(binary) :: {binary, binary}
def split_gene(gene) do
  base2_size =
    gene
    |> byte_size()
    |> div(4)

  # 1/4 is base2
  <<binary_base2::bytes-size(base2_size), binary_base10::binary>> = gene
  {binary_base2, binary_base10}
end

只需一行代码,借助变量层面的模式匹配,我们便抽离出了binary_base2binary_base10

Rust:

Rust 中可借助nom这个库,以下是解析 hex color 为 RGB 的例子:

fn hex_color(input: &str) -> IResult<&str, Color> {
  let (input, _) = tag("#")(input)?;
  let (input, (red, green, blue)) = tuple((hex_primary, hex_primary, hex_primary))(input)?;

  Ok((input, Color { red, green, blue }))
}

Binary 的编码方式

常见地,「元」 Binary 可以被编码为 Base16、Base32、Base64 等多种形式。

image-20210802134357395

Binary 编码有助于让信息的传输更有效率,因此,Base64编码在我们的日常开发中非常常见。

和区块链相关的还有一种很重要的Binary 编码形式:Base58,Base58 的改进版 Base58Check 被用于比特币的地址生成中。

base58和base64一样是一种二进制转可视字符串的算法,主要用来转换大整数值。区别是,转换出来的字符串,去除了几个看起来会产生歧义的字符,如 0 (零), O (大写字母O), I (大写的字母i) and l (小写的字母L) ,和几个影响双击选择的字符,如/, +。

——

比特币之所以加入改进版的 Base58 算法,主要为了解决 Base58 导出的字符串没有校验机制,这样,在传播过程中,如果漏写了几个字符,会检测不出来。所以使用了改进版的算法 Base58Check。

实现是:在encode前,在输入流尾部加入输入内容的hash值(4个字节)。然后再对输入流进行 Base58Encode。

在 decode 时候:先 Base58Decode, 然后拆成两部分(内容和校验值),判断对内容计算的校验值和校验值字段是否一致。

比特币Base58相关源码地址:

——

在 Rust 中,我们通过base**库来实现 Binary 的编码:

use base64::{encode, decode};
......

let result = encode(payload);

本文参与2022世界杯预选赛赛程直播社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
李大狗
李大狗

上海对外经贸大学区块链研究中心副主任

53 篇文章, 5929 学分