Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for certificates in transactions #14

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions address/stake_credential.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const (
)

type StakeCredential struct {
_ struct{} `cbor:",toarray"`
Kind StakeCredentialType `cbor:"0,keyasint,omitempty"`
Payload []byte `cbor:"1,keyasint,omitempty"`
}
Expand Down
32 changes: 32 additions & 0 deletions tx/certificates.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package tx

import (
"github.com/fivebinaries/go-cardano-serialization/tx/certificates"
)

type Certificate = certificates.Certificate

type CertificateKind = certificates.CertificateKind

const (
StakeRegistration = certificates.StakeRegistration
StakeDeregistration = certificates.StakeDeregistration
StakeDelegation = certificates.StakeDelegation
PoolRegistration = certificates.PoolRegistration
PoolRetirement = certificates.PoolRetirement
)

type StakeCertificate = certificates.StakeCert
type DelegationCertificate = certificates.DelegationCert
type PoolRegistrationCertificate = certificates.PoolRegistrationCert
type PoolRetirementCertificate = certificates.PoolRetirementCert

var (
NewCertificate = certificates.NewCertificate

NewStakeRegistrationCertificate = certificates.NewStakeRegistrationCertificate
NewStakeDeregistrationCertificate = certificates.NewStakeDeregistrationCertificate
NewStakeDelegationCertificate = certificates.NewStakeDelegationCertificate
NewPoolRegistrationCertificate = certificates.NewPoolRegistrationCertificate
NewPoolRetirmentCertificate = certificates.NewPoolRetirmentCertificate
)
274 changes: 274 additions & 0 deletions tx/certificates/certificate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
package certificates

import (
"encoding/hex"
"github.com/fivebinaries/go-cardano-serialization/address"
"github.com/fivebinaries/go-cardano-serialization/internal/bech32"
"github.com/fxamacker/cbor/v2"
)

// opaque polymorphic certificate struct
type Certificate struct {
kind CertificateKind

cred *address.StakeCredential
poolKeyHash [28]byte
poolParams interface{}
epoch uint
}

var _ iWithKind = &Certificate{}

func (c *Certificate) Kind() CertificateKind { return c.kind }

func (c *Certificate) AsCert() interface{} {
switch c.kind {
case StakeRegistration, StakeDeregistration:
return &asStakeCert{iWithKind: c, iWithCred: &certWithCred{Certificate: c}}
case StakeDelegation:
return &asDelegationCert{iWithKind: c, iWithCred: &certWithCred{Certificate: c}, iWithPool: &certWithPool{Certificate: c}}
case PoolRegistration:
return &asPoolRegistrationCert{iWithKind: c, iWithParams: &certWithParams{c}}
case PoolRetirement:
return &asPoolRetirementCert{iWithKind: c, iWithPool: &certWithPool{Certificate: c}, iWithEpoch: &certWithEpoch{c}}
}
return UnknownCertKind
}

func (c *Certificate) AsStakeCert() (StakeCert, error) {
if c.kind != StakeRegistration && c.kind != StakeDeregistration {
return nil, WrongCertKind
}
return c.AsCert().(StakeCert), nil
}
func (c *Certificate) AsDelegationCert() (DelegationCert, error) {
if c.kind != StakeDelegation {
return nil, WrongCertKind
}
return c.AsCert().(DelegationCert), nil
}
func (c *Certificate) AsPoolRegistrationCert() (PoolRegistrationCert, error) {
if c.kind != PoolRegistration {
return nil, WrongCertKind
}
return c.AsCert().(PoolRegistrationCert), nil
}
func (c *Certificate) AsPoolRetirementCert() (PoolRetirementCert, error) {
if c.kind != PoolRetirement {
return nil, WrongCertKind
}
return c.AsCert().(PoolRetirementCert), nil
}

func (c *Certificate) UnmarshalCBOR(in []byte) (err error) {
certKind := CertificateKind(in[1])
switch certKind {
case StakeRegistration, StakeDeregistration:
x := &stakeCert{}
err = cbor.Unmarshal(in, x)
if err != nil {
return
}
c.kind = certKind
c.cred = x.Cred
case StakeDelegation:
x := &delegationCert{}
err = cbor.Unmarshal(in, x)
if err != nil {
return
}
c.kind = certKind
c.cred = x.Cred
copy(c.poolKeyHash[:], x.Pool[:])
case PoolRegistration:
x := &poolRegistrationCert{}
err = cbor.Unmarshal(in, x)
if err != nil {
return
}
c.kind = certKind
c.poolParams = x.Params
case PoolRetirement:
x := &poolRetirementCert{}
err = cbor.Unmarshal(in, x)
if err != nil {
return
}
c.kind = certKind
copy(c.poolKeyHash[:], x.Pool[:])
c.epoch = x.Epoch
}
return
}

func (c *Certificate) MarshalCBOR() ([]byte, error) {
switch c.kind {
case StakeRegistration, StakeDeregistration:
x := &stakeCert{
Kind: c.kind,
Cred: c.cred,
}
return cbor.Marshal(x)
case StakeDelegation:
x := &delegationCert{
Kind: c.kind,
Cred: c.cred,
}
copy(x.Pool[:], c.poolKeyHash[:])
return cbor.Marshal(x)
case PoolRegistration:
x := &poolRegistrationCert{
Kind: c.kind,
Params: c.poolParams,
}
return cbor.Marshal(x)
case PoolRetirement:
x := &poolRetirementCert{
Kind: c.kind,
Epoch: c.epoch,
}
copy(x.Pool[:], c.poolKeyHash[:])
return cbor.Marshal(x)
}
return nil, UnknownCertKind
}

