filecoin-lotus钱包

Base16,Base32,Base64编码的介绍
filecoin lotus 公钥私钥地址生成过程
Filecoin地址结构详解
filecoin钱包

数字钱包是用来储存和管理私钥的工具,提供钱包地址的创建、加密数字货币转账、钱包地址交易历史的查询等基础金融功能,是数字货币的关键基础设施。
钱包的核心要素有哪些:

●钱包地址:钱包地址类似于银行卡号,一个人可以拥有多张银行卡,所以也可以拥有多个钱包地址,在一个钱包中,一个人也可以拥有多个钱包地址,但一个钱包地址只能对应一个私钥;

●钱包识别码:类似于银行卡的客户ID

●公钥和私钥:密钥的算法属于不对称加密算法,该算法拥有两个密钥:公钥和私钥,使用私钥加密的数据可以用公钥解密,反之亦可。通过公钥可以算出钱包地址,私钥可以计算出公钥,私钥的持有者才是数字货币的持有者。

●Keystore:使用用户自定义的密码加密私钥后得到的字符串,用于交易转账等钱包操作。转账时要通过自定义的密码解密,从Keystore中把私钥解密出来,因此Keystore的密码非常重要。

●助记词:由于私钥难以记忆,因此可通过某种算法,把私钥转换成一系列的单词,这些单词就是助记词,其实,助记词实质上相当于私钥。谁拥有了助记词,谁就拥有了钱包的使用权。

钱包按照私钥的存储方式又分为冷钱包和热钱包,其中热钱包是我们联网状态下使用的钱包,也就是我们在交易时使用的钱包,例如电脑客户端钱包,手机APP钱包,网页钱包等,热钱包的优点是使用方便,效率更高,但是安全性不如冷钱包好。
而冷钱包是指网络不能访问到你私钥的钱包,即离线钱包,它的优点是特别安全,不用担心私钥被盗,其缺点是交易繁琐,通常情况下我们使用的都是热钱包,但是当持有数字货币数量较多,且不经常用于交易的时候,我们就需要用到冷钱包。

从使用类型上,钱包还分为全节点钱包、轻钱包和链下钱包(又称为中心化钱包)。

●全节点钱包:全节点钱包是历史最早的钱包,其特点在于“全”,除了保存私钥外,全节点钱包还有保存了所有区块的数据,这样就可以在本地直接验证交易数据的有效性。

●轻钱包:顾名思义,“轻”钱包是不保存所有区块的数据,只保存跟自己相关的数据的钱包,体积很小,反应较快,使用感较好。

●链下钱包(中心化钱包):链下钱包又成为中心化钱包,完全依赖运行钱包的公司和服务器,我们存储在交易所的数字货币,就是在链下钱包(中心化)钱包上运行的。

由上,我们了解了“数字钱包”的概念以及相关分类,之所以称数字钱包是数字货币关键的底层设施,是因为我们在进行和数字货币相关的交易时,我们的资产都是需要放在钱包里的。

在Filecoin地址规则中,t代表测试网,f代表即将上线的正式网,1代表钱包地址,2是合约地址,3则是矿工地址
地址有4种,分别是: ID,SECP256K1,Actor,BLS

创建一个secp256k1钱包
lotus wallet new
f1hidwc7bsnjitkd6csmhunq4iiq2xpqs3prn5tmy

创建一个BLS钱包
lotus wallet new bls
f3r4udenceimbb5onjvi23gndpjnqrpcfybnqaumuibz5yynf22ikjdinfujp5zlduggujymw6b2bo4fqfcpva

生成账户ID
lotus-miner init --owner=f3r4udenceimbb5onjvi23gndpjnqrpcfybnqaumuibz5yynf22ikjdinfujp5zlduggujymw6b2bo4fqfcpva --worker=f3r4udenceimbb5onjvi23gndpjnqrpcfybnqaumuibz5yynf22ikjdinfujp5zlduggujymw6b2bo4fqfcpva #owner跟worker可以是不同的BLS钱包
f2j4ycric7bsnvmbpxfxojox5y32dnnbr3xy47yha
Actor f020489

算法简介

blake2

BLAKE2的定位是目前安全系数最高的哈希函数。BLAKE2是基于BLAKE实现的,BLAKE是2008年被提交至SHA-3竞赛的一种哈希函数。

