lotus/chain/wallet.go
2019-07-12 17:41:32 -07:00

208 lines
3.7 KiB
Go

package chain
import (
"encoding/binary"
"fmt"
"sort"
"github.com/filecoin-project/go-lotus/chain/address"
"github.com/filecoin-project/go-lotus/lib/bls-signatures"
"github.com/filecoin-project/go-lotus/lib/crypto"
"github.com/minio/blake2b-simd"
)
const (
KTSecp256k1 = "secp256k1"
KTBLS = "bls"
)
type Wallet struct {
keys map[address.Address]*KeyInfo
}
func NewWallet() *Wallet {
return &Wallet{keys: make(map[address.Address]*KeyInfo)}
}
type Signature struct {
Type string
Data []byte
}
func SignatureFromBytes(x []byte) (Signature, error) {
val, nr := binary.Uvarint(x)
if nr != 1 {
return Signature{}, fmt.Errorf("signatures with type field longer than one byte are invalid")
}
var ts string
switch val {
case 1:
ts = KTSecp256k1
default:
return Signature{}, fmt.Errorf("unsupported signature type: %d", val)
}
return Signature{
Type: ts,
Data: x[1:],
}, nil
}
func (s *Signature) Verify(addr address.Address, msg []byte) error {
b2sum := blake2b.Sum256(msg)
switch s.Type {
case KTSecp256k1:
pubk, err := crypto.EcRecover(b2sum[:], s.Data)
if err != nil {
return err
}
maybeaddr, err := address.NewSecp256k1Address(pubk)
if err != nil {
return err
}
if addr != maybeaddr {
return fmt.Errorf("signature did not match")
}
return nil
default:
return fmt.Errorf("cannot verify signature of unsupported type: %s", s.Type)
}
}
func (s *Signature) TypeCode() int {
switch s.Type {
case KTSecp256k1:
return 1
case KTBLS:
return 2
default:
panic("unsupported signature type")
}
}
func (w *Wallet) Sign(addr address.Address, msg []byte) (*Signature, error) {
ki, err := w.findKey(addr)
if err != nil {
return nil, err
}
switch ki.Type {
case KTSecp256k1:
b2sum := blake2b.Sum256(msg)
sig, err := crypto.Sign(ki.PrivateKey, b2sum[:])
if err != nil {
return nil, err
}
return &Signature{
Type: KTSecp256k1,
Data: sig,
}, nil
case KTBLS:
var pk bls.PrivateKey
copy(pk[:], ki.PrivateKey)
sig := bls.PrivateKeySign(pk, msg)
return &Signature{
Type: KTBLS,
Data: sig[:],
}, nil
default:
panic("cant do it sir")
}
}
func (w *Wallet) findKey(addr address.Address) (*KeyInfo, error) {
ki, ok := w.keys[addr]
if !ok {
return nil, fmt.Errorf("key not for given address not found in wallet")
}
return ki, nil
}
func (w *Wallet) Export(addr address.Address) ([]byte, error) {
panic("nyi")
}
func (w *Wallet) Import(kdata []byte) (address.Address, error) {
panic("nyi")
}
func (w *Wallet) ListAddrs() []address.Address {
var out []address.Address
for a := range w.keys {
out = append(out, a)
}
sort.Slice(out, func(i, j int) bool {
return out[i].String() < out[j].String()
})
return out
}
func (w *Wallet) GenerateKey(typ string) (address.Address, error) {
switch typ {
case KTSecp256k1:
k, err := crypto.GenerateKey()
if err != nil {
return address.Undef, err
}
ki := &KeyInfo{
PrivateKey: k,
Type: typ,
}
addr := ki.Address()
w.keys[addr] = ki
return addr, nil
case KTBLS:
priv := bls.PrivateKeyGenerate()
ki := &KeyInfo{
PrivateKey: priv[:],
Type: KTBLS,
}
addr := ki.Address()
w.keys[addr] = ki
return addr, nil
default:
return address.Undef, fmt.Errorf("invalid key type: %s", typ)
}
}
type KeyInfo struct {
PrivateKey []byte
Type string
}
func (ki *KeyInfo) Address() address.Address {
switch ki.Type {
case KTSecp256k1:
pub := crypto.PublicKey(ki.PrivateKey)
addr, err := address.NewSecp256k1Address(pub)
if err != nil {
panic(err)
}
return addr
case KTBLS:
var pk bls.PrivateKey
copy(pk[:], ki.PrivateKey)
pub := bls.PrivateKeyPublicKey(pk)
a, err := address.NewBLSAddress(pub[:])
if err != nil {
panic(err)
}
return a
default:
panic("unsupported key type")
}
}