diff --git a/api.go b/api.go index 8854aa1b4..d3cbe84b4 100644 --- a/api.go +++ b/api.go @@ -110,7 +110,7 @@ type ProtocolParameters interface { NetworkID() NetworkID // Bech32HRP defines the HRP prefix used for Bech32 addresses in the network. Bech32HRP() NetworkPrefix - // StorageScoreStructure defines the storage score structure used by the given network. + // StorageScoreParameters defines the storage score structure used by the given network. StorageScoreParameters() *StorageScoreParameters // WorkScoreParameters defines the work score parameters used by the given network. WorkScoreParameters() *WorkScoreParameters @@ -119,6 +119,10 @@ type ProtocolParameters interface { // TokenSupply defines the current token supply on the network. TokenSupply() BaseToken + // GenesisBlockID defines the block ID of the genesis block. + GenesisBlockID() BlockID + // GenesisSlot defines the slot of the genesis. + GenesisSlot() SlotIndex // GenesisUnixTimestamp defines the genesis timestamp at which the slots start to count. GenesisUnixTimestamp() int64 // SlotDurationInSeconds defines the duration of each slot in seconds. diff --git a/api/epoch_based_provider.go b/api/epoch_based_provider.go index fe0802afd..d98c6fedc 100644 --- a/api/epoch_based_provider.go +++ b/api/epoch_based_provider.go @@ -7,7 +7,7 @@ import ( "github.com/iotaledger/hive.go/ierrors" "github.com/iotaledger/hive.go/lo" "github.com/iotaledger/hive.go/runtime/options" - "github.com/iotaledger/hive.go/serializer/v2/marshalutil" + "github.com/iotaledger/hive.go/serializer/v2/stream" iotago "github.com/iotaledger/iota.go/v4" ) @@ -167,10 +167,15 @@ func (e *EpochBasedProvider) VersionsAndProtocolParametersHash() (iotago.Identif e.mutex.RLock() defer e.mutex.RUnlock() - util := marshalutil.New() + byteBuffer := stream.NewByteBuffer() + for _, version := range e.protocolVersions.Slice() { - util.WriteBytes(lo.PanicOnErr(version.Version.Bytes())) - util.WriteUint64(uint64(version.StartEpoch)) + if err := stream.Write(byteBuffer, version.Version); err != nil { + return iotago.Identifier{}, ierrors.Wrap(err, "failed to write Version") + } + if err := stream.Write(byteBuffer, version.StartEpoch); err != nil { + return iotago.Identifier{}, ierrors.Wrap(err, "failed to write StartEpoch") + } var paramsHash iotago.Identifier params, paramsExist := e.protocolParametersByVersion[version.Version] @@ -186,14 +191,12 @@ func (e *EpochBasedProvider) VersionsAndProtocolParametersHash() (iotago.Identif } } - hashBytes, err := paramsHash.Bytes() - if err != nil { - return iotago.Identifier{}, ierrors.Wrap(err, "failed to get protocol parameters hash bytes") + if err := stream.Write(byteBuffer, paramsHash); err != nil { + return iotago.Identifier{}, ierrors.Wrap(err, "failed to write protocol parameters hash bytes") } - util.WriteBytes(hashBytes) } - return iotago.IdentifierFromData(util.Bytes()), nil + return iotago.IdentifierFromData(lo.PanicOnErr(byteBuffer.Bytes())), nil } func (e *EpochBasedProvider) ProtocolParameters(version iotago.Version) iotago.ProtocolParameters { diff --git a/api_protocol_parameters.go b/api_protocol_parameters.go index 80b5a498a..6678581fb 100644 --- a/api_protocol_parameters.go +++ b/api_protocol_parameters.go @@ -19,46 +19,48 @@ type basicProtocolParameters struct { // TokenSupply defines the current token supply on the network. TokenSupply BaseToken `serix:"6,mapKey=tokenSupply"` + // GenesisSlot defines the slot of the genesis. + GenesisSlot SlotIndex `serix:"7,mapKey=genesisSlot"` // GenesisUnixTimestamp defines the genesis timestamp at which the slots start to count. - GenesisUnixTimestamp int64 `serix:"7,mapKey=genesisUnixTimestamp"` + GenesisUnixTimestamp int64 `serix:"8,mapKey=genesisUnixTimestamp"` // SlotDurationInSeconds defines the duration of each slot in seconds. - SlotDurationInSeconds uint8 `serix:"8,mapKey=slotDurationInSeconds"` + SlotDurationInSeconds uint8 `serix:"9,mapKey=slotDurationInSeconds"` // SlotsPerEpochExponent is the number of slots in an epoch expressed as an exponent of 2. // (2**SlotsPerEpochExponent) == slots in an epoch. - SlotsPerEpochExponent uint8 `serix:"9,mapKey=slotsPerEpochExponent"` + SlotsPerEpochExponent uint8 `serix:"10,mapKey=slotsPerEpochExponent"` // StakingUnbondingPeriod defines the unbonding period in epochs before an account can stop staking. - StakingUnbondingPeriod EpochIndex `serix:"10,mapKey=stakingUnbondingPeriod"` + StakingUnbondingPeriod EpochIndex `serix:"11,mapKey=stakingUnbondingPeriod"` // ValidationBlocksPerSlot is the number of validation blocks that each validator should issue each slot. - ValidationBlocksPerSlot uint8 `serix:"11,mapKey=validationBlocksPerSlot"` + ValidationBlocksPerSlot uint8 `serix:"12,mapKey=validationBlocksPerSlot"` // PunishmentEpochs is the number of epochs worth of Mana that a node is punished with for each additional validation block it issues. - PunishmentEpochs EpochIndex `serix:"12,mapKey=punishmentEpochs"` + PunishmentEpochs EpochIndex `serix:"13,mapKey=punishmentEpochs"` // LivenessThresholdLowerBound is used by tip-selection to determine if a block is eligible by evaluating issuingTimes. // and commitments in its past-cone to ATT and lastCommittedSlot respectively. - LivenessThresholdLowerBoundInSeconds uint16 `serix:"13,mapKey=livenessThresholdLowerBound"` + LivenessThresholdLowerBoundInSeconds uint16 `serix:"14,mapKey=livenessThresholdLowerBound"` // LivenessThresholdUpperBound is used by tip-selection to determine if a block is eligible by evaluating issuingTimes // and commitments in its past-cone to ATT and lastCommittedSlot respectively. - LivenessThresholdUpperBoundInSeconds uint16 `serix:"14,mapKey=livenessThresholdUpperBound"` + LivenessThresholdUpperBoundInSeconds uint16 `serix:"15,mapKey=livenessThresholdUpperBound"` // MinCommittableAge is the minimum age relative to the accepted tangle time slot index that a slot can be committed. // For example, if the last accepted slot is in slot 100, and minCommittableAge=10, then the latest committed slot can be at most 100-10=90. - MinCommittableAge SlotIndex `serix:"15,mapKey=minCommittableAge"` + MinCommittableAge SlotIndex `serix:"16,mapKey=minCommittableAge"` // MaxCommittableAge is the maximum age for a slot commitment to be included in a block relative to the slot index of the block issuing time. // For example, if the last accepted slot is in slot 100, and maxCommittableAge=20, then the oldest referencable commitment is 100-20=80. - MaxCommittableAge SlotIndex `serix:"16,mapKey=maxCommittableAge"` + MaxCommittableAge SlotIndex `serix:"17,mapKey=maxCommittableAge"` // EpochNearingThreshold is used by the epoch orchestrator to detect the slot that should trigger a new committee // selection for the next and upcoming epoch. - EpochNearingThreshold SlotIndex `serix:"17,mapKey=epochNearingThreshold"` + EpochNearingThreshold SlotIndex `serix:"18,mapKey=epochNearingThreshold"` // RMCParameters defines the parameters used by to calculate the Reference Mana Cost (RMC). - CongestionControlParameters CongestionControlParameters `serix:"18,mapKey=congestionControlParameters"` + CongestionControlParameters CongestionControlParameters `serix:"19,mapKey=congestionControlParameters"` // VersionSignaling defines the parameters used for version upgrades. - VersionSignaling VersionSignaling `serix:"19,mapKey=versionSignaling"` + VersionSignaling VersionSignaling `serix:"20,mapKey=versionSignaling"` // RewardsParameters defines the parameters used for reward calculation. - RewardsParameters RewardsParameters `serix:"20,mapKey=rewardsParameters"` + RewardsParameters RewardsParameters `serix:"21,mapKey=rewardsParameters"` // TargetCommitteeSize defines the target size of the committee. If there's fewer candidates the actual committee size could be smaller in a given epoch. - TargetCommitteeSize uint8 `serix:"21,mapKey=targetCommitteeSize"` + TargetCommitteeSize uint8 `serix:"22,mapKey=targetCommitteeSize"` } func (b basicProtocolParameters) Equals(other basicProtocolParameters) bool { @@ -69,6 +71,7 @@ func (b basicProtocolParameters) Equals(other basicProtocolParameters) bool { b.WorkScoreParameters.Equals(other.WorkScoreParameters) && b.ManaParameters.Equals(other.ManaParameters) && b.TokenSupply == other.TokenSupply && + b.GenesisSlot == other.GenesisSlot && b.GenesisUnixTimestamp == other.GenesisUnixTimestamp && b.SlotDurationInSeconds == other.SlotDurationInSeconds && b.SlotsPerEpochExponent == other.SlotsPerEpochExponent && diff --git a/api_test.go b/api_test.go index 6683f3f66..bfa3b8866 100644 --- a/api_test.go +++ b/api_test.go @@ -100,6 +100,7 @@ func TestProtocolParametersJSONMarshalling(t *testing.T) { 10, ), iotago.WithTimeProviderOptions( + 654978, 1681373293, 10, 13, @@ -135,7 +136,7 @@ func TestProtocolParametersJSONMarshalling(t *testing.T) { iotago.WithRewardsOptions(8, 8, 31, 1154, 2, 1), ) - protoParamsJSON := `{"type":0,"version":3,"networkName":"xxxNetwork","bech32Hrp":"xxx","storageScoreParameters":{"storageCost":"6","factorData":7,"offsetOutputOverhead":"8","offsetEd25519BlockIssuerKey":"9","offsetStakingFeature":"10","offsetDelegation":"10"},"workScoreParameters":{"dataByte":1,"block":2,"input":3,"contextInput":4,"output":5,"nativeToken":6,"staking":7,"blockIssuer":8,"allotment":9,"signatureEd25519":10},"manaParameters":{"bitsCount":1,"generationRate":1,"generationRateExponent":27,"decayFactors":[10,20],"decayFactorsExponent":32,"decayFactorEpochsSum":1337,"decayFactorEpochsSumExponent":20},"tokenSupply":"1234567890987654321","genesisUnixTimestamp":"1681373293","slotDurationInSeconds":10,"slotsPerEpochExponent":13,"stakingUnbondingPeriod":11,"validationBlocksPerSlot":10,"punishmentEpochs":9,"livenessThresholdLowerBound":15,"livenessThresholdUpperBound":30,"minCommittableAge":10,"maxCommittableAge":20,"epochNearingThreshold":24,"congestionControlParameters":{"minReferenceManaCost":"500","increase":"500","decrease":"500","increaseThreshold":800000,"decreaseThreshold":500000,"schedulerRate":100000,"maxBufferSize":1000,"maxValidationBufferSize":100},"versionSignaling":{"windowSize":3,"windowTargetRatio":4,"activationOffset":1},"rewardsParameters":{"profitMarginExponent":8,"bootstrappingDuration":1154,"manaShareCoefficient":"2","decayBalancingConstantExponent":8,"decayBalancingConstant":"1","poolCoefficientExponent":31},"targetCommitteeSize":32}` + protoParamsJSON := `{"type":0,"version":3,"networkName":"xxxNetwork","bech32Hrp":"xxx","storageScoreParameters":{"storageCost":"6","factorData":7,"offsetOutputOverhead":"8","offsetEd25519BlockIssuerKey":"9","offsetStakingFeature":"10","offsetDelegation":"10"},"workScoreParameters":{"dataByte":1,"block":2,"input":3,"contextInput":4,"output":5,"nativeToken":6,"staking":7,"blockIssuer":8,"allotment":9,"signatureEd25519":10},"manaParameters":{"bitsCount":1,"generationRate":1,"generationRateExponent":27,"decayFactors":[10,20],"decayFactorsExponent":32,"decayFactorEpochsSum":1337,"decayFactorEpochsSumExponent":20},"tokenSupply":"1234567890987654321","genesisSlot":654978,"genesisUnixTimestamp":"1681373293","slotDurationInSeconds":10,"slotsPerEpochExponent":13,"stakingUnbondingPeriod":11,"validationBlocksPerSlot":10,"punishmentEpochs":9,"livenessThresholdLowerBound":15,"livenessThresholdUpperBound":30,"minCommittableAge":10,"maxCommittableAge":20,"epochNearingThreshold":24,"congestionControlParameters":{"minReferenceManaCost":"500","increase":"500","decrease":"500","increaseThreshold":800000,"decreaseThreshold":500000,"schedulerRate":100000,"maxBufferSize":1000,"maxValidationBufferSize":100},"versionSignaling":{"windowSize":3,"windowTargetRatio":4,"activationOffset":1},"rewardsParameters":{"profitMarginExponent":8,"bootstrappingDuration":1154,"manaShareCoefficient":"2","decayBalancingConstantExponent":8,"decayBalancingConstant":"1","poolCoefficientExponent":31}"targetCommitteeSize":32}` jsonProtoParams, err := tpkg.TestAPI.JSONEncode(protoParams) require.NoError(t, err) diff --git a/api_v3.go b/api_v3.go index f12a40774..1b6a6461b 100644 --- a/api_v3.go +++ b/api_v3.go @@ -298,7 +298,7 @@ func (v *v3api) Decode(b []byte, obj interface{}, opts ...serix.Option) (int, er func V3API(protoParams ProtocolParameters) API { api := CommonSerixAPI() - timeProvider := NewTimeProvider(protoParams.GenesisUnixTimestamp(), int64(protoParams.SlotDurationInSeconds()), protoParams.SlotsPerEpochExponent()) + timeProvider := NewTimeProvider(protoParams.GenesisSlot(), protoParams.GenesisUnixTimestamp(), int64(protoParams.SlotDurationInSeconds()), protoParams.SlotsPerEpochExponent()) maxBlockWork, err := protoParams.WorkScoreParameters().MaxBlockWork() must(err) @@ -456,9 +456,6 @@ func V3API(protoParams ProtocolParameters) API { { must(api.RegisterTypeSettings(AccountOutput{}, serix.TypeSettings{}.WithObjectType(uint8(OutputAccount)))) - must(api.RegisterValidators(AccountOutput{}, nil, func(ctx context.Context, account AccountOutput) error { - return account.syntacticallyValidate() - })) must(api.RegisterTypeSettings(AccountOutputUnlockConditions{}, serix.TypeSettings{}.WithLengthPrefixType(serix.LengthPrefixTypeAsByte).WithArrayRules(accountOutputV3UnlockCondArrRules), @@ -485,9 +482,6 @@ func V3API(protoParams ProtocolParameters) API { { must(api.RegisterTypeSettings(AnchorOutput{}, serix.TypeSettings{}.WithObjectType(uint8(OutputAnchor)))) - must(api.RegisterValidators(AnchorOutput{}, nil, func(ctx context.Context, anchor AnchorOutput) error { - return anchor.syntacticallyValidate() - })) must(api.RegisterTypeSettings(AnchorOutputUnlockConditions{}, serix.TypeSettings{}.WithLengthPrefixType(serix.LengthPrefixTypeAsByte).WithArrayRules(anchorOutputV3UnlockCondArrRules), @@ -515,10 +509,6 @@ func V3API(protoParams ProtocolParameters) API { must(api.RegisterTypeSettings(FoundryOutput{}, serix.TypeSettings{}.WithObjectType(uint8(OutputFoundry))), ) - must(api.RegisterValidators(FoundryOutput{}, nil, func(ctx context.Context, foundry FoundryOutput) error { - //nolint:contextcheck - return foundry.syntacticallyValidate() - })) must(api.RegisterTypeSettings(FoundryOutputUnlockConditions{}, serix.TypeSettings{}.WithLengthPrefixType(serix.LengthPrefixTypeAsByte).WithArrayRules(foundryOutputV3UnlockCondArrRules), @@ -547,9 +537,6 @@ func V3API(protoParams ProtocolParameters) API { must(api.RegisterTypeSettings(NFTOutput{}, serix.TypeSettings{}.WithObjectType(uint8(OutputNFT))), ) - must(api.RegisterValidators(NFTOutput{}, nil, func(ctx context.Context, nft NFTOutput) error { - return nft.syntacticallyValidate() - })) must(api.RegisterTypeSettings(NFTOutputUnlockConditions{}, serix.TypeSettings{}.WithLengthPrefixType(serix.LengthPrefixTypeAsByte).WithArrayRules(nftOutputV3UnlockCondArrRules), @@ -578,9 +565,6 @@ func V3API(protoParams ProtocolParameters) API { { must(api.RegisterTypeSettings(DelegationOutput{}, serix.TypeSettings{}.WithObjectType(uint8(OutputDelegation)))) - must(api.RegisterValidators(DelegationOutput{}, nil, func(ctx context.Context, delegation DelegationOutput) error { - return delegation.syntacticallyValidate() - })) must(api.RegisterTypeSettings(DelegationOutputUnlockConditions{}, serix.TypeSettings{}.WithLengthPrefixType(serix.LengthPrefixTypeAsByte).WithArrayRules(delegationOutputV3UnlockCondArrRules), @@ -701,6 +685,7 @@ func V3API(protoParams ProtocolParameters) API { { merklehasher.RegisterSerixRules[*APIByter[TxEssenceOutput]](api) + merklehasher.RegisterSerixRules[Identifier](api) } return v3 diff --git a/api_v3_protocol_parameters.go b/api_v3_protocol_parameters.go index 5ad82360f..81f837679 100644 --- a/api_v3_protocol_parameters.go +++ b/api_v3_protocol_parameters.go @@ -22,7 +22,7 @@ func NewV3ProtocolParameters(opts ...options.Option[V3ProtocolParameters]) *V3Pr WithNetworkOptions("testnet", PrefixTestnet), WithSupplyOptions(1813620509061365, 100, 1, 10, 100, 100, 100), WithWorkScoreOptions(0, 1, 0, 0, 0, 0, 0, 0, 0, 0), - WithTimeProviderOptions(time.Now().Unix(), 10, 13), + WithTimeProviderOptions(0, time.Now().Unix(), 10, 13), WithManaOptions(63, 1, 17, @@ -80,6 +80,16 @@ func (p *V3ProtocolParameters) NetworkID() NetworkID { return NetworkIDFromString(p.basicProtocolParameters.NetworkName) } +// GenesisBlockID defines the block ID of the genesis block. +func (p *V3ProtocolParameters) GenesisBlockID() BlockID { + return NewBlockID(p.basicProtocolParameters.GenesisSlot, EmptyIdentifier) +} + +// GenesisSlot defines the genesis slot. +func (p *V3ProtocolParameters) GenesisSlot() SlotIndex { + return p.basicProtocolParameters.GenesisSlot +} + // GenesisUnixTimestamp defines the genesis timestamp at which the slots start to count. func (p *V3ProtocolParameters) GenesisUnixTimestamp() int64 { return p.basicProtocolParameters.GenesisUnixTimestamp @@ -164,7 +174,7 @@ func (p *V3ProtocolParameters) Hash() (Identifier, error) { } func (p *V3ProtocolParameters) String() string { - return fmt.Sprintf("ProtocolParameters: {\n\tVersion: %d\n\tNetwork Name: %s\n\tBech32 HRP Prefix: %s\n\tStorageScore Structure: %v\n\tWorkScore Structure: %v\n\tMana Structure: %v\n\tToken Supply: %d\n\tGenesis Unix Timestamp: %d\n\tSlot Duration in Seconds: %d\n\tSlots per Epoch Exponent: %d\n\tStaking Unbonding Period: %d\n\tValidation Blocks per Slot: %d\n\tPunishment Epochs: %d\n\tLiveness Threshold Lower Bound: %d\n\tLiveness Threshold Upper Bound: %d\n\tMin Committable Age: %d\n\tMax Committable Age: %d\n\tEpoch Nearing Threshold: %d\n\tCongestion Control parameters: %v\n\tVersion Signaling: %v\n\tRewardsParameters: %v\n", + return fmt.Sprintf("ProtocolParameters: {\n\tVersion: %d\n\tNetwork Name: %s\n\tBech32 HRP Prefix: %s\n\tStorageScore Structure: %v\n\tWorkScore Structure: %v\n\tMana Structure: %v\n\tToken Supply: %d\n\tGenesis Slot: %d\n\tGenesis Unix Timestamp: %d\n\tSlot Duration in Seconds: %d\n\tSlots per Epoch Exponent: %d\n\tStaking Unbonding Period: %d\n\tValidation Blocks per Slot: %d\n\tPunishment Epochs: %d\n\tLiveness Threshold Lower Bound: %d\n\tLiveness Threshold Upper Bound: %d\n\tMin Committable Age: %d\n\tMax Committable Age: %d\n\tEpoch Nearing Threshold: %d\n\tCongestion Control parameters: %v\n\tVersion Signaling: %v\n\tRewardsParameters: %v\n", p.basicProtocolParameters.Version, p.basicProtocolParameters.NetworkName, p.basicProtocolParameters.Bech32HRP, @@ -172,6 +182,7 @@ func (p *V3ProtocolParameters) String() string { p.basicProtocolParameters.WorkScoreParameters, p.basicProtocolParameters.ManaParameters, p.basicProtocolParameters.TokenSupply, + p.basicProtocolParameters.GenesisSlot, p.basicProtocolParameters.GenesisUnixTimestamp, p.basicProtocolParameters.SlotDurationInSeconds, p.basicProtocolParameters.SlotsPerEpochExponent, @@ -265,8 +276,9 @@ func WithManaOptions(bitsCount uint8, generationRate uint8, generationRateExpone } } -func WithTimeProviderOptions(genesisTimestamp int64, slotDurationInSeconds uint8, slotsPerEpochExponent uint8) options.Option[V3ProtocolParameters] { +func WithTimeProviderOptions(genesisSlot SlotIndex, genesisTimestamp int64, slotDurationInSeconds uint8, slotsPerEpochExponent uint8) options.Option[V3ProtocolParameters] { return func(p *V3ProtocolParameters) { + p.basicProtocolParameters.GenesisSlot = genesisSlot p.basicProtocolParameters.GenesisUnixTimestamp = genesisTimestamp p.basicProtocolParameters.SlotDurationInSeconds = slotDurationInSeconds p.basicProtocolParameters.SlotsPerEpochExponent = slotsPerEpochExponent diff --git a/block.go b/block.go index 2a3c53468..531b9d478 100644 --- a/block.go +++ b/block.go @@ -289,8 +289,10 @@ func (b *Block) syntacticallyValidate() error { } } - minCommittableAge := b.API.ProtocolParameters().MinCommittableAge() - maxCommittableAge := b.API.ProtocolParameters().MaxCommittableAge() + protocolParams := b.API.ProtocolParameters() + genesisSlot := protocolParams.GenesisSlot() + minCommittableAge := protocolParams.MinCommittableAge() + maxCommittableAge := protocolParams.MaxCommittableAge() commitmentSlot := b.Header.SlotCommitmentID.Slot() blockID, err := b.ID() if err != nil { @@ -299,7 +301,7 @@ func (b *Block) syntacticallyValidate() error { blockSlot := blockID.Slot() // check that commitment is not too recent. - if commitmentSlot > 0 && // Don't filter commitments to genesis based on being too recent. + if commitmentSlot > genesisSlot && // Don't filter commitments to genesis based on being too recent. blockSlot < commitmentSlot+minCommittableAge { return ierrors.Wrapf(ErrCommitmentTooRecent, "block at slot %d committing to slot %d", blockSlot, b.Header.SlotCommitmentID.Slot()) } diff --git a/block_issuer_key.go b/block_issuer_key.go index ec47851cc..366420cd1 100644 --- a/block_issuer_key.go +++ b/block_issuer_key.go @@ -2,12 +2,15 @@ package iotago import ( "bytes" + "context" + "io" "slices" "github.com/iotaledger/hive.go/constraints" "github.com/iotaledger/hive.go/ierrors" "github.com/iotaledger/hive.go/lo" "github.com/iotaledger/hive.go/serializer/v2" + "github.com/iotaledger/hive.go/serializer/v2/stream" ) // BlockIssuerKeyType defines the type of block issuer key. @@ -133,15 +136,73 @@ func (keys BlockIssuerKeys) StorageScore(storageScoreStruct *StorageScoreStructu return storageScore } +func (keys BlockIssuerKeys) Bytes() ([]byte, error) { + return CommonSerixAPI().Encode(context.TODO(), keys) +} + // BlockIssuerKey is a key that is allowed to issue blocks from an account with a BlockIssuerFeature. type BlockIssuerKey interface { Sizer NonEphemeralObject constraints.Cloneable[BlockIssuerKey] constraints.Equalable[BlockIssuerKey] + serializer.Byter - // Bytes returns a byte slice consisting of the type prefix and the unique identifier of the key. - Bytes() []byte // Type returns the BlockIssuerKeyType. Type() BlockIssuerKeyType } + +func BlockIssuerKeysFromReader(reader io.ReadSeeker) (BlockIssuerKeys, error) { + b := make(BlockIssuerKeys, 0) + // serix.LengthPrefixTypeAsByte is used to read the length prefix of the array + if err := stream.ReadCollection(reader, serializer.SeriLengthPrefixTypeAsByte, func(i int) error { + blockIssuerKey, err := BlockIssuerKeyFromReader(reader) + if err != nil { + return ierrors.Wrapf(err, "unable to read block issuer key %d", i) + } + + b = append(b, blockIssuerKey) + + return nil + }); err != nil { + return nil, ierrors.Wrap(err, "unable to read block issuer keys") + } + + return b, nil +} + +func BlockIssuerKeyFromReader(reader io.ReadSeeker) (BlockIssuerKey, error) { + blockIssuerKeyType, err := stream.PeekSize(reader, serializer.SeriLengthPrefixTypeAsByte) + if err != nil { + return nil, ierrors.Wrap(err, "unable to read block issuer key type") + } + + switch BlockIssuerKeyType(blockIssuerKeyType) { + case BlockIssuerKeyEd25519PublicKey: + readBytes, err := stream.ReadBytes(reader, Ed25519PublicKeyBlockIssuerKeyLength) + if err != nil { + return nil, ierrors.Wrap(err, "unable to read block issuer key bytes") + } + + return Ed25519PublicKeyBlockIssuerKeyFromBytes(readBytes) + case BlockIssuerKeyPublicKeyHash: + readBytes, err := stream.ReadBytes(reader, Ed25519PublicKeyHashBlockIssuerKeyLength) + if err != nil { + return nil, ierrors.Wrap(err, "unable to read block issuer key bytes") + } + + return Ed25519PublicKeyHashBlockIssuerKeyFromBytes(readBytes) + default: + return nil, ierrors.Errorf("unsupported block issuer key type: %d", blockIssuerKeyType) + } +} + +func BlockIssuerKeyFromBytes(bytes []byte) (BlockIssuerKey, error) { + var blockIssuerKey BlockIssuerKey + + if _, err := CommonSerixAPI().Decode(context.TODO(), bytes, blockIssuerKey); err != nil { + return nil, ierrors.Wrap(err, "unable to decode block issuer key") + } + + return blockIssuerKey, nil +} diff --git a/block_issuer_key_ed25519_pubkey.go b/block_issuer_key_ed25519_pubkey.go index 0d6f0b353..4d6624ba0 100644 --- a/block_issuer_key_ed25519_pubkey.go +++ b/block_issuer_key_ed25519_pubkey.go @@ -5,10 +5,11 @@ import ( "context" "github.com/iotaledger/hive.go/crypto/ed25519" - "github.com/iotaledger/hive.go/lo" "github.com/iotaledger/hive.go/serializer/v2" ) +const Ed25519PublicKeyBlockIssuerKeyLength = serializer.SmallTypeDenotationByteSize + ed25519.PublicKeySize + // A Ed25519 public key Block Issuer Key. type Ed25519PublicKeyBlockIssuerKey struct { PublicKey ed25519.PublicKey `serix:"0,mapKey=publicKey"` @@ -30,9 +31,19 @@ func (key *Ed25519PublicKeyBlockIssuerKey) ToEd25519PublicKey() ed25519.PublicKe return key.PublicKey } +func Ed25519PublicKeyBlockIssuerKeyFromBytes(bytes []byte) (*Ed25519PublicKeyBlockIssuerKey, error) { + blockIssuerKey := &Ed25519PublicKeyBlockIssuerKey{} + _, err := CommonSerixAPI().Decode(context.TODO(), bytes, blockIssuerKey) + if err != nil { + return nil, err + } + + return blockIssuerKey, nil +} + // Bytes returns a byte slice consisting of the type prefix and the public key bytes. -func (key *Ed25519PublicKeyBlockIssuerKey) Bytes() []byte { - return lo.PanicOnErr(CommonSerixAPI().Encode(context.TODO(), key)) +func (key *Ed25519PublicKeyBlockIssuerKey) Bytes() ([]byte, error) { + return CommonSerixAPI().Encode(context.TODO(), key) } // Type returns the BlockIssuerKeyType. @@ -55,7 +66,7 @@ func (key *Ed25519PublicKeyBlockIssuerKey) Compare(other *Ed25519PublicKeyBlockI // Size returns the size of the block issuer key when serialized. func (key *Ed25519PublicKeyBlockIssuerKey) Size() int { - return serializer.SmallTypeDenotationByteSize + ed25519.PublicKeySize + return Ed25519PublicKeyBlockIssuerKeyLength } func (key *Ed25519PublicKeyBlockIssuerKey) StorageScore(storageScoreStruct *StorageScoreStructure, _ StorageScoreFunc) StorageScore { diff --git a/block_issuer_key_ed25519_pubkeyhash.go b/block_issuer_key_ed25519_pubkeyhash.go index 41f5aa6b1..9de42edc5 100644 --- a/block_issuer_key_ed25519_pubkeyhash.go +++ b/block_issuer_key_ed25519_pubkeyhash.go @@ -7,12 +7,14 @@ import ( "golang.org/x/crypto/blake2b" - "github.com/iotaledger/hive.go/lo" "github.com/iotaledger/hive.go/serializer/v2" ) // Ed25519PublicKeyHashBytesLength is the length of an Ed25519 public key hash. -const Ed25519PublicKeyHashBytesLength = blake2b.Size256 +const ( + Ed25519PublicKeyHashBytesLength = blake2b.Size256 + Ed25519PublicKeyHashBlockIssuerKeyLength = serializer.SmallTypeDenotationByteSize + Ed25519PublicKeyHashBytesLength +) // An Ed25519 Address Block Issuer Key. type Ed25519PublicKeyHashBlockIssuerKey struct { @@ -42,9 +44,19 @@ func (key *Ed25519PublicKeyHashBlockIssuerKey) Clone() BlockIssuerKey { } } +func Ed25519PublicKeyHashBlockIssuerKeyFromBytes(bytes []byte) (*Ed25519PublicKeyHashBlockIssuerKey, error) { + blockIssuerKey := &Ed25519PublicKeyHashBlockIssuerKey{} + _, err := CommonSerixAPI().Decode(context.TODO(), bytes, blockIssuerKey) + if err != nil { + return nil, err + } + + return blockIssuerKey, nil +} + // Bytes returns a byte slice consisting of the type prefix and the raw address. -func (key *Ed25519PublicKeyHashBlockIssuerKey) Bytes() []byte { - return lo.PanicOnErr(CommonSerixAPI().Encode(context.TODO(), key)) +func (key *Ed25519PublicKeyHashBlockIssuerKey) Bytes() ([]byte, error) { + return CommonSerixAPI().Encode(context.TODO(), key) } // Type returns the BlockIssuerKeyType. @@ -67,7 +79,7 @@ func (key *Ed25519PublicKeyHashBlockIssuerKey) Compare(other *Ed25519PublicKeyHa // Size returns the size of the block issuer key when serialized. func (key *Ed25519PublicKeyHashBlockIssuerKey) Size() int { - return serializer.SmallTypeDenotationByteSize + Ed25519PublicKeyHashBytesLength + return Ed25519PublicKeyHashBlockIssuerKeyLength } func (key *Ed25519PublicKeyHashBlockIssuerKey) StorageScore(storageScoreStructure *StorageScoreStructure, _ StorageScoreFunc) StorageScore { diff --git a/block_test.go b/block_test.go index 5c8bcbb9b..1cbbedcb6 100644 --- a/block_test.go +++ b/block_test.go @@ -154,7 +154,7 @@ func TestBlock_Commitments(t *testing.T) { apiProvider := api.NewEpochBasedProvider() apiProvider.AddProtocolParametersAtEpoch( iotago.NewV3ProtocolParameters( - iotago.WithTimeProviderOptions(time.Now().Add(-20*time.Minute).Unix(), 10, 13), + iotago.WithTimeProviderOptions(0, time.Now().Add(-20*time.Minute).Unix(), 10, 13), iotago.WithLivenessOptions(15, 30, 11, 21, 4), ), 0) @@ -174,7 +174,7 @@ func TestBlock_Commitments1(t *testing.T) { apiProvider := api.NewEpochBasedProvider() apiProvider.AddProtocolParametersAtEpoch( iotago.NewV3ProtocolParameters( - iotago.WithTimeProviderOptions(time.Now().Add(-20*time.Minute).Unix(), 10, 13), + iotago.WithTimeProviderOptions(0, time.Now().Add(-20*time.Minute).Unix(), 10, 13), iotago.WithLivenessOptions(15, 30, 7, 21, 4), ), 0) @@ -199,7 +199,7 @@ func TestBlock_TransactionCreationTime(t *testing.T) { apiProvider := api.NewEpochBasedProvider() apiProvider.AddProtocolParametersAtEpoch( iotago.NewV3ProtocolParameters( - iotago.WithTimeProviderOptions(time.Now().Add(-20*time.Minute).Unix(), 10, 13), + iotago.WithTimeProviderOptions(0, time.Now().Add(-20*time.Minute).Unix(), 10, 13), iotago.WithLivenessOptions(15, 30, 7, 21, 4), ), 0) @@ -266,7 +266,7 @@ func TestBlock_WeakParents(t *testing.T) { apiProvider := api.NewEpochBasedProvider() apiProvider.AddProtocolParametersAtEpoch( iotago.NewV3ProtocolParameters( - iotago.WithTimeProviderOptions(time.Now().Add(-20*time.Minute).Unix(), 10, 13), + iotago.WithTimeProviderOptions(0, time.Now().Add(-20*time.Minute).Unix(), 10, 13), iotago.WithLivenessOptions(15, 30, 10, 20, 4), ), 0) strongParent1 := tpkg.RandBlockID() @@ -325,7 +325,7 @@ func TestBlock_TransactionCommitmentInput(t *testing.T) { apiProvider := api.NewEpochBasedProvider() apiProvider.AddProtocolParametersAtEpoch( iotago.NewV3ProtocolParameters( - iotago.WithTimeProviderOptions(time.Now().Add(-20*time.Minute).Unix(), 10, 13), + iotago.WithTimeProviderOptions(0, time.Now().Add(-20*time.Minute).Unix(), 10, 13), iotago.WithLivenessOptions(15, 30, 11, 21, 4), ), 0) @@ -442,7 +442,7 @@ func TestBasicBlock_MinSize(t *testing.T) { Header: iotago.BlockHeader{ ProtocolVersion: tpkg.TestAPI.Version(), IssuingTime: tpkg.RandUTCTime(), - SlotCommitmentID: iotago.NewEmptyCommitment(tpkg.TestAPI.Version()).MustID(), + SlotCommitmentID: iotago.NewEmptyCommitment(tpkg.TestAPI).MustID(), }, Signature: tpkg.RandEd25519Signature(), Body: &iotago.BasicBlockBody{ @@ -470,7 +470,7 @@ func TestValidationBlock_MinSize(t *testing.T) { Header: iotago.BlockHeader{ ProtocolVersion: tpkg.TestAPI.Version(), IssuingTime: tpkg.RandUTCTime(), - SlotCommitmentID: iotago.NewEmptyCommitment(tpkg.TestAPI.Version()).MustID(), + SlotCommitmentID: iotago.NewEmptyCommitment(tpkg.TestAPI).MustID(), }, Signature: tpkg.RandEd25519Signature(), Body: &iotago.ValidationBlockBody{ @@ -498,7 +498,7 @@ func TestValidationBlock_HighestSupportedVersion(t *testing.T) { Header: iotago.BlockHeader{ ProtocolVersion: tpkg.TestAPI.Version(), IssuingTime: tpkg.RandUTCTime(), - SlotCommitmentID: iotago.NewEmptyCommitment(tpkg.TestAPI.Version()).MustID(), + SlotCommitmentID: iotago.NewEmptyCommitment(tpkg.TestAPI).MustID(), }, Signature: tpkg.RandEd25519Signature(), } @@ -543,7 +543,7 @@ func TestValidationBlock_HighestSupportedVersion(t *testing.T) { func TestBlockJSONMarshalling(t *testing.T) { networkID := iotago.NetworkIDFromString("xxxNetwork") issuingTime := tpkg.RandUTCTime() - commitmentID := iotago.NewEmptyCommitment(tpkg.TestAPI.Version()).MustID() + commitmentID := iotago.NewEmptyCommitment(tpkg.TestAPI).MustID() issuerID := tpkg.RandAccountID() signature := tpkg.RandEd25519Signature() strongParents := tpkg.SortedRandBlockIDs(1) diff --git a/builder/block_builder.go b/builder/block_builder.go index df11208e2..750b1500e 100644 --- a/builder/block_builder.go +++ b/builder/block_builder.go @@ -204,7 +204,7 @@ func NewValidationBlockBuilder(api iotago.API) *ValidationBlockBuilder { API: api, Header: iotago.BlockHeader{ ProtocolVersion: api.ProtocolParameters().Version(), - SlotCommitmentID: iotago.NewEmptyCommitment(api.ProtocolParameters().Version()).MustID(), + SlotCommitmentID: iotago.NewEmptyCommitment(api).MustID(), IssuingTime: time.Now().UTC(), }, Signature: &iotago.Ed25519Signature{}, diff --git a/builder/output_builder_test.go b/builder/output_builder_test.go index 12a4394fb..a94fcec11 100644 --- a/builder/output_builder_test.go +++ b/builder/output_builder_test.go @@ -19,7 +19,7 @@ func TestBasicOutputBuilder(t *testing.T) { nativeTokenFeature = tpkg.RandNativeTokenFeature() expirationTarget = tpkg.RandEd25519Address() metadata = []byte("123456") - slotTimeProvider = iotago.NewTimeProvider(time.Now().Unix(), 10, 10) + slotTimeProvider = iotago.NewTimeProvider(0, time.Now().Unix(), 10, 10) ) timelock := slotTimeProvider.SlotFromTime(time.Now().Add(5 * time.Minute)) expiration := slotTimeProvider.SlotFromTime(time.Now().Add(10 * time.Minute)) diff --git a/commitment.go b/commitment.go index 536ba6e95..3b14ba45b 100644 --- a/commitment.go +++ b/commitment.go @@ -29,9 +29,10 @@ func NewCommitment(version Version, slot SlotIndex, prevID CommitmentID, rootsID } } -func NewEmptyCommitment(version Version) *Commitment { +func NewEmptyCommitment(api API) *Commitment { return &Commitment{ - ProtocolVersion: version, + ProtocolVersion: api.ProtocolParameters().Version(), + Slot: api.ProtocolParameters().GenesisSlot(), } } diff --git a/feat_native_token_test.go b/feat_native_token_test.go index ca499d419..e4d986ee6 100644 --- a/feat_native_token_test.go +++ b/feat_native_token_test.go @@ -74,6 +74,9 @@ func TestNativeToken_SyntacticalValidation(t *testing.T) { } foundryBytes, err := tpkg.TestAPI.Encode(foundryIn, serix.WithValidation()) + if err == nil { + err = iotago.OutputsSyntacticalFoundry()(0, foundryIn) + } if test.wantErr != nil { require.ErrorIs(t, err, test.wantErr) return diff --git a/go.mod b/go.mod index 2dafbb245..33257d46e 100644 --- a/go.mod +++ b/go.mod @@ -5,14 +5,14 @@ go 1.21 require ( github.com/eclipse/paho.mqtt.golang v1.4.3 github.com/ethereum/go-ethereum v1.13.4 - github.com/iotaledger/hive.go/constraints v0.0.0-20231019122130-383150822505 - github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20231019122130-383150822505 - github.com/iotaledger/hive.go/crypto v0.0.0-20231019122130-383150822505 - github.com/iotaledger/hive.go/ierrors v0.0.0-20231019122130-383150822505 - github.com/iotaledger/hive.go/lo v0.0.0-20231019122130-383150822505 - github.com/iotaledger/hive.go/runtime v0.0.0-20231019122130-383150822505 - github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231019122130-383150822505 - github.com/iotaledger/hive.go/stringify v0.0.0-20231019122130-383150822505 + github.com/iotaledger/hive.go/constraints v0.0.0-20231108050255-98e0fa35e936 + github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20231108050255-98e0fa35e936 + github.com/iotaledger/hive.go/crypto v0.0.0-20231108050255-98e0fa35e936 + github.com/iotaledger/hive.go/ierrors v0.0.0-20231108050255-98e0fa35e936 + github.com/iotaledger/hive.go/lo v0.0.0-20231108050255-98e0fa35e936 + github.com/iotaledger/hive.go/runtime v0.0.0-20231108050255-98e0fa35e936 + github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231108050255-98e0fa35e936 + github.com/iotaledger/hive.go/stringify v0.0.0-20231108050255-98e0fa35e936 github.com/pasztorpisti/qs v0.0.0-20171216220353-8d6c33ee906c github.com/samber/lo v1.38.1 github.com/stretchr/testify v1.8.4 diff --git a/go.sum b/go.sum index a3c5158aa..674ded5fc 100644 --- a/go.sum +++ b/go.sum @@ -23,22 +23,22 @@ github.com/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw= github.com/iancoleman/orderedmap v0.3.0 h1:5cbR2grmZR/DiVt+VJopEhtVs9YGInGIxAoMJn+Ichc= github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJDkXXS7VoV7XGE= -github.com/iotaledger/hive.go/constraints v0.0.0-20231019122130-383150822505 h1:5HIWxSUvDb1ZfFwfcjdN0H9+ndDyJ644YDyKgM8gcDU= -github.com/iotaledger/hive.go/constraints v0.0.0-20231019122130-383150822505/go.mod h1:dOBOM2s4se3HcWefPe8sQLUalGXJ8yVXw58oK8jke3s= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20231019122130-383150822505 h1:rqTSevVlVit9Ae8pRb3gVDi0zjvw1JkJdM5LzPo9J9g= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20231019122130-383150822505/go.mod h1:Mc+ACqBGPxrPMIPUBOm6/HL0J6m0iVMwjtIEKW3uow8= -github.com/iotaledger/hive.go/crypto v0.0.0-20231019122130-383150822505 h1:cgN+qu3JRMKbpTsPEw84HZ7zvCN+lXnhywekHRgb8tU= -github.com/iotaledger/hive.go/crypto v0.0.0-20231019122130-383150822505/go.mod h1:h3o6okvMSEK3KOX6pOp3yq1h9ohTkTfo6X8MzEadeb0= -github.com/iotaledger/hive.go/ierrors v0.0.0-20231019122130-383150822505 h1:lO9xx87AnXo09j2Cz1FCTcLdcnx9N9nvjguiCudULWc= -github.com/iotaledger/hive.go/ierrors v0.0.0-20231019122130-383150822505/go.mod h1:HcE8B5lP96enc/OALTb2/rIIi+yOLouRoHOKRclKmC8= -github.com/iotaledger/hive.go/lo v0.0.0-20231019122130-383150822505 h1:4mbDhIubtFvOGVbgf5FnNe60ssaEJVT5ODhhNoQxEok= -github.com/iotaledger/hive.go/lo v0.0.0-20231019122130-383150822505/go.mod h1:s4kzx9QY1MVWHJralj+3q5kI0eARtrJhphYD/iBbPfo= -github.com/iotaledger/hive.go/runtime v0.0.0-20231019122130-383150822505 h1:YXHDF9du4MASDbTuqXPMwu5aAJYkJ/EGgmblv2SxJPw= -github.com/iotaledger/hive.go/runtime v0.0.0-20231019122130-383150822505/go.mod h1:jRw8yFipiPaqmTPHh7hTcxAP9u6pjRGpByS3REJKkbY= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231019122130-383150822505 h1:8F98TOpUzb4Ku9HhHMFvjgX9ky4Sr8ESPo8JOjAqbg8= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231019122130-383150822505/go.mod h1:SdK26z8/VhWtxaqCuQrufm80SELgowQPmu9T/8eUQ8g= -github.com/iotaledger/hive.go/stringify v0.0.0-20231019122130-383150822505 h1:S2WvE6nWtLpxLXKcn7KwGNfh4VNSUKnz42jcjq0Dv0g= -github.com/iotaledger/hive.go/stringify v0.0.0-20231019122130-383150822505/go.mod h1:FTo/UWzNYgnQ082GI9QVM9HFDERqf9rw9RivNpqrnTs= +github.com/iotaledger/hive.go/constraints v0.0.0-20231108050255-98e0fa35e936 h1:qkq0Wz+Y3J8QYRLd0fwTgHuur/A3k7d82BxOKSfvk8c= +github.com/iotaledger/hive.go/constraints v0.0.0-20231108050255-98e0fa35e936/go.mod h1:dOBOM2s4se3HcWefPe8sQLUalGXJ8yVXw58oK8jke3s= +github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20231108050255-98e0fa35e936 h1:GtsYwcCqRomhMo190TPxBrOzs6YnVmqkmQgT/lJrJRo= +github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20231108050255-98e0fa35e936/go.mod h1:CdixkrB7VdQzEDlVuwsxPtsiJL/WXrQgz3PELIqlLko= +github.com/iotaledger/hive.go/crypto v0.0.0-20231108050255-98e0fa35e936 h1:Xeb4w0g0Kv2ZjdCZQqz8oiqAU5qAy8OXG8kGTXSPzuY= +github.com/iotaledger/hive.go/crypto v0.0.0-20231108050255-98e0fa35e936/go.mod h1:OQ9EVTTQT1mkO/16BgwSIyQlAhEg+Cptud/yutevWsI= +github.com/iotaledger/hive.go/ierrors v0.0.0-20231108050255-98e0fa35e936 h1:o5S4KUAwToOLXoYYRj9ZgqeDsFv1VRM4+Mni0Tdj2Ck= +github.com/iotaledger/hive.go/ierrors v0.0.0-20231108050255-98e0fa35e936/go.mod h1:HcE8B5lP96enc/OALTb2/rIIi+yOLouRoHOKRclKmC8= +github.com/iotaledger/hive.go/lo v0.0.0-20231108050255-98e0fa35e936 h1:coXPklQ7JgqTXIUXh3b4OHml1VIvI8x7pQsjsES/u/s= +github.com/iotaledger/hive.go/lo v0.0.0-20231108050255-98e0fa35e936/go.mod h1:6Ee7i6b4tuTHuRYnPP8VUb0wr9XFI5qlqtnttBd9jRg= +github.com/iotaledger/hive.go/runtime v0.0.0-20231108050255-98e0fa35e936 h1:XbC1fmY87UJ/yMs8U2YqlUdJsqb0Xqj/ZYQKlZ7AUG8= +github.com/iotaledger/hive.go/runtime v0.0.0-20231108050255-98e0fa35e936/go.mod h1:DrZPvUvLarK8C2qb+3H2vdypp/MuhpQmB3iMJbDCr/Q= +github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231108050255-98e0fa35e936 h1:LXhLW2cN9bQYoHQsgmJRb/jiRBRU5s2rLoCNjZfgHdg= +github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231108050255-98e0fa35e936/go.mod h1:FoH3T6yKlZJp8xm8K+zsQiibSynp32v21CpWx8xkek8= +github.com/iotaledger/hive.go/stringify v0.0.0-20231108050255-98e0fa35e936 h1:Y4HgL5gm9S27usg5M2t6wi1BSdCxVorM62lwnpKuMd4= +github.com/iotaledger/hive.go/stringify v0.0.0-20231108050255-98e0fa35e936/go.mod h1:FTo/UWzNYgnQ082GI9QVM9HFDERqf9rw9RivNpqrnTs= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= diff --git a/mana_decay_provider_test.go b/mana_decay_provider_test.go index 93afafe8a..16398ba35 100644 --- a/mana_decay_provider_test.go +++ b/mana_decay_provider_test.go @@ -39,7 +39,7 @@ func TestMain(m *testing.M) { testManaDecayFactors = tpkg.ManaDecayFactors(betaPerYear, 1<> t.slotsPerEpochExponent) + if slot <= t.genesisSlot { + return 0 + } + + return EpochIndex((slot - t.genesisSlot) >> t.slotsPerEpochExponent) } // EpochStart calculates the start slot of the given epoch. func (t *TimeProvider) EpochStart(epoch EpochIndex) SlotIndex { - return SlotIndex(epoch << t.slotsPerEpochExponent) + return t.genesisSlot + SlotIndex(epoch< 0 { + t.Run(fmt.Sprintf("Test Below Genesis %d", genesisSlot), func(t *testing.T) { + firstEpoch := iotago.EpochIndex(0) + belowGenesisTime := genesisTime.Add(-time.Nanosecond) + + require.EqualValues(t, genesisSlot, tp.SlotFromTime(belowGenesisTime)) + + require.EqualValues(t, belowGenesisTime, tp.SlotStartTime(genesisSlot-1)) + require.EqualValues(t, belowGenesisTime, tp.SlotStartTime(0)) + + require.EqualValues(t, belowGenesisTime, tp.SlotEndTime(genesisSlot-1)) + require.EqualValues(t, belowGenesisTime, tp.SlotEndTime(0)) + + require.EqualValues(t, firstEpoch, tp.EpochFromSlot(genesisSlot-1)) + require.EqualValues(t, firstEpoch, tp.EpochFromSlot(0)) + + require.EqualValues(t, 0, tp.SlotsBeforeNextEpoch(genesisSlot-1)) + require.EqualValues(t, 0, tp.SlotsBeforeNextEpoch(0)) + + require.EqualValues(t, 0, tp.SlotsSinceEpochStart(genesisSlot-1)) + require.EqualValues(t, 0, tp.SlotsSinceEpochStart(0)) + }) + } + + t.Run(fmt.Sprintf("Test SlotFromTime %d", genesisSlot), func(t *testing.T) { slot0StartTime := genesisTime.Add(-time.Nanosecond) - require.EqualValues(t, 0, tp.SlotFromTime(slot0StartTime)) + require.EqualValues(t, genesisSlot, tp.SlotFromTime(slot0StartTime)) slot1StartTime := genesisTime - require.EqualValues(t, 1, tp.SlotFromTime(slot1StartTime)) + require.EqualValues(t, genesisSlot+1, tp.SlotFromTime(slot1StartTime)) slot2StartTime := genesisTime.Add(time.Duration(slotDurationSeconds) * time.Second) - require.EqualValues(t, 2, tp.SlotFromTime(slot2StartTime)) + require.EqualValues(t, genesisSlot+2, tp.SlotFromTime(slot2StartTime)) arbitraryTime := genesisTime.Add(time.Duration(slotDurationSeconds*3)*time.Second + 5*time.Second + 300*time.Millisecond) - require.EqualValues(t, 4, tp.SlotFromTime(arbitraryTime)) + require.EqualValues(t, genesisSlot+4, tp.SlotFromTime(arbitraryTime)) }) - t.Run("Test SlotStartTime", func(t *testing.T) { + t.Run(fmt.Sprintf("Test SlotStartTime %d", genesisSlot), func(t *testing.T) { slot0StartTime := genesisTime.Add(-time.Nanosecond) - require.EqualValues(t, slot0StartTime, tp.SlotStartTime(0)) + require.EqualValues(t, slot0StartTime, tp.SlotStartTime(genesisSlot)) slot1StartTime := genesisTime - require.EqualValues(t, slot1StartTime, tp.SlotStartTime(1)) + require.EqualValues(t, slot1StartTime, tp.SlotStartTime(genesisSlot+1)) slot2StartTime := genesisTime.Add(time.Duration(slotDurationSeconds) * time.Second) - require.EqualValues(t, slot2StartTime, tp.SlotStartTime(2)) + require.EqualValues(t, slot2StartTime, tp.SlotStartTime(genesisSlot+2)) slot4000StartTime := genesisTime.Add(time.Duration(slotDurationSeconds*3999) * time.Second) - require.EqualValues(t, slot4000StartTime, tp.SlotStartTime(4000)) + require.EqualValues(t, slot4000StartTime, tp.SlotStartTime(genesisSlot+4000)) }) - t.Run("Test SlotEndTime", func(t *testing.T) { + t.Run(fmt.Sprintf("Test SlotEndTime %d", genesisSlot), func(t *testing.T) { slot0EndTime := genesisTime.Add(-time.Nanosecond) - require.EqualValues(t, slot0EndTime, tp.SlotEndTime(0)) + require.EqualValues(t, slot0EndTime, tp.SlotEndTime(genesisSlot)) slot1EndTime := genesisTime.Add(time.Duration(slotDurationSeconds) * time.Second).Add(-time.Nanosecond) - require.EqualValues(t, slot1EndTime, tp.SlotEndTime(1)) + require.EqualValues(t, slot1EndTime, tp.SlotEndTime(genesisSlot+1)) slot2EndTime := genesisTime.Add(time.Duration(slotDurationSeconds*2) * time.Second).Add(-time.Nanosecond) - require.EqualValues(t, slot2EndTime, tp.SlotEndTime(2)) + require.EqualValues(t, slot2EndTime, tp.SlotEndTime(genesisSlot+2)) slot4000EndTime := genesisTime.Add(time.Duration(slotDurationSeconds*4000) * time.Second).Add(-time.Nanosecond) - require.EqualValues(t, slot4000EndTime, tp.SlotEndTime(4000)) + require.EqualValues(t, slot4000EndTime, tp.SlotEndTime(genesisSlot+4000)) }) - t.Run("Test EpochFromSlot", func(t *testing.T) { - require.EqualValues(t, 0, tp.EpochFromSlot(0)) - require.EqualValues(t, 0, tp.EpochFromSlot(7)) - require.EqualValues(t, 1, tp.EpochFromSlot(8)) - require.EqualValues(t, 1, tp.EpochFromSlot(15)) - require.EqualValues(t, 4000, tp.EpochFromSlot(32000)) - require.EqualValues(t, 4000, tp.EpochFromSlot(32007)) + t.Run(fmt.Sprintf("Test EpochFromSlot %d", genesisSlot), func(t *testing.T) { + require.EqualValues(t, 0, tp.EpochFromSlot(genesisSlot)) + require.EqualValues(t, 0, tp.EpochFromSlot(genesisSlot+7)) + require.EqualValues(t, 1, tp.EpochFromSlot(genesisSlot+8)) + require.EqualValues(t, 1, tp.EpochFromSlot(genesisSlot+15)) + require.EqualValues(t, 4000, tp.EpochFromSlot(genesisSlot+32000)) + require.EqualValues(t, 4000, tp.EpochFromSlot(genesisSlot+32007)) }) - t.Run("Test EpochStart", func(t *testing.T) { - require.EqualValues(t, 0, tp.EpochStart(0)) - require.EqualValues(t, 8, tp.EpochStart(1)) - require.EqualValues(t, 16, tp.EpochStart(2)) - require.EqualValues(t, 32000, tp.EpochStart(4000)) + t.Run(fmt.Sprintf("Test EpochStart %d", genesisSlot), func(t *testing.T) { + require.EqualValues(t, genesisSlot, tp.EpochStart(0)) + require.EqualValues(t, genesisSlot+8, tp.EpochStart(1)) + require.EqualValues(t, genesisSlot+16, tp.EpochStart(2)) + require.EqualValues(t, genesisSlot+32000, tp.EpochStart(4000)) }) - t.Run("Test EpochEnd", func(t *testing.T) { - require.EqualValues(t, 7, tp.EpochEnd(0)) - require.EqualValues(t, 15, tp.EpochEnd(1)) - require.EqualValues(t, 23, tp.EpochEnd(2)) - require.EqualValues(t, 32007, tp.EpochEnd(4000)) + t.Run(fmt.Sprintf("Test EpochEnd %d", genesisSlot), func(t *testing.T) { + require.EqualValues(t, genesisSlot+7, tp.EpochEnd(0)) + require.EqualValues(t, genesisSlot+15, tp.EpochEnd(1)) + require.EqualValues(t, genesisSlot+23, tp.EpochEnd(2)) + require.EqualValues(t, genesisSlot+32007, tp.EpochEnd(4000)) }) - t.Run("Test SlotsBeforeNextEpoch", func(t *testing.T) { - require.EqualValues(t, 8, tp.SlotsBeforeNextEpoch(0)) - require.EqualValues(t, 1, tp.SlotsBeforeNextEpoch(7)) - require.EqualValues(t, 1, tp.SlotsBeforeNextEpoch(15)) - require.EqualValues(t, 8, tp.SlotsBeforeNextEpoch(32000)) - require.EqualValues(t, 1, tp.SlotsBeforeNextEpoch(32007)) + t.Run(fmt.Sprintf("Test SlotsBeforeNextEpoch %d", genesisSlot), func(t *testing.T) { + require.EqualValues(t, genesisSlot+8, tp.SlotsBeforeNextEpoch(genesisSlot)) + require.EqualValues(t, genesisSlot+1, tp.SlotsBeforeNextEpoch(genesisSlot+7)) + require.EqualValues(t, genesisSlot+1, tp.SlotsBeforeNextEpoch(genesisSlot+15)) + require.EqualValues(t, genesisSlot+8, tp.SlotsBeforeNextEpoch(genesisSlot+32000)) + require.EqualValues(t, genesisSlot+1, tp.SlotsBeforeNextEpoch(genesisSlot+32007)) }) - t.Run("Test SlotsSinceEpochStart", func(t *testing.T) { - require.EqualValues(t, 0, tp.SlotsSinceEpochStart(0)) - require.EqualValues(t, 7, tp.SlotsSinceEpochStart(7)) - require.EqualValues(t, 0, tp.SlotsSinceEpochStart(8)) - require.EqualValues(t, 7, tp.SlotsSinceEpochStart(15)) - require.EqualValues(t, 0, tp.SlotsSinceEpochStart(32000)) - require.EqualValues(t, 7, tp.SlotsSinceEpochStart(32007)) + t.Run(fmt.Sprintf("Test SlotsSinceEpochStart %d", genesisSlot), func(t *testing.T) { + require.EqualValues(t, genesisSlot, tp.SlotsSinceEpochStart(genesisSlot)) + require.EqualValues(t, genesisSlot+7, tp.SlotsSinceEpochStart(genesisSlot+7)) + require.EqualValues(t, genesisSlot, tp.SlotsSinceEpochStart(genesisSlot+8)) + require.EqualValues(t, genesisSlot+7, tp.SlotsSinceEpochStart(genesisSlot+15)) + require.EqualValues(t, genesisSlot+0, tp.SlotsSinceEpochStart(genesisSlot+32000)) + require.EqualValues(t, genesisSlot+7, tp.SlotsSinceEpochStart(genesisSlot+32007)) }) } diff --git a/tpkg/util.go b/tpkg/util.go index 47729077b..81404d0a4 100644 --- a/tpkg/util.go +++ b/tpkg/util.go @@ -713,7 +713,7 @@ func RandBlock(blockBody iotago.BlockBody, api iotago.API, rmc iotago.Mana) *iot Header: iotago.BlockHeader{ ProtocolVersion: TestAPI.Version(), IssuingTime: RandUTCTime(), - SlotCommitmentID: iotago.NewEmptyCommitment(api.ProtocolParameters().Version()).MustID(), + SlotCommitmentID: iotago.NewEmptyCommitment(api).MustID(), IssuerID: RandAccountID(), }, Body: basicBlock, @@ -726,7 +726,7 @@ func RandBlock(blockBody iotago.BlockBody, api iotago.API, rmc iotago.Mana) *iot Header: iotago.BlockHeader{ ProtocolVersion: TestAPI.Version(), IssuingTime: RandUTCTime(), - SlotCommitmentID: iotago.NewEmptyCommitment(api.ProtocolParameters().Version()).MustID(), + SlotCommitmentID: iotago.NewEmptyCommitment(api).MustID(), IssuerID: RandAccountID(), }, Body: blockBody, @@ -1058,7 +1058,7 @@ func RandProtocolParameters() iotago.ProtocolParameters { RandWorkScore(math.MaxUint32), RandWorkScore(math.MaxUint32), ), - iotago.WithTimeProviderOptions(time.Now().Unix(), RandUint8(math.MaxUint8), RandUint8(math.MaxUint8)), + iotago.WithTimeProviderOptions(RandSlot(), time.Now().Unix(), RandUint8(math.MaxUint8), RandUint8(math.MaxUint8)), iotago.WithLivenessOptions(RandUint16(math.MaxUint16), RandUint16(math.MaxUint16), RandSlot(), RandSlot(), RandSlot()), iotago.WithCongestionControlOptions( RandMana(iotago.MaxMana), diff --git a/transaction.go b/transaction.go index 5de45ac62..8f48ba25c 100644 --- a/transaction.go +++ b/transaction.go @@ -44,8 +44,6 @@ var ( ErrAnchorOutputCyclicAddress = ierrors.New("anchor output's AnchorID corresponds to state and/or governance controller") // ErrNFTOutputCyclicAddress gets returned if an NFTOutput's NFTID results into the same address as the address field within the output. ErrNFTOutputCyclicAddress = ierrors.New("NFT output's ID corresponds to address field") - // ErrDelegationValidatorAddressZeroed gets returned if a Delegation Output's Validator address is zeroed out. - ErrDelegationValidatorAddressZeroed = ierrors.New("delegation output's validator address is zeroed") // ErrOutputsSumExceedsTotalSupply gets returned if the sum of the output deposits exceeds the total supply of tokens. ErrOutputsSumExceedsTotalSupply = ierrors.New("accumulated output balance exceeds total supply") // ErrOutputAmountMoreThanTotalSupply gets returned if an output base token amount is more than the total supply. @@ -251,10 +249,10 @@ func (t *Transaction) Size() int { // syntacticallyValidate checks whether the transaction essence is syntactically valid. // The function does not syntactically validate the input or outputs themselves. -func (t *Transaction) syntacticallyValidate(api API) error { +func (t *Transaction) SyntacticallyValidate(api API) error { protoParams := api.ProtocolParameters() - if err := t.TransactionEssence.SyntacticallyValidate(api); err != nil { + if err := t.TransactionEssence.syntacticallyValidateEssence(api); err != nil { return err } @@ -268,6 +266,8 @@ func (t *Transaction) syntacticallyValidate(api API) error { OutputsSyntacticalAnchor(), OutputsSyntacticalNFT(), OutputsSyntacticalDelegation(), + OutputsSyntacticalAddressRestrictions(), + OutputsSyntacticalImplicitAccountCreationAddress(), ) } diff --git a/transaction_essence.go b/transaction_essence.go index 3e5a35b58..594293f1d 100644 --- a/transaction_essence.go +++ b/transaction_essence.go @@ -108,7 +108,10 @@ func (u *TransactionEssence) WorkScore(workScoreParameters *WorkScoreParameters) // SyntacticallyValidate checks whether the transaction essence is syntactically valid. // The function does not syntactically validate the input or outputs themselves. -func (u *TransactionEssence) SyntacticallyValidate(api API) error { +// +// This function includes "essence" in its name to disambiguate it from the "SyntacticallyValidate" function on the Transaction, +// and since the essence is embedded in the Transaction a similar name could easily lead to confusion. +func (u *TransactionEssence) syntacticallyValidateEssence(api API) error { if err := BitMaskNonTrailingZeroBytesValidatorFunc(u.Capabilities); err != nil { return ierrors.Wrapf(ErrTxEssenceCapabilitiesInvalid, "invalid capabilities bitmask: %w", err) } diff --git a/vm/nova/vm.go b/vm/nova/vm.go index 1cef11685..d6939da0b 100644 --- a/vm/nova/vm.go +++ b/vm/nova/vm.go @@ -20,7 +20,7 @@ func NewVirtualMachine() vm.VirtualMachine { vm.ExecFuncBalancedNativeTokens(), vm.ExecFuncChainTransitions(), vm.ExecFuncBalancedMana(), - vm.ExecFuncAddressRestrictions(), + vm.ExecFuncAtMostOneImplicitAccountCreationAddress(), }, } } diff --git a/vm/nova/vm_test.go b/vm/nova/vm_test.go index 68b7bc422..0e19ced62 100644 --- a/vm/nova/vm_test.go +++ b/vm/nova/vm_test.go @@ -41,7 +41,7 @@ var ( iotago.WithNetworkOptions("test", "test"), iotago.WithSupplyOptions(tpkg.TestTokenSupply, 100, 1, 10, 100, 100, 100), iotago.WithWorkScoreOptions(1, 100, 20, 20, 20, 20, 100, 100, 100, 200), - iotago.WithTimeProviderOptions(100, slotDurationSeconds, slotsPerEpochExponent), + iotago.WithTimeProviderOptions(0, 100, slotDurationSeconds, slotsPerEpochExponent), iotago.WithManaOptions(bitsCount, generationRate, generationRateExponent, @@ -6999,7 +6999,7 @@ func TestManaRewardsClaimingDelegation(t *testing.T) { require.NoError(t, validateAndExecuteSignedTransaction(tx, resolvedInputs)) } -func TestTxSemanticAddressRestrictions(t *testing.T) { +func TestTxSyntacticAddressRestrictions(t *testing.T) { type testParameters struct { name string address iotago.Address @@ -7013,8 +7013,6 @@ func TestTxSemanticAddressRestrictions(t *testing.T) { _, ident, identAddrKeys := tpkg.RandEd25519Identity() addr := tpkg.RandEd25519Address() - iotago.RestrictedAddressWithCapabilities(addr) - tests := []*test{ { createTestOutput: func(address iotago.Address) iotago.Output { @@ -7328,6 +7326,7 @@ func TestTxSemanticAddressRestrictions(t *testing.T) { transaction := &iotago.Transaction{ API: testAPI, TransactionEssence: &iotago.TransactionEssence{ + NetworkID: testAPI.ProtocolParameters().NetworkID(), Inputs: inputIDs.UTXOInputs(), CreationSlot: 10, }, @@ -7347,9 +7346,8 @@ func TestTxSemanticAddressRestrictions(t *testing.T) { t.Run(testInput.name, func(t *testing.T) { testOutput := tt.createTestOutput(testInput.address) - inputs, sig, transaction := makeTransaction(testOutput) + _, sig, transaction := makeTransaction(testOutput) - resolvedInputs := vm.ResolvedInputs{InputSet: inputs} tx := &iotago.SignedTransaction{ API: testAPI, Transaction: transaction, @@ -7358,13 +7356,18 @@ func TestTxSemanticAddressRestrictions(t *testing.T) { }, } - _, err := novaVM.Execute(tx.Transaction, resolvedInputs, make(vm.UnlockedIdentities), vm.ExecFuncAddressRestrictions()) - if testInput.wantErr != nil { - require.ErrorIs(t, err, testInput.wantErr) - return - } + addressRestrictionFunc := iotago.OutputsSyntacticalAddressRestrictions() + + for index, output := range tx.Transaction.Outputs { + err := addressRestrictionFunc(index, output) + + if testInput.wantErr != nil { + require.ErrorIs(t, err, testInput.wantErr) + return + } - require.NoError(t, err) + require.NoError(t, err) + } }) } } @@ -7847,7 +7850,13 @@ func TestTxSemanticImplicitAccountCreationAndTransition(t *testing.T) { resolvedInputs.CommitmentInput = &tests[idx].resolvedCommitmentInput t.Run(tt.name, func(t *testing.T) { - err := validateAndExecuteSignedTransaction(tx, resolvedInputs) + var err error + // Some constraints are implicitly tested as part of the address restrictions, which are syntactic checks. + err = tx.Transaction.SyntacticallyValidate(tx.API) + if err == nil { + err = validateAndExecuteSignedTransaction(tx, resolvedInputs) + } + if tt.wantErr != nil { require.ErrorIs(t, err, tt.wantErr) return diff --git a/vm/vm.go b/vm/vm.go index 1d4d3b247..89f19cf28 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -768,83 +768,10 @@ func ExecFuncBalancedNativeTokens() ExecFunc { } } -func checkAddressRestrictions(output iotago.TxEssenceOutput, address iotago.Address) error { - addrWithCapabilities, isAddrWithCapabilities := address.(iotago.AddressCapabilities) - if !isAddrWithCapabilities { - // no restrictions - return nil - } - - if addrWithCapabilities.CannotReceiveNativeTokens() && output.FeatureSet().HasNativeTokenFeature() { - return iotago.ErrAddressCannotReceiveNativeTokens - } - - if addrWithCapabilities.CannotReceiveMana() && output.StoredMana() != 0 { - return iotago.ErrAddressCannotReceiveMana - } - - if addrWithCapabilities.CannotReceiveOutputsWithTimelockUnlockCondition() && output.UnlockConditionSet().HasTimelockCondition() { - return iotago.ErrAddressCannotReceiveTimelockUnlockCondition - } - - if addrWithCapabilities.CannotReceiveOutputsWithExpirationUnlockCondition() && output.UnlockConditionSet().HasExpirationCondition() { - return iotago.ErrAddressCannotReceiveExpirationUnlockCondition - } - - if addrWithCapabilities.CannotReceiveOutputsWithStorageDepositReturnUnlockCondition() && output.UnlockConditionSet().HasStorageDepositReturnCondition() { - return iotago.ErrAddressCannotReceiveStorageDepositReturnUnlockCondition - } - - if addrWithCapabilities.CannotReceiveAccountOutputs() && output.Type() == iotago.OutputAccount { - return iotago.ErrAddressCannotReceiveAccountOutput - } - - if addrWithCapabilities.CannotReceiveAnchorOutputs() && output.Type() == iotago.OutputAnchor { - return iotago.ErrAddressCannotReceiveAnchorOutput - } - - if addrWithCapabilities.CannotReceiveNFTOutputs() && output.Type() == iotago.OutputNFT { - return iotago.ErrAddressCannotReceiveNFTOutput - } - - if addrWithCapabilities.CannotReceiveDelegationOutputs() && output.Type() == iotago.OutputDelegation { - return iotago.ErrAddressCannotReceiveDelegationOutput - } - - return nil -} - -// Returns a func that checks the capability flag restrictions on addresses, and checks that -// no more than one Implicit Account Creation Address is on the input side of a transaction. -// -// Does not validate the Return Address in StorageDepositReturnUnlockCondition because such a Return Address -// already is as restricted as the most restricted address. -func ExecFuncAddressRestrictions() ExecFunc { +// Returns a func that checks that no more than one Implicit Account Creation Address +// is on the input side of a transaction. +func ExecFuncAtMostOneImplicitAccountCreationAddress() ExecFunc { return func(vm VirtualMachine, vmParams *Params) error { - for _, output := range vmParams.WorkingSet.Tx.Outputs { - if addressUnlockCondition := output.UnlockConditionSet().Address(); addressUnlockCondition != nil { - if err := checkAddressRestrictions(output, addressUnlockCondition.Address); err != nil { - return err - } - } - if stateControllerUnlockCondition := output.UnlockConditionSet().StateControllerAddress(); stateControllerUnlockCondition != nil { - if err := checkAddressRestrictions(output, stateControllerUnlockCondition.Address); err != nil { - return err - } - } - if governorUnlockCondition := output.UnlockConditionSet().GovernorAddress(); governorUnlockCondition != nil { - if err := checkAddressRestrictions(output, governorUnlockCondition.Address); err != nil { - return err - } - } - if expirationUnlockCondition := output.UnlockConditionSet().Expiration(); expirationUnlockCondition != nil { - if err := checkAddressRestrictions(output, expirationUnlockCondition.ReturnAddress); err != nil { - return err - } - } - } - - // Check that no more than one Implicit Account Creation Address is on the input side of a transaction. transactionHasImplicitAccountCreationAddress := false for _, input := range vmParams.WorkingSet.UTXOInputs { addressUnlockCondition := input.UnlockConditionSet().Address()