GoでEd25519+SHA256のアドレス導出してみる(Symbol,Catapult,RC5)

# はじめに

catapult-serverのv0.9.3.1から、公開鍵の導出方法が変わりました。

https://github.com/nemtech/catapult-server/releases/tag/v0.9.3.1

いままでは、Ed25519+Keccak256だったのが、標準のEd25519に変わりました。

ということで、GoのEd25519ライブラリが使えるんじゃないかと思うので、やってみたいと思います。


# 秘密鍵から公開鍵とアドレスを導出する

こちらのNIP10を参考にやっていきます。

https://github.com/nemtech/NIP/blob/eef91708a05fc6ce24a903d742ff46682008a8b7/NIPs/nip-0010.md

## 公開鍵

NIP10に例示してある秘密鍵と公開鍵は、

```yaml
private: 575dbb3062267eff57c970a336ebbc8fbcfe12c5bd3ed7bc11eb0481d7704ced
public:  2e834140fd66cf87b254a693a2c7862c819217b676d3943267156625e816ec6f
```

なので、これをやってみる。

```go
package main

import "fmt"
import "encoding/hex"
import "crypto/ed25519"

// https://golang.org/pkg/crypto/ed25519/

func main() {
	seed3, _ := hex.DecodeString("575dbb3062267eff57c970a336ebbc8fbcfe12c5bd3ed7bc11eb0481d7704ced")
	priv3 := ed25519.NewKeyFromSeed(seed3)
	var pub3 interface{} = priv3.Public()
	var pub3a ed25519.PublicKey = pub3.(ed25519.PublicKey)
	fmt.Println(hex.EncodeToString(pub3a))
}
```

`seed3`に代入しているのが秘密鍵です。

実行結果

```
2e834140fd66cf87b254a693a2c7862c819217b676d3943267156625e816ec6f
```

できました。

NIP10にはオールゼロの秘密鍵も例示してあります。

```yaml
private : 0000000000000000000000000000000000000000000000000000000000000000
public  : 3b6a27bcceb6a42d62a3a8d02a6f0d73653215771de243a63ac048a18b59da29
```

`seed3`にオールゼロを代行してみます。

実行結果

```
3b6a27bcceb6a42d62a3a8d02a6f0d73653215771de243a63ac048a18b59da29
```

## アドレス

次にアドレスです。NIP10には以下のものが例示されています。

```yaml
public : 2e834140fd66cf87b254a693a2c7862c819217b676d3943267156625e816ec6f
address: TATNE7Q5BITMUTRRN6IB4I7FLSDRDWZA37JGO5UW
```

### ハッシュ化アドレス

次にアドレスです。

NIP10によると、次の手順が必要です。

1. 公開鍵をSHA3-256する
2. それをRIPEMD160する
3. ネットワークIDを付加する
4. それをSHA3-256して先頭4バイトを取得する
5. 3にそれを付加する

6.の「BASE32する」は次でやります。

```go
package main

import (
	"encoding/hex"
	"fmt"
	"io"
)
import "golang.org/x/crypto/sha3"
import "golang.org/x/crypto/ripemd160"

// https://godoc.org/golang.org/x/crypto/sha3
// https://godoc.org/golang.org/x/crypto/ripemd160

func main() {
	var publicKey string = "2e834140fd66cf87b254a693a2c7862c819217b676d3943267156625e816ec6f"
	pubByte, _ := hex.DecodeString(publicKey)
	hash1 := sha3.Sum256(pubByte)
	rip := ripemd160.New()
	_, _ = io.WriteString(rip, string(hash1[:]))
	hash2 := rip.Sum(nil)
	prefix, _ := hex.DecodeString("98")
	rawAddress := make([]byte, 20)
	copy(rawAddress, hash2)
	rawAddress = append(rawAddress[:1], rawAddress[0:]...)
	rawAddress[0] = prefix[0]
	checksum := sha3.Sum256(rawAddress)
	rawAddress = append(rawAddress, checksum[:4]...)
	fmt.Println(hex.EncodeToString(rawAddress))
}
```

実行結果

```
9826d27e1d0a26ca4e316f901e23e55c8711db20dfd2677696
```

この段階ではNIP10と一致しているかははっきりとしません。次に行きます。


### BASE32

先ほど導出したハッシュ化アドレスをBASE32エンコーディングします。

```go
package main

import (
	"encoding/base32"
	"encoding/hex"
	"fmt"
)

func main() {
	address := "9826d27e1d0a26ca4e316f901e23e55c8711db20dfd2677696"
	addressByte, _ := hex.DecodeString(address)
	encode := base32.StdEncoding.EncodeToString(addressByte)
	fmt.Println(encode)
}
```

実行結果

```
TATNE7Q5BITMUTRRN6IB4I7FLSDRDWZA37JGO5UW
```

NIP10と同じものが導出できました。

# おわりに

Goに存在する一般的なライブラリ類を使って、秘密鍵から公開鍵とアドレスを導出することができました。

ソースの書き方がメチャクチャなのは許してください。