# Vanity GG18

Original: <https://lifelonglearn.ing/vanity-gg18>

Author: [奶爸](https://lifelonglearn.ing/users/3b04650c-8231-499b-8ef0-381fc4d7efb0)

Published: 2022-11-21T13:49:37Z

Updated: 2026-09-27T06:57:43Z

Version: 1

本文作为 [Dive into Threshold Signature](https://lifelonglearn.ing/r/go?url=aHR0cHM6Ly9saWZlbG9uZ2xlYXJuaS5uZy9kaXZlLWludG8tc21wYy1iYXNlZC10c3M=) 系列的收官文章，对 GG18 的详细内容不再赘述了，而是以一个简单的小的应用去结束这个系列。

## GG18

关于 GG18，我司的另外一位大佬总结和描述的已经十分清晰 [Multiparty Threshold ECDSA](https://lifelonglearn.ing/r/go?url=aHR0cDovL2FhbmRkcy5jb20vYmxvZy9tdWx0aXBhcnR5LXRocmVzaG9sZC1lY2RzYS5odG1s)，推荐去学习，文中有些关于 ECDSA 签名、Shamir Secret Share、Feldman VSS 的前置基础知识可以通过本系列里面提供的小学数学即可理解的例子去理解。

未避免未来引用的博文链接的变更，贴一个图大家可以根据内容来搜索到最新的域名。

![image.png](https://lifelonglearn.ing/upload/866b82ea-6226-4f4a-c33e-877840d11b75.png)

## Vanity GG18

整个 GG18 学习过来，深深的被密码学中蕴含的数学之美所折服。很多人都有搞个自定义地址的需求，我们就基于 GG18 来做一个给 GG18 使用的 vanity-eth。

### 「私钥」？

GG18 其实是利用分散在每个参与方中的一个随机数组成的私钥，通过 VSS、同态加密 等保护每个人的随机数不被其他人知道且可以共同运算，我们使用 GG18 生成的 share 来验证一下：

PrivateKey = \sum{u_i} \mod N

```go
package main

import (
	"crypto/ecdsa"
	"log"
	"math/big"

	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/crypto/secp256k1"
)

func main() {
	// 来自 share1 中的 u_i
	share1, ok := new(big.Int).SetString("75a437da5304cd5e68e6978ad8ca3ef824baba5d732ab7e4e3d2afa0f0833f3", 16)
	if !ok {
		panic("invalid share")
	}
	// 来自 share2 中的 u_i
	share2, ok := new(big.Int).SetString("4f1c5e2282b6d2b0813422d792cbfea124992bb062159a5cc2992b9be34d3545", 16)
	if !ok {
		panic("invalid share")
	}
	// privateKey = \sum(u_i) mod N
	privateKey, err := crypto.HexToECDSA(new(big.Int).Mod(new(big.Int).Add(share1, share2), secp256k1.S256().N).Text(16))
	if err != nil {
		panic(err)
	}
	log.Println(privateKey.PublicKey.X.Text(16))
	log.Println(privateKey.PublicKey.Y.Text(16))
}
```

可以验证 x 和 y 为 share 数据中的 y\_sum.x 和 y\_sum.y 。

### 将普通私钥分发成 GG18 的 share (Vanity-ETH)

在 GG18 的 keygen 过程中，各方需要生成一个 u_i 随机数，仅自己知道，他们的和就是私钥。那么我们可以在生成过程中通过同态加密秘密的验证（production）/ 在生成前找到这个私钥然后分配给各参与方（演示）。

我们将使用演示的方式快速的实现这个功能，首先确保你的电脑上有 rust 环境 😃

1. 将 [https://github.com/naiba-archived/vanity-gg18](https://lifelonglearn.ing/r/go?url=aHR0cHM6Ly9naXRodWIuY29tL25haWJhLWFyY2hpdmVkL3Zhbml0eS1nZzE4) 克隆到本地，并进入。
2. 启动服务器

```shell
cargo run --bin server
```

1. 生成以 aa 开头的以太坊地址的私钥分片参数

```shell
$ cargo run --bin client http://127.0.0.1:8000 "share1.json" "create"
u_1: 54227dc697e4965ed7da323fbfa0d4e334bc12a497be2c0670726c6abc37b670
u_2: 326311d62a3a4cdb43527dc23689b498a1055a3f2a96661a6b8cfb4f68439b01
u_3: 3e276a5aac20e925e1345f6434ad73552908729362b587d153c6f93c9acd5a96
y: Secp256k1Point { purpose: "generator_mul", ge: Some(PK(PublicKey(dc4290abd01e7263ea742b8cf36dd77d3c32406d2a68df61ade476bfab5113a9e5e7ed516f3fef7f01ae5d067010f0631e87e1a892e6ac3521434482969c7ba1))) }
```

1. 打开新窗口，启动第 1 个参与方

```shell
cargo run --bin client http://127.0.0.1:8000 share1.json 54227dc697e4965ed7da323fbfa0d4e334bc12a497be2c0670726c6abc37b670
```

1. 打开新窗口，启动第 2 个参与方

```shell
cargo run --bin client http://127.0.0.1:8000 share2.json 326311d62a3a4cdb43527dc23689b498a1055a3f2a96661a6b8cfb4f68439b01
```

1. 打开新窗口，启动第 3 个参与方

```shell
cargo run --bin client http://127.0.0.1:8000 share3.json 3e276a5aac20e925e1345f6434ad73552908729362b587d153c6f93c9acd5a96
```

1. 最终检查生成出来的 MPC 钱包的公钥跟 **步骤 3** 碰撞出来的地址的公钥是相同的，成功创建了一个自定义钱包地址的 MPC 钱包。

当遇到 Ethereum Transaction 签名无法提交上链时可以借助这个工具 [https://toolkit.abdk.consulting/ethereum#recover-address](https://lifelonglearn.ing/r/go?url=aHR0cHM6Ly90b29sa2l0LmFiZGsuY29uc3VsdGluZy9ldGhlcmV1bSNyZWNvdmVyLWFkZHJlc3M=) 来 recover 一下签名后的交易，检查对应的地址和公钥是否正确。
