Skip to content

Commit

Permalink
Merge branch 'serix-iota-core' into feat/implicit-accounts
Browse files Browse the repository at this point in the history
  • Loading branch information
cyberphysic4l committed Sep 21, 2023
2 parents 733459a + 31fb36e commit e3fceae
Show file tree
Hide file tree
Showing 52 changed files with 424 additions and 219 deletions.
5 changes: 2 additions & 3 deletions address.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"

"github.com/iotaledger/hive.go/constraints"
"github.com/iotaledger/hive.go/ierrors"
"github.com/iotaledger/hive.go/serializer/v2"
"github.com/iotaledger/iota.go/v4/bech32"
Expand Down Expand Up @@ -111,6 +112,7 @@ type Address interface {
Sizer
NonEphemeralObject
fmt.Stringer
constraints.Cloneable[Address]

// Type returns the type of the address.
Type() AddressType
Expand All @@ -127,9 +129,6 @@ type Address interface {

// Key returns a string which can be used to index the Address in a map.
Key() string

// Clone clones the Address.
Clone() Address
}

type AddressCapabilities interface {
Expand Down
18 changes: 15 additions & 3 deletions allotment.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,33 @@ import (
"bytes"
"sort"

"github.com/iotaledger/hive.go/lo"
"github.com/iotaledger/hive.go/serializer/v2"
)

// BlockIssuanceCredits defines the type of block issuance credits.
type BlockIssuanceCredits int64

// Allotments is a slice of Allotment.
type Allotments []*Allotment

// Allotment is a struct that represents a list of account IDs and an allotted value.
type Allotment struct {
AccountID AccountID `serix:"0"`
Value Mana `serix:"1"`
}

func (a *Allotment) Clone() *Allotment {
return &Allotment{
AccountID: a.AccountID,
Value: a.Value,
}
}

// Allotments is a slice of Allotment.
type Allotments []*Allotment

func (a Allotments) Clone() Allotments {
return lo.CloneSlice(a)
}

// Sort sorts the allotments in lexical order.
func (a Allotments) Sort() {
sort.Slice(a, func(i, j int) bool {
Expand Down
2 changes: 1 addition & 1 deletion api/single_version_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
iotago "github.com/iotaledger/iota.go/v4"
)

func SingleVersionProvider(api iotago.API) Provider {
func SingleVersionProvider(api iotago.API) iotago.APIProvider {
return &singleVersionProvider{api: api}
}

Expand Down
16 changes: 7 additions & 9 deletions api/api_provider.go → api_provider.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
package api
package iotago

import iotago "github.com/iotaledger/iota.go/v4"

type Provider interface {
type APIProvider interface {
// APIForVersion returns the API for the given version.
APIForVersion(iotago.Version) (iotago.API, error)
APIForVersion(Version) (API, error)

// APIForSlot returns the API for the given slot.
APIForSlot(iotago.SlotIndex) iotago.API
APIForSlot(SlotIndex) API

// APIForEpoch returns the API for the given epoch.
APIForEpoch(iotago.EpochIndex) iotago.API
APIForEpoch(EpochIndex) API

// CurrentAPI returns the API for the current slot.
CurrentAPI() iotago.API
CurrentAPI() API

// LatestAPI returns the API for the latest supported protocol version.
LatestAPI() iotago.API
LatestAPI() API
}
2 changes: 1 addition & 1 deletion api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ func TestProtocolParametersJSONMarshalling(t *testing.T) {
iotago.WithRewardsOptions(10, 8, 8, 31, 1154, 2, 1),
)

protoParamsJSON := `{"type":0,"version":3,"networkName":"xxxNetwork","bech32Hrp":"xxx","rentStructure":{"vByteCost":6,"vByteFactorData":7,"vByteFactorKey":8,"vByteFactorIssuerKeys":9,"vByteFactorStakingFeature":10,"vByteFactorDelegation":10},"workScoreStructure":{"dataKilobyte":1,"block":2,"missingParent":3,"input":4,"contextInput":5,"output":6,"nativeToken":7,"staking":8,"blockIssuer":9,"allotment":10,"signatureEd25519":11,"minStrongParentsThreshold":12},"tokenSupply":"1234567890987654321","genesisUnixTimestamp":"1681373293","slotDurationInSeconds":10,"slotsPerEpochExponent":13,"manaStructure":{"manaBitsCount":1,"manaGenerationRate":1,"manaGenerationRateExponent":27,"manaDecayFactors":[10,20],"manaDecayFactorsExponent":32,"manaDecayFactorEpochsSum":1337,"manaDecayFactorEpochsSumExponent":20},"stakingUnbondingPeriod":"11","validationBlocksPerSlot":10,"punishmentEpochs":"9","livenessThreshold":"3","minCommittableAge":"10","maxCommittableAge":"20","epochNearingThreshold":"24","congestionControlParameters":{"rmcMin":"500","increase":"500","decrease":"500","increaseThreshold":800000,"decreaseThreshold":500000,"schedulerRate":100000,"minMana":"1","maxBufferSize":1000,"maxValidationBufferSize":100},"versionSignaling":{"windowSize":3,"windowTargetRatio":4,"activationOffset":1},"rewardsParameters":{"validatorBlocksPerSlot":10,"profitMarginExponent":8,"bootstrappinDuration":"1154","rewardsManaShareCoefficient":"2","decayBalancingConstantExponent":8,"decayBalancingConstant":"1","poolCoefficientExponent":31}}`
protoParamsJSON := `{"type":0,"version":3,"networkName":"xxxNetwork","bech32Hrp":"xxx","rentStructure":{"vByteCost":6,"vByteFactorData":7,"vByteFactorKey":8,"vByteFactorBlockIssuerKey":9,"vByteFactorStakingFeature":10,"vByteFactorDelegation":10},"workScoreStructure":{"dataByte":1,"block":2,"missingParent":3,"input":4,"contextInput":5,"output":6,"nativeToken":7,"staking":8,"blockIssuer":9,"allotment":10,"signatureEd25519":11,"minStrongParentsThreshold":12},"tokenSupply":"1234567890987654321","genesisUnixTimestamp":"1681373293","slotDurationInSeconds":10,"slotsPerEpochExponent":13,"manaStructure":{"bitsCount":1,"generationRate":1,"generationRateExponent":27,"decayFactors":[10,20],"decayFactorsExponent":32,"decayFactorEpochsSum":1337,"decayFactorEpochsSumExponent":20},"stakingUnbondingPeriod":"11","validationBlocksPerSlot":10,"punishmentEpochs":"9","livenessThreshold":"3","minCommittableAge":"10","maxCommittableAge":"20","epochNearingThreshold":"24","congestionControlParameters":{"minReferenceManaCost":"500","increase":"500","decrease":"500","increaseThreshold":800000,"decreaseThreshold":500000,"schedulerRate":100000,"minMana":"1","maxBufferSize":1000,"maxValidationBufferSize":100},"versionSignaling":{"windowSize":3,"windowTargetRatio":4,"activationOffset":1},"rewardsParameters":{"validatorBlocksPerSlot":10,"profitMarginExponent":8,"bootstrappinDuration":"1154","manaShareCoefficient":"2","decayBalancingConstantExponent":8,"decayBalancingConstant":"1","poolCoefficientExponent":31}}`

jsonProtoParams, err := tpkg.TestAPI.JSONEncode(protoParams)
require.NoError(t, err)
Expand Down
2 changes: 1 addition & 1 deletion api_v3.go
Original file line number Diff line number Diff line change
Expand Up @@ -631,7 +631,7 @@ func calculateRewards(protoParams ProtocolParameters) (initialRewards, finalRewa
manaStructure := protoParams.ManaDecayProvider()

// final reward, after bootstrapping phase
result, err := safemath.SafeMul(uint64(protoParams.TokenSupply()), protoParams.RewardsParameters().RewardsManaShareCoefficient)
result, err := safemath.SafeMul(uint64(protoParams.TokenSupply()), protoParams.RewardsParameters().ManaShareCoefficient)
if err != nil {
return 0, 0, ierrors.Wrap(err, "failed to calculate target reward due to tokenSupply and RewardsManaShareCoefficient multiplication overflow")
}
Expand Down
32 changes: 16 additions & 16 deletions api_v3_protocol_parameters.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,22 +167,22 @@ func WithNetworkOptions(networkName string, bech32HRP NetworkPrefix) options.Opt
}
}

func WithSupplyOptions(totalSupply BaseToken, vByteCost uint32, vBFactorData, vBFactorKey, vBFactorIssuerKeys, vBFactorStakingFeature, vBFactorDelegation VByteCostFactor) options.Option[V3ProtocolParameters] {
func WithSupplyOptions(totalSupply BaseToken, vByteCost uint32, vBFactorData, vBFactorKey, vBFactorBlockIssuerKey, vBFactorStakingFeature, vBFactorDelegation VByteCostFactor) options.Option[V3ProtocolParameters] {
return func(p *V3ProtocolParameters) {
p.basicProtocolParameters.TokenSupply = totalSupply
p.basicProtocolParameters.RentStructure = RentStructure{
VByteCost: vByteCost,
VBFactorData: vBFactorData,
VBFactorKey: vBFactorKey,
VBFactorIssuerKeys: vBFactorIssuerKeys,
VBFactorBlockIssuerKey: vBFactorBlockIssuerKey,
VBFactorStakingFeature: vBFactorStakingFeature,
VBFactorDelegation: vBFactorDelegation,
}
}
}

func WithWorkScoreOptions(
dataKilobyte WorkScore,
dataByte WorkScore,
block WorkScore,
missingParent WorkScore,
input WorkScore,
Expand All @@ -197,7 +197,7 @@ func WithWorkScoreOptions(
) options.Option[V3ProtocolParameters] {
return func(p *V3ProtocolParameters) {
p.basicProtocolParameters.WorkScoreStructure = WorkScoreStructure{
DataKilobyte: dataKilobyte,
DataByte: dataByte,
Block: block,
MissingParent: missingParent,
Input: input,
Expand All @@ -221,15 +221,15 @@ func WithTimeProviderOptions(genesisTimestamp int64, slotDuration uint8, slotsPe
}
}

func WithManaOptions(manaBitsCount uint8, manaGenerationRate uint8, manaGenerationRateExponent uint8, manaDecayFactors []uint32, manaDecayFactorsExponent uint8, manaDecayFactorEpochsSum uint32, manaDecayFactorEpochsSumExponent uint8) options.Option[V3ProtocolParameters] {
func WithManaOptions(bitsCount uint8, generationRate uint8, generationRateExponent uint8, decayFactors []uint32, decayFactorsExponent uint8, decayFactorEpochsSum uint32, decayFactorEpochsSumExponent uint8) options.Option[V3ProtocolParameters] {
return func(p *V3ProtocolParameters) {
p.basicProtocolParameters.ManaStructure.ManaBitsCount = manaBitsCount
p.basicProtocolParameters.ManaStructure.ManaGenerationRate = manaGenerationRate
p.basicProtocolParameters.ManaStructure.ManaGenerationRateExponent = manaGenerationRateExponent
p.basicProtocolParameters.ManaStructure.ManaDecayFactors = manaDecayFactors
p.basicProtocolParameters.ManaStructure.ManaDecayFactorsExponent = manaDecayFactorsExponent
p.basicProtocolParameters.ManaStructure.ManaDecayFactorEpochsSum = manaDecayFactorEpochsSum
p.basicProtocolParameters.ManaStructure.ManaDecayFactorEpochsSumExponent = manaDecayFactorEpochsSumExponent
p.basicProtocolParameters.ManaStructure.BitsCount = bitsCount
p.basicProtocolParameters.ManaStructure.GenerationRate = generationRate
p.basicProtocolParameters.ManaStructure.GenerationRateExponent = generationRateExponent
p.basicProtocolParameters.ManaStructure.DecayFactors = decayFactors
p.basicProtocolParameters.ManaStructure.DecayFactorsExponent = decayFactorsExponent
p.basicProtocolParameters.ManaStructure.DecayFactorEpochsSum = decayFactorEpochsSum
p.basicProtocolParameters.ManaStructure.DecayFactorEpochsSumExponent = decayFactorEpochsSumExponent
}
}

Expand All @@ -242,9 +242,9 @@ func WithLivenessOptions(livenessThreshold SlotIndex, minCommittableAge SlotInde
}
}

func WithCongestionControlOptions(rmcMin Mana, rmcIncrease Mana, rmcDecrease Mana, rmcIncreaseThreshold WorkScore, rmcDecreaseThreshold WorkScore, schedulerRate WorkScore, minMana Mana, maxBufferSize uint32, maxValBufferSize uint32) options.Option[V3ProtocolParameters] {
func WithCongestionControlOptions(minReferenceManaCost Mana, rmcIncrease Mana, rmcDecrease Mana, rmcIncreaseThreshold WorkScore, rmcDecreaseThreshold WorkScore, schedulerRate WorkScore, minMana Mana, maxBufferSize uint32, maxValBufferSize uint32) options.Option[V3ProtocolParameters] {
return func(p *V3ProtocolParameters) {
p.basicProtocolParameters.CongestionControlParameters.RMCMin = rmcMin
p.basicProtocolParameters.CongestionControlParameters.MinReferenceManaCost = minReferenceManaCost
p.basicProtocolParameters.CongestionControlParameters.Increase = rmcIncrease
p.basicProtocolParameters.CongestionControlParameters.Decrease = rmcDecrease
p.basicProtocolParameters.CongestionControlParameters.IncreaseThreshold = rmcIncreaseThreshold
Expand Down Expand Up @@ -274,12 +274,12 @@ func WithVersionSignalingOptions(windowSize uint8, windowTargetRatio uint8, acti
}
}

func WithRewardsOptions(validatorBlocksPerSlot, profitMarginExponent, decayBalancingConstantExponent, poolCoefficientExponent uint8, bootstrappingDuration EpochIndex, rewardsManaShareCoefficient, decayBalancingConstant uint64) options.Option[V3ProtocolParameters] {
func WithRewardsOptions(validatorBlocksPerSlot, profitMarginExponent, decayBalancingConstantExponent, poolCoefficientExponent uint8, bootstrappingDuration EpochIndex, manaShareCoefficient, decayBalancingConstant uint64) options.Option[V3ProtocolParameters] {
return func(p *V3ProtocolParameters) {
p.basicProtocolParameters.RewardsParameters.ValidatorBlocksPerSlot = validatorBlocksPerSlot
p.basicProtocolParameters.RewardsParameters.ProfitMarginExponent = profitMarginExponent
p.basicProtocolParameters.RewardsParameters.BootstrappingDuration = bootstrappingDuration
p.basicProtocolParameters.RewardsParameters.RewardsManaShareCoefficient = rewardsManaShareCoefficient
p.basicProtocolParameters.RewardsParameters.ManaShareCoefficient = manaShareCoefficient
p.basicProtocolParameters.RewardsParameters.DecayBalancingConstantExponent = decayBalancingConstantExponent
p.basicProtocolParameters.RewardsParameters.DecayBalancingConstant = decayBalancingConstant
p.basicProtocolParameters.RewardsParameters.PoolCoefficientExponent = poolCoefficientExponent
Expand Down
34 changes: 26 additions & 8 deletions attestation.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
hiveEd25519 "github.com/iotaledger/hive.go/crypto/ed25519"
"github.com/iotaledger/hive.go/ierrors"
"github.com/iotaledger/hive.go/lo"
"github.com/iotaledger/hive.go/serializer/v2/serix"
)

// Attestations is a slice of Attestation.
Expand All @@ -27,6 +28,23 @@ func NewAttestation(api API, block *ProtocolBlock) *Attestation {
}
}

func AttestationFromBytes(apiProvider APIProvider) func(bytes []byte) (attestation *Attestation, consumedBytes int, err error) {
return func(bytes []byte) (attestation *Attestation, consumedBytes int, err error) {
attestation = new(Attestation)

var version Version
if version, consumedBytes, err = VersionFromBytes(bytes); err != nil {
err = ierrors.Wrap(err, "failed to parse version")
} else if attestation.API, err = apiProvider.APIForVersion(version); err != nil {
err = ierrors.Wrapf(err, "failed to retrieve API for version %d", version)
} else if consumedBytes, err = attestation.API.Decode(bytes, attestation, serix.WithValidation()); err != nil {
err = ierrors.Wrap(err, "failed to deserialize attestation")
}

return attestation, consumedBytes, err
}
}

func (a *Attestation) Compare(other *Attestation) int {
switch {
case a == nil && other == nil:
Expand All @@ -48,34 +66,34 @@ func (a *Attestation) Compare(other *Attestation) int {
}
}

func (a Attestation) BlockID(api API) (BlockID, error) {
signatureBytes, err := api.Encode(a.Signature)
func (a Attestation) BlockID() (BlockID, error) {
signatureBytes, err := a.API.Encode(a.Signature)
if err != nil {
return EmptyBlockID(), ierrors.Errorf("failed to create blockID: %w", err)
}

headerHash, err := a.BlockHeader.Hash(api)
headerHash, err := a.BlockHeader.Hash(a.API)
if err != nil {
return EmptyBlockID(), ierrors.Errorf("failed to create blockID: %w", err)
}

id := blockIdentifier(headerHash, a.BlockHash, signatureBytes)
slotIndex := api.TimeProvider().SlotFromTime(a.IssuingTime)
slotIndex := a.API.TimeProvider().SlotFromTime(a.IssuingTime)

return NewSlotIdentifier(slotIndex, id), nil
}

func (a *Attestation) signingMessage(api API) ([]byte, error) {
headerHash, err := a.BlockHeader.Hash(api)
func (a *Attestation) signingMessage() ([]byte, error) {
headerHash, err := a.BlockHeader.Hash(a.API)
if err != nil {
return nil, ierrors.Errorf("failed to create signing message: %w", err)
}

return blockSigningMessage(headerHash, a.BlockHash), nil
}

func (a *Attestation) VerifySignature(api API) (valid bool, err error) {
signingMessage, err := a.signingMessage(api)
func (a *Attestation) VerifySignature() (valid bool, err error) {
signingMessage, err := a.signingMessage()
if err != nil {
return false, err
}
Expand Down
4 changes: 2 additions & 2 deletions attestation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,15 @@ func TestAttestation(t *testing.T) {
blockID, err := block.ID(tpkg.TestAPI)
require.NoError(t, err)

blockIDFromAttestation, err := attestation.BlockID(tpkg.TestAPI)
blockIDFromAttestation, err := attestation.BlockID()
require.NoError(t, err)

require.Equal(t, blockID, blockIDFromAttestation)
}

// Check validity of signature.
{
valid, err := attestation.VerifySignature(tpkg.TestAPI)
valid, err := attestation.VerifySignature()
require.NoError(t, err)
require.True(t, valid)
}
Expand Down
27 changes: 12 additions & 15 deletions block.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ const (
BlockMaxParents = 8
// BlockTypeValidationMaxParents defines the maximum amount of parents in a ValidationBlock. TODO: replace number with committee size.
BlockTypeValidationMaxParents = BlockMaxParents + 42

// block type + strong parents count + weak parents count + shallow like parents count + payload type + mana.
BasicBlockSizeEmptyParentsAndEmptyPayload = serializer.OneByte + serializer.OneByte + serializer.OneByte + serializer.OneByte + serializer.UInt32ByteSize + ManaSize
)

var (
Expand Down Expand Up @@ -105,14 +108,15 @@ type BlockPayload interface {
Payload
}

// version + networkID + time + commitmentID + slotIndex + accountID.
const BlockHeaderLength = serializer.OneByte + serializer.UInt64ByteSize + serializer.UInt64ByteSize + CommitmentIDLength + SlotIndexLength + AccountIDLength

type BlockHeader struct {
ProtocolVersion Version `serix:"0,mapKey=protocolVersion"`
NetworkID NetworkID `serix:"1,mapKey=networkId"`

IssuingTime time.Time `serix:"2,mapKey=issuingTime"`
SlotCommitmentID CommitmentID `serix:"3,mapKey=slotCommitment"`
SlotCommitmentID CommitmentID `serix:"3,mapKey=slotCommitmentId"`
LatestFinalizedSlot SlotIndex `serix:"4,mapKey=latestFinalizedSlot"`

IssuerID AccountID `serix:"5,mapKey=issuerId"`
Expand Down Expand Up @@ -277,12 +281,6 @@ func (b *ProtocolBlock) ForEachParent(consumer func(parent Parent)) {
}

func (b *ProtocolBlock) WorkScore(workScoreStructure *WorkScoreStructure) (WorkScore, error) {
workScoreBytes, err := workScoreStructure.DataKilobyte.Multiply(b.Size())
if err != nil {
return 0, err
}
workScoreKilobytes := workScoreBytes / 1024

workScoreHeader, err := b.BlockHeader.WorkScore(workScoreStructure)
if err != nil {
return 0, err
Expand All @@ -298,7 +296,7 @@ func (b *ProtocolBlock) WorkScore(workScoreStructure *WorkScoreStructure) (WorkS
return 0, err
}

return workScoreKilobytes.Add(workScoreHeader, workScoreBlock, workScoreSignature)
return workScoreHeader.Add(workScoreHeader, workScoreBlock, workScoreSignature)
}

// Size returns the size of the block in bytes.
Expand Down Expand Up @@ -430,7 +428,7 @@ func (b *BasicBlock) ManaCost(rmc Mana, workScoreStructure *WorkScoreStructure)
return 0, err
}

return Mana(workScore) * rmc, nil
return ManaCost(rmc, workScore)
}

func (b *BasicBlock) Size() int {
Expand All @@ -439,12 +437,11 @@ func (b *BasicBlock) Size() int {
payloadSize = b.Payload.Size()
}

return serializer.OneByte + // block type
serializer.OneByte + len(b.StrongParents)*SlotIdentifierLength +
serializer.OneByte + len(b.WeakParents)*SlotIdentifierLength +
serializer.OneByte + len(b.ShallowLikeParents)*SlotIdentifierLength +
serializer.UInt32ByteSize + payloadSize + // payload size in serialization + actual payload
ManaSize
return BasicBlockSizeEmptyParentsAndEmptyPayload +
len(b.StrongParents)*SlotIdentifierLength +
len(b.WeakParents)*SlotIdentifierLength +
len(b.ShallowLikeParents)*SlotIdentifierLength +
payloadSize
}

// syntacticallyValidate syntactically validates the BasicBlock.
Expand Down
2 changes: 1 addition & 1 deletion block_issuer_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func (keys BlockIssuerKeys) VBytes(rentStruct *RentStructure, _ VBytesFunc) VByt
vbytes += key.VBytes(rentStruct, nil)
}

return rentStruct.VBFactorIssuerKeys.Multiply(vbytes)
return rentStruct.VBFactorBlockIssuerKey.Multiply(vbytes)
}

// BlockIssuerKey is a key that is allowed to issue blocks from an account with a BlockIssuerFeature.
Expand Down
2 changes: 1 addition & 1 deletion block_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,7 @@ func TestBlockJSONMarshalling(t *testing.T) {
Signature: signature,
}

blockJSON := fmt.Sprintf(`{"protocolVersion":%d,"networkId":"%d","issuingTime":"%s","slotCommitment":"%s","latestFinalizedSlot":"0","issuerId":"%s","block":{"type":%d,"strongParents":["%s"],"weakParents":[],"shallowLikeParents":[],"highestSupportedVersion":%d,"protocolParametersHash":"0x0000000000000000000000000000000000000000000000000000000000000000"},"signature":{"type":%d,"publicKey":"%s","signature":"%s"}}`,
blockJSON := fmt.Sprintf(`{"protocolVersion":%d,"networkId":"%d","issuingTime":"%s","slotCommitmentId":"%s","latestFinalizedSlot":"0","issuerId":"%s","block":{"type":%d,"strongParents":["%s"],"weakParents":[],"shallowLikeParents":[],"highestSupportedVersion":%d,"protocolParametersHash":"0x0000000000000000000000000000000000000000000000000000000000000000"},"signature":{"type":%d,"publicKey":"%s","signature":"%s"}}`,
tpkg.TestAPI.Version(),
networkID,
strconv.FormatUint(serializer.TimeToUint64(issuingTime), 10),
Expand Down
Loading

0 comments on commit e3fceae

Please sign in to comment.