package types import ( "bytes" "encoding/binary" "fmt" "io" cbor "github.com/ipfs/go-ipld-cbor" "github.com/polydawn/refmt/obj/atlas" cbg "github.com/whyrusleeping/cbor-gen" ) const SignatureMaxLength = 200 const ( KTSecp256k1 = "secp256k1" KTBLS = "bls" ) const ( IKTUnknown = -1 IKTSecp256k1 = iota IKTBLS ) func init() { cbor.RegisterCborType(atlas.BuildEntry(Signature{}).Transform(). TransformMarshal(atlas.MakeMarshalTransformFunc( func(s Signature) ([]byte, error) { buf := make([]byte, 4) n := binary.PutUvarint(buf, uint64(s.TypeCode())) return append(buf[:n], s.Data...), nil })). TransformUnmarshal(atlas.MakeUnmarshalTransformFunc( func(x []byte) (Signature, error) { return SignatureFromBytes(x) })). Complete()) } 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 IKTSecp256k1: ts = KTSecp256k1 case IKTBLS: ts = KTBLS default: return Signature{}, fmt.Errorf("unsupported signature type: %d", val) } return Signature{ Type: ts, Data: x[1:], }, nil } func (s *Signature) TypeCode() int { switch s.Type { case KTSecp256k1: return IKTSecp256k1 case KTBLS: return IKTBLS case "": return IKTUnknown default: panic("unsupported signature type") } } func (s *Signature) MarshalCBOR(w io.Writer) error { header := cbg.CborEncodeMajorType(cbg.MajByteString, uint64(len(s.Data)+1)) if _, err := w.Write(header); err != nil { return err } if _, err := w.Write([]byte{byte(s.TypeCode())}); err != nil { return err } if _, err := w.Write(s.Data); err != nil { return err } return nil } func (s *Signature) UnmarshalCBOR(br io.Reader) error { maj, l, err := cbg.CborReadHeader(br) if err != nil { return err } if maj != cbg.MajByteString { return fmt.Errorf("cbor input for signature was not a byte string") } if l > SignatureMaxLength { return fmt.Errorf("cbor byte array for signature was too long") } buf := make([]byte, l) if _, err := io.ReadFull(br, buf); err != nil { return err } switch buf[0] { default: return fmt.Errorf("invalid signature type in cbor input: %d", buf[0]) case IKTSecp256k1: s.Type = KTSecp256k1 case IKTBLS: s.Type = KTBLS } s.Data = buf[1:] return nil } func (s *Signature) Equals(o *Signature) bool { return s.Type == o.Type && bytes.Equal(s.Data, o.Data) }