BLAKE2不仅仅只是一个简单的哈希函数而已!首先,BLAKE2有两大主要版本:BLAKE2b和BLAKE2s。BLAKE2b是BLAKE的64位版本,它可以生成最高512位的任意长度哈希。BLAKE2s是BLAKE的32位版本,它可以生成最高256位的任意长度哈希。

BLAKE2x是对BLAKE2的简单扩展,它可以生成任意长度的哈希值(长度不受限制)。

ECC

椭圆曲线加密算法,即:Elliptic Curve Cryptography,简称ECC,是基于椭圆曲线数学理论实现的一种非对称加密算法。相比RSA,ECC优势是可以使用更短的密钥,来实现与RSA相当或更高的安全。据研究,160位ECC加密安全性相当于1024位RSA加密,210位ECC加密安全性相当于2048位RSA加密。

Base32编码

Base32编码是使用32个可打印字符(字母A-Z和数字2-7)对任意字节数据进行编码的方案,编码后的字符串不用区分大小写并排除了容易混淆的字符,可以方便地由人类使用并由计算机处理。Base32将任意字符串按照字节进行切分,并将每个字节对应的二进制值(不足8比特高位补0)串联起来,按照5比特一组进行切分,并将每组二进制值转换成十进制来对应32个可打印字符中的一个。

RFC

Request For Comments(RFC),是一系列以编号排定的文件。文件收集了有关互联网相关信息,以及UNIX和互联网社区的软件文件。目前RFC文件是由Internet Society(ISOC)赞助发行。基本的互联网通信协议都有在RFC文件内详细说明。RFC文件还额外加入许多在标准内的论题,例如对于互联网新开发的协议及发展中所有的记录。因此几乎所有的互联网标准都有收录在RFC文件之中。

地址生成步骤

  1. 随机生成256位私钥

privateKey = generatePrivateKey()“0x81232f16fa8f8bc2d31096d2407d9e392c25f048861a0e0f640f4febb4f22996”
2. 利用椭圆曲线加密算法生成公钥

publicKey=ECC.getPublicKeyFromPrivateKey(privateKey)“0xeb15c814543f9c1e62f1e4e13e2b5fd2a4d224b58f8cfeb1e587378d46a96c06dfaf876bc176911e7613c3e8ffe1e928025e266918a935fc6bd921606fcca5b3”
3. 将公钥前加入0x04值后,进行20位的blake2b计算

blake2Hash = blake2b(“0x04eb15c814543f9c1e62f1e4e13e2b5fd2a4d224b58f8cfeb1e587378d46a96c06dfaf876bc176911e7613c3e8ffe1e928025e266918a935fc6bd921606fcca5b3”, 20)“0x50cedfe81c8cdcf03fede2d075db9025d7f09463”
4. 将得到的blake2哈希值前添加0x01后,继续用blake2b算法计算4位校验和。

checksum = blake2b(“0x0150cedfe81c8cdcf03fede2d075db9025d7f09463”, 4)“0x62c27772”

  1. 将20位公钥哈希值和4位校验和连接起来,并用遵照RFC4648标准的Base32编码格式进行编码。

sourceAddress = Base32Encode(blake2Hash checksum, ‘RFC4648’)“6j5iu4elnajlrrs4ugk5tjnd365b42csgh7b3hq”
6. 将编码后的字符串根据地址属性,属于测试网(t),还是正式网(f),是钱包地址(1)还是合约地址(2)加上相应的前缀得到最终地址

filecoin lotus 公钥私钥地址生成过程
fil有两种加密类型的私钥:secp256k1 和 bls
另:代码中的切片在此皆称作数组

一、secp256k1 过程
私钥
私钥一定是32位的
由系统库"crypto/ecdsa"生成key
先创建一个32位的数组(默认值是32个0),
copy上面的注释是确保私钥长度是32位,打印过blob的长度就是32,内部算法没看懂是否会生成小于32的
如果小于32位,那么私钥前面几位就是0 (可以理解成私钥长度不够32位,前面补0)

公钥
公钥长度是65位的,也可以说是64位; 因为65位的第1位是4,固定的

