lotus/chain/address/address.go
Jakub Sztandera d745fa1af1 Switch to go-bls-sigs
License: MIT
Signed-off-by: Jakub Sztandera <kubuxu@protonmail.ch>
2019-07-19 13:56:58 +02:00

315 lines
7.6 KiB
Go

package address
import (
"bytes"
"encoding/json"
"fmt"
"strconv"
"strings"
"github.com/filecoin-project/go-bls-sigs"
"github.com/filecoin-project/go-leb128"
cbor "github.com/ipfs/go-ipld-cbor"
"github.com/minio/blake2b-simd"
"github.com/polydawn/refmt/obj/atlas"
)
func init() {
cbor.RegisterCborType(addressAtlasEntry)
}
var addressAtlasEntry = atlas.BuildEntry(Address{}).Transform().
TransformMarshal(atlas.MakeMarshalTransformFunc(
func(a Address) (string, error) {
return string(a.Bytes()), nil
})).
TransformUnmarshal(atlas.MakeUnmarshalTransformFunc(
func(x string) (Address, error) {
return NewFromBytes([]byte(x))
})).
Complete()
// Address is the go type that represents an address in the filecoin network.
type Address struct{ str string }
// Undef is the type that represents an undefined address.
var Undef = Address{}
// 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
)
// MainnetPrefix is the main network prefix.
const MainnetPrefix = "f"
// TestnetPrefix is the main network prefix.
const TestnetPrefix = "t"
// Protocol represents which protocol an address uses.
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
)
// Protocol returns the protocol used by the address.
func (a Address) Protocol() Protocol {
return a.str[0]
}
// Payload returns the payload of the address.
func (a Address) Payload() []byte {
return []byte(a.str[1:])
}
// Bytes returns the address as bytes.
func (a Address) Bytes() []byte {
return []byte(a.str)
}
// String returns an address encoded as a string.
func (a Address) String() string {
str, err := encode(Testnet, a)
if err != nil {
panic(err)
}
return str
}
// Empty returns true if the address is empty, false otherwise.
func (a Address) Empty() bool {
return a == Undef
}
// Unmarshal unmarshals the cbor bytes into the address.
func (a Address) Unmarshal(b []byte) error {
return cbor.DecodeInto(b, &a)
}
// Marshal marshals the address to cbor.
func (a Address) Marshal() ([]byte, error) {
return cbor.DumpObject(a)
}
// UnmarshalJSON implements the json unmarshal interface.
func (a *Address) UnmarshalJSON(b []byte) error {
in := strings.TrimSuffix(strings.TrimPrefix(string(b), `"`), `"`)
addr, err := decode(in)
if err != nil {
return err
}
*a = addr
return nil
}
// MarshalJSON implements the json marshal interface.
func (a Address) MarshalJSON() ([]byte, error) {
return json.Marshal(a.String())
}
// Format implements the Formatter interface.
func (a Address) Format(f fmt.State, c rune) {
switch c {
case 'v':
if a.Empty() {
fmt.Fprint(f, UndefAddressString) //nolint: errcheck
} else {
fmt.Fprintf(f, "[%x - %x]", a.Protocol(), a.Payload()) // nolint: errcheck
}
case 's':
fmt.Fprintf(f, "%s", a.String()) // nolint: errcheck
default:
fmt.Fprintf(f, "%"+string(c), a.Bytes()) // nolint: errcheck
}
}
// NewIDAddress returns an address using the ID protocol.
func NewIDAddress(id uint64) (Address, error) {
return newAddress(ID, leb128.FromUInt64(id))
}
// NewSecp256k1Address returns an address using the SECP256K1 protocol.
func NewSecp256k1Address(pubkey []byte) (Address, error) {
return newAddress(SECP256K1, addressHash(pubkey))
}
// NewActorAddress returns an address using the Actor protocol.
func NewActorAddress(data []byte) (Address, error) {
return newAddress(Actor, addressHash(data))
}
// NewBLSAddress returns an address using the BLS protocol.
func NewBLSAddress(pubkey []byte) (Address, error) {
return newAddress(BLS, pubkey)
}
// NewFromString returns the address represented by the string `addr`.
func NewFromString(addr string) (Address, error) {
return decode(addr)
}
// NewFromBytes return the address represented by the bytes `addr`.
func NewFromBytes(addr []byte) (Address, error) {
if len(addr) == 0 {
return Undef, nil
}
if len(addr) == 1 {
return Undef, ErrInvalidLength
}
return newAddress(addr[0], addr[1:])
}
// Checksum returns the checksum of `ingest`.
func Checksum(ingest []byte) []byte {
return hash(ingest, checksumHashConfig)
}
// 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)
}
func addressHash(ingest []byte) []byte {
return hash(ingest, payloadHashConfig)
}
func newAddress(protocol Protocol, payload []byte) (Address, error) {
switch protocol {
case ID:
case SECP256K1, Actor:
if len(payload) != PayloadHashLength {
return Undef, ErrInvalidPayload
}
case BLS:
if len(payload) != bls.PublicKeyBytes {
return Undef, ErrInvalidPayload
}
default:
return Undef, ErrUnknownProtocol
}
explen := 1 + len(payload)
buf := make([]byte, explen)
buf[0] = protocol
copy(buf[1:], payload)
return Address{string(buf)}, nil
}
func encode(network Network, addr Address) (string, error) {
if addr == Undef {
return UndefAddressString, nil
}
var ntwk string
switch network {
case Mainnet:
ntwk = MainnetPrefix
case Testnet:
ntwk = TestnetPrefix
default:
return UndefAddressString, ErrUnknownNetwork
}
var strAddr string
switch addr.Protocol() {
case SECP256K1, Actor, BLS:
cksm := Checksum(append([]byte{addr.Protocol()}, addr.Payload()...))
strAddr = ntwk + fmt.Sprintf("%d", addr.Protocol()) + AddressEncoding.WithPadding(-1).EncodeToString(append(addr.Payload(), cksm[:]...))
case ID:
strAddr = ntwk + fmt.Sprintf("%d", addr.Protocol()) + fmt.Sprintf("%d", leb128.ToUInt64(addr.Payload()))
default:
return UndefAddressString, ErrUnknownProtocol
}
return strAddr, nil
}
func decode(a string) (Address, error) {
if len(a) == 0 {
return Undef, nil
}
if a == UndefAddressString {
return Undef, nil
}
if len(a) > MaxAddressStringLength || len(a) < 3 {
return Undef, ErrInvalidLength
}
if string(a[0]) != MainnetPrefix && string(a[0]) != TestnetPrefix {
return Undef, ErrUnknownNetwork
}
var protocol Protocol
switch a[1] {
case '0':
protocol = ID
case '1':
protocol = SECP256K1
case '2':
protocol = Actor
case '3':
protocol = BLS
default:
return Undef, ErrUnknownProtocol
}
raw := a[2:]
if protocol == ID {
// 20 is length of math.MaxUint64 as a string
if len(raw) > 20 {
return Undef, ErrInvalidLength
}
id, err := strconv.ParseUint(raw, 10, 64)
if err != nil {
return Undef, ErrInvalidPayload
}
return newAddress(protocol, leb128.FromUInt64(id))
}
payloadcksm, err := AddressEncoding.WithPadding(-1).DecodeString(raw)
if err != nil {
return Undef, err
}
payload := payloadcksm[:len(payloadcksm)-ChecksumHashLength]
cksm := payloadcksm[len(payloadcksm)-ChecksumHashLength:]
if protocol == SECP256K1 || protocol == Actor {
if len(payload) != 20 {
return Undef, ErrInvalidPayload
}
}
if !ValidateChecksum(append([]byte{protocol}, payload...), cksm) {
return Undef, ErrInvalidChecksum
}
return newAddress(protocol, payload)
}
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))
}
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))
}
return hasher.Sum(nil)
}