func parseStakeCred(in interface{}) (*address.StakeCredential, error) {
var cred address.StakeCredential
switch v := in.(type) {
case string:
{
var (
addr address.Address
raddr *address.RewardAddress
err error
ok bool
)
addr, err = address.NewAddress(v)
if err != nil {
addr, err = address.NewAddressFromHex(v)
if err != nil {
return nil, BadStakeCredential
}
}
raddr, ok = addr.(*address.RewardAddress)
if !ok {
return nil, BadStakeCredential
}
cred = raddr.Stake
}
case *address.StakeCredential:
cred = *v
case address.StakeCredential:
cred = v
}
return &cred, nil
}

func parsePool(in interface{}) ([28]byte, error) {
pool := [28]byte{0}
switch v := in.(type) {
case [28]byte:
// assuming it is already raw bytes
copy(pool[:], v[:])
case string:
if len(v) < 5 {
return [28]byte{0}, BadPoolId
}
if v[:5] == "pool1" {
// is bech32 encoded
hrp, data, err := bech32.Decode(v)
if err != nil {
return [28]byte{0}, err
}
if hrp != "pool" {
return [28]byte{0}, BadPoolId
}
copy(pool[:], data)
} else {
// assuming this string is the HEX of the data
if hex.DecodedLen(len(v)) != 28 {
return [28]byte{0}, BadPoolId
}
data, err := hex.DecodeString(v)
if err != nil {
return [28]byte{0}, err
}
if len(data) != 28 {
return [28]byte{0}, BadPoolId
}
copy(pool[:], data)
}
}
return pool, nil
}

func NewCertificate(kind CertificateKind, args ...interface{}) (*Certificate, error) {
switch kind {
case StakeRegistration, StakeDeregistration:
if len(args) != 1 {
return nil, BadCertificateParams
}
cred, err := parseStakeCred(args[0])
if err != nil {
return nil, err
}
return &Certificate{kind: kind, cred: cred}, nil
case StakeDelegation:
if len(args) != 2 {
return nil, BadCertificateParams
}
cred, err := parseStakeCred(args[0])
if err != nil {
return nil, err
}
pool, err := parsePool(args[1])
if err != nil {
return nil, err
}
return &Certificate{kind: StakeDelegation, cred: cred, poolKeyHash: pool}, nil

case PoolRegistration:
if len(args) != 1 {
return nil, BadCertificateParams
}
// nothing to parse yet
return &Certificate{kind: PoolRegistration, poolParams: args[0]}, nil

case PoolRetirement:
if len(args) != 2 {
return nil, BadCertificateParams
}
pool, err := parsePool(args[0])
if err != nil {
return nil, err
}
epoch, ok := args[1].(uint)
if !ok {
return nil, BadCertificateParams
}
return &Certificate{kind: PoolRegistration, poolKeyHash: pool, epoch: epoch}, nil
default:
return nil, UnknownCertKind
}
}

func NewStakeRegistrationCertificate(in interface{}) (*Certificate, error) {
return NewCertificate(StakeRegistration, in)
}

func NewStakeDeregistrationCertificate(in interface{}) (*Certificate, error) {
return NewCertificate(StakeDeregistration, in)
}

func NewStakeDelegationCertificate(credIn, poolIn interface{}) (*Certificate, error) {
return NewCertificate(StakeDelegation, credIn, poolIn)
}

func NewPoolRegistrationCertificate(params interface{}) (*Certificate, error) {
return NewCertificate(PoolRegistration, params)
}

func NewPoolRetirmentCertificate(poolIn, epochIn interface{}) (*Certificate, error) {
return NewCertificate(PoolRegistration, poolIn, epochIn)
}
13 changes: 13 additions & 0 deletions tx/certificates/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package certificates

import (
"errors"
)

var (
UnknownCertKind = errors.New("Unkown certificate kind")
WrongCertKind = errors.New("Wrong certificate kind")
BadCertificateParams = errors.New("Unable to parse certificate parameters")
BadStakeCredential = errors.New("Unable to parse stake credential")
BadPoolId = errors.New("Unable to parse stake pool identifier")
)
53 changes: 53 additions & 0 deletions tx/certificates/interfaces.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package certificates

import (
"github.com/fivebinaries/go-cardano-serialization/address"
)

// implicit for code-sharing
type (
iWithKind interface {
Kind() CertificateKind
}

iWithCred interface {
Cred() address.StakeCredential
}

iWithPool interface {
Pool() [28]byte
}

iWithEpoch interface {
Epoch() uint
}

iWithParams interface {
Params() interface{}
}
)

// explicit ifaces
type (
StakeCert interface {
Kind() CertificateKind
Cred() address.StakeCredential
}

DelegationCert interface {
Kind() CertificateKind
Cred() address.StakeCredential
Pool() [28]byte
}

PoolRegistrationCert interface {
Kind() CertificateKind
Params() interface{}
}

PoolRetirementCert interface {
Kind() CertificateKind
Pool() [28]byte
Epoch() uint
}
)
Loading