ScalarBaseMult 获得 x,y

elliptic.Marshal(secp256k1.S256(), x, y) 获得公钥
系统库 “crypto/elliptic”
代码中的BitSize 查看上面代码中的BitCurve,secp256k1.S256()初始参数
theCurve.BitSize = 256
(256+7)>>3 = 32
这个代码简单,生成一个65位的数组,0位值固定是4,将上面得到的x,y写入到之后64位(其实就是上面的point,上面拆成x,y,这里再合并起来)
所以也可以说,公钥是64位的,因为前面有个固定值4;
如果是使用十六进制就是前面加04

blake2b.New(cfg),hasher.Sum(nil)
代码路径
“github.com/minio/blake2b-simd”
blake2b.go

func New(c *Config) (hash.Hash, error) {
…//部分代码省略
d := new(digest)
d.initialize©
return d, nil
}

// initialize initializes digest with the given
// config, which must be non-nil and verified.
func (d *digest) initialize(c *Config) {
… //略
// Initialize.
d.size = c.Size
… //略
}

// Sum returns the calculated checksum.
func (d *digest) Sum(in []byte) []byte {
// Make a copy of d so that caller can keep writing and summing.
d0 := *d
hash := d0.checkSum()//得到一个64位数组
return append(in, hash[:d0.size]…)//取数组的前size位返回,这个size就是初始化时的(在上文blake2b转换时长度20)
}
//下面的write/checkSum 可不看,算法的… 以其他语言实现时,可用于参考
//注意,下面方法中使用到了两个常量
const (
BlockSize = 128 // block size of algorithm
Size = 64 // maximum digest size

func (d *digest) Write(p []byte) (nn int, err error) {
…//略
//内部会修改d.nx
}
func (d *digest) checkSum() [Size]byte {
// Do not create unnecessary copies of the key.
if d.isKeyed {
for i := 0; i < len(d.paddedKey); i++ {
d.paddedKey[i] = 0
}
}

dec := BlockSize - uint64(d.nx)
if d.t[0] < dec {
d.t[1]–
}
d.t[0] -= dec

// Pad buffer with zeros.
for i := d.nx; i < len(d.x); i++ {
d.x[i] = 0
}
// Set last block flag.
d.f[0] = 0xffffffffffffffff
if d.isLastNode {
d.f[1] = 0xffffffffffffffff
}
// Compress last block.
compress(d, d.x[:])

var out [Size]byte
j := 0
for _, s := range d.h[:(d.size-1)/8+1] {
out[j+0] = byte(s >> 0)
out[j+1] = byte(s >> 8)
out[j+2] = byte(s >> 16)
out[j+3] = byte(s >> 24)
out[j+4] = byte(s >> 32)
out[j+5] = byte(s >> 40)
out[j+6] = byte(s >> 48)
out[j+7] = byte(s >> 56)
j += 8
}
return out
}

地址转换 字符串
上面所得到公钥/私钥都是byte数组,地址是一个结构体, 如何得到字符串…
如果直接将byte数组转字符串会发现结果乱码… 结构体中的str也是乱码…

type Key struct {
types.KeyInfo

PublicKey []byte
Address address.Address
}
type KeyInfo struct {
Type string
PrivateKey []byte
}

Key是一个完整的信息,公私钥,地址; 类型分别是[]byte 和address.Address

公私钥转字符串
在转json的时候,具体查看系统包json.Marshal,内部针对切片byte特殊处理

func newSliceEncoder(t reflect.Type) encoderFunc {
// Byte slices get special treatment; arrays don’t.
if t.Elem().Kind() == reflect.Uint8 {
p := reflect.PtrTo(t.Elem())
if !p.Implements(marshalerType) && !p.Implements(textMarshalerType) {
return encodeByteSlice //内部进行base64处理
}
}
enc := sliceEncoder{newArrayEncoder(t)}
return enc.encode
}

转换代码
priArr, err := GenPrivate()
//base64获取字符串
prikeyBase64Str := base64.StdEncoding.EncodeToString(priArr)
//16进制
priKeyHex := hex.EncodeToString(priArr)

地址转字符串
这个是最坑的…
得到type Address struct{ str string } ,以为内部的str就是地址了,结果不是!
后面才发现它重写了序列化/format的代码

// MarshalJSON implements the json marshal interface.
func (a Address) MarshalJSON() ([]byte, error) {
return []byte(" + a.String() + "), nil
}

// String returns an address encoded as a string.
func (a Address) String() string {
str, err := encode(Testnet, a)//这写死了是测试网
if err != nil {
panic(err) // I don’t know if this one is okay
}
return str
}

// 取第0个,上文中有一个长度21的数组,0位置就是类型
func (a Address) Protocol() Protocol {
if len(a.str) == 0 {
return Unknown
}
return a.str[0]
}

// 获取的是那个长度为20的byte数组
func (a Address) Payload() []byte {
return []byte(a.str[1:])//从1位置开启取, a.str的长度是21
}

//blake2b 转换
// ChecksumHashLength defines the hash length used for calculating address checksums.
const ChecksumHashLength = 4
var checksumHashConfig = &blake2b.Config{Size: ChecksumHashLength}
// Checksum returns the checksum of ingest.
func Checksum(ingest []byte) []byte {
return hash(ingest, checksumHashConfig)//长度4, hash方法查看上文转换长度20时的代码
}

func encode(network Network, addr Address) (string, error) {
if addr == Undef {
return UndefAddressString, nil
}
//根据网络类型 获取前缀
var ntwk string
switch network {
case Mainnet:
ntwk = MainnetPrefix //f
case Testnet:
ntwk = TestnetPrefix //t
default:
return UndefAddressString, ErrUnknownNetwork
}
//根据地址类型获得字符串
var strAddr string
switch addr.Protocol() {
case SECP256K1, Actor, BLS:
//checkSum 注意传值是之前那个长度为21的数组,通过这个21的数组,得到一个长度4的数组
cksm := Checksum(append([]byte{addr.Protocol()}, addr.Payload()…))
//将长度20的数组和长度4的数组组成一个长度24的数组,对它base32
//加上前缀 t1 (测试网)
strAddr = ntwk + fmt.Sprintf("%d", addr.Protocol()) + AddressEncoding.WithPadding(-1).EncodeToString(append(addr.Payload(), cksm[:]…))
case ID:
i, n, err := varint.FromUvarint(addr.Payload())
if err != nil {
return UndefAddressString, xerrors.Errorf(“could not decode varint: %w”, err)
}
if n != len(addr.Payload()) {
return UndefAddressString, xerrors.Errorf(“payload contains additional bytes”)
}
strAddr = fmt.Sprintf("%s%d%d", ntwk, addr.Protocol(), i)
default:
return UndefAddressString, ErrUnknownProtocol
}
return strAddr, nil
}

//以下是几个常量说明
// Network represents which network an address belongs to.
type Network = byte
const (
// Mainnet is the main network.
Mainnet Network = iota
// Testnet is the test network.
Testnet
)
// 主网前缀
const MainnetPrefix = “f”
// 测试网前缀
const TestnetPrefix = “t”

// 地址格式,从0开始 ID:0,SECP256K1:1,Actor:2,BLS:3
type Protocol = byte
const (
// ID represents the address ID protocol.
ID Protocol = iota
// SECP256K1 represents the address SECP256K1 protocol.
SECP256K1
// Actor represents the address Actor protocol.
Actor
// BLS represents the address BLS protocol.
BLS

Unknown = Protocol(255)
)

针对base32中WithPadding(-1) 说明
-1表示不填充,默认是填充 =

package base32
const (
StdPadding rune = ‘=’ // Standard padding character
NoPadding rune = -1 // No padding
)
Base32编码

与Base16编码区别的是,Base32使用了ASCII编码中可打印的32个字符(大写字母AZ和数字27)对任
意字节数据进行编码.Base32将串起来的二进制数据按照5个二进制位分为一组,由于传输数据的单位是
字节(即8个二进制位).所以分割之前的二进制位数是40的倍数(40是5和8的最小公倍数).如果不足40位,
则在编码后数据补充"=",一个"="相当于一个组(5个二进制位),编码后的数据是原先的8/5倍

上面是针对一个24位的进行base32, 参考base32说明, 每5个一组,就还缺一个,所以会自动填充一个=, -1的作用就是不要这个= (这个结论是猜测的…)

完整生成代码
lotus代码在windows没法编译,不能运行,
这个生成代码是抽出来的,可以在windows直接跑
package main

import (
“bytes”
“encoding/base32”
“encoding/base64”
“encoding/hex”
“fmt”
“github.com/filecoin-project/go-address”
goCrypto “github.com/filecoin-project/go-crypto”
“github.com/minio/blake2b-simd”
)

const encodeStd = “abcdefghijklmnopqrstuvwxyz234567”

var AddressEncoding = base32.NewEncoding(encodeStd)

func main() {
k, err := GenPrivate()
//测试代码
//k = []byte{126, 25, 208, 242, 124, 230, 119, 157, 89, 166, 200, 224, 212, 218, 245, 211, 223, 67, 202, 62, 5, 222, 129, 216, 251, 87, 113, 250, 62, 248, 118, 12}
fmt.Println(“私钥数组:”, k, “,长度:”, len(k), err)
p, err := ToPublic(k)
fmt.Println(“公钥数组:”, p, “,长度:”, len§, err)

var payloadHashConfig = &blake2b.Config{Size: 20}
b20 := hash(p, payloadHashConfig)
fmt.Println("通过公钥blake2b/20 得到数组 ", b20)
explen := 1 + len(b20)
buf := make([]byte, explen)
buf[0] = address.SECP256K1
copy(buf[1:], b20)
fmt.Println("得到21数组 ", buf)
cksm := Checksum(buf)
fmt.Println("通过21数组 blake2b/4,得到数组 ", cksm)

codeBuf := make([]byte, 0)
codeBuf = append(codeBuf, b20[:]…)
codeBuf = append(codeBuf, cksm[:]…)
fmt.Println("20和4的数组组合 ", codeBuf)
str := AddressEncoding.WithPadding(-1).EncodeToString(codeBuf)
fmt.Println("通过base 32 padding -1 encode 得来字符串 ", str)
re := “t1” + str
fmt.Println("加上前缀得最终结果 ", re)

prikeyBase64Str := base64.StdEncoding.EncodeToString(k)
pubkeyBase64Str := base64.StdEncoding.EncodeToString§
fmt.Println(“公私钥base64 私钥:”, prikeyBase64Str, “,公钥:”, pubkeyBase64Str)
priHex := hex.EncodeToString(k)
pubHex := hex.EncodeToString§
fmt.Println(“公私钥hex 私钥len:”, len(priHex)/2, “:”, priHex, “,公钥len:”, len(pubHex)/2, “:”, pubHex)

//keystore文件
kStr := “wallet-” + re
//kStr = “default” // 设置节点默认账号,结果是MRSWMYLVNR2A
encName := base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString([]byte(kStr))
fmt.Println("keystore文件名: " + encName)
}

func GenPrivate() ([]byte, error) {
priv, err := goCrypto.GenerateKey()
if err != nil {
return nil, err
}
return priv, nil
}
func ToPublic(pk []byte) ([]byte, error) {
return goCrypto.PublicKey(pk), nil
}

func hash(ingest []byte, cfg *blake2b.Config) []byte {
hasher, err := blake2b.New(cfg)
if err != nil {
// If this happens sth is very wrong.
panic(fmt.Sprintf(“invalid address hash configuration: %v”, err)) // ok
}
if _, err := hasher.Write(ingest); err != nil {
// blake2bs Write implementation never returns an error in its current
// setup. So if this happens sth went very wrong.
panic(fmt.Sprintf(“blake2b is unable to process hashes: %v”, err)) // ok
}
return hasher.Sum(nil)
}

// ValidateChecksum returns true if the checksum of ingest is equal to expected>
func ValidateChecksum(ingest, expect []byte) bool {
digest := Checksum(ingest)
return bytes.Equal(digest, expect)
}

// Checksum returns the checksum of ingest.
func Checksum(ingest []byte) []byte {
return hash(ingest, checksumHashConfig)
}

var checksumHashConfig = &blake2b.Config{Size: 4}

输出结果
私钥数组: [126 25 208 242 124 230 119 157 89 166 200 224 212 218 245 211 223 67 202 62 5 222 129 216 251 87 113 250 62 248 118 12] ,长度: 32
公钥数组: [4 171 214 7 153 167 93 7 209 192 190 228 172 59 76 180 124 141 250 100 8 122 187 73 8 154 73 24 112 5 219 216 95 74 157 145 238 230 241 53 156 56 242 220 73 124 147 39 181 22 59 247 203 70 200 51 74 207 196 249 154 182 48 214 84] ,长度: 65
通过公钥blake2b/20 得到数组 [36 50 207 231 244 218 10 6 114 95 192 90 220 95 40 126 114 141 85 127]
得到21数组 [1 36 50 207 231 244 218 10 6 114 95 192 90 220 95 40 126 114 141 85 127]
通过21数组 blake2b/4,得到数组 [33 188 123 200]
20和4的数组组合 [36 50 207 231 244 218 10 6 114 95 192 90 220 95 40 126 114 141 85 127 33 188 123 200]
通过base 32 padding -1 encode 得来字符串 eqzm7z7u3ifam4s7ybnnyxzipzzi2vl7eg6hxsa
加上前缀得最终结果 t1eqzm7z7u3ifam4s7ybnnyxzipzzi2vl7eg6hxsa
公私钥base64 私钥: fhnQ8nzmd51Zpsjg1Nr1099Dyj4F3oHY+1dx+j74dgw= ,公钥: BKvWB5mnXQfRwL7krDtMtHyN+mQIertJCJpJGHAF29hfSp2R7ubxNZw48txJfJMntRY798tGyDNKz8T5mrYw1lQ=
公私钥hex 私钥len: 32 : 7e19d0f27ce6779d59a6c8e0d4daf5d3df43ca3e05de81d8fb5771fa3ef8760c ,公钥len: 65 : 04abd60799a75d07d1c0bee4ac3b4cb47c8dfa64087abb49089a49187005dbd85f4a9d91eee6f1359c38f2dc497c9327b5163bf7cb46c8334acfc4f99ab630d654
keystore文件名: O5QWY3DFOQWXIMLFOF5G2N32G52TG2LGMFWTI4ZXPFRG43TZPB5GS4D2PJUTE5TMG5SWONTIPBZWC

filecoin钱包支持方(filecoin并未推出自己的钱包)

麦子钱包是多链钱包,多链的钱包的主要特点在于能打通 Filecoin 与其他公链之间的生态,让其他公链的用户能更轻松便捷地进入到另一个生态中。麦子钱包是一个多平台的跨链钱包,产品包括 APP钱包、网页钱包、浏览器插件钱包、硬件钱包等,且支持 BTC、ETH、EOS、Polkadot、Filecoin、Cosmos、币安链等50多个公链,支持去中心化的跨链交易,构建了一个多链的 DApp 生态系统。
麦子钱包是在Filecoin开发测试网期间就率先支持Filecoin网络的钱包。如今,麦子钱包已内置了 IPFS/Filecoin 的部分应用

imToken官方发文称将在Filecoin主网上线后支持Filecoin,Filecoin将成为imToken支持的第十一条公链,此前 imToken 已支持包括 ETH、BTC、ATOM、EOS、TRX、CKB、BCH、LTC、KSM、DOT 在内的十条公链。

作为去中心化的存储网络,Filecoin 具有内置的经济激励机制,可确保即使时间推移也能够可靠地存储文件,Filecoin 使用的存储证明共识协议不仅可以保证存储网络的安全,也可以用于 FIL 代币转账,用于运行加密合约和市场机制等。
目前imToken已经完成Filecoin所有开发功能,进入最后测试和安全审计阶段。

TJ Wallet已经宣布将第一时间更新支持Filecoin正式网 f3矿工钱包地址!在Filecoin地址规则中,t代表测试网,f代表即将上线的正式网,1代表钱包地址,2是合约地址,3则是矿工地址。
TJ Wallet宣布支持f3矿工钱包地址,这也代表着TJ Wallet凭借自己独家的技术实力成为Filecoin生态中首家支持f3地址的钱包项目方。同时,TJ Wallet也是全球第一个支持blst算法的Filecoin钱包。

除了上述介绍的钱包,还有许多已经开发以及正在开发的Filecoin钱包,诸如TRUSR、Ballet等。

你可能感兴趣的:(lotus)