Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/develop' into feat/simplify-seri…
Browse files Browse the repository at this point in the history
…alization
  • Loading branch information
jonastheis committed Nov 6, 2023
2 parents b61e53d + 5d5b735 commit afca7f4
Show file tree
Hide file tree
Showing 29 changed files with 3,454 additions and 1,840 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/golangci-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
jobs:
golangci-lint:
name: GolangCI-Lint
runs-on: ubuntu-latest
runs-on: self-hosted
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v3
Expand Down
7 changes: 5 additions & 2 deletions .github/workflows/test_and_benchmark.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,20 @@ jobs:
strategy:
matrix:
go-version: [ "1.21.x" ]
platform: [ ubuntu-latest ]
runs-on: ${{ matrix.platform }}
platform: [ self-hosted ]
runs-on: ${{ matrix.platform }}
steps:
- name: Install Go
if: success()
uses: actions/setup-go@v4
with:
go-version: ${{ matrix.go-version }}

- name: Checkout code
uses: actions/checkout@v3

- name: Run tests
run: go test -v -covermode=count ./...

- name: Run Benchmarks
run: go test -bench=. ./...
2 changes: 2 additions & 0 deletions address.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ var (
ErrUnknownAddrType = ierrors.New("unknown address type")
// ErrInvalidAddressType gets returned when an address type is invalid.
ErrInvalidAddressType = ierrors.New("invalid address type")
// ErrInvalidRestrictedAddress gets returned when a RestrictedAddress is invalid.
ErrInvalidRestrictedAddress = ierrors.New("invalid restricted address")
// ErrInvalidNestedAddressType gets returned when a nested address inside a MultiAddress or RestrictedAddress is invalid.
ErrInvalidNestedAddressType = ierrors.New("invalid nested address type")
// ErrImplicitAccountCreationAddressInInvalidUnlockCondition gets returned when a Implicit Account Creation Address
Expand Down
63 changes: 60 additions & 3 deletions address_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func TestAddressDeSerialize(t *testing.T) {
},
{
name: "ok - RestrictedEd25519Address without capabilities",
source: tpkg.RandRestrictedEd25519Address(iotago.AddressCapabilitiesBitMask{0x0}),
source: tpkg.RandRestrictedEd25519Address(iotago.AddressCapabilitiesBitMask{}),
target: &iotago.RestrictedAddress{},
},
{
Expand Down Expand Up @@ -433,6 +433,63 @@ func TestRestrictedAddressCapabilities(t *testing.T) {
assertRestrictedAddresses(t, addresses)
}

//nolint:dupl // we have a lot of similar tests
func TestRestrictedAddressCapabilitiesBitMask(t *testing.T) {

type test struct {
name string
addr *iotago.RestrictedAddress
wantErr error
}

tests := []*test{
{
name: "ok - no trailing zero bytes",
addr: &iotago.RestrictedAddress{
Address: tpkg.RandEd25519Address(),
AllowedCapabilities: iotago.AddressCapabilitiesBitMask{0x01, 0x02},
},
wantErr: nil,
},
{
name: "ok - empty capabilities",
addr: &iotago.RestrictedAddress{
Address: tpkg.RandEd25519Address(),
AllowedCapabilities: iotago.AddressCapabilitiesBitMask{},
},
wantErr: nil,
},
{
name: "fail - trailing zero bytes",
addr: &iotago.RestrictedAddress{
Address: tpkg.RandEd25519Address(),
AllowedCapabilities: iotago.AddressCapabilitiesBitMask{0x01, 0x00},
},
wantErr: iotago.ErrBitmaskTrailingZeroBytes,
},
{
name: "fail - single zero bytes",
addr: &iotago.RestrictedAddress{
Address: tpkg.RandEd25519Address(),
AllowedCapabilities: iotago.AddressCapabilitiesBitMask{0x00},
},
wantErr: iotago.ErrBitmaskTrailingZeroBytes,
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
_, err := tpkg.TestAPI.Encode(test.addr, serix.WithValidation())
if test.wantErr != nil {
require.ErrorIs(t, err, test.wantErr)

return
}
require.NoError(t, err)
})
}
}

type outputsSyntacticalValidationTest struct {
// the name of the testcase
name string
Expand Down Expand Up @@ -462,7 +519,7 @@ func runOutputsSyntacticalValidationTest(t *testing.T, testAPI iotago.API, test

func TestRestrictedAddressSyntacticalValidation(t *testing.T) {

defaultAmount := OneMi
defaultAmount := OneIOTA

tests := []*outputsSyntacticalValidationTest{
// ok - Valid address types nested inside of a RestrictedAddress
Expand Down Expand Up @@ -588,7 +645,7 @@ func TestRestrictedAddressSyntacticalValidation(t *testing.T) {

func TestMultiAddressSyntacticalValidation(t *testing.T) {

defaultAmount := OneMi
defaultAmount := OneIOTA

tests := []*outputsSyntacticalValidationTest{
// fail - threshold > cumulativeWeight
Expand Down
5 changes: 5 additions & 0 deletions api_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,12 @@ var (
// restrictedAddressValidatorFunc is a validator which checks that:
// 1. ImplicitAccountCreationAddress are not nested inside the RestrictedAddress.
// 2. RestrictedAddresses are not nested inside the RestrictedAddress.
// 3. The bitmask does not contain trailing zero bytes.
restrictedAddressValidatorFunc = func(ctx context.Context, addr RestrictedAddress) error {
if err := BitMaskNonTrailingZeroBytesValidatorFunc(addr.AllowedCapabilities); err != nil {
return ierrors.Wrapf(ErrInvalidRestrictedAddress, "invalid allowed capabilities bitmask: %w", err)
}

switch addr.Address.(type) {
case *Ed25519Address, *AccountAddress, *NFTAddress, *AnchorAddress, *MultiAddress:
// allowed address types
Expand Down
2 changes: 1 addition & 1 deletion api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
)

const (
OneMi iotago.BaseToken = 1_000_000
OneIOTA iotago.BaseToken = 1_000_000
)

type deSerializeTest struct {
Expand Down
108 changes: 85 additions & 23 deletions api_v3.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ func disallowImplicitAccountCreationAddress(address Address) error {

var (
basicOutputV3UnlockCondArrRules = &serix.ArrayRules{
Min: 1,
Max: 4,
Min: 1, // Min: AddressUnlockCondition
Max: 4, // Max: AddressUnlockCondition, StorageDepositReturnUnlockCondition, TimelockUnlockCondition, ExpirationUnlockCondition
MustOccur: serializer.TypePrefixes{
uint32(UnlockConditionAddress): struct{}{},
},
Expand All @@ -42,42 +42,71 @@ var (
serializer.ArrayValidationModeAtMostOneOfEachTypeByte,
}
basicOutputV3FeatBlocksArrRules = &serix.ArrayRules{
Min: 0,
Max: 4,
Min: 0, // Min: -
Max: 4, // Max: SenderFeature, MetadataFeature, TagFeature, NativeTokenFeature
ValidationMode: serializer.ArrayValidationModeNoDuplicates |
serializer.ArrayValidationModeLexicalOrdering |
serializer.ArrayValidationModeAtMostOneOfEachTypeByte,
}

accountOutputV3UnlockCondArrRules = &serix.ArrayRules{
Min: 2, Max: 2,
Min: 1, // Min: AddressUnlockCondition
Max: 1, // Max: AddressUnlockCondition
MustOccur: serializer.TypePrefixes{
uint32(UnlockConditionStateControllerAddress): struct{}{},
uint32(UnlockConditionGovernorAddress): struct{}{},
uint32(UnlockConditionAddress): struct{}{},
},
ValidationMode: serializer.ArrayValidationModeNoDuplicates |
serializer.ArrayValidationModeLexicalOrdering |
serializer.ArrayValidationModeAtMostOneOfEachTypeByte,
}

accountOutputV3FeatBlocksArrRules = &serix.ArrayRules{
Min: 0,
Max: 4,
Min: 0, // Min: -
Max: 4, // Max: SenderFeature, MetadataFeature, BlockIssuerFeature, StakingFeature
ValidationMode: serializer.ArrayValidationModeNoDuplicates |
serializer.ArrayValidationModeLexicalOrdering |
serializer.ArrayValidationModeAtMostOneOfEachTypeByte,
}

accountOutputV3ImmFeatBlocksArrRules = &serix.ArrayRules{
Min: 0,
Max: 2,
Min: 0, // Min: -
Max: 2, // Max: IssuerFeature, MetadataFeature
ValidationMode: serializer.ArrayValidationModeNoDuplicates |
serializer.ArrayValidationModeLexicalOrdering |
serializer.ArrayValidationModeAtMostOneOfEachTypeByte,
}

anchorOutputV3UnlockCondArrRules = &serix.ArrayRules{
Min: 2, // Min: StateControllerAddressUnlockCondition, GovernorAddressUnlockCondition
Max: 2, // Max: StateControllerAddressUnlockCondition, GovernorAddressUnlockCondition
MustOccur: serializer.TypePrefixes{
uint32(UnlockConditionStateControllerAddress): struct{}{},
uint32(UnlockConditionGovernorAddress): struct{}{},
},
ValidationMode: serializer.ArrayValidationModeNoDuplicates |
serializer.ArrayValidationModeLexicalOrdering |
serializer.ArrayValidationModeAtMostOneOfEachTypeByte,
}

anchorOutputV3FeatBlocksArrRules = &serix.ArrayRules{
Min: 0, // Min: -
Max: 2, // Max: SenderFeature, MetadataFeature
ValidationMode: serializer.ArrayValidationModeNoDuplicates |
serializer.ArrayValidationModeLexicalOrdering |
serializer.ArrayValidationModeAtMostOneOfEachTypeByte,
}

anchorOutputV3ImmFeatBlocksArrRules = &serix.ArrayRules{
Min: 0, // Min: -
Max: 2, // Max: IssuerFeature, MetadataFeature
ValidationMode: serializer.ArrayValidationModeNoDuplicates |
serializer.ArrayValidationModeLexicalOrdering |
serializer.ArrayValidationModeAtMostOneOfEachTypeByte,
}

foundryOutputV3UnlockCondArrRules = &serix.ArrayRules{
Min: 1, Max: 1,
Min: 1, // Min: ImmutableAccountUnlockCondition
Max: 1, // Max: ImmutableAccountUnlockCondition
MustOccur: serializer.TypePrefixes{
uint32(UnlockConditionImmutableAccount): struct{}{},
},
Expand All @@ -87,21 +116,24 @@ var (
}

foundryOutputV3FeatBlocksArrRules = &serix.ArrayRules{
Min: 0, Max: 2,
Min: 0, // Min: -
Max: 2, // Max: MetadataFeature, NativeTokenFeature
ValidationMode: serializer.ArrayValidationModeNoDuplicates |
serializer.ArrayValidationModeLexicalOrdering |
serializer.ArrayValidationModeAtMostOneOfEachTypeByte,
}

foundryOutputV3ImmFeatBlocksArrRules = &serix.ArrayRules{
Min: 0, Max: 1,
Min: 0, // Min: -
Max: 1, // Max: MetadataFeature
ValidationMode: serializer.ArrayValidationModeNoDuplicates |
serializer.ArrayValidationModeLexicalOrdering |
serializer.ArrayValidationModeAtMostOneOfEachTypeByte,
}

nftOutputV3UnlockCondArrRules = &serix.ArrayRules{
Min: 1, Max: 4,
Min: 1, // Min: AddressUnlockCondition
Max: 4, // Max: AddressUnlockCondition, StorageDepositReturnUnlockCondition, TimelockUnlockCondition, ExpirationUnlockCondition
MustOccur: serializer.TypePrefixes{
uint32(UnlockConditionAddress): struct{}{},
},
Expand All @@ -111,23 +143,24 @@ var (
}

nftOutputV3FeatBlocksArrRules = &serix.ArrayRules{
Min: 0,
Max: 3,
Min: 0, // Min: -
Max: 3, // Max: SenderFeature, MetadataFeature, TagFeature
ValidationMode: serializer.ArrayValidationModeNoDuplicates |
serializer.ArrayValidationModeLexicalOrdering |
serializer.ArrayValidationModeAtMostOneOfEachTypeByte,
}

nftOutputV3ImmFeatBlocksArrRules = &serix.ArrayRules{
Min: 0,
Max: 2,
Min: 0, // Min: -
Max: 2, // Max: IssuerFeature, MetadataFeature
ValidationMode: serializer.ArrayValidationModeNoDuplicates |
serializer.ArrayValidationModeLexicalOrdering |
serializer.ArrayValidationModeAtMostOneOfEachTypeByte,
}

delegationOutputV3UnlockCondArrRules = &serix.ArrayRules{
Min: 1, Max: 1,
Min: 1, // Min: AddressUnlockCondition
Max: 1, // Max: AddressUnlockCondition
MustOccur: serializer.TypePrefixes{
uint32(UnlockConditionAddress): struct{}{},
},
Expand Down Expand Up @@ -163,7 +196,8 @@ var (
}

txV3UnlocksArrRules = &serix.ArrayRules{
Min: 1, Max: MaxInputsCount,
Min: 1,
Max: MaxInputsCount,
}

blockIDsArrRules = &serix.ArrayRules{
Expand Down Expand Up @@ -430,8 +464,7 @@ func V3API(protoParams ProtocolParameters) API {
serix.TypeSettings{}.WithLengthPrefixType(serix.LengthPrefixTypeAsByte).WithArrayRules(accountOutputV3UnlockCondArrRules),
))

must(api.RegisterInterfaceObjects((*accountOutputUnlockCondition)(nil), (*StateControllerAddressUnlockCondition)(nil)))
must(api.RegisterInterfaceObjects((*accountOutputUnlockCondition)(nil), (*GovernorAddressUnlockCondition)(nil)))
must(api.RegisterInterfaceObjects((*accountOutputUnlockCondition)(nil), (*AddressUnlockCondition)(nil)))

must(api.RegisterTypeSettings(AccountOutputFeatures{},
serix.TypeSettings{}.WithLengthPrefixType(serix.LengthPrefixTypeAsByte).WithArrayRules(accountOutputV3FeatBlocksArrRules),
Expand All @@ -450,6 +483,34 @@ func V3API(protoParams ProtocolParameters) API {
must(api.RegisterInterfaceObjects((*accountOutputImmFeature)(nil), (*MetadataFeature)(nil)))
}

{
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),
))

must(api.RegisterInterfaceObjects((*anchorOutputUnlockCondition)(nil), (*StateControllerAddressUnlockCondition)(nil)))
must(api.RegisterInterfaceObjects((*anchorOutputUnlockCondition)(nil), (*GovernorAddressUnlockCondition)(nil)))

must(api.RegisterTypeSettings(AnchorOutputFeatures{},
serix.TypeSettings{}.WithLengthPrefixType(serix.LengthPrefixTypeAsByte).WithArrayRules(anchorOutputV3FeatBlocksArrRules),
))

must(api.RegisterInterfaceObjects((*anchorOutputFeature)(nil), (*SenderFeature)(nil)))
must(api.RegisterInterfaceObjects((*anchorOutputFeature)(nil), (*MetadataFeature)(nil)))

must(api.RegisterTypeSettings(AnchorOutputImmFeatures{},
serix.TypeSettings{}.WithLengthPrefixType(serix.LengthPrefixTypeAsByte).WithArrayRules(anchorOutputV3ImmFeatBlocksArrRules),
))

must(api.RegisterInterfaceObjects((*anchorOutputImmFeature)(nil), (*IssuerFeature)(nil)))
must(api.RegisterInterfaceObjects((*anchorOutputImmFeature)(nil), (*MetadataFeature)(nil)))
}

{
must(api.RegisterTypeSettings(FoundryOutput{},
serix.TypeSettings{}.WithObjectType(uint8(OutputFoundry))),
Expand Down Expand Up @@ -569,6 +630,7 @@ func V3API(protoParams ProtocolParameters) API {

must(api.RegisterInterfaceObjects((*TxEssenceOutput)(nil), (*BasicOutput)(nil)))
must(api.RegisterInterfaceObjects((*TxEssenceOutput)(nil), (*AccountOutput)(nil)))
must(api.RegisterInterfaceObjects((*TxEssenceOutput)(nil), (*AnchorOutput)(nil)))
must(api.RegisterInterfaceObjects((*TxEssenceOutput)(nil), (*DelegationOutput)(nil)))
must(api.RegisterInterfaceObjects((*TxEssenceOutput)(nil), (*FoundryOutput)(nil)))
must(api.RegisterInterfaceObjects((*TxEssenceOutput)(nil), (*NFTOutput)(nil)))
Expand Down
19 changes: 19 additions & 0 deletions bitmask.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
package iotago

import (
"github.com/iotaledger/hive.go/ierrors"
"github.com/iotaledger/iota.go/v4/hexutil"
)

var (
// ErrBitmaskTrailingZeroBytes gets returned when the trailing bytes of a bitmask are zero.
ErrBitmaskTrailingZeroBytes = ierrors.New("bitmask trailing bytes are zero")
)

func BitMaskHasBit(bm []byte, bit uint) bool {
byteIndex := bit / 8
if uint(len(bm)) <= byteIndex {
Expand All @@ -21,3 +31,12 @@ func BitMaskSetBit(bm []byte, bit uint) []byte {

return newBitmask
}

// BitMaskNonTrailingZeroBytesValidatorFunc checks that the trailing bytes of the bitmask are not zero.
func BitMaskNonTrailingZeroBytesValidatorFunc(bm []byte) error {
if len(bm) == 0 || bm[len(bm)-1] != 0 {
return nil
}

return ierrors.Wrapf(ErrBitmaskTrailingZeroBytes, "bitmask: %s", hexutil.EncodeHex(bm))
}
Loading

0 comments on commit afca7f4

Please sign in to comment.