diff --git a/.github/workflows/client-import.yml b/.github/workflows/client-import.yml index 8bc89c055c..e2643a1bff 100644 --- a/.github/workflows/client-import.yml +++ b/.github/workflows/client-import.yml @@ -1,9 +1,6 @@ name: Client Lib Import Check on: - push: - paths-ignore: - - 'docs/**' pull_request: paths-ignore: - 'docs/**' @@ -24,4 +21,4 @@ jobs: uses: actions/checkout@v2 - name: Run Import Check Script - run: ./scripts/client_import_check.sh \ No newline at end of file + run: ./scripts/client_import_check.sh diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 40e3411440..82b7b851ac 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -7,7 +7,7 @@ on: jobs: - build: + unit-tests: name: Unit tests runs-on: ubuntu-latest steps: @@ -36,4 +36,36 @@ jobs: ${{ runner.os }}-go- - name: Run Tests - run: go test ./... -tags rocksdb -count=1 + run: go test ./... -tags rocksdb -count=1 -timeout 10m + + + unit-tests-race: + name: Unit tests -race + runs-on: ubuntu-latest + steps: + + - name: Setup dependencies + run: sudo apt-get install libgflags-dev libsnappy-dev zlib1g-dev libbz2-dev liblz4-dev libzstd-dev + + - name: Setup Go 1.18 + uses: actions/setup-go@v1 + with: + go-version: 1.18 + + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Cache Go build and dependencies + uses: actions/cache@v2 + with: + path: | + ~/.cache/go-build + ~/go/pkg/mod + # make sure concurrent runs (not really supported) do not match the same key but instead fall back to a reasonable cache + key: ${{ runner.os }}-go-${{ hashFiles('go.sum') }}-${{ github.event.action }}${{ github.event.after }} + restore-keys: | + ${{ runner.os }}-go-${{ hashFiles('go.sum') }} + ${{ runner.os }}-go- + + - name: Run Tests with -race + run: go test ./... -tags rocksdb -count=1 -race -short -timeout 20m diff --git a/client/evilwallet/connector.go b/client/evilwallet/connector.go index 913d85bb43..3b849e0906 100644 --- a/client/evilwallet/connector.go +++ b/client/evilwallet/connector.go @@ -219,7 +219,12 @@ func (c *WebClient) SendFaucetRequest(address string) (err error) { // PostTransaction sends a transaction to the Tangle via a given client. func (c *WebClient) PostTransaction(tx *devnetvm.Transaction) (txID utxo.TransactionID, err error) { - resp, err := c.api.PostTransaction(tx.Bytes()) + txBytes, err := tx.Bytes() + if err != nil { + return + } + + resp, err := c.api.PostTransaction(txBytes) if err != nil { return } diff --git a/client/evilwallet/wallets.go b/client/evilwallet/wallets.go index 5f52026eec..5ba9068f23 100644 --- a/client/evilwallet/wallets.go +++ b/client/evilwallet/wallets.go @@ -4,6 +4,7 @@ import ( "sync" "github.com/cockroachdb/errors" + "github.com/iotaledger/hive.go/generics/lo" "github.com/iotaledger/hive.go/types" "go.uber.org/atomic" @@ -418,7 +419,7 @@ func (w *Wallet) Sign(addr devnetvm.Address, txEssence *devnetvm.TransactionEsse defer w.RUnlock() index := w.AddrIndexMap(addr.Base58()) kp := w.seed.KeyPair(index) - return devnetvm.NewED25519Signature(kp.PublicKey, kp.PrivateKey.Sign(txEssence.Bytes())) + return devnetvm.NewED25519Signature(kp.PublicKey, kp.PrivateKey.Sign(lo.PanicOnErr(txEssence.Bytes()))) } // UpdateUnspentOutputID updates the unspent output on the address specified. diff --git a/client/faucet.go b/client/faucet.go index aa37774e55..3bb2016842 100644 --- a/client/faucet.go +++ b/client/faucet.go @@ -70,7 +70,10 @@ func computeFaucetPoW(address devnetvm.Address, aManaPledgeID, cManaPledgeID ide faucetRequest := faucet.NewRequest(address, aManaPledgeID, cManaPledgeID, 0) - objectBytes := faucetRequest.Bytes() + objectBytes, err := faucetRequest.Bytes() + if err != nil { + return + } powRelevantBytes := objectBytes[:len(objectBytes)-pow.NonceBytes] return powWorker.Mine(context.Background(), powRelevantBytes, powTarget) diff --git a/client/wallet/wallet.go b/client/wallet/wallet.go index 2b34559592..86d85d13fc 100644 --- a/client/wallet/wallet.go +++ b/client/wallet/wallet.go @@ -7,6 +7,7 @@ import ( "github.com/cockroachdb/errors" "github.com/iotaledger/hive.go/bitmask" + "github.com/iotaledger/hive.go/generics/lo" "github.com/iotaledger/hive.go/identity" "github.com/iotaledger/hive.go/marshalutil" "golang.org/x/crypto/blake2b" @@ -144,9 +145,13 @@ func (wallet *Wallet) SendFunds(options ...sendoptions.SendFundsOption) (tx *dev unlockBlocks, inputsAsOutputsInOrder := wallet.buildUnlockBlocks(inputs, outputsByID, txEssence) tx = devnetvm.NewTransaction(txEssence, unlockBlocks) - + txBytes, err := tx.Bytes() + if err != nil { + return nil, err + } // check syntactical validity by marshaling an unmarshalling - tx, err = new(devnetvm.Transaction).FromBytes(tx.Bytes()) + tx = new(devnetvm.Transaction) + err = tx.FromBytes(txBytes) if err != nil { return nil, err } @@ -226,12 +231,16 @@ func (wallet *Wallet) ConsolidateFunds(options ...consolidateoptions.Consolidate tx := devnetvm.NewTransaction(txEssence, unlockBlocks) - // check syntactical validity by marshaling an unmarshaling - tx, err = new(devnetvm.Transaction).FromBytes(tx.Bytes()) + txBytes, err := tx.Bytes() + if err != nil { + return nil, err + } + // check syntactical validity by marshaling an unmarshalling + tx = new(devnetvm.Transaction) + err = tx.FromBytes(txBytes) if err != nil { return nil, err } - // check tx validity (balances, unlock blocks) ok, cErr := checkBalancesAndUnlocks(inputsAsOutputsInOrder, tx) if cErr != nil { @@ -303,8 +312,13 @@ func (wallet *Wallet) ClaimConditionalFunds(options ...claimconditionaloptions.C tx = devnetvm.NewTransaction(txEssence, unlockBlocks) - // check syntactical validity by marshaling an unmarshaling - tx, err = new(devnetvm.Transaction).FromBytes(tx.Bytes()) + txBytes, err := tx.Bytes() + if err != nil { + return nil, err + } + // check syntactical validity by marshaling an unmarshalling + tx = new(devnetvm.Transaction) + err = tx.FromBytes(txBytes) if err != nil { return nil, err } @@ -472,8 +486,13 @@ func (wallet *Wallet) DelegateFunds(options ...delegateoptions.DelegateFundsOpti unlockBlocks, inputsAsOutputsInOrder := wallet.buildUnlockBlocks(inputs, outputsByID, txEssence) tx = devnetvm.NewTransaction(txEssence, unlockBlocks) - // check syntactical validity by marshaling an unmarshaling - tx, err = new(devnetvm.Transaction).FromBytes(tx.Bytes()) + txBytes, err := tx.Bytes() + if err != nil { + return + } + // check syntactical validity by marshaling an unmarshalling + tx = new(devnetvm.Transaction) + err = tx.FromBytes(txBytes) if err != nil { return } @@ -599,10 +618,15 @@ func (wallet *Wallet) CreateNFT(options ...createnftoptions.CreateNFTOption) (tx tx = devnetvm.NewTransaction(txEssence, unlockBlocks) - // check syntactical validity by marshaling an unmarshaling - tx, err = new(devnetvm.Transaction).FromBytes(tx.Bytes()) + txBytes, err := tx.Bytes() if err != nil { - return nil, nil, err + return + } + // check syntactical validity by marshaling an unmarshalling + tx = new(devnetvm.Transaction) + err = tx.FromBytes(txBytes) + if err != nil { + return } // check tx validity (balances, unlock blocks) @@ -717,11 +741,15 @@ func (wallet *Wallet) TransferNFT(options ...transfernftoptions.TransferNFTOptio // there is only one input, so signing is easy keyPair := wallet.Seed().KeyPair(walletAlias.Address.Index) tx = devnetvm.NewTransaction(essence, devnetvm.UnlockBlocks{ - devnetvm.NewSignatureUnlockBlock(devnetvm.NewED25519Signature(keyPair.PublicKey, keyPair.PrivateKey.Sign(essence.Bytes()))), + devnetvm.NewSignatureUnlockBlock(devnetvm.NewED25519Signature(keyPair.PublicKey, keyPair.PrivateKey.Sign(lo.PanicOnErr(essence.Bytes())))), }) // check syntactical validity by marshaling an unmarshaling - tx, err = new(devnetvm.Transaction).FromBytes(tx.Bytes()) + txBytes, err := tx.Bytes() + if err != nil { + return nil, err + } + err = new(devnetvm.Transaction).FromBytes(txBytes) if err != nil { return nil, err } @@ -813,11 +841,15 @@ func (wallet *Wallet) DestroyNFT(options ...destroynftoptions.DestroyNFTOption) // there is only one input, so signing is easy keyPair := wallet.Seed().KeyPair(walletAlias.Address.Index) tx = devnetvm.NewTransaction(essence, devnetvm.UnlockBlocks{ - devnetvm.NewSignatureUnlockBlock(devnetvm.NewED25519Signature(keyPair.PublicKey, keyPair.PrivateKey.Sign(essence.Bytes()))), + devnetvm.NewSignatureUnlockBlock(devnetvm.NewED25519Signature(keyPair.PublicKey, keyPair.PrivateKey.Sign(lo.PanicOnErr(essence.Bytes())))), }) // check syntactical validity by marshaling an unmarshaling - tx, err = new(devnetvm.Transaction).FromBytes(tx.Bytes()) + txBytes, err := tx.Bytes() + if err != nil { + return nil, err + } + err = new(devnetvm.Transaction).FromBytes(txBytes) if err != nil { return nil, err } @@ -925,11 +957,15 @@ func (wallet *Wallet) WithdrawFundsFromNFT(options ...withdrawfromnftoptions.Wit // there is only one input, so signing is easy keyPair := wallet.Seed().KeyPair(walletAlias.Address.Index) tx = devnetvm.NewTransaction(essence, devnetvm.UnlockBlocks{ - devnetvm.NewSignatureUnlockBlock(devnetvm.NewED25519Signature(keyPair.PublicKey, keyPair.PrivateKey.Sign(essence.Bytes()))), + devnetvm.NewSignatureUnlockBlock(devnetvm.NewED25519Signature(keyPair.PublicKey, keyPair.PrivateKey.Sign(lo.PanicOnErr(essence.Bytes())))), }) // check syntactical validity by marshaling an unmarshaling - tx, err = new(devnetvm.Transaction).FromBytes(tx.Bytes()) + txBytes, err := tx.Bytes() + if err != nil { + return nil, err + } + err = new(devnetvm.Transaction).FromBytes(txBytes) if err != nil { return nil, err } @@ -1044,7 +1080,11 @@ func (wallet *Wallet) DepositFundsToNFT(options ...deposittonftoptions.DepositFu tx = devnetvm.NewTransaction(txEssence, unlockBlocks) // check syntactical validity by marshaling an unmarshaling - tx, err = new(devnetvm.Transaction).FromBytes(tx.Bytes()) + txBytes, err := tx.Bytes() + if err != nil { + return nil, err + } + err = new(devnetvm.Transaction).FromBytes(txBytes) if err != nil { return nil, err } @@ -1162,7 +1202,7 @@ func (wallet Wallet) SweepNFTOwnedFunds(options ...sweepnftownedoptions.SweepNFT casted := input.(*devnetvm.UTXOInput) if casted.ReferencedOutputID() == alias.ID() { keyPair := wallet.Seed().KeyPair(walletAlias.Address.Index) - unlockBlock := devnetvm.NewSignatureUnlockBlock(devnetvm.NewED25519Signature(keyPair.PublicKey, keyPair.PrivateKey.Sign(essence.Bytes()))) + unlockBlock := devnetvm.NewSignatureUnlockBlock(devnetvm.NewED25519Signature(keyPair.PublicKey, keyPair.PrivateKey.Sign(lo.PanicOnErr(essence.Bytes())))) unlockBlocks[index] = unlockBlock aliasInputIndex = index } @@ -1183,7 +1223,11 @@ func (wallet Wallet) SweepNFTOwnedFunds(options ...sweepnftownedoptions.SweepNFT tx = devnetvm.NewTransaction(essence, unlockBlocks) // check syntactical validity by marshaling an unmarshaling - tx, err = new(devnetvm.Transaction).FromBytes(tx.Bytes()) + txBytes, err := tx.Bytes() + if err != nil { + return nil, err + } + err = new(devnetvm.Transaction).FromBytes(txBytes) if err != nil { return nil, err } @@ -1312,7 +1356,7 @@ func (wallet *Wallet) SweepNFTOwnedNFTs(options ...sweepnftownednftsoptions.Swee casted := input.(*devnetvm.UTXOInput) if casted.ReferencedOutputID() == alias.ID() { keyPair := wallet.Seed().KeyPair(walletAlias.Address.Index) - unlockBlock := devnetvm.NewSignatureUnlockBlock(devnetvm.NewED25519Signature(keyPair.PublicKey, keyPair.PrivateKey.Sign(essence.Bytes()))) + unlockBlock := devnetvm.NewSignatureUnlockBlock(devnetvm.NewED25519Signature(keyPair.PublicKey, keyPair.PrivateKey.Sign(lo.PanicOnErr(essence.Bytes())))) unlockBlocks[index] = unlockBlock aliasInputIndex = index } @@ -2220,7 +2264,7 @@ func (wallet *Wallet) buildUnlockBlocks(inputs devnetvm.Inputs, consumedOutputsB } keyPair := wallet.Seed().KeyPair(output.Address.Index) - unlockBlock := devnetvm.NewSignatureUnlockBlock(devnetvm.NewED25519Signature(keyPair.PublicKey, keyPair.PrivateKey.Sign(essence.Bytes()))) + unlockBlock := devnetvm.NewSignatureUnlockBlock(devnetvm.NewED25519Signature(keyPair.PublicKey, keyPair.PrivateKey.Sign(lo.PanicOnErr(essence.Bytes())))) unlocks[outputIndex] = unlockBlock existingUnlockBlocks[output.Address] = uint16(outputIndex) } diff --git a/client/wallet/webconnector.go b/client/wallet/webconnector.go index 358e2687dc..f47837bfa2 100644 --- a/client/wallet/webconnector.go +++ b/client/wallet/webconnector.go @@ -102,7 +102,11 @@ func (webConnector WebConnector) UnspentOutputs(addresses ...address.Address) (u // SendTransaction sends a new transaction to the network. func (webConnector WebConnector) SendTransaction(tx *devnetvm.Transaction) (err error) { - _, err = webConnector.client.PostTransaction(tx.Bytes()) + txBytes, err := tx.Bytes() + if err != nil { + return err + } + _, err = webConnector.client.PostTransaction(txBytes) return } diff --git a/go.mod b/go.mod index 2a2f4b4cdc..0fb5e807eb 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/gin-gonic/gin v1.7.0 github.com/go-resty/resty/v2 v2.6.0 github.com/gorilla/websocket v1.5.0 - github.com/iotaledger/hive.go v0.0.0-20220604100743-40898337a727 + github.com/iotaledger/hive.go v0.0.0-20220607150119-1be29e962175 github.com/labstack/echo v3.3.10+incompatible github.com/labstack/gommon v0.3.0 github.com/libp2p/go-libp2p v0.15.0 diff --git a/go.sum b/go.sum index 47a56cd6fb..f3a49db149 100644 --- a/go.sum +++ b/go.sum @@ -473,8 +473,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -github.com/iotaledger/hive.go v0.0.0-20220604100743-40898337a727 h1:NDNgpftyrWboDyKHgcbISPdYKRVqeXw+mBYomCax+qw= -github.com/iotaledger/hive.go v0.0.0-20220604100743-40898337a727/go.mod h1:8f9U7qHFby0W3cxv/nKnz9LHn9BbwWU0tMsWDnfqzRI= +github.com/iotaledger/hive.go v0.0.0-20220607150119-1be29e962175 h1:IgXxiPx51WJglOL5EtIurlMbujnrLP4vLYQqyfmR0zg= +github.com/iotaledger/hive.go v0.0.0-20220607150119-1be29e962175/go.mod h1:8f9U7qHFby0W3cxv/nKnz9LHn9BbwWU0tMsWDnfqzRI= github.com/ipfs/go-cid v0.0.1/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= github.com/ipfs/go-cid v0.0.2/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= github.com/ipfs/go-cid v0.0.3/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= diff --git a/packages/chat/chat.go b/packages/chat/chat.go index 7ea792672b..a26fe389cc 100644 --- a/packages/chat/chat.go +++ b/packages/chat/chat.go @@ -1,13 +1,10 @@ package chat import ( - "context" "fmt" - "sync" - "github.com/cockroachdb/errors" + "github.com/iotaledger/hive.go/generics/model" "github.com/iotaledger/hive.go/serix" - "github.com/iotaledger/hive.go/stringify" "github.com/iotaledger/goshimmer/packages/tangle/payload" ) @@ -43,61 +40,22 @@ const ( // Payload represents the chat payload type. type Payload struct { + model.Immutable[Payload, *Payload, payloadModel] `serix:"0"` +} + +type payloadModel struct { From string `serix:"0,lengthPrefixType=uint32"` To string `serix:"1,lengthPrefixType=uint32"` Message string `serix:"2,lengthPrefixType=uint32"` - - bytes []byte - bytesMutex sync.RWMutex } // NewPayload creates a new chat payload. func NewPayload(from, to, message string) *Payload { - return &Payload{ + return model.NewImmutable[Payload](&payloadModel{ From: from, To: to, Message: message, - } -} - -// FromBytes parses the marshaled version of a Payload into a Go object. -// It either returns a new Payload or fills an optionally provided Payload with the parsed information. -func FromBytes(bytes []byte) (payloadDecoded *Payload, consumedBytes int, err error) { - payloadDecoded = new(Payload) - - consumedBytes, err = serix.DefaultAPI.Decode(context.Background(), bytes, payloadDecoded, serix.WithValidation()) - if err != nil { - err = errors.Errorf("failed to parse Chat Payload: %w", err) - return - } - payloadDecoded.bytes = bytes - - return -} - -// Bytes returns a marshaled version of this Payload. -func (p *Payload) Bytes() []byte { - p.bytesMutex.Lock() - defer p.bytesMutex.Unlock() - if objBytes := p.bytes; objBytes != nil { - return objBytes - } - - objBytes, err := serix.DefaultAPI.Encode(context.Background(), p, serix.WithValidation()) - if err != nil { - // TODO: what do? - panic(err) - } - p.bytes = objBytes - return objBytes -} - -// String returns a human-friendly representation of the Payload. -func (p *Payload) String() string { - return stringify.Struct("ChatPayload", - stringify.StructField("from", p.From), - stringify.StructField("to", p.To), - stringify.StructField("Message", p.Message), + }, ) } @@ -108,3 +66,18 @@ var Type = payload.NewType(payloadType, PayloadName) func (p *Payload) Type() payload.Type { return Type } + +// From returns an author of the message. +func (p *Payload) From() string { + return p.M.From +} + +// To returns a recipient of the message. +func (p *Payload) To() string { + return p.M.To +} + +// Message returns the message contents. +func (p *Payload) Message() string { + return p.M.Message +} diff --git a/packages/chat/payload_test.go b/packages/chat/payload_test.go index a260f68cc7..ea3a4d98d0 100644 --- a/packages/chat/payload_test.go +++ b/packages/chat/payload_test.go @@ -3,14 +3,16 @@ package chat import ( "testing" + "github.com/iotaledger/hive.go/generics/lo" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestPayload(t *testing.T) { a := NewPayload("me", "you", "ciao") - abytes := a.Bytes() - b, _, err := FromBytes(abytes) + abytes := lo.PanicOnErr(a.Bytes()) + b := new(Payload) + err := b.FromBytes(abytes) require.NoError(t, err) assert.Equal(t, a, b) } diff --git a/packages/conflictdag/storage.go b/packages/conflictdag/storage.go index 90b683a099..78e3307b12 100644 --- a/packages/conflictdag/storage.go +++ b/packages/conflictdag/storage.go @@ -29,8 +29,8 @@ type Storage[ConflictID comparable, ConflictSetID comparable] struct { } // newStorage returns a new Storage instance configured with the given options. -func newStorage[ConflictID comparable, ConflictSetID comparable](options *options) (new *Storage[ConflictID, ConflictSetID]) { - new = &Storage[ConflictID, ConflictSetID]{ +func newStorage[ConflictID comparable, ConflictSetID comparable](options *options) (storage *Storage[ConflictID, ConflictSetID]) { + storage = &Storage[ConflictID, ConflictSetID]{ branchStorage: objectstorage.NewStructStorage[Conflict[ConflictID, ConflictSetID]]( objectstorage.NewStoreWithRealm(options.store, database.PrefixConflictDAG, PrefixBranchStorage), options.cacheTimeProvider.CacheTime(options.branchCacheTime), @@ -38,21 +38,21 @@ func newStorage[ConflictID comparable, ConflictSetID comparable](options *option ), childBranchStorage: objectstorage.NewStructStorage[ChildBranch[ConflictID]]( objectstorage.NewStoreWithRealm(options.store, database.PrefixConflictDAG, PrefixChildBranchStorage), - objectstorage.PartitionKey(ChildBranch[ConflictID]{}.KeyPartitions()...), + objectstorage.PartitionKey(new(ChildBranch[ConflictID]).KeyPartitions()...), options.cacheTimeProvider.CacheTime(options.childBranchCacheTime), objectstorage.LeakDetectionEnabled(false), objectstorage.StoreOnCreation(true), ), conflictMemberStorage: objectstorage.NewStructStorage[ConflictMember[ConflictSetID, ConflictID]]( objectstorage.NewStoreWithRealm(options.store, database.PrefixConflictDAG, PrefixConflictMemberStorage), - objectstorage.PartitionKey(ConflictMember[ConflictSetID, ConflictID]{}.KeyPartitions()...), + objectstorage.PartitionKey(new(ConflictMember[ConflictSetID, ConflictID]).KeyPartitions()...), options.cacheTimeProvider.CacheTime(options.conflictMemberCacheTime), objectstorage.LeakDetectionEnabled(false), objectstorage.StoreOnCreation(true), ), } - return new + return storage } // CachedConflict retrieves the CachedObject representing the named Conflict. The optional computeIfAbsentCallback can be diff --git a/packages/conflictdag/types.go b/packages/conflictdag/types.go index 45c8a1b5d8..6cf9a02aa0 100644 --- a/packages/conflictdag/types.go +++ b/packages/conflictdag/types.go @@ -11,7 +11,7 @@ import ( // Conflict represents a container for transactions and outputs spawning off from a conflicting transaction. type Conflict[ConflictID, ConflictSetID comparable] struct { - model.Storable[ConflictID, conflict[ConflictID, ConflictSetID]] `serix:"0"` + model.Storable[ConflictID, Conflict[ConflictID, ConflictSetID], *Conflict[ConflictID, ConflictSetID], conflict[ConflictID, ConflictSetID]] `serix:"0"` } type conflict[ConflictID, ConflictSetID comparable] struct { @@ -26,11 +26,11 @@ type conflict[ConflictID, ConflictSetID comparable] struct { } func NewConflict[ConflictID comparable, ConflictSetID comparable](id ConflictID, parents *set.AdvancedSet[ConflictID], conflicts *set.AdvancedSet[ConflictSetID]) (new *Conflict[ConflictID, ConflictSetID]) { - new = &Conflict[ConflictID, ConflictSetID]{model.NewStorable[ConflictID](conflict[ConflictID, ConflictSetID]{ + new = model.NewStorable[ConflictID, Conflict[ConflictID, ConflictSetID]](&conflict[ConflictID, ConflictSetID]{ Parents: parents, ConflictIDs: conflicts, InclusionState: Pending, - })} + }) new.SetID(id) return new @@ -104,22 +104,22 @@ func (b *Conflict[ConflictID, ConflictSetID]) setInclusionState(inclusionState I // ChildBranch represents the reference between a Conflict and its children. type ChildBranch[ConflictID comparable] struct { - model.StorableReference[ConflictID, ConflictID] `serix:"0"` + model.StorableReference[ChildBranch[ConflictID], *ChildBranch[ConflictID], ConflictID, ConflictID] `serix:"0"` } // NewChildBranch return a new ChildBranch reference from the named parent to the named child. -func NewChildBranch[ConflictID comparable](parentBranchID, childBranchID ConflictID) (new *ChildBranch[ConflictID]) { - return &ChildBranch[ConflictID]{model.NewStorableReference(parentBranchID, childBranchID)} +func NewChildBranch[ConflictID comparable](parentBranchID, childBranchID ConflictID) *ChildBranch[ConflictID] { + return model.NewStorableReference[ChildBranch[ConflictID]](parentBranchID, childBranchID) } // ParentBranchID returns the identifier of the parent Conflict. func (c *ChildBranch[ConflictID]) ParentBranchID() (parentBranchID ConflictID) { - return c.SourceID + return c.SourceID() } // ChildBranchID returns the identifier of the child Conflict. func (c *ChildBranch[ConflictID]) ChildBranchID() (childBranchID ConflictID) { - return c.TargetID + return c.TargetID() } // endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -128,22 +128,22 @@ func (c *ChildBranch[ConflictID]) ChildBranchID() (childBranchID ConflictID) { // ConflictMember represents the reference between a Conflict and its contained Conflict. type ConflictMember[ConflictSetID comparable, ConflictID comparable] struct { - model.StorableReference[ConflictSetID, ConflictID] `serix:"0"` + model.StorableReference[ConflictMember[ConflictSetID, ConflictID], *ConflictMember[ConflictSetID, ConflictID], ConflictSetID, ConflictID] `serix:"0"` } // NewConflictMember return a new ConflictMember reference from the named conflict to the named Conflict. func NewConflictMember[ConflictSetID comparable, ConflictID comparable](conflictSetID ConflictSetID, conflictID ConflictID) (new *ConflictMember[ConflictSetID, ConflictID]) { - return &ConflictMember[ConflictSetID, ConflictID]{model.NewStorableReference(conflictSetID, conflictID)} + return model.NewStorableReference[ConflictMember[ConflictSetID, ConflictID]](conflictSetID, conflictID) } // ConflictSetID returns the identifier of the Conflict. func (c *ConflictMember[ConflictSetID, ConflictID]) ConflictSetID() (conflictID ConflictSetID) { - return c.SourceID + return c.SourceID() } // ConflictID returns the identifier of the Conflict. func (c *ConflictMember[ConflictSetID, ConflictID]) ConflictID() (branchID ConflictID) { - return c.TargetID + return c.TargetID() } // endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/packages/faucet/request.go b/packages/faucet/request.go index c108731f9d..7e3e64faef 100644 --- a/packages/faucet/request.go +++ b/packages/faucet/request.go @@ -5,9 +5,9 @@ import ( "fmt" "github.com/cockroachdb/errors" + "github.com/iotaledger/hive.go/generics/model" "github.com/iotaledger/hive.go/identity" "github.com/iotaledger/hive.go/serix" - "github.com/iotaledger/hive.go/stringify" "github.com/iotaledger/goshimmer/packages/ledger/vm/devnetvm" "github.com/iotaledger/goshimmer/packages/tangle" @@ -33,10 +33,10 @@ const ( // Payload represents a faucet request which contains an address for the faucet to send funds to. type Payload struct { - requestInner `serix:"0"` + model.Immutable[Payload, *Payload, requestModel] `serix:"0"` } -type requestInner struct { +type requestModel struct { PayloadType payload.Type Address devnetvm.Address `serix:"1"` AccessManaPledgeID identity.ID `serix:"2"` @@ -51,15 +51,15 @@ var ( // NewRequest is the constructor of a Payload and creates a new Payload object from the given details. func NewRequest(addr devnetvm.Address, accessManaPledgeID, consensusManaPledgeID identity.ID, nonce uint64) *Payload { - p := &Payload{ - requestInner{ + p := model.NewImmutable[Payload]( + &requestModel{ PayloadType: RequestType, Address: addr, AccessManaPledgeID: accessManaPledgeID, ConsensusManaPledgeID: consensusManaPledgeID, Nonce: nonce, }, - } + ) return p } @@ -78,43 +78,24 @@ func FromBytes(data []byte) (payloadDecoded *Payload, consumedBytes int, err err return } -// RequestType returns the type of the faucet Payload. +// Type returns the type of the faucet Payload. func (p *Payload) Type() payload.Type { return RequestType } // Address returns the address of the faucet Payload. func (p *Payload) Address() devnetvm.Address { - return p.requestInner.Address + return p.M.Address } // AccessManaPledgeID returns the access mana pledge ID of the faucet request. func (p *Payload) AccessManaPledgeID() identity.ID { - return p.requestInner.AccessManaPledgeID + return p.M.AccessManaPledgeID } // ConsensusManaPledgeID returns the consensus mana pledge ID of the faucet request. func (p *Payload) ConsensusManaPledgeID() identity.ID { - return p.requestInner.ConsensusManaPledgeID -} - -// Bytes returns a marshaled version of the Payload. -func (p *Payload) Bytes() []byte { - objBytes, err := serix.DefaultAPI.Encode(context.Background(), p, serix.WithValidation()) - if err != nil { - // TODO: what do? - panic(err) - } - return objBytes -} - -// String returns a human readable version of faucet Payload payload (for debug purposes). -func (p *Payload) String() string { - return stringify.Struct("FaucetPayload", - stringify.StructField("address", p.Address().Base58()), - stringify.StructField("accessManaPledgeID", p.AccessManaPledgeID().String()), - stringify.StructField("consensusManaPledgeID", p.ConsensusManaPledgeID().String()), - ) + return p.M.ConsensusManaPledgeID } // IsFaucetReq checks if the message is faucet payload. diff --git a/packages/faucet/request_test.go b/packages/faucet/request_test.go index 34d88d8b72..84473a6309 100644 --- a/packages/faucet/request_test.go +++ b/packages/faucet/request_test.go @@ -6,6 +6,7 @@ import ( "time" "github.com/iotaledger/hive.go/crypto/ed25519" + "github.com/iotaledger/hive.go/generics/lo" "github.com/iotaledger/hive.go/identity" "github.com/iotaledger/hive.go/types" "github.com/stretchr/testify/assert" @@ -48,7 +49,7 @@ func TestRequest(t *testing.T) { originalRequest := NewRequest(address, access, consensus, 0) - clonedRequest, _, err := FromBytes(originalRequest.Bytes()) + clonedRequest, _, err := FromBytes(lo.PanicOnErr(originalRequest.Bytes())) if err != nil { panic(err) } @@ -56,7 +57,7 @@ func TestRequest(t *testing.T) { assert.Equal(t, originalRequest.AccessManaPledgeID(), clonedRequest.AccessManaPledgeID()) assert.Equal(t, originalRequest.ConsensusManaPledgeID(), clonedRequest.ConsensusManaPledgeID()) - clonedRequest2, _, err := FromBytes(clonedRequest.Bytes()) + clonedRequest2, _, err := FromBytes(lo.PanicOnErr(clonedRequest.Bytes())) if err != nil { panic(err) } diff --git a/packages/jsonmodels/ledgerstate.go b/packages/jsonmodels/ledgerstate.go index a7fc0dda07..78b13cb54e 100644 --- a/packages/jsonmodels/ledgerstate.go +++ b/packages/jsonmodels/ledgerstate.go @@ -631,7 +631,7 @@ func NewTransaction(transaction *devnetvm.Transaction) *Transaction { dataPayload := make([]byte, 0) if transaction.Essence().Payload() != nil { - dataPayload = transaction.Essence().Payload().Bytes() + dataPayload = lo.PanicOnErr(transaction.Essence().Payload().Bytes()) } return &Transaction{ @@ -701,7 +701,7 @@ func NewUnlockBlock(unlockBlock devnetvm.UnlockBlock) *UnlockBlock { switch unlockBlock.Type() { case devnetvm.SignatureUnlockBlockType: - signature, _, _ := devnetvm.SignatureFromBytes(unlockBlock.Bytes()) + signature, _, _ := devnetvm.SignatureFromBytes(lo.PanicOnErr(unlockBlock.Bytes())) result.SignatureType = signature.Type() switch signature.Type() { case devnetvm.ED25519SignatureType: @@ -714,7 +714,7 @@ func NewUnlockBlock(unlockBlock devnetvm.UnlockBlock) *UnlockBlock { result.Signature = signature.Signature.String() } case devnetvm.ReferenceUnlockBlockType: - referenceUnlockBlock, _, _ := devnetvm.ReferenceUnlockBlockFromBytes(unlockBlock.Bytes()) + referenceUnlockBlock, _, _ := devnetvm.ReferenceUnlockBlockFromBytes(lo.PanicOnErr(unlockBlock.Bytes())) result.ReferencedIndex = referenceUnlockBlock.ReferencedIndex() } diff --git a/packages/ledger/booker.go b/packages/ledger/booker.go index d3b1874d8e..dfdbed22dd 100644 --- a/packages/ledger/booker.go +++ b/packages/ledger/booker.go @@ -96,7 +96,6 @@ func (b *booker) storeOutputs(outputs *utxo.Outputs, branchIDs *set.AdvancedSet[ _ = outputs.ForEach(func(output utxo.Output) (err error) { outputMetadata := NewOutputMetadata(output.ID()) outputMetadata.SetBranchIDs(branchIDs) - b.ledger.Storage.outputMetadataStorage.Store(outputMetadata).Release() b.ledger.Storage.outputStorage.Store(output).Release() diff --git a/packages/ledger/models.go b/packages/ledger/models.go index 33f3d359e3..271ddf7a51 100644 --- a/packages/ledger/models.go +++ b/packages/ledger/models.go @@ -18,7 +18,7 @@ import ( // TransactionMetadata represents a container for additional information about a Transaction. type TransactionMetadata struct { - model.Storable[utxo.TransactionID, transactionMetadata] `serix:"0"` + model.Storable[utxo.TransactionID, TransactionMetadata, *TransactionMetadata, transactionMetadata] `serix:"0"` } type transactionMetadata struct { @@ -46,10 +46,10 @@ type transactionMetadata struct { // NewTransactionMetadata returns new TransactionMetadata for the given TransactionID. func NewTransactionMetadata(txID utxo.TransactionID) (new *TransactionMetadata) { - new = &TransactionMetadata{model.NewStorable[utxo.TransactionID](transactionMetadata{ + new = model.NewStorable[utxo.TransactionID, TransactionMetadata](&transactionMetadata{ BranchIDs: utxo.NewTransactionIDs(), OutputIDs: utxo.NewOutputIDs(), - })} + }) new.SetID(txID) return new @@ -203,7 +203,7 @@ func (t *TransactionMetadata) IsConflicting() (isConflicting bool) { // OutputMetadata represents a container for additional information about an Output. type OutputMetadata struct { - model.Storable[utxo.OutputID, outputMetadata] `serix:"0"` + model.Storable[utxo.OutputID, OutputMetadata, *OutputMetadata, outputMetadata] `serix:"0"` } type outputMetadata struct { @@ -231,9 +231,9 @@ type outputMetadata struct { // NewOutputMetadata returns new OutputMetadata for the given OutputID. func NewOutputMetadata(outputID utxo.OutputID) (new *OutputMetadata) { - new = &OutputMetadata{model.NewStorable[utxo.OutputID](outputMetadata{ + new = model.NewStorable[utxo.OutputID, OutputMetadata](&outputMetadata{ BranchIDs: utxo.NewTransactionIDs(), - })} + }) new.SetID(outputID) return new @@ -471,7 +471,7 @@ func (o *OutputsMetadata) String() (humanReadable string) { // Consumer represents the reference between an Output and its spending Transaction. type Consumer struct { - model.StorableReferenceWithMetadata[utxo.OutputID, utxo.TransactionID, consumer] `serix:"0"` + model.StorableReferenceWithMetadata[Consumer, *Consumer, utxo.OutputID, utxo.TransactionID, consumer] `serix:"0"` } type consumer struct { @@ -481,17 +481,17 @@ type consumer struct { // NewConsumer return a new Consumer reference from the named Output to the named Transaction. func NewConsumer(consumedInput utxo.OutputID, transactionID utxo.TransactionID) (new *Consumer) { - return &Consumer{model.NewStorableReferenceWithMetadata(consumedInput, transactionID, consumer{})} + return model.NewStorableReferenceWithMetadata[Consumer](consumedInput, transactionID, &consumer{}) } // ConsumedInput returns the identifier of the Output that was spent. func (c *Consumer) ConsumedInput() (outputID utxo.OutputID) { - return c.SourceID + return c.SourceID() } // TransactionID returns the identifier of the spending Transaction. func (c *Consumer) TransactionID() (spendingTransaction utxo.TransactionID) { - return c.TargetID + return c.TargetID() } // IsBooked returns a boolean flag that indicates whether the Consumer was completely booked. diff --git a/packages/ledger/storage.go b/packages/ledger/storage.go index 48f29bba32..33c0831617 100644 --- a/packages/ledger/storage.go +++ b/packages/ledger/storage.go @@ -45,8 +45,8 @@ type Storage struct { } // newStorage returns a new storage instance for the given Ledger. -func newStorage(ledger *Ledger) (new *Storage) { - new = &Storage{ +func newStorage(ledger *Ledger) (storage *Storage) { + storage = &Storage{ transactionStorage: objectstorage.NewInterfaceStorage[utxo.Transaction]( objectstorage.NewStoreWithRealm(ledger.options.store, database.PrefixLedger, PrefixTransactionStorage), transactionFactory(ledger.options.vm), @@ -75,12 +75,11 @@ func newStorage(ledger *Ledger) (new *Storage) { objectstorage.NewStoreWithRealm(ledger.options.store, database.PrefixLedger, PrefixConsumerStorage), ledger.options.cacheTimeProvider.CacheTime(ledger.options.consumerCacheTime), objectstorage.LeakDetectionEnabled(false), - objectstorage.PartitionKey(Consumer{}.KeyPartitions()...), + objectstorage.PartitionKey(new(Consumer).KeyPartitions()...), ), ledger: ledger, } - - return new + return storage } // CachedTransaction retrieves the CachedObject representing the named Transaction. The optional computeIfAbsentCallback diff --git a/packages/ledger/testframework.go b/packages/ledger/testframework.go index fbcb90826c..763b3e200f 100644 --- a/packages/ledger/testframework.go +++ b/packages/ledger/testframework.go @@ -358,7 +358,7 @@ var _ utxo.Input = new(MockedInput) // MockedOutput is the container for the data produced by executing a MockedTransaction. type MockedOutput struct { - model.Storable[utxo.OutputID, mockedOutput] `serix:"0"` + model.Storable[utxo.OutputID, MockedOutput, *MockedOutput, mockedOutput] `serix:"0"` } type mockedOutput struct { @@ -372,10 +372,10 @@ type mockedOutput struct { // NewMockedOutput creates a new MockedOutput based on the utxo.TransactionID and its index within the MockedTransaction. func NewMockedOutput(txID utxo.TransactionID, index uint16) (out *MockedOutput) { - out = &MockedOutput{model.NewStorable[utxo.OutputID](mockedOutput{ + out = model.NewStorable[utxo.OutputID, MockedOutput](&mockedOutput{ TxID: txID, Index: index, - })} + }) out.SetID(utxo.OutputID{TransactionID: txID, Index: index}) return out } @@ -389,7 +389,7 @@ var _ utxo.Output = new(MockedOutput) // MockedTransaction is the type that is used to describe instructions how to modify the ledger state for MockedVM. type MockedTransaction struct { - model.Storable[utxo.TransactionID, mockedTransaction] `serix:"0"` + model.Storable[utxo.TransactionID, MockedTransaction, *MockedTransaction, mockedTransaction] `serix:"0"` } type mockedTransaction struct { @@ -406,11 +406,11 @@ type mockedTransaction struct { // NewMockedTransaction creates a new MockedTransaction with the given inputs and specified outputCount. // A unique essence is simulated by an atomic counter, incremented globally for each MockedTransaction created. func NewMockedTransaction(inputs []*MockedInput, outputCount uint16) (tx *MockedTransaction) { - tx = &MockedTransaction{model.NewStorable[utxo.TransactionID](mockedTransaction{ + tx = model.NewStorable[utxo.TransactionID, MockedTransaction](&mockedTransaction{ Inputs: inputs, OutputCount: outputCount, UniqueEssence: atomic.AddUint64(&_uniqueEssenceCounter, 1), - })} + }) b := types.Identifier{} binary.BigEndian.PutUint64(b[:], tx.M.UniqueEssence) diff --git a/packages/ledger/utxo/outputcommitment_test.go b/packages/ledger/utxo/outputcommitment_test.go index 91060a5967..2f04650009 100644 --- a/packages/ledger/utxo/outputcommitment_test.go +++ b/packages/ledger/utxo/outputcommitment_test.go @@ -28,7 +28,7 @@ func TestOutputCommitment(t *testing.T) { // MockedOutput is the container for the data produced by executing a MockedTransaction. type MockedOutput struct { - model.Storable[OutputID, mockedOutput] `serix:"0"` + model.Storable[OutputID, MockedOutput, *MockedOutput, mockedOutput] `serix:"0"` } type mockedOutput struct { @@ -37,9 +37,9 @@ type mockedOutput struct { // NewMockedOutput creates a new MockedOutput based on the utxo.TransactionID and its index within the MockedTransaction. func NewMockedOutput() (new *MockedOutput) { - return &MockedOutput{model.NewStorable[OutputID](mockedOutput{ + return model.NewStorable[OutputID, MockedOutput](&mockedOutput{ UniqueEssence: atomic.AddUint64(&_uniqueEssenceCounter, 1), - })} + }) } var _uniqueEssenceCounter uint64 diff --git a/packages/ledger/vm/devnetvm/indexer/models.go b/packages/ledger/vm/devnetvm/indexer/models.go index ce68328d24..c50d7cee66 100644 --- a/packages/ledger/vm/devnetvm/indexer/models.go +++ b/packages/ledger/vm/devnetvm/indexer/models.go @@ -11,22 +11,22 @@ import ( // AddressOutputMapping is a mapping from an Address to an OutputID than enables lookups of stored Outputs. type AddressOutputMapping struct { - model.StorableReference[devnetvm.Address, utxo.OutputID] `serix:"0"` + model.StorableReference[AddressOutputMapping, *AddressOutputMapping, devnetvm.Address, utxo.OutputID] `serix:"0"` } // NewAddressOutputMapping creates a new AddressOutputMapping. func NewAddressOutputMapping(address devnetvm.Address, outputID utxo.OutputID) *AddressOutputMapping { - return &AddressOutputMapping{model.NewStorableReference(address, outputID)} + return model.NewStorableReference[AddressOutputMapping](address, outputID) } // Address returns the Address of the AddressOutputMapping. func (a *AddressOutputMapping) Address() devnetvm.Address { - return a.SourceID + return a.SourceID() } // OutputID returns the OutputID of the AddressOutputMapping. func (a *AddressOutputMapping) OutputID() utxo.OutputID { - return a.TargetID + return a.TargetID() } // endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/packages/ledger/vm/devnetvm/input.go b/packages/ledger/vm/devnetvm/input.go index 54b6b04409..6877eb09ac 100644 --- a/packages/ledger/vm/devnetvm/input.go +++ b/packages/ledger/vm/devnetvm/input.go @@ -2,11 +2,12 @@ package devnetvm import ( "bytes" - "context" "fmt" "sort" "strconv" + "github.com/iotaledger/hive.go/generics/lo" + "github.com/iotaledger/hive.go/generics/model" "github.com/iotaledger/hive.go/serix" "github.com/iotaledger/hive.go/stringify" "github.com/iotaledger/hive.go/types" @@ -66,7 +67,7 @@ type Input interface { Type() InputType // Bytes returns a marshaled version of the Input. - Bytes() []byte + Bytes() ([]byte, error) // String returns a human readable version of the Input. String() string @@ -96,7 +97,7 @@ func NewInputs(optionalInputs ...Input) (inputs Inputs) { // filter duplicates (store marshaled version so we don't need to marshal a second time during sort) for _, input := range optionalInputs { - marshaledInput := input.Bytes() + marshaledInput := lo.PanicOnErr(input.Bytes()) marshaledInputAsString := typeutils.BytesToString(marshaledInput) if _, seenAlready := seenInputs[marshaledInputAsString]; seenAlready { @@ -160,19 +161,17 @@ func (i Inputs) Strings() (result []string) { // UTXOInput represents a reference to an Output in the UTXODAG. type UTXOInput struct { - utxoInputInner `serix:"0"` + model.Immutable[UTXOInput, *UTXOInput, utxoInputModel] `serix:"0"` } -type utxoInputInner struct { +type utxoInputModel struct { ReferencedOutputID utxo.OutputID `serix:"0"` } // NewUTXOInput is the constructor for UTXOInputs. func NewUTXOInput(referencedOutputID utxo.OutputID) *UTXOInput { - return &UTXOInput{ - utxoInputInner{ - ReferencedOutputID: referencedOutputID, - }, - } + return model.NewImmutable[UTXOInput](&utxoInputModel{ + ReferencedOutputID: referencedOutputID, + }) } // Type returns the type of the Input. @@ -182,35 +181,18 @@ func (u *UTXOInput) Type() InputType { // ReferencedOutputID returns the OutputID that this Input references. func (u *UTXOInput) ReferencedOutputID() utxo.OutputID { - return u.utxoInputInner.ReferencedOutputID -} - -// Bytes returns a marshaled version of the Input. -func (u *UTXOInput) Bytes() []byte { - objBytes, err := serix.DefaultAPI.Encode(context.Background(), u, serix.WithValidation()) - if err != nil { - // TODO: what do? - return nil - } - return objBytes + return u.M.ReferencedOutputID } // Base58 returns the base58 encoded referenced output ID of this input. func (u *UTXOInput) Base58() string { - return u.utxoInputInner.ReferencedOutputID.Base58() + return u.M.ReferencedOutputID.Base58() } // Compare offers a comparator for Inputs which returns -1 if other Input is bigger, 1 if it is smaller and 0 if they // are the same. func (u *UTXOInput) Compare(other Input) int { - return bytes.Compare(u.Bytes(), other.Bytes()) -} - -// String returns a human readable version of the Input. -func (u *UTXOInput) String() string { - return stringify.Struct("UTXOInput", - stringify.StructField("ReferencedOutputID", u.ReferencedOutputID()), - ) + return bytes.Compare(lo.PanicOnErr(u.Bytes()), lo.PanicOnErr(other.Bytes())) } // code contract (make sure the struct implements all required methods) diff --git a/packages/ledger/vm/devnetvm/output.go b/packages/ledger/vm/devnetvm/output.go index a7b1e06722..060ca8abf6 100644 --- a/packages/ledger/vm/devnetvm/output.go +++ b/packages/ledger/vm/devnetvm/output.go @@ -331,7 +331,7 @@ func (o OutputsByID) String() string { // SigLockedSingleOutput is an Output that holds exactly one uncolored balance and that can be unlocked by providing a // signature for an Address. type SigLockedSingleOutput struct { - model.Storable[utxo.OutputID, sigLockedSingleOutput] `serix:"0"` + model.Storable[utxo.OutputID, SigLockedSingleOutput, *SigLockedSingleOutput, sigLockedSingleOutput] `serix:"0"` } type sigLockedSingleOutput struct { @@ -341,11 +341,11 @@ type sigLockedSingleOutput struct { // NewSigLockedSingleOutput is the constructor for a SigLockedSingleOutput. func NewSigLockedSingleOutput(balance uint64, address Address) *SigLockedSingleOutput { - return &SigLockedSingleOutput{model.NewStorable[utxo.OutputID]( - sigLockedSingleOutput{ + return model.NewStorable[utxo.OutputID, SigLockedSingleOutput]( + &sigLockedSingleOutput{ Balance: balance, Address: address, - })} + }) } // Type returns the type of the Output which allows us to generically handle Outputs of different types. @@ -367,7 +367,11 @@ func (s *SigLockedSingleOutput) UnlockValid(tx *Transaction, unlockBlock UnlockB switch blk := unlockBlock.(type) { case *SignatureUnlockBlock: // unlocking by signature - unlockValid = blk.AddressSignatureValid(s.M.Address, tx.Essence().Bytes()) + txBytes, err := tx.Essence().Bytes() + if err != nil { + return false, errors.Wrap(err, "could not get essence bytes") + } + unlockValid = blk.AddressSignatureValid(s.M.Address, txBytes) case *AliasUnlockBlock: // unlocking by alias reference. The unlock is valid if: @@ -424,39 +428,6 @@ func (s *SigLockedSingleOutput) Compare(other Output) int { return bytes.Compare(lo.PanicOnErr(s.Bytes()), lo.PanicOnErr(other.Bytes())) } -func (s *SigLockedSingleOutput) FromBytes(bytes []byte) (err error) { - s.Lock() - defer s.Unlock() - - _, err = serix.DefaultAPI.Decode(context.Background(), bytes, s, serix.WithValidation()) - return -} - -func (s *SigLockedSingleOutput) FromObjectStorage(key, data []byte) (err error) { - if err = s.IDFromBytes(key); err != nil { - return errors.Errorf("failed to decode ID: %w", err) - } - - if err = s.FromBytes(data); err != nil { - return errors.Errorf("failed to decode Model: %w", err) - } - - return nil -} - -// ObjectStorageValue marshals the Output into a sequence of bytes. The ID is not serialized here as it is only used as -// a key in the ObjectStorage. -func (s *SigLockedSingleOutput) ObjectStorageValue() (value []byte) { - return lo.PanicOnErr(s.Bytes()) -} - -func (s *SigLockedSingleOutput) Bytes() (bytes []byte, err error) { - s.RLock() - defer s.RUnlock() - - return serix.DefaultAPI.Encode(context.Background(), s, serix.WithValidation()) -} - // endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// // region SigLockedColoredOutput /////////////////////////////////////////////////////////////////////////////////////// @@ -464,7 +435,7 @@ func (s *SigLockedSingleOutput) Bytes() (bytes []byte, err error) { // SigLockedColoredOutput is an Output that holds colored balances and that can be unlocked by providing a signature for // an Address. type SigLockedColoredOutput struct { - model.Storable[utxo.OutputID, sigLockedColoredOutput] `serix:"0"` + model.Storable[utxo.OutputID, SigLockedColoredOutput, *SigLockedColoredOutput, sigLockedColoredOutput] `serix:"0"` } type sigLockedColoredOutput struct { Balances *ColoredBalances `serix:"0"` @@ -473,11 +444,11 @@ type sigLockedColoredOutput struct { // NewSigLockedColoredOutput is the constructor for a SigLockedColoredOutput. func NewSigLockedColoredOutput(balances *ColoredBalances, address Address) *SigLockedColoredOutput { - return &SigLockedColoredOutput{model.NewStorable[utxo.OutputID]( - sigLockedColoredOutput{ + return model.NewStorable[utxo.OutputID, SigLockedColoredOutput]( + &sigLockedColoredOutput{ Balances: balances, Address: address, - })} + }) } // Type returns the type of the Output which allows us to generically handle Outputs of different types. @@ -494,8 +465,12 @@ func (s *SigLockedColoredOutput) Balances() *ColoredBalances { func (s *SigLockedColoredOutput) UnlockValid(tx *Transaction, unlockBlock UnlockBlock, inputs []Output) (unlockValid bool, err error) { switch blk := unlockBlock.(type) { case *SignatureUnlockBlock: + txBytes, err := tx.Essence().Bytes() + if err != nil { + return false, errors.Wrap(err, "could not get essence bytes") + } // unlocking by signature - unlockValid = blk.AddressSignatureValid(s.M.Address, tx.Essence().Bytes()) + unlockValid = blk.AddressSignatureValid(s.M.Address, txBytes) case *AliasUnlockBlock: // unlocking by alias reference. The unlock is valid if: @@ -657,6 +632,7 @@ type AliasOutput struct { // governance transition delegationTimelock time.Time + mutex sync.RWMutex objectstorage.StorableObjectFlags } @@ -711,7 +687,7 @@ func (a *AliasOutput) WithDelegationAndTimelock(lockUntil time.Time) *AliasOutpu func (a *AliasOutput) Decode(b []byte) (int, error) { marshalUtil := marshalutil.New(b) - if _, err := a.FromMarshalUtil(marshalUtil); err != nil { + if _, err := a.fromMarshalUtil(marshalUtil); err != nil { return marshalUtil.ReadOffset(), errors.Errorf("failed to parse AliasOutput from MarshalUtil: %w", err) } @@ -721,7 +697,6 @@ func (a *AliasOutput) Decode(b []byte) (int, error) { func (a *AliasOutput) Encode() ([]byte, error) { flags := a.mustFlags() ret := marshalutil.New(). - WriteByte(byte(AliasOutputType)). WriteByte(byte(flags)). WriteBytes(a.aliasAddress.Bytes()). WriteBytes(a.balances.Bytes()). @@ -749,17 +724,14 @@ func (a *AliasOutput) Encode() ([]byte, error) { } // FromObjectStorage creates an AliasOutput from sequences of key and bytes. -func (a *AliasOutput) FromObjectStorage(key, bytes []byte) error { - _, err := a.Decode(bytes) - if err != nil { - err = errors.Errorf("failed to parse ExtendedLockedOutput from bytes: %w", err) - return err +func (a *AliasOutput) FromObjectStorage(key, data []byte) (err error) { + if err = a.FromBytes(data); err != nil { + return errors.Errorf("failed to parse AliasOutput from bytes: %w", err) } var outputID utxo.OutputID if _, err = serix.DefaultAPI.Decode(context.Background(), key, &outputID, serix.WithValidation()); err != nil { - err = errors.Errorf("failed to parse OutputID from bytes: %w", err) - return err + return errors.Errorf("failed to parse OutputID from bytes: %w", err) } a.SetID(outputID) @@ -773,22 +745,35 @@ func (a *AliasOutput) ObjectStorageKey() []byte { // ObjectStorageValue binary form. func (a *AliasOutput) ObjectStorageValue() []byte { - return lo.PanicOnErr(a.Encode()) + return lo.PanicOnErr(a.Bytes()) } -// FromMarshalUtil unmarshals a AliasOutput using a MarshalUtil (for easier unmarshaling). -func (a *AliasOutput) FromMarshalUtil(marshalUtil *marshalutil.MarshalUtil) (output *AliasOutput, err error) { - if output = a; output == nil { - output = new(AliasOutput) - } +// Bytes serialized form. +func (a *AliasOutput) Bytes() ([]byte, error) { + return serix.DefaultAPI.Encode(context.Background(), a, serix.WithValidation()) +} - outputType, err := marshalUtil.ReadByte() +// FromBytes creates an AliasOutput from sequences of bytes. +func (a *AliasOutput) FromBytes(data []byte) error { + consumedBytes, err := serix.DefaultAPI.Decode(context.Background(), data, a, serix.WithValidation()) if err != nil { - return nil, errors.Errorf("aliasOutput: failed to parse OutputType (%v): %w", err, cerrors.ErrParseBytesFailed) + err = errors.Errorf("failed to parse AliasOutput from bytes: %w", err) + return err } - if OutputType(outputType) != AliasOutputType { - return nil, errors.Errorf("aliasOutput: invalid OutputType (%X): %w", outputType, cerrors.ErrParseBytesFailed) + + if len(data) != consumedBytes { + return errors.Errorf("consumed bytes %d not equal total bytes %d: %w", consumedBytes, len(data), cerrors.ErrParseBytesFailed) } + + return nil +} + +// fromMarshalUtil unmarshals a AliasOutput using a MarshalUtil (for easier unmarshaling). +func (a *AliasOutput) fromMarshalUtil(marshalUtil *marshalutil.MarshalUtil) (output *AliasOutput, err error) { + if output = a; output == nil { + output = new(AliasOutput) + } + flagsByte, err1 := marshalUtil.ReadByte() if err1 != nil { return nil, errors.Errorf("aliasOutput: failed to parse AliasOutput flags (%v): %w", err1, cerrors.ErrParseBytesFailed) @@ -883,6 +868,8 @@ func (a *AliasOutput) SetBalances(balances map[Color]uint64) error { // GetAliasAddress calculates new ID if it is a minting output. Otherwise it takes stored value. func (a *AliasOutput) GetAliasAddress() *AliasAddress { + a.mutex.RLock() + defer a.mutex.RUnlock() if a.aliasAddress.IsNil() { return NewAliasAddress(a.ID().Bytes()) } @@ -891,11 +878,15 @@ func (a *AliasOutput) GetAliasAddress() *AliasAddress { // SetAliasAddress sets the alias address of the alias output. func (a *AliasOutput) SetAliasAddress(addr *AliasAddress) { + a.mutex.Lock() + defer a.mutex.Unlock() a.aliasAddress = *addr } // IsOrigin returns true if it starts the chain. func (a *AliasOutput) IsOrigin() bool { + a.mutex.RLock() + defer a.mutex.RUnlock() return a.isOrigin } @@ -906,6 +897,8 @@ func (a *AliasOutput) SetIsOrigin(isOrigin bool) { // IsDelegated returns true if the output is delegated. func (a *AliasOutput) IsDelegated() bool { + a.mutex.RLock() + defer a.mutex.RUnlock() return a.isDelegated } @@ -916,11 +909,15 @@ func (a *AliasOutput) SetIsDelegated(isDelegated bool) { // IsSelfGoverned returns if governing address is not set which means that stateAddress is same as governingAddress. func (a *AliasOutput) IsSelfGoverned() bool { + a.mutex.RLock() + defer a.mutex.RUnlock() return a.governingAddress == nil } // GetStateAddress return state controlling address. func (a *AliasOutput) GetStateAddress() Address { + a.mutex.RLock() + defer a.mutex.RUnlock() return a.stateAddress } @@ -949,8 +946,12 @@ func (a *AliasOutput) SetGoverningAddress(addr Address) { // GetGoverningAddress return governing address. If self-governed, it is the same as state controlling address. func (a *AliasOutput) GetGoverningAddress() Address { if a.IsSelfGoverned() { + a.mutex.RLock() + defer a.mutex.RUnlock() return a.stateAddress } + a.mutex.RLock() + defer a.mutex.RUnlock() return a.governingAddress } @@ -966,6 +967,8 @@ func (a *AliasOutput) SetStateData(data []byte) error { // GetStateData gets the state data. func (a *AliasOutput) GetStateData() []byte { + a.mutex.RLock() + defer a.mutex.RUnlock() return a.stateData } @@ -981,6 +984,8 @@ func (a *AliasOutput) SetGovernanceMetadata(data []byte) error { // GetGovernanceMetadata gets the governance metadata. func (a *AliasOutput) GetGovernanceMetadata() []byte { + a.mutex.RLock() + defer a.mutex.RUnlock() return a.governanceMetadata } @@ -991,6 +996,8 @@ func (a *AliasOutput) SetStateIndex(index uint32) { // GetIsGovernanceUpdated returns if the output was unlocked for governance in the transaction. func (a *AliasOutput) GetIsGovernanceUpdated() bool { + a.mutex.RLock() + defer a.mutex.RUnlock() return a.isGovernanceUpdate } @@ -1001,11 +1008,15 @@ func (a *AliasOutput) SetIsGovernanceUpdated(i bool) { // GetStateIndex returns the state index. func (a *AliasOutput) GetStateIndex() uint32 { + a.mutex.RLock() + defer a.mutex.RUnlock() return a.stateIndex } // GetImmutableData gets the state data. func (a *AliasOutput) GetImmutableData() []byte { + a.mutex.RLock() + defer a.mutex.RUnlock() return a.immutableData } @@ -1031,6 +1042,8 @@ func (a *AliasOutput) SetDelegationTimelock(timelock time.Time) error { // DelegationTimelock returns the delegation timelock. If the output is not delegated, or delegation timelock is // not set, it returns the zero time object. func (a *AliasOutput) DelegationTimelock() time.Time { + a.mutex.RLock() + defer a.mutex.RUnlock() if !a.isDelegated { return time.Time{} } @@ -1039,6 +1052,8 @@ func (a *AliasOutput) DelegationTimelock() time.Time { // DelegationTimeLockedNow determines if the alias output is delegation timelocked at a given time. func (a *AliasOutput) DelegationTimeLockedNow(nowis time.Time) bool { + a.mutex.RLock() + defer a.mutex.RUnlock() if !a.isDelegated || a.delegationTimelock.IsZero() { return false } @@ -1047,6 +1062,8 @@ func (a *AliasOutput) DelegationTimeLockedNow(nowis time.Time) bool { // Clone clones the structure. func (a *AliasOutput) Clone() Output { + a.mutex.RLock() + defer a.mutex.RUnlock() return a.clone() } @@ -1099,6 +1116,8 @@ func (a *AliasOutput) Type() OutputType { // Balances return colored balances of the output. func (a *AliasOutput) Balances() *ColoredBalances { + a.mutex.RLock() + defer a.mutex.RUnlock() return a.balances } @@ -1116,11 +1135,6 @@ func (a *AliasOutput) Input() Input { return NewUTXOInput(a.ID()) } -// Bytes serialized form. -func (a *AliasOutput) Bytes() ([]byte, error) { - return a.ObjectStorageValue(), nil -} - // String human readable form. func (a *AliasOutput) String() string { ret := "AliasOutput:\n" @@ -1151,13 +1165,21 @@ func (a *AliasOutput) UnlockValid(tx *Transaction, unlockBlock UnlockBlock, inpu if chained != nil { // chained output is present if chained.isGovernanceUpdate { + txBytes, err := tx.Essence().Bytes() + if err != nil { + return false, errors.Wrap(err, "could not get essence bytes") + } // check if signature is valid against governing address - if !blk.AddressSignatureValid(a.GetGoverningAddress(), tx.Essence().Bytes()) { + if !blk.AddressSignatureValid(a.GetGoverningAddress(), txBytes) { return false, errors.New("signature is invalid for governance unlock") } } else { + txBytes, err := tx.Essence().Bytes() + if err != nil { + return false, errors.Wrap(err, "could not get essence bytes") + } // check if signature is valid against state address - if !blk.AddressSignatureValid(a.GetStateAddress(), tx.Essence().Bytes()) { + if !blk.AddressSignatureValid(a.GetStateAddress(), txBytes) { return false, errors.New("signature is invalid for state unlock") } } @@ -1166,9 +1188,13 @@ func (a *AliasOutput) UnlockValid(tx *Transaction, unlockBlock UnlockBlock, inpu return false, err } } else { + txBytes, err := tx.Essence().Bytes() + if err != nil { + return false, errors.Wrap(err, "could not get essence bytes") + } // no chained output found. Alias is being destroyed? // check if governance is unlocked - if !blk.AddressSignatureValid(a.GetGoverningAddress(), tx.Essence().Bytes()) { + if !blk.AddressSignatureValid(a.GetGoverningAddress(), txBytes) { return false, errors.New("signature is invalid for chain output deletion") } // validate deletion constraint @@ -1597,7 +1623,7 @@ func (o *ExtendedLockedOutput) SetPayload(data []byte) error { func (o *ExtendedLockedOutput) Decode(b []byte) (int, error) { marshalUtil := marshalutil.New(b) - if _, err := o.FromMarshalUtil(marshalUtil); err != nil { + if _, err := o.fromMarshalUtil(marshalUtil); err != nil { return marshalUtil.ReadOffset(), errors.Errorf("failed to parse ExtendedLockedOutput from MarshalUtil: %w", err) } @@ -1607,7 +1633,6 @@ func (o *ExtendedLockedOutput) Decode(b []byte) (int, error) { func (o *ExtendedLockedOutput) Encode() ([]byte, error) { flags := o.compressFlags() ret := marshalutil.New(). - WriteByte(byte(ExtendedLockedOutputType)). WriteBytes(o.balances.Bytes()). WriteBytes(o.address.Bytes()). WriteByte(byte(flags)) @@ -1626,17 +1651,14 @@ func (o *ExtendedLockedOutput) Encode() ([]byte, error) { } // FromObjectStorage creates an ExtendedLockedOutput from sequences of key and bytes. -func (o *ExtendedLockedOutput) FromObjectStorage(key, value []byte) error { - _, err := o.Decode(value) - if err != nil { - err = errors.Errorf("failed to parse ExtendedLockedOutput from bytes: %w", err) - return err +func (o *ExtendedLockedOutput) FromObjectStorage(key, value []byte) (err error) { + if err = o.FromBytes(value); err != nil { + return errors.Errorf("failed to parse ExtendedLockedOutput from bytes: %w", err) } var outputID utxo.OutputID if _, err = serix.DefaultAPI.Decode(context.Background(), key, &outputID, serix.WithValidation()); err != nil { - err = errors.Errorf("failed to parse OutputID from bytes: %w", err) - return err + return errors.Errorf("failed to parse OutputID from bytes: %w", err) } o.SetID(outputID) @@ -1650,23 +1672,33 @@ func (o *ExtendedLockedOutput) ObjectStorageKey() []byte { // ObjectStorageValue binary form. func (o *ExtendedLockedOutput) ObjectStorageValue() []byte { - return lo.PanicOnErr(o.Encode()) + return lo.PanicOnErr(o.Bytes()) } -// FromMarshalUtil unmarshals a ExtendedLockedOutput using a MarshalUtil (for easier unmarshalling). -func (o *ExtendedLockedOutput) FromMarshalUtil(marshalUtil *marshalutil.MarshalUtil) (output *ExtendedLockedOutput, err error) { - if output = o; output == nil { - output = new(ExtendedLockedOutput) - } +// Bytes returns a marshaled version of the Output. +func (o *ExtendedLockedOutput) Bytes() ([]byte, error) { + return serix.DefaultAPI.Encode(context.Background(), o, serix.WithValidation()) +} - outputType, err := marshalUtil.ReadByte() +// FromBytes creates an AliasOutput from sequences of bytes. +func (o *ExtendedLockedOutput) FromBytes(data []byte) error { + consumedBytes, err := serix.DefaultAPI.Decode(context.Background(), data, o, serix.WithValidation()) if err != nil { - err = errors.Errorf("failed to parse OutputType (%v): %w", err, cerrors.ErrParseBytesFailed) - return + err = errors.Errorf("failed to parse ExtendedLockedOutput from bytes: %w", err) + return err } - if OutputType(outputType) != ExtendedLockedOutputType { - err = errors.Errorf("invalid OutputType (%X): %w", outputType, cerrors.ErrParseBytesFailed) - return + + if len(data) != consumedBytes { + return errors.Errorf("consumed bytes %d not equal total bytes %d: %w", consumedBytes, len(data), cerrors.ErrParseBytesFailed) + } + + return nil +} + +// fromMarshalUtil unmarshals a ExtendedLockedOutput using a MarshalUtil (for easier unmarshalling). +func (o *ExtendedLockedOutput) fromMarshalUtil(marshalUtil *marshalutil.MarshalUtil) (output *ExtendedLockedOutput, err error) { + if output = o; output == nil { + output = new(ExtendedLockedOutput) } balances, bytesRead, err := ColoredBalancesFromBytes(marshalUtil.Bytes()[marshalUtil.ReadOffset():]) @@ -1780,8 +1812,12 @@ func (o *ExtendedLockedOutput) UnlockValid(tx *Transaction, unlockBlock UnlockBl switch blk := unlockBlock.(type) { case *SignatureUnlockBlock: + txBytes, err := tx.Essence().Bytes() + if err != nil { + return false, errors.Wrap(err, "could not get essence bytes") + } // unlocking by signature - unlockValid = blk.AddressSignatureValid(addr, tx.Essence().Bytes()) + unlockValid = blk.AddressSignatureValid(addr, txBytes) case *AliasUnlockBlock: // unlocking by alias reference. The unlock is valid if: @@ -1869,11 +1905,6 @@ func (o *ExtendedLockedOutput) UpdateMintingColor() Output { return updatedOutput } -// Bytes returns a marshaled version of the Output. -func (o *ExtendedLockedOutput) Bytes() ([]byte, error) { - return o.ObjectStorageValue(), nil -} - // Compare offers a comparator for Outputs which returns -1 if the other Output is bigger, 1 if it is smaller and 0 if // they are the same. func (o *ExtendedLockedOutput) Compare(other Output) int { diff --git a/packages/ledger/vm/devnetvm/output_test.go b/packages/ledger/vm/devnetvm/output_test.go index 957e23922f..b740b44f66 100644 --- a/packages/ledger/vm/devnetvm/output_test.go +++ b/packages/ledger/vm/devnetvm/output_test.go @@ -11,6 +11,7 @@ import ( "github.com/iotaledger/hive.go/byteutils" "github.com/iotaledger/hive.go/crypto/ed25519" "github.com/iotaledger/hive.go/generics/lo" + "github.com/iotaledger/hive.go/generics/model" "github.com/iotaledger/hive.go/identity" "github.com/iotaledger/hive.go/marshalutil" "github.com/stretchr/testify/assert" @@ -162,8 +163,8 @@ func TestAliasOutputFromMarshalUtil(t *testing.T) { originBytes := lo.PanicOnErr(originAlias.Bytes()) // manually change output type byte originBytes[0] = 1 - marshalUtil := marshalutil.New(originBytes) - _, err := new(AliasOutput).FromMarshalUtil(marshalUtil) + restoredAlias := new(AliasOutput) + err := restoredAlias.FromBytes(originBytes) assert.Error(t, err) }) @@ -380,8 +381,8 @@ func TestAliasOutput_Bytes(t *testing.T) { t.Run("Happy path", func(t *testing.T) { alias := dummyAliasOutput() aBytes := lo.PanicOnErr(alias.Bytes()) - mUtil := marshalutil.New(aBytes) - restoredAlias, err := new(AliasOutput).FromMarshalUtil(mUtil) + restoredAlias := new(AliasOutput) + err := restoredAlias.FromBytes(aBytes) assert.NoError(t, err) assert.True(t, alias.GetAliasAddress().Equals(restoredAlias.GetAliasAddress())) assert.True(t, alias.GetStateAddress().Equals(restoredAlias.GetStateAddress())) @@ -399,8 +400,8 @@ func TestAliasOutput_Compare(t *testing.T) { t.Run("CASE: Happy path", func(t *testing.T) { alias := dummyAliasOutput() aBytes := lo.PanicOnErr(alias.Bytes()) - mUtil := marshalutil.New(aBytes) - restoredAlias, err := new(AliasOutput).FromMarshalUtil(mUtil) + restoredAlias := new(AliasOutput) + err := restoredAlias.FromBytes(aBytes) assert.NoError(t, err) assert.True(t, alias.Compare(restoredAlias) == 0) }) @@ -953,7 +954,12 @@ func TestAliasOutput_validateTransition(t *testing.T) { prev := dummyAliasOutput().WithDelegation() next := prev.NewAliasOutputNext(true) assert.Equal(t, true, next.IsDelegated()) - err := prev.validateTransition(next, &Transaction{transactionInner{Essence: &TransactionEssence{transactionEssenceInner{Timestamp: time.Now()}}}}) + tx := model.NewStorable[utxo.TransactionID, Transaction](&transactionModel{ + Essence: model.NewImmutable[TransactionEssence](&transactionEssenceModel{ + Timestamp: time.Now(), + }), + }) + err := prev.validateTransition(next, tx) assert.NoError(t, err) }) @@ -963,10 +969,10 @@ func TestAliasOutput_validateTransition(t *testing.T) { next := prev.NewAliasOutputNext(true) assert.Equal(t, true, next.IsDelegated()) // happy case, time-lock expired - err := prev.validateTransition(next, &Transaction{transactionInner{Essence: &TransactionEssence{transactionEssenceInner{Timestamp: timeLock.Add(time.Second)}}}}) + err := prev.validateTransition(next, model.NewStorable[utxo.TransactionID, Transaction](&transactionModel{Essence: model.NewImmutable[TransactionEssence](&transactionEssenceModel{Timestamp: timeLock.Add(time.Second)})})) assert.NoError(t, err) // not happy case, time-lock is still active - err = prev.validateTransition(next, &Transaction{transactionInner{Essence: &TransactionEssence{transactionEssenceInner{Timestamp: timeLock.Add(-time.Second)}}}}) + err = prev.validateTransition(next, model.NewStorable[utxo.TransactionID, Transaction](&transactionModel{Essence: model.NewImmutable[TransactionEssence](&transactionEssenceModel{Timestamp: timeLock.Add(-time.Second)})})) t.Log(err) assert.Error(t, err) }) @@ -974,7 +980,7 @@ func TestAliasOutput_validateTransition(t *testing.T) { t.Run("CASE: State update, delegation without time-lock", func(t *testing.T) { prev := dummyAliasOutput().WithDelegation() next := prev.NewAliasOutputNext(false) - err := prev.validateTransition(next, &Transaction{transactionInner{Essence: &TransactionEssence{transactionEssenceInner{Timestamp: time.Now()}}}}) + err := prev.validateTransition(next, model.NewStorable[utxo.TransactionID, Transaction](&transactionModel{Essence: model.NewImmutable[TransactionEssence](&transactionEssenceModel{Timestamp: time.Now()})})) assert.NoError(t, err) }) @@ -983,10 +989,10 @@ func TestAliasOutput_validateTransition(t *testing.T) { prev := dummyAliasOutput().WithDelegationAndTimelock(timeLock) next := prev.NewAliasOutputNext(false) // time-lock is active state transition allowed - err := prev.validateTransition(next, &Transaction{transactionInner{Essence: &TransactionEssence{transactionEssenceInner{Timestamp: timeLock.Add(-time.Second)}}}}) + err := prev.validateTransition(next, model.NewStorable[utxo.TransactionID, Transaction](&transactionModel{Essence: model.NewImmutable[TransactionEssence](&transactionEssenceModel{Timestamp: timeLock.Add(-time.Second)})})) assert.NoError(t, err) // time-lock expired, state transition should fail - err = prev.validateTransition(next, &Transaction{transactionInner{Essence: &TransactionEssence{transactionEssenceInner{Timestamp: timeLock.Add(time.Second)}}}}) + err = prev.validateTransition(next, model.NewStorable[utxo.TransactionID, Transaction](&transactionModel{Essence: model.NewImmutable[TransactionEssence](&transactionEssenceModel{Timestamp: timeLock.Add(time.Second)})})) t.Log(err) assert.Error(t, err) }) @@ -995,7 +1001,7 @@ func TestAliasOutput_validateTransition(t *testing.T) { prev := dummyAliasOutput().WithDelegation() next := prev.NewAliasOutputNext(false) next.delegationTimelock = time.Now() - err := prev.validateTransition(next, &Transaction{transactionInner{Essence: &TransactionEssence{transactionEssenceInner{Timestamp: time.Now()}}}}) + err := prev.validateTransition(next, model.NewStorable[utxo.TransactionID, Transaction](&transactionModel{Essence: model.NewImmutable[TransactionEssence](&transactionEssenceModel{Timestamp: time.Now()})})) t.Log(err) assert.Error(t, err) }) @@ -1328,18 +1334,18 @@ func TestAliasOutput_unlockedGovernanceByAliasIndex(t *testing.T) { var indexOfAliasInput, indexOfGoverningAliasInput int for i, input := range inputsOfTx { castedInput := input.(*UTXOInput) - if castedInput.utxoInputInner.ReferencedOutputID == alias.ID() { + if castedInput.ReferencedOutputID() == alias.ID() { indexOfAliasInput = i inputs = append(inputs, alias) } - if castedInput.utxoInputInner.ReferencedOutputID == governingAlias.ID() { + if castedInput.ReferencedOutputID() == governingAlias.ID() { indexOfGoverningAliasInput = i inputs = append(inputs, governingAlias) } } unlocks := make(UnlockBlocks, len(inputsOfTx)) unlocks[indexOfAliasInput] = NewAliasUnlockBlock(uint16(indexOfGoverningAliasInput)) - unlocks[indexOfGoverningAliasInput] = NewSignatureUnlockBlock(NewED25519Signature(governingAliasStateKeyPair.PublicKey, governingAliasStateKeyPair.PrivateKey.Sign(essence.Bytes()))) + unlocks[indexOfGoverningAliasInput] = NewSignatureUnlockBlock(NewED25519Signature(governingAliasStateKeyPair.PublicKey, governingAliasStateKeyPair.PrivateKey.Sign(lo.PanicOnErr(essence.Bytes())))) tx := NewTransaction(essence, unlocks) @@ -1592,11 +1598,11 @@ func TestAliasOutput_UnlockValid(t *testing.T) { var indexOfAliasInput, indexOfGoverningAliasInput int for i, input := range inputsOfTx { castedInput := input.(*UTXOInput) - if castedInput.utxoInputInner.ReferencedOutputID == governedAlias.ID() { + if castedInput.ReferencedOutputID() == governedAlias.ID() { indexOfAliasInput = i inputs = append(inputs, governedAlias) } - if castedInput.utxoInputInner.ReferencedOutputID == governingAlias.ID() { + if castedInput.ReferencedOutputID() == governingAlias.ID() { indexOfGoverningAliasInput = i inputs = append(inputs, governingAlias) } @@ -1613,8 +1619,19 @@ func TestAliasOutput_UnlockValid(t *testing.T) { }) t.Run("CASE: Unsupported unlock block", func(t *testing.T) { - txEssence := NewTransactionEssence(0, time.Time{}, identity.ID{}, identity.ID{}, nil, nil) - tx := NewTransaction(txEssence, nil) + issuerKeyPair := ed25519.GenerateKeyPair() + issuerIdentity := identity.New(issuerKeyPair.PublicKey) + inputs := NewInputs( + NewUTXOInput(utxo.NewOutputID(utxo.TransactionID{}, 0)), + ) + outputs := NewOutputs( + NewSigLockedSingleOutput(12, NewED25519Address(issuerKeyPair.PublicKey)), + ) + txEssence := NewTransactionEssence(0, time.Time{}, issuerIdentity.ID(), issuerIdentity.ID(), inputs, outputs) + unlockBlocks := UnlockBlocks{ + NewSignatureUnlockBlock(NewED25519Signature(issuerKeyPair.PublicKey, issuerKeyPair.PrivateKey.Sign(lo.PanicOnErr(txEssence.Bytes())))), + } + tx := NewTransaction(txEssence, unlockBlocks) ok, err := alias.UnlockValid(tx, NewReferenceUnlockBlock(0), Outputs{}) t.Log(err) assert.Error(t, err) @@ -1822,7 +1839,7 @@ func TestExtendedLockedOutput_Input(t *testing.T) { output := dummyExtendedLockedOutput() input, ok := output.Input().(*UTXOInput) assert.True(t, ok) - assert.Equal(t, input.utxoInputInner.ReferencedOutputID.Bytes(), output.ID().Bytes()) + assert.Equal(t, input.ReferencedOutputID().Bytes(), output.ID().Bytes()) }) t.Run("CASE: No output id yet", func(t *testing.T) { @@ -2005,10 +2022,9 @@ func TestExtendedOutputFromMarshalUtil(t *testing.T) { t.Run("CASE: Happy path", func(t *testing.T) { output := dummyExtendedLockedOutput() outputBytes := lo.PanicOnErr(output.Bytes()) - marshalUtil := marshalutil.New(outputBytes) - restored, err := new(ExtendedLockedOutput).FromMarshalUtil(marshalUtil) + restored := new(ExtendedLockedOutput) + err := restored.FromBytes(outputBytes) assert.NoError(t, err) - assert.Equal(t, len(outputBytes), marshalUtil.ReadOffset()) assert.Equal(t, outputBytes, lo.PanicOnErr(restored.Bytes())) }) @@ -2017,7 +2033,7 @@ func TestExtendedOutputFromMarshalUtil(t *testing.T) { outputBytes := lo.PanicOnErr(output.Bytes()) outputBytes[0] = byte(AliasOutputType) marshalUtil := marshalutil.New(outputBytes) - _, err := new(ExtendedLockedOutput).FromMarshalUtil(marshalUtil) + _, err := new(ExtendedLockedOutput).fromMarshalUtil(marshalUtil) t.Log(err) assert.Error(t, err) }) @@ -2239,7 +2255,7 @@ func TestExtendedLockedOutput_UnlockValid(t *testing.T) { input := NewExtendedLockedOutput(map[Color]uint64{ColorIOTA: 1}, randAliasAddress()) unlockBlock := NewReferenceUnlockBlock(0) - valid, err := input.UnlockValid(&Transaction{transactionInner{Essence: new(TransactionEssence)}}, unlockBlock, Outputs{input}) + valid, err := input.UnlockValid(model.NewStorable[utxo.TransactionID, Transaction](&transactionModel{Essence: new(TransactionEssence)}), unlockBlock, Outputs{input}) t.Log(err) assert.Error(t, err) assert.False(t, valid) diff --git a/packages/ledger/vm/devnetvm/testutils.go b/packages/ledger/vm/devnetvm/testutils.go index e8fae9dec2..f6ed95ae96 100644 --- a/packages/ledger/vm/devnetvm/testutils.go +++ b/packages/ledger/vm/devnetvm/testutils.go @@ -2,6 +2,7 @@ package devnetvm import ( "github.com/iotaledger/hive.go/crypto/ed25519" + "github.com/iotaledger/hive.go/generics/lo" ) type wallet struct { @@ -38,7 +39,7 @@ func createWallets(n int) []wallet { } func (w wallet) sign(txEssence *TransactionEssence) *ED25519Signature { - return NewED25519Signature(w.publicKey(), w.privateKey().Sign(txEssence.Bytes())) + return NewED25519Signature(w.publicKey(), w.privateKey().Sign(lo.PanicOnErr(txEssence.Bytes()))) } func (w wallet) unlockBlocks(txEssence *TransactionEssence) []UnlockBlock { diff --git a/packages/ledger/vm/devnetvm/transaction.go b/packages/ledger/vm/devnetvm/transaction.go index 11d7a9b811..a589be8bf5 100644 --- a/packages/ledger/vm/devnetvm/transaction.go +++ b/packages/ledger/vm/devnetvm/transaction.go @@ -5,16 +5,15 @@ import ( "fmt" "strconv" "strings" - "sync" "time" "github.com/cockroachdb/errors" "github.com/iotaledger/hive.go/cerrors" - "github.com/iotaledger/hive.go/generics/objectstorage" + "github.com/iotaledger/hive.go/generics/lo" + "github.com/iotaledger/hive.go/generics/model" "github.com/iotaledger/hive.go/identity" "github.com/iotaledger/hive.go/serializer" "github.com/iotaledger/hive.go/serix" - "github.com/iotaledger/hive.go/stringify" "github.com/iotaledger/hive.go/types" "github.com/iotaledger/goshimmer/packages/ledger/utxo" @@ -145,20 +144,27 @@ func (t TransactionIDs) Base58s() (transactionIDs []string) { // Transaction represents a payload that executes a value transfer in the ledger state. type Transaction struct { - transactionInner `serix:"0"` + model.Storable[utxo.TransactionID, Transaction, *Transaction, transactionModel] `serix:"0"` } -type transactionInner struct { - id *utxo.TransactionID - idMutex sync.RWMutex + +type transactionModel struct { Essence *TransactionEssence `serix:"1"` UnlockBlocks UnlockBlocks `serix:"2,lengthPrefixType=uint16"` +} + +// ID returns the identifier of the Transaction. Since calculating the TransactionID is a resource intensive operation +// we calculate this value lazy and use double-checked locking. +func (t *Transaction) ID() utxo.TransactionID { + if t.Storable.ID() == utxo.EmptyTransactionID { + t.Storable.SetID(utxo.NewTransactionID(lo.PanicOnErr(t.Bytes()))) + } - objectstorage.StorableObjectFlags + return t.Storable.ID() } func (t *Transaction) Inputs() (inputs []utxo.Input) { inputs = make([]utxo.Input, 0) - for _, input := range t.transactionInner.Essence.Inputs() { + for _, input := range t.Essence().Inputs() { inputs = append(inputs, input) } @@ -171,12 +177,10 @@ func NewTransaction(essence *TransactionEssence, unlockBlocks UnlockBlocks) (tra panic(fmt.Sprintf("in NewTransaction: Amount of UnlockBlocks (%d) does not match amount of Inputs (%d)", len(unlockBlocks), len(essence.Inputs()))) } - transaction = &Transaction{ - transactionInner{ - Essence: essence, - UnlockBlocks: unlockBlocks, - }, - } + transaction = model.NewStorable[utxo.TransactionID, Transaction](&transactionModel{ + Essence: essence, + UnlockBlocks: unlockBlocks, + }) SetOutputID(essence, transaction.ID()) @@ -185,71 +189,19 @@ func NewTransaction(essence *TransactionEssence, unlockBlocks UnlockBlocks) (tra // FromObjectStorage creates an Transaction from sequences of key and bytes. func (t *Transaction) FromObjectStorage(key, value []byte) error { - tx := t - if tx == nil { - tx = new(Transaction) - } - - _, err := serix.DefaultAPI.Decode(context.Background(), value, tx, serix.WithValidation()) - if err != nil { - err = errors.Errorf("failed to parse Transaction: %w", err) - return err - } - transactionID := new(utxo.TransactionID) - _, err = serix.DefaultAPI.Decode(context.Background(), key, transactionID, serix.WithValidation()) - if err != nil { - err = errors.Errorf("failed to parse Transaction.id: %w", err) - return err - } - tx.transactionInner.id = transactionID + err := t.Storable.FromObjectStorage(key, value) - SetOutputID(tx.Essence(), tx.ID()) + SetOutputID(t.Essence(), t.ID()) return err } // FromBytes unmarshals a Transaction from a sequence of bytes. -func (t *Transaction) FromBytes(data []byte) (*Transaction, error) { - tx := new(Transaction) - if t != nil { - tx = t - } - _, err := serix.DefaultAPI.Decode(context.Background(), data, tx) - if err != nil { - err = errors.Errorf("failed to parse Transaction: %w", err) - return tx, err - } - SetOutputID(tx.Essence(), tx.ID()) +func (t *Transaction) FromBytes(data []byte) error { + err := t.Storable.FromBytes(data) + SetOutputID(t.Essence(), t.ID()) - return tx, nil -} - -// ID returns the identifier of the Transaction. Since calculating the TransactionID is a resource intensive operation -// we calculate this value lazy and use double-checked locking. -func (t *Transaction) ID() utxo.TransactionID { - t.idMutex.RLock() - if t.id != nil { - defer t.idMutex.RUnlock() - - return *t.id - } - - t.idMutex.RUnlock() - t.idMutex.Lock() - defer t.idMutex.Unlock() - - if t.id != nil { - return *t.id - } - - return utxo.NewTransactionID(t.Bytes()) -} - -func (t *Transaction) SetID(id utxo.TransactionID) { - t.idMutex.Lock() - defer t.idMutex.Unlock() - - t.id = &id + return err } // Type returns the Type of the Payload. @@ -259,43 +211,12 @@ func (t *Transaction) Type() payload.Type { // Essence returns the TransactionEssence of the Transaction. func (t *Transaction) Essence() *TransactionEssence { - return t.transactionInner.Essence + return t.M.Essence } // UnlockBlocks returns the UnlockBlocks of the Transaction. func (t *Transaction) UnlockBlocks() UnlockBlocks { - return t.transactionInner.UnlockBlocks -} - -// Bytes returns a marshaled version of the Transaction. -func (t *Transaction) Bytes() []byte { - objBytes, err := serix.DefaultAPI.Encode(context.Background(), t) - if err != nil { - // TODO: what do? - panic(err) - } - return objBytes -} - -// String returns a human-readable version of the Transaction. -func (t *Transaction) String() string { - return stringify.Struct("Transaction", - stringify.StructField("id", t.ID()), - stringify.StructField("essence", t.Essence()), - stringify.StructField("unlockBlocks", t.UnlockBlocks()), - ) -} - -// ObjectStorageKey returns the key that is used to store the object in the database. It is required to match the -// StorableObject interface. -func (t *Transaction) ObjectStorageKey() []byte { - return t.ID().Bytes() -} - -// ObjectStorageValue marshals the Transaction into a sequence of bytes. The ID is not serialized here as it is only -// used as a key in the ObjectStorage. -func (t *Transaction) ObjectStorageValue() []byte { - return t.Bytes() + return t.M.UnlockBlocks } // SetOutputID assigns TransactionID to all outputs in TransactionEssence @@ -325,18 +246,16 @@ var _ payload.Payload = new(Transaction) var _ utxo.Transaction = new(Transaction) -// code contract (make sure the struct implements all required methods) -var _ objectstorage.StorableObject = new(Transaction) - // endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// // region TransactionEssence /////////////////////////////////////////////////////////////////////////////////////////// // TransactionEssence contains the transfer related information of the Transaction (without the unlocking details). type TransactionEssence struct { - transactionEssenceInner `serix:"0"` + model.Immutable[TransactionEssence, *TransactionEssence, transactionEssenceModel] `serix:"0"` } -type transactionEssenceInner struct { + +type transactionEssenceModel struct { Version TransactionEssenceVersion `serix:"0"` // timestamp is the timestamp of the transaction. Timestamp time.Time `serix:"1"` @@ -358,16 +277,15 @@ func NewTransactionEssence( inputs Inputs, outputs Outputs, ) *TransactionEssence { - return &TransactionEssence{ - transactionEssenceInner{ - Version: version, - Timestamp: timestamp, - AccessPledgeID: accessPledgeID, - ConsensusPledgeID: consensusPledgeID, - Inputs: inputs, - Outputs: outputs, - }, - } + return model.NewImmutable[TransactionEssence](&transactionEssenceModel{ + Version: version, + Timestamp: timestamp, + AccessPledgeID: accessPledgeID, + ConsensusPledgeID: consensusPledgeID, + Inputs: inputs, + Outputs: outputs, + }, + ) } // TransactionEssenceFromBytes unmarshals a TransactionEssence from a sequence of bytes. @@ -383,65 +301,42 @@ func TransactionEssenceFromBytes(data []byte) (transactionEssence *TransactionEs // SetPayload set the optional Payload of the TransactionEssence. func (t *TransactionEssence) SetPayload(p payload.Payload) { - t.transactionEssenceInner.Payload = p + t.M.Payload = p } // Version returns the Version of the TransactionEssence. func (t *TransactionEssence) Version() TransactionEssenceVersion { - return t.transactionEssenceInner.Version + return t.M.Version } // Timestamp returns the timestamp of the TransactionEssence. func (t *TransactionEssence) Timestamp() time.Time { - return t.transactionEssenceInner.Timestamp + return t.M.Timestamp } // AccessPledgeID returns the access mana pledge nodeID of the TransactionEssence. func (t *TransactionEssence) AccessPledgeID() identity.ID { - return t.transactionEssenceInner.AccessPledgeID + return t.M.AccessPledgeID } // ConsensusPledgeID returns the consensus mana pledge nodeID of the TransactionEssence. func (t *TransactionEssence) ConsensusPledgeID() identity.ID { - return t.transactionEssenceInner.ConsensusPledgeID + return t.M.ConsensusPledgeID } // Inputs returns the Inputs of the TransactionEssence. func (t *TransactionEssence) Inputs() Inputs { - return t.transactionEssenceInner.Inputs + return t.M.Inputs } // Outputs returns the Outputs of the TransactionEssence. func (t *TransactionEssence) Outputs() Outputs { - return t.transactionEssenceInner.Outputs + return t.M.Outputs } // Payload returns the optional Payload of the TransactionEssence. func (t *TransactionEssence) Payload() payload.Payload { - return t.transactionEssenceInner.Payload -} - -// Bytes returns a marshaled version of the TransactionEssence. -func (t *TransactionEssence) Bytes() []byte { - objBytes, err := serix.DefaultAPI.Encode(context.Background(), t) - if err != nil { - // TODO: what do? - panic(err) - } - return objBytes -} - -// String returns a human-readable version of the TransactionEssence. -func (t *TransactionEssence) String() string { - return stringify.Struct("TransactionEssence", - stringify.StructField("Version", t.transactionEssenceInner.Version), - stringify.StructField("Timestamp", t.transactionEssenceInner.Timestamp), - stringify.StructField("AccessPledgeID", t.transactionEssenceInner.AccessPledgeID), - stringify.StructField("ConsensusPledgeID", t.transactionEssenceInner.ConsensusPledgeID), - stringify.StructField("Inputs", t.transactionEssenceInner.Inputs), - stringify.StructField("Outputs", t.transactionEssenceInner.Outputs), - stringify.StructField("Payload", t.transactionEssenceInner.Payload), - ) + return t.M.Payload } // endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/packages/ledger/vm/devnetvm/transaction_test.go b/packages/ledger/vm/devnetvm/transaction_test.go index e474a9f683..05ccc143e1 100644 --- a/packages/ledger/vm/devnetvm/transaction_test.go +++ b/packages/ledger/vm/devnetvm/transaction_test.go @@ -5,6 +5,7 @@ import ( "time" "github.com/iotaledger/hive.go/crypto/ed25519" + "github.com/iotaledger/hive.go/generics/lo" "github.com/iotaledger/hive.go/identity" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -32,10 +33,10 @@ func TestTransaction_Bytes(t *testing.T) { ) transaction := NewTransaction(transactionEssence, UnlockBlocks{ - NewSignatureUnlockBlock(NewED25519Signature(issuerKeyPair.PublicKey, issuerKeyPair.PrivateKey.Sign(transactionEssence.Bytes()))), + NewSignatureUnlockBlock(NewED25519Signature(issuerKeyPair.PublicKey, issuerKeyPair.PrivateKey.Sign(lo.PanicOnErr(transactionEssence.Bytes())))), }) - - _tx, err := new(Transaction).FromBytes(transaction.Bytes()) + _tx := new(Transaction) + err := _tx.FromBytes(lo.PanicOnErr(transaction.Bytes())) assert.NoError(t, err) assert.Equal(t, transaction.ID(), _tx.ID()) assert.Equal(t, transaction.Essence().Outputs()[0].Balances(), _tx.Essence().Outputs()[0].Balances()) @@ -63,7 +64,7 @@ func TestTransaction_Complex(t *testing.T) { }) // party1 prepares a TransactionEssence that party2 is supposed to complete for the exchange of tokens - sentParty1Essence := NewTransactionEssence(0, time.Now(), identity.ID{}, identity.ID{}, + sentParty1Essence, err := NewTransactionEssence(0, time.Now(), identity.ID{}, identity.ID{}, // he consumes 200 tokens of Color2 NewInputs(unspentOutputsDB[party1ControlledOutputID].Input()), @@ -77,6 +78,7 @@ func TestTransaction_Complex(t *testing.T) { }), party1RemainderAddress), ), ).Bytes() + require.NoError(t, err) // party2 unmarshals the prepared TransactionEssence he received from party1 receivedParty1Essence, _, err := TransactionEssenceFromBytes(sentParty1Essence) @@ -181,7 +183,7 @@ func addressFromInput(input Input, outputsByID OutputsByID) Address { // signTransaction is a utility function that iterates through a transactions inputs and signs the addresses that are // part of the signers key chain. func signTransaction(essence *TransactionEssence, unlockBlocks UnlockBlocks, unspentOutputsDB OutputsByID, keyChain map[Address]ed25519.KeyPair) { - essenceBytesToSign := essence.Bytes() + essenceBytesToSign := lo.PanicOnErr(essence.Bytes()) for i, input := range essence.Inputs() { if keyPair, keyPairExists := keyChain[addressFromInput(input, unspentOutputsDB)]; keyPairExists { diff --git a/packages/ledger/vm/devnetvm/unlockblock.go b/packages/ledger/vm/devnetvm/unlockblock.go index 5dc047943e..4021892c64 100644 --- a/packages/ledger/vm/devnetvm/unlockblock.go +++ b/packages/ledger/vm/devnetvm/unlockblock.go @@ -6,6 +6,7 @@ import ( "strconv" "github.com/cockroachdb/errors" + "github.com/iotaledger/hive.go/generics/model" "github.com/iotaledger/hive.go/serix" "github.com/iotaledger/hive.go/stringify" ) @@ -73,7 +74,7 @@ type UnlockBlock interface { Type() UnlockBlockType // Bytes returns a marshaled version of the UnlockBlock. - Bytes() []byte + Bytes() ([]byte, error) // String returns a human readable version of the UnlockBlock. String() string @@ -123,29 +124,17 @@ func (u UnlockBlocks) String() string { // SignatureUnlockBlock represents an UnlockBlock that contains a Signature for an Address. type SignatureUnlockBlock struct { - signatureUnlockBlockInner `serix:"0"` + model.Immutable[SignatureUnlockBlock, *SignatureUnlockBlock, signatureUnlockBlockModel] `serix:"0"` } -type signatureUnlockBlockInner struct { +type signatureUnlockBlockModel struct { Signature Signature `serix:"0"` } // NewSignatureUnlockBlock is the constructor for SignatureUnlockBlock objects. func NewSignatureUnlockBlock(signature Signature) *SignatureUnlockBlock { - return &SignatureUnlockBlock{ - signatureUnlockBlockInner{ - Signature: signature, - }, - } -} - -// SignatureUnlockBlockFromBytes unmarshals a SignatureUnlockBlock from a sequence of bytes. -func SignatureUnlockBlockFromBytes(bytes []byte) (unlockBlock *SignatureUnlockBlock, consumedBytes int, err error) { - unlockBlock = new(SignatureUnlockBlock) - _, err = serix.DefaultAPI.Decode(context.Background(), bytes, unlockBlock, serix.WithValidation()) - if err != nil { - return nil, consumedBytes, err - } - return + return model.NewImmutable[SignatureUnlockBlock](&signatureUnlockBlockModel{ + Signature: signature, + }) } // AddressSignatureValid returns true if the UnlockBlock correctly signs the given Address. @@ -158,26 +147,9 @@ func (s *SignatureUnlockBlock) Type() UnlockBlockType { return SignatureUnlockBlockType } -// Bytes returns a marshaled version of the UnlockBlock. -func (s *SignatureUnlockBlock) Bytes() []byte { - objBytes, err := serix.DefaultAPI.Encode(context.Background(), s, serix.WithValidation()) - if err != nil { - // TODO: what do? - return nil - } - return objBytes -} - -// String returns a human readable version of the UnlockBlock. -func (s *SignatureUnlockBlock) String() string { - return stringify.Struct("SignatureUnlockBlock", - stringify.StructField("signature", s.Signature()), - ) -} - // Signature return the signature itself. func (s *SignatureUnlockBlock) Signature() Signature { - return s.signatureUnlockBlockInner.Signature + return s.M.Signature } // code contract (make sure the type implements all required methods) @@ -190,19 +162,17 @@ var _ UnlockBlock = &SignatureUnlockBlock{} // ReferenceUnlockBlock defines an UnlockBlock which references a previous UnlockBlock (which must not be another // ReferenceUnlockBlock). type ReferenceUnlockBlock struct { - referenceUnlockBlockInner `serix:"0"` + model.Immutable[ReferenceUnlockBlock, *ReferenceUnlockBlock, referenceUnlockBlockModel] `serix:"0"` } -type referenceUnlockBlockInner struct { +type referenceUnlockBlockModel struct { ReferencedIndex uint16 `serix:"0"` } // NewReferenceUnlockBlock is the constructor for ReferenceUnlockBlocks. func NewReferenceUnlockBlock(referencedIndex uint16) *ReferenceUnlockBlock { - return &ReferenceUnlockBlock{ - referenceUnlockBlockInner{ - ReferencedIndex: referencedIndex, - }, - } + return model.NewImmutable[ReferenceUnlockBlock](&referenceUnlockBlockModel{ + ReferencedIndex: referencedIndex, + }) } // ReferenceUnlockBlockFromBytes unmarshals a ReferenceUnlockBlock from a sequence of bytes. @@ -217,7 +187,7 @@ func ReferenceUnlockBlockFromBytes(bytes []byte) (unlockBlock *ReferenceUnlockBl // ReferencedIndex returns the index of the referenced UnlockBlock. func (r *ReferenceUnlockBlock) ReferencedIndex() uint16 { - return r.referenceUnlockBlockInner.ReferencedIndex + return r.M.ReferencedIndex } // Type returns the UnlockBlockType of the UnlockBlock. @@ -225,23 +195,6 @@ func (r *ReferenceUnlockBlock) Type() UnlockBlockType { return ReferenceUnlockBlockType } -// Bytes returns a marshaled version of the Address. -func (r *ReferenceUnlockBlock) Bytes() []byte { - objBytes, err := serix.DefaultAPI.Encode(context.Background(), r, serix.WithValidation()) - if err != nil { - // TODO: what do? - return nil - } - return objBytes -} - -// String returns a human readable version of the UnlockBlock. -func (r *ReferenceUnlockBlock) String() string { - return stringify.Struct("ReferenceUnlockBlock", - stringify.StructField("referencedIndex", int(r.ReferencedIndex())), - ) -} - // code contract (make sure the type implements all required methods) var _ UnlockBlock = &ReferenceUnlockBlock{} @@ -251,24 +204,22 @@ var _ UnlockBlock = &ReferenceUnlockBlock{} // AliasUnlockBlock defines an UnlockBlock which contains an index of corresponding AliasOutput. type AliasUnlockBlock struct { - aliasUnlockBlockInner `serix:"0"` + model.Immutable[AliasUnlockBlock, *AliasUnlockBlock, aliasUnlockBlockModel] `serix:"0"` } -type aliasUnlockBlockInner struct { +type aliasUnlockBlockModel struct { ReferencedIndex uint16 `serix:"0"` } // NewAliasUnlockBlock is the constructor for AliasUnlockBlocks. func NewAliasUnlockBlock(chainInputIndex uint16) *AliasUnlockBlock { - return &AliasUnlockBlock{ - aliasUnlockBlockInner{ - ReferencedIndex: chainInputIndex, - }, - } + return model.NewImmutable[AliasUnlockBlock](&aliasUnlockBlockModel{ + ReferencedIndex: chainInputIndex, + }) } // AliasInputIndex returns the index of the input, the AliasOutput which contains AliasAddress. func (r *AliasUnlockBlock) AliasInputIndex() uint16 { - return r.ReferencedIndex + return r.M.ReferencedIndex } // Type returns the UnlockBlockType of the UnlockBlock. @@ -276,23 +227,6 @@ func (r *AliasUnlockBlock) Type() UnlockBlockType { return AliasUnlockBlockType } -// Bytes returns a marshaled version of the Address. -func (r *AliasUnlockBlock) Bytes() []byte { - objBytes, err := serix.DefaultAPI.Encode(context.Background(), r, serix.WithValidation()) - if err != nil { - // TODO: what do? - return nil - } - return objBytes -} - -// String returns a human readable version of the UnlockBlock. -func (r *AliasUnlockBlock) String() string { - return stringify.Struct("AliasUnlockBlock", - stringify.StructField("ReferencedIndex", int(r.ReferencedIndex)), - ) -} - // code contract (make sure the type implements all required methods). var _ UnlockBlock = &AliasUnlockBlock{} diff --git a/packages/ledger/vm/devnetvm/vm.go b/packages/ledger/vm/devnetvm/vm.go index ee30eaaa87..1290004917 100644 --- a/packages/ledger/vm/devnetvm/vm.go +++ b/packages/ledger/vm/devnetvm/vm.go @@ -10,7 +10,9 @@ import ( type VM struct{} func (d *VM) ParseTransaction(transactionBytes []byte) (transaction utxo.Transaction, err error) { - return new(Transaction).FromBytes(transactionBytes) + tx := new(Transaction) + err = tx.FromBytes(transactionBytes) + return tx, err } func (d *VM) ParseOutput(outputBytes []byte) (output utxo.Output, err error) { diff --git a/packages/mana/accessbase.go b/packages/mana/accessbase.go index 7f1fac24f8..481f3765bf 100644 --- a/packages/mana/accessbase.go +++ b/packages/mana/accessbase.go @@ -9,7 +9,7 @@ import ( // AccessBaseMana holds information about the access base mana values of a single node. type AccessBaseMana struct { - model.Model[accessBaseManaModel] `serix:"0"` + model.Mutable[AccessBaseMana, *AccessBaseMana, accessBaseManaModel] `serix:"0"` } type accessBaseManaModel struct { @@ -20,11 +20,11 @@ type accessBaseManaModel struct { // NewAccessBaseMana returns new base access mana vector. func NewAccessBaseMana(baseMana, effectiveBaseMana float64, lastUpdated time.Time) (newAccessBaseMana *AccessBaseMana) { - return &AccessBaseMana{model.New[accessBaseManaModel](accessBaseManaModel{ + return model.NewMutable[AccessBaseMana](&accessBaseManaModel{ BaseValue: baseMana, EffectiveValue: effectiveBaseMana, LastUpdate: lastUpdated, - })} + }) } // EffectiveValue returns effective base mana value. diff --git a/packages/mana/accessbase_test.go b/packages/mana/accessbase_test.go index 72b4aa650b..0080ea57bc 100644 --- a/packages/mana/accessbase_test.go +++ b/packages/mana/accessbase_test.go @@ -14,7 +14,7 @@ var delta = 0.001 func TestUpdateBM2(t *testing.T) { t.Run("CASE: Zero values", func(t *testing.T) { bm := AccessBaseMana{} - + bm.Init() // 0 initial values, timely update should not change anything bm.updateBM2(time.Hour) assert.Equal(t, 0.0, bm.BaseValue()) @@ -22,7 +22,7 @@ func TestUpdateBM2(t *testing.T) { t.Run("CASE: Batch update", func(t *testing.T) { bm := AccessBaseMana{} - + bm.Init() // pledge BM2 at t = o bm.M.BaseValue = 1.0 bm.updateBM2(time.Hour * 6) @@ -31,6 +31,7 @@ func TestUpdateBM2(t *testing.T) { t.Run("CASE: Incremental update", func(t *testing.T) { bm := AccessBaseMana{} + bm.Init() // pledge BM2 at t = o bm.M.BaseValue = 1.0 @@ -45,6 +46,7 @@ func TestUpdateBM2(t *testing.T) { func TestUpdateEBM2CoeffEqual(t *testing.T) { t.Run("CASE: Zero values", func(t *testing.T) { bm := AccessBaseMana{} + bm.Init() // 0 initial values, timely update should not change anything bm.updateEBM2(time.Hour) @@ -53,6 +55,7 @@ func TestUpdateEBM2CoeffEqual(t *testing.T) { t.Run("CASE: Batch and incremental update", func(t *testing.T) { bmBatch := AccessBaseMana{} + bmBatch.Init() // first, let's calculate once on a 6 hour span // pledge BM2 at t = o @@ -62,6 +65,8 @@ func TestUpdateEBM2CoeffEqual(t *testing.T) { bmBatch.updateEBM2(time.Hour * 6) bmInc := AccessBaseMana{} + bmInc.Init() + // second, let's calculate the same but every hour // pledge BM2 at t = o bmInc.M.BaseValue = 1.0 @@ -78,6 +83,7 @@ func TestUpdateEBM2CoeffEqual(t *testing.T) { t.Run("CASE: Large durations BM2", func(t *testing.T) { bmBatch := AccessBaseMana{} + bmBatch.Init() // first, let's calculate once on a 6 hour span // pledge BM2 at t = o @@ -95,6 +101,8 @@ func TestUpdateEBM2CoeffEqual(t *testing.T) { t.Run("CASE: Large durations EBM2 Decay==emaCoeff2", func(t *testing.T) { bmBatch := AccessBaseMana{} + bmBatch.Init() + // pledge BM2 at t = o bmBatch.M.BaseValue = 1.0 bmBatch.M.EffectiveValue = 1.0 @@ -114,6 +122,8 @@ func TestUpdateEBM2CoeffEqual(t *testing.T) { }) t.Run("CASE: Large durations EBM2 Decay!=emaCoeff2", func(t *testing.T) { bmBatch := AccessBaseMana{} + bmBatch.Init() + SetCoefficients(0.00003209, 0.0057762265, 0.00003209) // pledge BM2 at t = o bmBatch.M.BaseValue = 1.0 diff --git a/packages/mana/accessbasevector.go b/packages/mana/accessbasevector.go index 87adc15ce2..7df6f63593 100644 --- a/packages/mana/accessbasevector.go +++ b/packages/mana/accessbasevector.go @@ -13,7 +13,7 @@ import ( // AccessBaseManaVector represents a base mana vector. type AccessBaseManaVector struct { - model.Model[accessBaseManaVectorModel] `serix:"0"` + model.Mutable[AccessBaseManaVector, *AccessBaseManaVector, accessBaseManaVectorModel] `serix:"0"` } type accessBaseManaVectorModel struct { @@ -74,7 +74,7 @@ func (a *AccessBaseManaVector) Book(txInfo *TxInfo) { pledgeNodeID := txInfo.PledgeID[a.Type()] if _, exist := a.M.Vector[pledgeNodeID]; !exist { // first time we see this node - a.M.Vector[pledgeNodeID] = &AccessBaseMana{} + a.M.Vector[pledgeNodeID] = NewAccessBaseMana(0, 0, time.Time{}) } // save it for proper event trigger oldMana := *a.M.Vector[pledgeNodeID] diff --git a/packages/mana/accessbasevector_test.go b/packages/mana/accessbasevector_test.go index 4995472276..6e65cdbe97 100644 --- a/packages/mana/accessbasevector_test.go +++ b/packages/mana/accessbasevector_test.go @@ -380,7 +380,7 @@ func TestAccessBaseManaVector_GetMana(t *testing.T) { mana, _, err := bmv.GetMana(randID) assert.Equal(t, 0.0, mana) assert.NoError(t, err) - bmv.SetMana(randID, &AccessBaseMana{}) + bmv.SetMana(randID, NewAccessBaseMana(0, 0, time.Time{})) mana, _, err = bmv.GetMana(randID) assert.Equal(t, 0.0, mana) assert.NoError(t, err) diff --git a/packages/mana/basevector.go b/packages/mana/basevector.go index b60ad4b0d1..c36b2c6600 100644 --- a/packages/mana/basevector.go +++ b/packages/mana/basevector.go @@ -48,13 +48,9 @@ type BaseManaVector interface { func NewBaseManaVector(vectorType Type) (BaseManaVector, error) { switch vectorType { case AccessMana: - return &AccessBaseManaVector{ - model.New(accessBaseManaVectorModel{Vector: make(map[identity.ID]*AccessBaseMana)}), - }, nil + return model.NewMutable[AccessBaseManaVector](&accessBaseManaVectorModel{Vector: make(map[identity.ID]*AccessBaseMana)}), nil case ConsensusMana: - return &ConsensusBaseManaVector{ - model.New(consensusBaseManaVectorModel{Vector: make(map[identity.ID]*ConsensusBaseMana)}), - }, nil + return model.NewMutable[ConsensusBaseManaVector](&consensusBaseManaVectorModel{Vector: make(map[identity.ID]*ConsensusBaseMana)}), nil default: return nil, errors.Errorf("error while creating base mana vector with type %d: %w", vectorType, ErrUnknownManaType) } diff --git a/packages/mana/consensusbase.go b/packages/mana/consensusbase.go index e4cec79bb3..b0f1bc1591 100644 --- a/packages/mana/consensusbase.go +++ b/packages/mana/consensusbase.go @@ -8,7 +8,7 @@ import ( // ConsensusBaseMana holds information about the consensus base mana values of a single node. type ConsensusBaseMana struct { - model.Model[consensusBaseManaModel] `serix:"0"` + model.Mutable[ConsensusBaseMana, *ConsensusBaseMana, consensusBaseManaModel] `serix:"0"` } type consensusBaseManaModel struct { @@ -16,9 +16,7 @@ type consensusBaseManaModel struct { } func NewConsensusBaseMana(baseMana float64) *ConsensusBaseMana { - return &ConsensusBaseMana{ - model.New(consensusBaseManaModel{BaseMana1: baseMana}), - } + return model.NewMutable[ConsensusBaseMana](&consensusBaseManaModel{BaseMana1: baseMana}) } func (c *ConsensusBaseMana) update(now time.Time) error { @@ -26,6 +24,8 @@ func (c *ConsensusBaseMana) update(now time.Time) error { } func (c *ConsensusBaseMana) revoke(amount float64) error { + c.Lock() + defer c.Unlock() //if c.BaseMana1-amount < 0.0 { // return ErrBaseManaNegative //} @@ -34,6 +34,8 @@ func (c *ConsensusBaseMana) revoke(amount float64) error { } func (c *ConsensusBaseMana) pledge(tx *TxInfo) (pledged float64) { + c.Lock() + defer c.Unlock() pledged = tx.sumInputs() c.M.BaseMana1 += pledged return pledged @@ -41,6 +43,8 @@ func (c *ConsensusBaseMana) pledge(tx *TxInfo) (pledged float64) { // BaseValue returns the base mana value (BM1). func (c *ConsensusBaseMana) BaseValue() float64 { + c.RLock() + defer c.RUnlock() return c.M.BaseMana1 } diff --git a/packages/mana/consensusbasevector.go b/packages/mana/consensusbasevector.go index 69682c44c4..e2a624b1f6 100644 --- a/packages/mana/consensusbasevector.go +++ b/packages/mana/consensusbasevector.go @@ -13,7 +13,7 @@ import ( // ConsensusBaseManaVector represents a base mana vector. type ConsensusBaseManaVector struct { - model.Model[consensusBaseManaVectorModel] `serix:"0"` + model.Mutable[ConsensusBaseManaVector, *ConsensusBaseManaVector, consensusBaseManaVectorModel] `serix:"0"` } type consensusBaseManaVectorModel struct { @@ -119,9 +119,7 @@ func (c *ConsensusBaseManaVector) LoadSnapshot(snapshot map[identity.ID]*Snapsho }) } - c.M.Vector[nodeID] = &ConsensusBaseMana{ - model.New(consensusBaseManaModel{BaseMana1: value}), - } + c.M.Vector[nodeID] = model.NewMutable[ConsensusBaseMana](&consensusBaseManaModel{BaseMana1: value}) } } @@ -142,6 +140,7 @@ func (c *ConsensusBaseManaVector) Book(txInfo *TxInfo) { if _, exist := c.M.Vector[oldPledgeNodeID]; !exist { // first time we see this node c.M.Vector[oldPledgeNodeID] = &ConsensusBaseMana{} + c.M.Vector[oldPledgeNodeID].Init() } // save old mana oldMana := *c.M.Vector[oldPledgeNodeID] @@ -158,7 +157,7 @@ func (c *ConsensusBaseManaVector) Book(txInfo *TxInfo) { newPledgeNodeID := txInfo.PledgeID[c.Type()] if _, exist := c.M.Vector[newPledgeNodeID]; !exist { // first time we see this node - c.M.Vector[newPledgeNodeID] = &ConsensusBaseMana{} + c.M.Vector[newPledgeNodeID] = NewConsensusBaseMana(0) } // save it for proper event trigger oldMana := *c.M.Vector[newPledgeNodeID] @@ -354,9 +353,7 @@ func (c *ConsensusBaseManaVector) FromPersistable(p *PersistableBaseMana) (err e } c.Lock() defer c.Unlock() - c.M.Vector[p.NodeID()] = &ConsensusBaseMana{ - model.New(consensusBaseManaModel{BaseMana1: p.BaseValues()[0]}), - } + c.M.Vector[p.NodeID()] = model.NewMutable[ConsensusBaseMana](&consensusBaseManaModel{BaseMana1: p.BaseValues()[0]}) return } diff --git a/packages/mana/consensusbasevector_test.go b/packages/mana/consensusbasevector_test.go index cc14808e57..41e8f4c7b8 100644 --- a/packages/mana/consensusbasevector_test.go +++ b/packages/mana/consensusbasevector_test.go @@ -42,7 +42,7 @@ func TestConsensusBaseManaVector_Has(t *testing.T) { has := bmv.Has(randID) assert.False(t, has) - bmv.SetMana(randID, &ConsensusBaseMana{}) + bmv.SetMana(randID, NewConsensusBaseMana(0)) has = bmv.Has(randID) assert.True(t, has) } @@ -136,7 +136,7 @@ func TestConsensusBaseManaVector_GetMana(t *testing.T) { assert.Equal(t, 0.0, mana) assert.Error(t, err) - bmv.SetMana(randID, &ConsensusBaseMana{}) + bmv.SetMana(randID, NewConsensusBaseMana(0)) mana, _, err = bmv.GetMana(randID) assert.NoError(t, err) assert.Equal(t, 0.0, mana) diff --git a/packages/mana/persistablebase.go b/packages/mana/persistablebase.go index 8b2c9b9c8b..d8b2e126ab 100644 --- a/packages/mana/persistablebase.go +++ b/packages/mana/persistablebase.go @@ -9,7 +9,7 @@ import ( // PersistableBaseMana represents a base mana vector that can be persisted. type PersistableBaseMana struct { - model.Storable[identity.ID, persistableBaseManaModel] `serix:"0"` + model.Storable[identity.ID, PersistableBaseMana, *PersistableBaseMana, persistableBaseManaModel] `serix:"0"` } type persistableBaseManaModel struct { @@ -20,16 +20,14 @@ type persistableBaseManaModel struct { } func NewPersistableBaseMana(nodeID identity.ID, manaType Type, baseValues, effectiveValues []float64, lastUpdated time.Time) *PersistableBaseMana { - persistableBaseMana := &PersistableBaseMana{ - model.NewStorable[identity.ID, persistableBaseManaModel]( - persistableBaseManaModel{ - ManaType: manaType, - BaseValues: baseValues, - EffectiveValues: effectiveValues, - LastUpdated: lastUpdated, - }, - ), - } + persistableBaseMana := model.NewStorable[identity.ID, PersistableBaseMana]( + &persistableBaseManaModel{ + ManaType: manaType, + BaseValues: baseValues, + EffectiveValues: effectiveValues, + LastUpdated: lastUpdated, + }, + ) persistableBaseMana.SetID(nodeID) return persistableBaseMana } diff --git a/packages/markers/models.go b/packages/markers/models.go index 89e1f8d44a..04ffe89b03 100644 --- a/packages/markers/models.go +++ b/packages/markers/models.go @@ -44,7 +44,7 @@ type IncreaseIndexCallback func(sequenceID SequenceID, currentHighestIndex Index // Marker represents a coordinate in a Sequence that is identified by an ever-increasing Index. type Marker struct { - model.Immutable[markerModel] `serix:"0"` + model.Immutable[Marker, *Marker, markerModel] `serix:"0"` } // markerModel contains the data of a Marker. @@ -55,10 +55,10 @@ type markerModel struct { // NewMarker returns a new marker. func NewMarker(sequenceID SequenceID, index Index) Marker { - return Marker{model.NewImmutable(markerModel{ + return *model.NewImmutable[Marker](&markerModel{ SequenceID: sequenceID, Index: index, - })} + }) } // SequenceID returns the identifier of the Sequence of the Marker. @@ -73,7 +73,7 @@ func (m Marker) Index() (index Index) { // Bytes returns a serialized version of the Marker. func (m Marker) Bytes() (serialized []byte) { - return lo.PanicOnErr(serix.DefaultAPI.Encode(context.Background(), m.M)) + return lo.PanicOnErr(m.Immutable.Bytes()) } // endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -82,7 +82,7 @@ func (m Marker) Bytes() (serialized []byte) { // Markers represents a collection of Markers that can contain exactly one Index per SequenceID. type Markers struct { - model.Model[markersModel] `serix:"0"` + model.Mutable[Markers, *Markers, markersModel] `serix:"0"` } type markersModel struct { @@ -93,9 +93,9 @@ type markersModel struct { // NewMarkers creates a new collection of Markers. func NewMarkers(markers ...Marker) (new *Markers) { - new = &Markers{model.New(markersModel{ + new = model.NewMutable[Markers](&markersModel{ Markers: make(map[SequenceID]Index), - })} + }) for _, marker := range markers { new.Set(marker.SequenceID(), marker.Index()) @@ -325,7 +325,7 @@ func (m *Markers) Bytes() []byte { // ReferencingMarkers is a data structure that allows to denote which Markers of child Sequences in the Sequence DAG // reference a given Marker in a Sequence. type ReferencingMarkers struct { - model.Model[referencingMarkersModel] `serix:"0"` + model.Mutable[ReferencingMarkers, *ReferencingMarkers, referencingMarkersModel] `serix:"0"` } type referencingMarkersModel struct { @@ -334,9 +334,9 @@ type referencingMarkersModel struct { // NewReferencingMarkers is the constructor for the ReferencingMarkers. func NewReferencingMarkers() (referencingMarkers *ReferencingMarkers) { - return &ReferencingMarkers{model.New(referencingMarkersModel{ + return model.NewMutable[ReferencingMarkers](&referencingMarkersModel{ ReferencingIndexesBySequence: make(map[SequenceID]*thresholdmap.ThresholdMap[uint64, Index]), - })} + }) } // Add adds a new referencing Marker to the ReferencingMarkers. @@ -432,7 +432,7 @@ func (r *ReferencingMarkers) String() (humanReadableReferencingMarkers string) { // ReferencedMarkers is a data structure that allows to denote which Marker of a Sequence references which other Markers // of its parent Sequences in the Sequence DAG. type ReferencedMarkers struct { - model.Model[referencedMarkersModel] `serix:"0"` + model.Mutable[ReferencedMarkers, *ReferencedMarkers, referencedMarkersModel] `serix:"0"` } type referencedMarkersModel struct { ReferencedIndexesBySequence map[SequenceID]*thresholdmap.ThresholdMap[uint64, Index] `serix:"0,lengthPrefixType=uint32"` @@ -440,9 +440,9 @@ type referencedMarkersModel struct { // NewReferencedMarkers is the constructor for the ReferencedMarkers. func NewReferencedMarkers(markers *Markers) (new *ReferencedMarkers) { - new = &ReferencedMarkers{model.New(referencedMarkersModel{ + new = model.NewMutable[ReferencedMarkers](&referencedMarkersModel{ ReferencedIndexesBySequence: make(map[SequenceID]*thresholdmap.ThresholdMap[uint64, Index]), - })} + }) initialSequenceIndex := markers.HighestIndex() + 1 markers.ForEach(func(sequenceID SequenceID, index Index) bool { @@ -554,7 +554,7 @@ func (r *ReferencedMarkers) String() (humanReadableReferencedMarkers string) { // Sequence represents a set of ever-increasing Indexes that are encapsulating a certain part of the DAG. type Sequence struct { - model.Storable[SequenceID, sequenceModel] `serix:"0"` + model.Storable[SequenceID, Sequence, *Sequence, sequenceModel] `serix:"0"` } type sequenceModel struct { @@ -571,12 +571,12 @@ func NewSequence(id SequenceID, referencedMarkers *Markers) (new *Sequence) { initialIndex-- } - new = &Sequence{model.NewStorable[SequenceID](sequenceModel{ + new = model.NewStorable[SequenceID, Sequence](&sequenceModel{ ReferencedMarkers: NewReferencedMarkers(referencedMarkers), ReferencingMarkers: NewReferencingMarkers(), LowestIndex: initialIndex, HighestIndex: initialIndex, - })} + }) new.SetID(id) return new @@ -747,7 +747,7 @@ func (s SequenceIDs) String() (humanReadableSequenceIDs string) { // StructureDetails represents a container for the complete Marker related information of a node in a DAG that are used // to interact with the public API of this package. type StructureDetails struct { - model.Model[structureDetailsModel] `serix:"0"` + model.Mutable[StructureDetails, *StructureDetails, structureDetailsModel] `serix:"0"` } type structureDetailsModel struct { @@ -759,9 +759,9 @@ type structureDetailsModel struct { // NewStructureDetails creates an empty StructureDetails object. func NewStructureDetails() (newStructureDetails *StructureDetails) { - return &StructureDetails{model.New(structureDetailsModel{ + return model.NewMutable[StructureDetails](&structureDetailsModel{ PastMarkers: NewMarkers(), - })} + }) } func (m *StructureDetails) Rank() (rank uint64) { @@ -822,12 +822,12 @@ func (m *StructureDetails) SetPastMarkers(pastMarkers *Markers) { // Clone creates a deep copy of the StructureDetails. func (m *StructureDetails) Clone() (clone *StructureDetails) { - return &StructureDetails{model.New(structureDetailsModel{ + return model.NewMutable[StructureDetails](&structureDetailsModel{ Rank: m.Rank(), PastMarkerGap: m.PastMarkerGap(), IsPastMarker: m.IsPastMarker(), PastMarkers: m.PastMarkers().Clone(), - })} + }) } // endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/packages/tangle/approvalweightmanager.models.go b/packages/tangle/approvalweightmanager.models.go index b82a33477f..013c8c67ff 100644 --- a/packages/tangle/approvalweightmanager.models.go +++ b/packages/tangle/approvalweightmanager.models.go @@ -22,14 +22,13 @@ import ( // BranchWeight is a data structure that tracks the weight of a BranchID. type BranchWeight struct { - model.Storable[utxo.TransactionID, float64] `serix:"0"` + model.Storable[utxo.TransactionID, BranchWeight, *BranchWeight, float64] `serix:"0"` } // NewBranchWeight creates a new BranchWeight. func NewBranchWeight(branchID utxo.TransactionID) (branchWeight *BranchWeight) { - branchWeight = &BranchWeight{ - model.NewStorable[utxo.TransactionID, float64](0.0), - } + weight := 0.0 + branchWeight = model.NewStorable[utxo.TransactionID, BranchWeight](&weight) branchWeight.SetID(branchID) return } @@ -99,16 +98,6 @@ func (v *Voters) Clone() (clonedVoters *Voters) { return } -// Encode returns a serialized byte slice of the object. -func (v *Voters) Encode() ([]byte, error) { - objBytes, err := serix.DefaultAPI.Encode(context.Background(), v.Set, serix.WithValidation()) - if err != nil { - // TODO: what do? - panic(err) - } - return objBytes, nil -} - // Decode deserializes bytes into a valid object. func (v *Voters) Decode(data []byte) (bytesRead int, err error) { @@ -148,14 +137,12 @@ func (v *Voters) String() string { // BranchVoters is a data structure that tracks which nodes support a branch. type BranchVoters struct { - model.Storable[utxo.TransactionID, *Voters] `serix:"0"` + model.Storable[utxo.TransactionID, BranchVoters, *BranchVoters, Voters] `serix:"0"` } // NewBranchVoters is the constructor for the BranchVoters object. func NewBranchVoters(branchID utxo.TransactionID) (branchVoters *BranchVoters) { - branchVoters = &BranchVoters{ - model.NewStorable[utxo.TransactionID, *Voters](NewVoters()), - } + branchVoters = model.NewStorable[utxo.TransactionID, BranchVoters](NewVoters()) branchVoters.SetID(branchID) return } @@ -242,18 +229,6 @@ const ( // endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// -// region latestMarkerVotesMap ///////////////////////////////////////////////////////////////////////////////////////// - -type latestMarkerVotesMap struct { - thresholdmap.ThresholdMap[markers.Index, VotePower] -} - -func newLatestMarkerVotesMap() *latestMarkerVotesMap { - return &latestMarkerVotesMap{*thresholdmap.New[markers.Index, VotePower](thresholdmap.UpperThresholdMode)} -} - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// - // region LatestMarkerVotes //////////////////////////////////////////////////////////////////////////////////////////// // VotePower is used to establish an absolute order of votes, regardless of their arrival order. @@ -270,14 +245,14 @@ var LatestMarkerVotesKeyPartition = objectstorage.PartitionKey(markers.SequenceI // Due to the nature of a Sequence, a vote casted for a certain Index clobbers votes for every lower index. // Similarly, if a vote for an Index is casted and an existing vote for an higher Index exists, the operation has no effect. type LatestMarkerVotes struct { - model.StorableReferenceWithMetadata[markers.SequenceID, Voter, latestMarkerVotesMap] `serix:"0"` + model.StorableReferenceWithMetadata[LatestMarkerVotes, *LatestMarkerVotes, markers.SequenceID, Voter, thresholdmap.ThresholdMap[markers.Index, VotePower]] `serix:"0"` } // NewLatestMarkerVotes creates a new NewLatestMarkerVotes instance associated with the given details. func NewLatestMarkerVotes(sequenceID markers.SequenceID, voter Voter) (newLatestMarkerVotes *LatestMarkerVotes) { - newLatestMarkerVotes = &LatestMarkerVotes{model.NewStorableReferenceWithMetadata[markers.SequenceID, Voter, latestMarkerVotesMap]( - sequenceID, voter, *newLatestMarkerVotesMap(), - )} + newLatestMarkerVotes = model.NewStorableReferenceWithMetadata[LatestMarkerVotes]( + sequenceID, voter, thresholdmap.New[markers.Index, VotePower](thresholdmap.UpperThresholdMode), + ) return } @@ -286,7 +261,7 @@ func NewLatestMarkerVotes(sequenceID markers.SequenceID, voter Voter) (newLatest func (l *LatestMarkerVotes) Voter() Voter { l.RLock() defer l.RUnlock() - return l.TargetID + return l.TargetID() } // Power returns the power of the vote for the given marker Index. @@ -357,7 +332,7 @@ func (c CachedLatestMarkerVotesByVoter) Consume(consumer func(latestMarkerVotes // LatestBranchVotes represents the branch supported from an Issuer. type LatestBranchVotes struct { - model.Storable[Voter, latestBranchVotesModel] `serix:"0"` + model.Storable[Voter, LatestBranchVotes, *LatestBranchVotes, latestBranchVotesModel] `serix:"0"` } type latestBranchVotesModel struct { @@ -366,13 +341,11 @@ type latestBranchVotesModel struct { // NewLatestBranchVotes creates a new LatestBranchVotes. func NewLatestBranchVotes(voter Voter) (latestBranchVotes *LatestBranchVotes) { - latestBranchVotes = &LatestBranchVotes{ - model.NewStorable[Voter, latestBranchVotesModel]( - latestBranchVotesModel{ - LatestBranchVotes: make(map[utxo.TransactionID]*BranchVote), - }, - ), - } + latestBranchVotes = model.NewStorable[Voter, LatestBranchVotes]( + &latestBranchVotesModel{ + LatestBranchVotes: make(map[utxo.TransactionID]*BranchVote), + }, + ) latestBranchVotes.SetID(voter) return } @@ -408,7 +381,7 @@ func (l *LatestBranchVotes) Store(vote *BranchVote) (stored bool) { // BranchVote represents a struct that holds information about what Opinion a certain Voter has on a Branch. type BranchVote struct { - model.Model[branchVoteModel] `serix:"0"` + model.Mutable[BranchVote, *BranchVote, branchVoteModel] `serix:"0"` } type branchVoteModel struct { @@ -420,48 +393,42 @@ type branchVoteModel struct { // NewBranchVote derives a vote for th. func NewBranchVote(voter Voter, votePower VotePower, branchID utxo.TransactionID, opinion Opinion) (voteWithOpinion *BranchVote) { - return &BranchVote{ - model.New[branchVoteModel]( - branchVoteModel{ - Voter: voter, - VotePower: votePower, - BranchID: branchID, - Opinion: opinion, - }, - ), - } + return model.NewMutable[BranchVote]( + &branchVoteModel{ + Voter: voter, + VotePower: votePower, + BranchID: branchID, + Opinion: opinion, + }, + ) } // WithOpinion derives a vote for the given Opinion. func (v *BranchVote) WithOpinion(opinion Opinion) (voteWithOpinion *BranchVote) { v.RLock() defer v.RUnlock() - return &BranchVote{ - model.New[branchVoteModel]( - branchVoteModel{ - Voter: v.M.Voter, - BranchID: v.M.BranchID, - Opinion: opinion, - VotePower: v.M.VotePower, - }, - ), - } + return model.NewMutable[BranchVote]( + &branchVoteModel{ + Voter: v.M.Voter, + BranchID: v.M.BranchID, + Opinion: opinion, + VotePower: v.M.VotePower, + }, + ) } // WithBranchID derives a vote for the given BranchID. func (v *BranchVote) WithBranchID(branchID utxo.TransactionID) (rejectedVote *BranchVote) { v.RLock() defer v.RUnlock() - return &BranchVote{ - model.New[branchVoteModel]( - branchVoteModel{ - Voter: v.M.Voter, - BranchID: branchID, - Opinion: v.M.Opinion, - VotePower: v.M.VotePower, - }, - ), - } + return model.NewMutable[BranchVote]( + &branchVoteModel{ + Voter: v.M.Voter, + BranchID: branchID, + Opinion: v.M.Opinion, + VotePower: v.M.VotePower, + }, + ) } func (v *BranchVote) Voter() Voter { diff --git a/packages/tangle/booker_test.go b/packages/tangle/booker_test.go index 6215858b6e..07f0d0ee5e 100644 --- a/packages/tangle/booker_test.go +++ b/packages/tangle/booker_test.go @@ -3937,6 +3937,7 @@ func TestFutureConeDislike(t *testing.T) { } func TestMultiThreadedBookingAndForkingParallel(t *testing.T) { + debug.SetEnabled(true) const layersNum = 127 const widthSize = 8 // since we reference all messages in the layer below, this is limited by the max parents diff --git a/packages/tangle/markersmanager.models.go b/packages/tangle/markersmanager.models.go index ce1637ae9f..79bcacca2e 100644 --- a/packages/tangle/markersmanager.models.go +++ b/packages/tangle/markersmanager.models.go @@ -14,11 +14,11 @@ import ( // region markerIndexBranchIDMap ///////////////////////////////////////////////////////////////////////////////////////// type markerIndexBranchIDMap struct { - thresholdmap.ThresholdMap[markers.Index, utxo.TransactionIDs] `serix:"0"` + T *thresholdmap.ThresholdMap[markers.Index, utxo.TransactionIDs] `serix:"0"` } func newMarkerIndexBranchIDMap() *markerIndexBranchIDMap { - return &markerIndexBranchIDMap{*thresholdmap.New[markers.Index, utxo.TransactionIDs](thresholdmap.LowerThresholdMode)} + return &markerIndexBranchIDMap{thresholdmap.New[markers.Index, utxo.TransactionIDs](thresholdmap.LowerThresholdMode)} } // endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -27,16 +27,14 @@ func newMarkerIndexBranchIDMap() *markerIndexBranchIDMap { // MarkerIndexBranchIDMapping is a data structure that allows to map marker Indexes to a BranchID. type MarkerIndexBranchIDMapping struct { - model.Storable[markers.SequenceID, *markerIndexBranchIDMap] `serix:"0"` + model.Storable[markers.SequenceID, MarkerIndexBranchIDMapping, *MarkerIndexBranchIDMapping, markerIndexBranchIDMap] `serix:"0"` } // NewMarkerIndexBranchIDMapping creates a new MarkerIndexBranchIDMapping for the given SequenceID. func NewMarkerIndexBranchIDMapping(sequenceID markers.SequenceID) (markerBranchMapping *MarkerIndexBranchIDMapping) { - markerBranchMapping = &MarkerIndexBranchIDMapping{ - model.NewStorable[markers.SequenceID, *markerIndexBranchIDMap]( - newMarkerIndexBranchIDMap(), - ), - } + markerBranchMapping = model.NewStorable[markers.SequenceID, MarkerIndexBranchIDMapping]( + newMarkerIndexBranchIDMap(), + ) markerBranchMapping.SetID(sequenceID) return } @@ -51,7 +49,7 @@ func (m *MarkerIndexBranchIDMapping) BranchIDs(markerIndex markers.Index) (branc m.RLock() defer m.RUnlock() - value, exists := m.M.Get(markerIndex) + value, exists := m.M.T.Get(markerIndex) if !exists { panic(fmt.Sprintf("tried to retrieve the BranchID of unknown marker.%s", markerIndex)) } @@ -64,7 +62,7 @@ func (m *MarkerIndexBranchIDMapping) SetBranchIDs(index markers.Index, branchIDs m.Lock() defer m.Unlock() - m.M.Set(index, branchIDs) + m.M.T.Set(index, branchIDs) m.SetModified() } @@ -73,7 +71,7 @@ func (m *MarkerIndexBranchIDMapping) DeleteBranchID(index markers.Index) { m.Lock() defer m.Unlock() - m.M.Delete(index) + m.M.T.Delete(index) m.SetModified() } @@ -83,7 +81,7 @@ func (m *MarkerIndexBranchIDMapping) Floor(index markers.Index) (marker markers. m.RLock() defer m.RUnlock() - if untypedIndex, untypedBranchIDs, exists := m.M.Floor(index); exists { + if untypedIndex, untypedBranchIDs, exists := m.M.T.Floor(index); exists { return untypedIndex, untypedBranchIDs, true } @@ -96,7 +94,7 @@ func (m *MarkerIndexBranchIDMapping) Ceiling(index markers.Index) (marker marker m.RLock() defer m.RUnlock() - if untypedIndex, untypedBranchIDs, exists := m.M.Ceiling(index); exists { + if untypedIndex, untypedBranchIDs, exists := m.M.T.Ceiling(index); exists { return untypedIndex, untypedBranchIDs, true } @@ -113,14 +111,12 @@ var MarkerMessageMappingPartitionKeys = objectstorage.PartitionKey(markers.Seque // MarkerMessageMapping is a data structure that denotes a mapping from a Marker to a Message. type MarkerMessageMapping struct { - model.Storable[markers.Marker, MessageID] `serix:"0"` + model.Storable[markers.Marker, MarkerMessageMapping, *MarkerMessageMapping, MessageID] `serix:"0"` } // NewMarkerMessageMapping is the constructor for the MarkerMessageMapping. func NewMarkerMessageMapping(marker markers.Marker, messageID MessageID) *MarkerMessageMapping { - markerMessageMapping := &MarkerMessageMapping{ - model.NewStorable[markers.Marker, MessageID](messageID), - } + markerMessageMapping := model.NewStorable[markers.Marker, MarkerMessageMapping](&messageID) markerMessageMapping.SetID(marker) return markerMessageMapping } diff --git a/packages/tangle/message.go b/packages/tangle/message.go index 5c5718fb4c..c09e77fc27 100644 --- a/packages/tangle/message.go +++ b/packages/tangle/message.go @@ -289,8 +289,8 @@ const ( // Message represents the core message for the base layer Tangle. type Message struct { - model.Storable[MessageID, MessageModel] `serix:"0"` - payload payload.Payload + model.Storable[MessageID, Message, *Message, MessageModel] `serix:"0"` + payload payload.Payload } type MessageModel struct { // core properties (get sent over the wire) @@ -311,19 +311,17 @@ func NewMessage(references ParentMessageIDs, issuingTime time.Time, issuerPublic if len(versionOpt) == 1 { version = versionOpt[0] } - msg := &Message{ - Storable: model.NewStorable[MessageID, MessageModel](MessageModel{ - Version: version, - Parents: references, - IssuerPublicKey: issuerPublicKey, - IssuingTime: issuingTime, - SequenceNumber: sequenceNumber, - PayloadBytes: msgPayload.Bytes(), - Nonce: nonce, - Signature: signature, - }), - payload: msgPayload, - } + msg := model.NewStorable[MessageID, Message](&MessageModel{ + Version: version, + Parents: references, + IssuerPublicKey: issuerPublicKey, + IssuingTime: issuingTime, + SequenceNumber: sequenceNumber, + PayloadBytes: lo.PanicOnErr(msgPayload.Bytes()), + Nonce: nonce, + Signature: signature, + }) + msg.payload = msgPayload return msg } @@ -595,7 +593,7 @@ func (p ParentMessageIDs) Clone() ParentMessageIDs { // MessageMetadata defines the metadata for a message. type MessageMetadata struct { - model.Storable[MessageID, messageMetadataModel] `serix:"0"` + model.Storable[MessageID, MessageMetadata, *MessageMetadata, messageMetadataModel] `serix:"0"` } type messageMetadataModel struct { @@ -619,14 +617,14 @@ type messageMetadataModel struct { // NewMessageMetadata creates a new MessageMetadata from the specified messageID. func NewMessageMetadata(messageID MessageID) *MessageMetadata { - meta := &MessageMetadata{model.NewStorable[MessageID](messageMetadataModel{ + metadata := model.NewStorable[MessageID, MessageMetadata](&messageMetadataModel{ ReceivedTime: clock.SyncedTime(), AddedBranchIDs: utxo.NewTransactionIDs(), SubtractedBranchIDs: utxo.NewTransactionIDs(), - })} - meta.SetID(messageID) + }) + metadata.SetID(messageID) - return meta + return metadata } // ReceivedTime returns the time when the message was received. diff --git a/packages/tangle/message_test.go b/packages/tangle/message_test.go index 3fe9433abf..f3ff3467b8 100644 --- a/packages/tangle/message_test.go +++ b/packages/tangle/message_test.go @@ -677,5 +677,5 @@ func (w wl) publicKey() ed25519.PublicKey { } func (w wl) sign(txEssence *devnetvm.TransactionEssence) *devnetvm.ED25519Signature { - return devnetvm.NewED25519Signature(w.publicKey(), w.privateKey().Sign(txEssence.Bytes())) + return devnetvm.NewED25519Signature(w.publicKey(), w.privateKey().Sign(lo.PanicOnErr(txEssence.Bytes()))) } diff --git a/packages/tangle/messagefactory.go b/packages/tangle/messagefactory.go index 36aff921be..a5f0a57b78 100644 --- a/packages/tangle/messagefactory.go +++ b/packages/tangle/messagefactory.go @@ -80,7 +80,14 @@ func (f *MessageFactory) IssuePayloadWithReferences(p payload.Payload, reference // It also triggers the MessageConstructed event once it's done, which is for example used by the plugins to listen for // messages that shall be attached to the tangle. func (f *MessageFactory) issuePayload(p payload.Payload, references ParentMessageIDs, parentsCount ...int) (*Message, error) { - payloadLen := len(p.Bytes()) + payloadBytes, err := p.Bytes() + if err != nil { + err = errors.Errorf("could not serialize payload: %w", err) + f.Events.Error.Trigger(err) + return nil, err + } + + payloadLen := len(payloadBytes) if payloadLen > payload.MaxSize { err := fmt.Errorf("maximum payload size of %d bytes exceeded", payloadLen) f.Events.Error.Trigger(err) diff --git a/packages/tangle/messagefactory_test.go b/packages/tangle/messagefactory_test.go index 3f0e520b8a..1f2acc6965 100644 --- a/packages/tangle/messagefactory_test.go +++ b/packages/tangle/messagefactory_test.go @@ -29,6 +29,9 @@ const ( ) func TestMessageFactory_BuildMessage(t *testing.T) { + if testing.Short() { + t.Skip("skipping test in short mode.") + } selfLocalIdentity := identity.GenerateLocalIdentity() tangle := NewTestTangle(Identity(selfLocalIdentity)) defer tangle.Shutdown() diff --git a/packages/tangle/parser_test.go b/packages/tangle/parser_test.go index 48c29b794e..337f281ca1 100644 --- a/packages/tangle/parser_test.go +++ b/packages/tangle/parser_test.go @@ -8,6 +8,7 @@ import ( "github.com/cockroachdb/errors" "github.com/iotaledger/hive.go/autopeering/peer" + "github.com/iotaledger/hive.go/crypto/ed25519" "github.com/iotaledger/hive.go/generics/event" "github.com/iotaledger/hive.go/generics/lo" "github.com/iotaledger/hive.go/identity" @@ -17,6 +18,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "github.com/iotaledger/goshimmer/packages/ledger/utxo" "github.com/iotaledger/goshimmer/packages/ledger/vm/devnetvm" "github.com/iotaledger/goshimmer/packages/pow" "github.com/iotaledger/goshimmer/packages/tangle/payload" @@ -77,6 +79,8 @@ func TestTransactionFilter_Filter(t *testing.T) { t.Run("skip non-transaction payloads", func(t *testing.T) { msg := &Message{} + msg.Init() + msg.payload = payload.NewGenericDataPayload([]byte("hello world")) m.On("Accept", msg, testPeer) filter.Filter(msg, testPeer) @@ -85,6 +89,8 @@ func TestTransactionFilter_Filter(t *testing.T) { func Test_isMessageAndTransactionTimestampsValid(t *testing.T) { msg := &Message{} + msg.Init() + t.Run("older tx timestamp within limit", func(t *testing.T) { tx := newTransaction(time.Now()) msg.M.IssuingTime = tx.Essence().Timestamp().Add(1 * time.Second) @@ -168,10 +174,17 @@ func (p *testTxPayload) Bytes() []byte { func (p *testTxPayload) String() string { return "tx" } func newTransaction(t time.Time) *devnetvm.Transaction { - ID, _ := identity.RandomID() - var inputs devnetvm.Inputs - var outputs devnetvm.Outputs - essence := devnetvm.NewTransactionEssence(1, t, ID, ID, inputs, outputs) - var unlockBlocks devnetvm.UnlockBlocks + issuerKeyPair := ed25519.GenerateKeyPair() + issuerIdentity := identity.New(issuerKeyPair.PublicKey) + inputs := devnetvm.NewInputs( + devnetvm.NewUTXOInput(utxo.NewOutputID(utxo.TransactionID{}, 0)), + ) + outputs := devnetvm.NewOutputs( + devnetvm.NewSigLockedSingleOutput(12, devnetvm.NewED25519Address(issuerKeyPair.PublicKey)), + ) + essence := devnetvm.NewTransactionEssence(0, t, issuerIdentity.ID(), issuerIdentity.ID(), inputs, outputs) + unlockBlocks := devnetvm.UnlockBlocks{ + devnetvm.NewSignatureUnlockBlock(devnetvm.NewED25519Signature(issuerKeyPair.PublicKey, issuerKeyPair.PrivateKey.Sign(lo.PanicOnErr(essence.Bytes())))), + } return devnetvm.NewTransaction(essence, unlockBlocks) } diff --git a/packages/tangle/payload/data.go b/packages/tangle/payload/data.go index 607021a828..77f5a5d699 100644 --- a/packages/tangle/payload/data.go +++ b/packages/tangle/payload/data.go @@ -1,12 +1,10 @@ package payload import ( - "context" "fmt" - "github.com/cockroachdb/errors" + "github.com/iotaledger/hive.go/generics/model" "github.com/iotaledger/hive.go/serix" - "github.com/iotaledger/hive.go/stringify" ) func init() { @@ -26,58 +24,25 @@ var GenericDataPayloadType = NewType(0, "GenericDataPayloadType") // GenericDataPayload represents a payload which just contains a blob of data. type GenericDataPayload struct { - genericDataPayloadInner `serix:"0"` + model.Immutable[GenericDataPayload, *GenericDataPayload, genericDataPayloadInner] `serix:"0"` } type genericDataPayloadInner struct { - payloadType Type - Data []byte `serix:"0,lengthPrefixType=uint32"` + Data []byte `serix:"0,lengthPrefixType=uint32"` } // NewGenericDataPayload creates new GenericDataPayload. func NewGenericDataPayload(data []byte) *GenericDataPayload { - return &GenericDataPayload{genericDataPayloadInner{ - payloadType: GenericDataPayloadType, - Data: data, - }} -} - -// GenericDataPayloadFromBytes unmarshals a GenericDataPayload from a sequence of bytes. -func GenericDataPayloadFromBytes(bytes []byte) (genericDataPayload *GenericDataPayload, consumedBytes int, err error) { - genericDataPayload = new(GenericDataPayload) - - consumedBytes, err = serix.DefaultAPI.Decode(context.Background(), bytes, genericDataPayload, serix.WithValidation()) - if err != nil { - err = errors.Errorf("failed to parse GenericDataPayload: %w", err) - return - } - - return + return model.NewImmutable[GenericDataPayload](&genericDataPayloadInner{ + Data: data, + }) } // Type returns the Type of the Payload. func (g *GenericDataPayload) Type() Type { - return g.payloadType + return GenericDataPayloadType } // Blob returns the contained data of the GenericDataPayload (without its type and size headers). func (g *GenericDataPayload) Blob() []byte { - return g.Data -} - -// Bytes returns a marshaled version of the Payload. -func (g *GenericDataPayload) Bytes() []byte { - objBytes, err := serix.DefaultAPI.Encode(context.Background(), g, serix.WithValidation()) - if err != nil { - // TODO: what do? - panic(err) - } - return objBytes -} - -// String returns a human readable version of the Payload. -func (g *GenericDataPayload) String() string { - return stringify.Struct("GenericDataPayload", - stringify.StructField("type", g.Type()), - stringify.StructField("blob", g.Blob()), - ) + return g.M.Data } diff --git a/packages/tangle/payload/payload.go b/packages/tangle/payload/payload.go index f251efa07c..20bdd4c6e5 100644 --- a/packages/tangle/payload/payload.go +++ b/packages/tangle/payload/payload.go @@ -19,7 +19,7 @@ type Payload interface { Type() Type // Bytes returns a marshaled version of the Payload. - Bytes() []byte + Bytes() ([]byte, error) // String returns a human readable version of the Payload. String() string diff --git a/packages/tangle/scheduler_test.go b/packages/tangle/scheduler_test.go index 3f0111a7fc..9520ca0777 100644 --- a/packages/tangle/scheduler_test.go +++ b/packages/tangle/scheduler_test.go @@ -20,6 +20,9 @@ import ( // region Scheduler_test ///////////////////////////////////////////////////////////////////////////////////////////// func TestScheduler_StartStop(t *testing.T) { + if testing.Short() { + t.Skip("skipping test in short mode.") + } tangle := NewTestTangle(Identity(selfLocalIdentity)) defer tangle.Shutdown() tangle.Scheduler.Start() diff --git a/packages/tangle/storage.go b/packages/tangle/storage.go index d2b2ade66b..4a62f722e8 100644 --- a/packages/tangle/storage.go +++ b/packages/tangle/storage.go @@ -91,7 +91,7 @@ func NewStorage(tangle *Tangle) (storage *Storage) { messageMetadataStorage: objectstorage.NewStructStorage[MessageMetadata](objectstorage.NewStoreWithRealm(tangle.Options.Store, database.PrefixTangle, PrefixMessageMetadata), cacheProvider.CacheTime(cacheTime), objectstorage.LeakDetectionEnabled(false)), approverStorage: objectstorage.NewStructStorage[Approver](objectstorage.NewStoreWithRealm(tangle.Options.Store, database.PrefixTangle, PrefixApprovers), cacheProvider.CacheTime(cacheTime), objectstorage.PartitionKey(MessageIDLength, ApproverTypeLength, MessageIDLength), objectstorage.LeakDetectionEnabled(false), objectstorage.StoreOnCreation(true)), missingMessageStorage: objectstorage.NewStructStorage[MissingMessage](objectstorage.NewStoreWithRealm(tangle.Options.Store, database.PrefixTangle, PrefixMissingMessage), cacheProvider.CacheTime(cacheTime), objectstorage.LeakDetectionEnabled(false), objectstorage.StoreOnCreation(true)), - attachmentStorage: objectstorage.NewStructStorage[Attachment](objectstorage.NewStoreWithRealm(tangle.Options.Store, database.PrefixTangle, PrefixAttachments), cacheProvider.CacheTime(cacheTime), objectstorage.PartitionKey(Attachment{}.KeyPartitions()...), objectstorage.LeakDetectionEnabled(false), objectstorage.StoreOnCreation(true)), + attachmentStorage: objectstorage.NewStructStorage[Attachment](objectstorage.NewStoreWithRealm(tangle.Options.Store, database.PrefixTangle, PrefixAttachments), cacheProvider.CacheTime(cacheTime), objectstorage.PartitionKey(new(Attachment).KeyPartitions()...), objectstorage.LeakDetectionEnabled(false), objectstorage.StoreOnCreation(true)), markerIndexBranchIDMappingStorage: objectstorage.NewStructStorage[MarkerIndexBranchIDMapping](objectstorage.NewStoreWithRealm(tangle.Options.Store, database.PrefixTangle, PrefixMarkerBranchIDMapping), cacheProvider.CacheTime(cacheTime), objectstorage.LeakDetectionEnabled(false)), branchVotersStorage: objectstorage.NewStructStorage[BranchVoters](objectstorage.NewStoreWithRealm(tangle.Options.Store, database.PrefixTangle, PrefixBranchVoters), cacheProvider.CacheTime(approvalWeightCacheTime), objectstorage.LeakDetectionEnabled(false)), latestBranchVotesStorage: objectstorage.NewStructStorage[LatestBranchVotes](objectstorage.NewStoreWithRealm(tangle.Options.Store, database.PrefixTangle, PrefixLatestBranchVotes), cacheProvider.CacheTime(approvalWeightCacheTime), objectstorage.LeakDetectionEnabled(false)), @@ -347,17 +347,15 @@ func (s *Storage) BranchWeight(branchID utxo.TransactionID, computeIfAbsentCallb func (s *Storage) storeGenesis() { s.MessageMetadata(EmptyMessageID, func() *MessageMetadata { - genesisMetadata := - &MessageMetadata{ - model.NewStorable[MessageID, messageMetadataModel](messageMetadataModel{ - AddedBranchIDs: utxo.NewTransactionIDs(), - SubtractedBranchIDs: utxo.NewTransactionIDs(), - SolidificationTime: clock.SyncedTime().Add(time.Duration(-20) * time.Minute), - Solid: true, - StructureDetails: markers.NewStructureDetails(), - Scheduled: true, - Booked: true, - })} + genesisMetadata := model.NewStorable[MessageID, MessageMetadata](&messageMetadataModel{ + AddedBranchIDs: utxo.NewTransactionIDs(), + SubtractedBranchIDs: utxo.NewTransactionIDs(), + SolidificationTime: clock.SyncedTime().Add(time.Duration(-20) * time.Minute), + Solid: true, + StructureDetails: markers.NewStructureDetails(), + Scheduled: true, + Booked: true, + }) genesisMetadata.SetID(EmptyMessageID) return genesisMetadata }).Release() @@ -537,7 +535,7 @@ func (a ApproverType) String() string { // Approver is an approver of a given referenced message. type Approver struct { - model.StorableReference[approverSourceModel, MessageID] `serix:"0"` + model.StorableReference[Approver, *Approver, approverSourceModel, MessageID] `serix:"0"` } type approverSourceModel struct { @@ -550,33 +548,25 @@ type approverSourceModel struct { // NewApprover creates a new approver relation to the given approved/referenced message. func NewApprover(approverType ApproverType, referencedMessageID MessageID, approverMessageID MessageID) *Approver { - return &Approver{ - model.NewStorableReference[approverSourceModel, MessageID](approverSourceModel{ - ReferencedMessageID: referencedMessageID, - ApproverType: approverType, - }, approverMessageID), - } + return model.NewStorableReference[Approver](approverSourceModel{ + ReferencedMessageID: referencedMessageID, + ApproverType: approverType, + }, approverMessageID) } // Type returns the type of the Approver reference. func (a *Approver) Type() ApproverType { - a.RLock() - defer a.RUnlock() - return a.SourceID.ApproverType + return a.SourceID().ApproverType } // ReferencedMessageID returns the ID of the message which is referenced by the approver. func (a *Approver) ReferencedMessageID() MessageID { - a.RLock() - defer a.RUnlock() - return a.SourceID.ReferencedMessageID + return a.SourceID().ReferencedMessageID } // ApproverMessageID returns the ID of the message which referenced the given approved message. func (a *Approver) ApproverMessageID() MessageID { - a.RLock() - defer a.RUnlock() - return a.TargetID + return a.TargetID() } // endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -586,28 +576,22 @@ func (a *Approver) ApproverMessageID() MessageID { // Attachment stores the information which transaction was attached by which message. We need this to be able to perform // reverse lookups from transactions to their corresponding messages that attach them. type Attachment struct { - model.StorableReference[utxo.TransactionID, MessageID] `serix:"0"` + model.StorableReference[Attachment, *Attachment, utxo.TransactionID, MessageID] `serix:"0"` } // NewAttachment creates an attachment object with the given information. func NewAttachment(transactionID utxo.TransactionID, messageID MessageID) *Attachment { - return &Attachment{model.NewStorableReference(transactionID, messageID)} + return model.NewStorableReference[Attachment](transactionID, messageID) } // TransactionID returns the transactionID of this Attachment. func (a *Attachment) TransactionID() utxo.TransactionID { - a.RLock() - defer a.RUnlock() - - return a.SourceID + return a.SourceID() } // MessageID returns the messageID of this Attachment. func (a *Attachment) MessageID() MessageID { - a.RLock() - defer a.RUnlock() - - return a.TargetID + return a.TargetID() } // endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -616,16 +600,16 @@ func (a *Attachment) MessageID() MessageID { // MissingMessage represents a missing message. type MissingMessage struct { - model.Storable[MessageID, time.Time] `serix:"0"` + model.Storable[MessageID, MissingMessage, *MissingMessage, time.Time] `serix:"0"` } // NewMissingMessage creates new missing message with the specified messageID. func NewMissingMessage(messageID MessageID) *MissingMessage { - missingMessage := &MissingMessage{ - model.NewStorable[MessageID, time.Time]( - time.Now(), - ), - } + now := time.Now() + missingMessage := model.NewStorable[MessageID, MissingMessage]( + &now, + ) + missingMessage.SetID(messageID) return missingMessage } diff --git a/packages/tangle/storage_test.go b/packages/tangle/storage_test.go index fb21dcd7f8..ecdda72d2d 100644 --- a/packages/tangle/storage_test.go +++ b/packages/tangle/storage_test.go @@ -4,11 +4,24 @@ import ( "math/rand" "testing" + "github.com/iotaledger/hive.go/generics/lo" "github.com/stretchr/testify/assert" "github.com/iotaledger/goshimmer/packages/ledger/utxo" ) +func TestMarkerIndexBranchIDMapping_Serialization(t *testing.T) { + m := NewMarkerIndexBranchIDMapping(1) + txID := utxo.NewTransactionID([]byte("1")) + txID.RegisterAlias("txID") + m.SetBranchIDs(10, utxo.NewTransactionIDs(txID)) + + restored := new(MarkerIndexBranchIDMapping) + err := restored.FromBytes(lo.PanicOnErr(m.Bytes())) + assert.NoError(t, err) + assert.Equal(t, m.BranchIDs(11), restored.BranchIDs(11)) +} + func TestStorage_StoreAttachment(t *testing.T) { tangle := NewTestTangle() defer tangle.Shutdown() diff --git a/packages/tangle/tangle_test.go b/packages/tangle/tangle_test.go index f41ce259f8..d3099a913e 100644 --- a/packages/tangle/tangle_test.go +++ b/packages/tangle/tangle_test.go @@ -363,6 +363,9 @@ func TestRetrieveAllTips(t *testing.T) { } func TestTangle_Flow(t *testing.T) { + if testing.Short() { + t.Skip("skipping test in short mode.") + } const ( testNetwork = "udp" testPort = 8000 @@ -594,7 +597,7 @@ func TestTangle_Flow(t *testing.T) { // IssueInvalidTsPayload creates a new message including sequence number and tip selection and returns it. func (f *MessageFactory) issueInvalidTsPayload(p payload.Payload, _ ...*Tangle) (*Message, error) { - payloadLen := len(p.Bytes()) + payloadLen := len(lo.PanicOnErr(p.Bytes())) if payloadLen > payload.MaxSize { err := fmt.Errorf("maximum payload size of %d bytes exceeded", payloadLen) f.Events.Error.Trigger(err) diff --git a/packages/tangle/testutils.go b/packages/tangle/testutils.go index 8572509517..11ad4bd4a5 100644 --- a/packages/tangle/testutils.go +++ b/packages/tangle/testutils.go @@ -9,6 +9,7 @@ import ( "github.com/iotaledger/hive.go/crypto/ed25519" "github.com/iotaledger/hive.go/generics/event" + "github.com/iotaledger/hive.go/generics/lo" "github.com/iotaledger/hive.go/generics/set" "github.com/iotaledger/hive.go/identity" "github.com/iotaledger/hive.go/types" @@ -746,7 +747,7 @@ func createWallets(n int) []wallet { } func (w wallet) sign(txEssence *devnetvm.TransactionEssence) *devnetvm.ED25519Signature { - return devnetvm.NewED25519Signature(w.publicKey(), w.privateKey().Sign(txEssence.Bytes())) + return devnetvm.NewED25519Signature(w.publicKey(), w.privateKey().Sign(lo.PanicOnErr(txEssence.Bytes()))) } // addressFromInput retrieves the Address belonging to an Input by looking it up in the outputs that we have created for diff --git a/packages/tangle/utils_test.go b/packages/tangle/utils_test.go deleted file mode 100644 index 406e118df4..0000000000 --- a/packages/tangle/utils_test.go +++ /dev/null @@ -1,48 +0,0 @@ -package tangle - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/iotaledger/hive.go/generics/event" - - "github.com/iotaledger/goshimmer/packages/markers" -) - -func TestUtils_AllTransactionsApprovedByMessages(t *testing.T) { - // imgages/util-AllTransactionsApprovedByMessages-parallel-markers.png - tangle := NewTestTangle() - defer tangle.Shutdown() - - tangle.Setup() - tangle.Events.Error.Hook(event.NewClosure(func(err error) { - panic(err) - })) - - mtf := NewMessageTestFramework(tangle, WithGenesisOutput("Genesis1", 5), WithGenesisOutput("Genesis2", 8)) - - mtf.CreateMessage("Message1", WithStrongParents("Genesis")) - mtf.CreateMessage("Message2", WithInputs("Genesis1"), WithOutput("A", 5), WithStrongParents("Message1")) - mtf.CreateMessage("Message3", WithStrongParents("Message2")) - mtf.CreateMessage("Message4", WithStrongParents("Genesis")) - mtf.CreateMessage("Message5", WithInputs("Genesis2"), WithOutput("B", 4), WithOutput("C", 4), WithStrongParents("Message4")) - mtf.CreateMessage("Message6", WithStrongParents("Message5")) - mtf.CreateMessage("Message7", WithInputs("A", "B", "C"), WithOutput("D", 13), WithStrongParents("Message3", "Message6")) - - mtf.IssueMessages("Message1", "Message2", "Message3", "Message4", "Message5", "Message6", "Message7").WaitUntilAllTasksProcessed() - - for messageAlias, expectedMarkers := range map[string]*markers.Markers{ - "Message1": markers.NewMarkers(markers.NewMarker(0, 1)), - "Message2": markers.NewMarkers(markers.NewMarker(0, 2)), - "Message3": markers.NewMarkers(markers.NewMarker(0, 3)), - "Message4": markers.NewMarkers(markers.NewMarker(0, 0)), - "Message5": markers.NewMarkers(markers.NewMarker(0, 0)), - "Message6": markers.NewMarkers(markers.NewMarker(0, 0)), - "Message7": markers.NewMarkers(markers.NewMarker(0, 4)), - } { - tangle.Storage.MessageMetadata(mtf.Message(messageAlias).ID()).Consume(func(messageMetadata *MessageMetadata) { - assert.True(t, messageMetadata.StructureDetails().PastMarkers().Equals(expectedMarkers)) - }) - } -} diff --git a/plugins/chat/plugin.go b/plugins/chat/plugin.go index 93c63b8b08..d445f70e83 100644 --- a/plugins/chat/plugin.go +++ b/plugins/chat/plugin.go @@ -51,17 +51,11 @@ func onReceiveMessageFromMessageLayer(messageID tangle.MessageID) { if message.Payload().Type() != chat.Type { return } - - chatPayload, _, err := chat.FromBytes(message.Payload().Bytes()) - if err != nil { - Plugin.LogError(err) - return - } - + chatPayload := message.Payload().(*chat.Payload) chatEvent = &chat.MessageReceivedEvent{ - From: chatPayload.From, - To: chatPayload.To, - Message: chatPayload.Message, + From: chatPayload.From(), + To: chatPayload.To(), + Message: chatPayload.Message(), Timestamp: message.IssuingTime(), MessageID: message.ID().Base58(), } diff --git a/plugins/dashboard/payload_handler.go b/plugins/dashboard/payload_handler.go index 1d1bf71b31..67332be9fb 100644 --- a/plugins/dashboard/payload_handler.go +++ b/plugins/dashboard/payload_handler.go @@ -1,6 +1,8 @@ package dashboard import ( + "github.com/iotaledger/hive.go/generics/lo" + chat2 "github.com/iotaledger/goshimmer/packages/chat" "github.com/iotaledger/goshimmer/packages/faucet" "github.com/iotaledger/goshimmer/packages/jsonmodels" @@ -99,25 +101,22 @@ func ProcessPayload(p payload.Payload) interface{} { case chat2.Type: chatPayload := p.(*chat2.Payload) return chat.Request{ - From: chatPayload.From, - To: chatPayload.To, - Message: chatPayload.Message, + From: chatPayload.From(), + To: chatPayload.To(), + Message: chatPayload.Message(), } default: // unknown payload return BasicPayload{ ContentTitle: "Bytes", - Content: p.Bytes(), + Content: lo.PanicOnErr(p.Bytes()), } } } // processTransactionPayload handles Value payload func processTransactionPayload(p payload.Payload) (tp TransactionPayload) { - tx, err := new(devnetvm.Transaction).FromBytes(p.Bytes()) - if err != nil { - return - } + tx := p.(*devnetvm.Transaction) tp.TxID = tx.ID().Base58() tp.Transaction = jsonmodels.NewTransaction(tx) // add consumed inputs diff --git a/plugins/faucet/plugin.go b/plugins/faucet/plugin.go index 9bcdf90f0e..20b3f1e111 100644 --- a/plugins/faucet/plugin.go +++ b/plugins/faucet/plugin.go @@ -231,10 +231,15 @@ func onMessageProcessed(messageID tangle.MessageID) { fundingRequest := message.Payload().(*faucet.Payload) addr := fundingRequest.Address() + requestBytes, err := fundingRequest.Bytes() + if err != nil { + Plugin.LogInfof("couldn't serialize faucet request: %w", err) + return + } // verify PoW - leadingZeroes, err := powVerifier.LeadingZeros(fundingRequest.Bytes()) + leadingZeroes, err := powVerifier.LeadingZeros(requestBytes) if err != nil { - Plugin.LogInfof("couldn't verify PoW of funding request for address %s", addr.Base58()) + Plugin.LogInfof("couldn't verify PoW of funding request for address %s: %w", addr.Base58(), err) return } diff --git a/plugins/faucet/state_manager.go b/plugins/faucet/state_manager.go index d6fd89db62..b1bbc5a877 100644 --- a/plugins/faucet/state_manager.go +++ b/plugins/faucet/state_manager.go @@ -9,6 +9,7 @@ import ( "github.com/cockroachdb/errors" "github.com/iotaledger/hive.go/crypto/ed25519" "github.com/iotaledger/hive.go/generics/event" + "github.com/iotaledger/hive.go/generics/lo" "github.com/iotaledger/hive.go/identity" "github.com/iotaledger/hive.go/types" "github.com/iotaledger/hive.go/typeutils" @@ -985,5 +986,5 @@ func (w wallet) publicKey() ed25519.PublicKey { } func (w wallet) sign(txEssence *devnetvm.TransactionEssence) *devnetvm.ED25519Signature { - return devnetvm.NewED25519Signature(w.publicKey(), w.privateKey().Sign(txEssence.Bytes())) + return devnetvm.NewED25519Signature(w.publicKey(), w.privateKey().Sign(lo.PanicOnErr(txEssence.Bytes()))) } diff --git a/plugins/manarefresher/wallet.go b/plugins/manarefresher/wallet.go index 28718ac41a..57a84dc45c 100644 --- a/plugins/manarefresher/wallet.go +++ b/plugins/manarefresher/wallet.go @@ -2,6 +2,7 @@ package manarefresher import ( "github.com/iotaledger/hive.go/crypto/ed25519" + "github.com/iotaledger/hive.go/generics/lo" "github.com/iotaledger/goshimmer/packages/ledger/vm/devnetvm" ) @@ -32,7 +33,7 @@ func (w *wallet) publicKey() ed25519.PublicKey { } func (w *wallet) sign(txEssence *devnetvm.TransactionEssence) *devnetvm.ED25519Signature { - return devnetvm.NewED25519Signature(w.publicKey(), w.privateKey().Sign(txEssence.Bytes())) + return devnetvm.NewED25519Signature(w.publicKey(), w.privateKey().Sign(lo.PanicOnErr(txEssence.Bytes()))) } // unlockBlocks returns the unlock blocks assuming all inputs can be unlocked by the same signature. diff --git a/plugins/networkdelay/payload.go b/plugins/networkdelay/payload.go index ea9bb936be..d97ac99aac 100644 --- a/plugins/networkdelay/payload.go +++ b/plugins/networkdelay/payload.go @@ -1,13 +1,11 @@ package networkdelay import ( - "context" "fmt" "sync" - "github.com/cockroachdb/errors" + "github.com/iotaledger/hive.go/generics/model" "github.com/iotaledger/hive.go/serix" - "github.com/iotaledger/hive.go/stringify" "github.com/mr-tron/base58" "github.com/iotaledger/goshimmer/packages/tangle/payload" @@ -40,10 +38,10 @@ func (id ID) String() string { // Payload represents the network delay payload type. type Payload struct { - payloadInner `serix:"0"` + model.Immutable[Payload, *Payload, payloadModel] `serix:"0"` } -type payloadInner struct { +type payloadModel struct { ID ID `serix:"0"` SentTime int64 `serix:"1"` // [ns] @@ -53,51 +51,22 @@ type payloadInner struct { // NewPayload creates a new network delay payload. func NewPayload(id ID, sentTime int64) *Payload { - return &Payload{ - payloadInner{ + return model.NewImmutable[Payload]( + &payloadModel{ ID: id, SentTime: sentTime, }, - } -} - -// FromBytes parses the marshaled version of a Payload into a Go object. -// It either returns a new Payload or fills an optionally provided Payload with the parsed information. -func FromBytes(bytes []byte) (payload *Payload, consumedBytes int, err error) { - payload = new(Payload) - - consumedBytes, err = serix.DefaultAPI.Decode(context.Background(), bytes, payload, serix.WithValidation()) - if err != nil { - err = errors.Errorf("failed to parse NetworkDelayPayload: %w", err) - return - } - - return + ) } -// Bytes returns a marshaled version of this Payload. -func (p *Payload) Bytes() []byte { - p.bytesMutex.Lock() - defer p.bytesMutex.Unlock() - if objBytes := p.payloadInner.bytes; objBytes != nil { - return objBytes - } - - objBytes, err := serix.DefaultAPI.Encode(context.Background(), p, serix.WithValidation()) - if err != nil { - // TODO: what do? - panic(err) - } - p.payloadInner.bytes = objBytes - return objBytes +// ID returns the ID of the Payload. +func (p *Payload) ID() ID { + return p.M.ID } -// String returns a human-friendly representation of the Payload. -func (p *Payload) String() string { - return stringify.Struct("NetworkDelayPayload", - stringify.StructField("id", p.ID), - stringify.StructField("sentTime", uint64(p.SentTime)), - ) +// SentTime returns the type of the Payload. +func (p *Payload) SentTime() int64 { + return p.M.SentTime } // region Payload implementation /////////////////////////////////////////////////////////////////////////////////////// diff --git a/plugins/networkdelay/plugin.go b/plugins/networkdelay/plugin.go index 26456cd27d..9ae45459f3 100644 --- a/plugins/networkdelay/plugin.go +++ b/plugins/networkdelay/plugin.go @@ -110,7 +110,7 @@ func onReceiveMessageFromMessageLayer(messageID tangle.MessageID) { // abort if message was sent more than 1min ago // this should only happen due to a node resyncing - if time.Duration(now-networkDelayObject.SentTime) > time.Minute { + if time.Duration(now-networkDelayObject.SentTime()) > time.Minute { app.LogDebugf("Received network delay message with >1min delay\n%s", networkDelayObject) return } @@ -122,10 +122,10 @@ func onReceiveMessageFromMessageLayer(messageID tangle.MessageID) { func sendToRemoteLog(networkDelayObject *Payload, receiveTime int64) { m := networkDelay{ NodeID: myID, - ID: networkDelayObject.ID.String(), - SentTime: networkDelayObject.SentTime, + ID: networkDelayObject.ID().String(), + SentTime: networkDelayObject.SentTime(), ReceiveTime: receiveTime, - Delta: receiveTime - networkDelayObject.SentTime, + Delta: receiveTime - networkDelayObject.SentTime(), Clock: clockEnabled, Sync: deps.Tangle.Synced(), Type: remoteLogType, @@ -136,7 +136,7 @@ func sendToRemoteLog(networkDelayObject *Payload, receiveTime int64) { func sendPoWInfo(payload *Payload, powDelta time.Duration) { m := networkDelay{ NodeID: myID, - ID: payload.ID.String(), + ID: payload.ID().String(), SentTime: 0, ReceiveTime: 0, Delta: powDelta.Nanoseconds(), diff --git a/plugins/webapi/ledgerstate/plugin.go b/plugins/webapi/ledgerstate/plugin.go index 2e2c15c2fa..8190eb4af6 100644 --- a/plugins/webapi/ledgerstate/plugin.go +++ b/plugins/webapi/ledgerstate/plugin.go @@ -528,7 +528,8 @@ func PostTransaction(c echo.Context) error { } // parse tx - tx, err := new(devnetvm.Transaction).FromBytes(request.TransactionBytes) + tx := new(devnetvm.Transaction) + err := tx.FromBytes(request.TransactionBytes) if err != nil { return c.JSON(http.StatusBadRequest, &jsonmodels.PostTransactionResponse{Error: err.Error()}) } diff --git a/plugins/webapi/message/plugin.go b/plugins/webapi/message/plugin.go index b9b30ebe9f..af8e8a910a 100644 --- a/plugins/webapi/message/plugin.go +++ b/plugins/webapi/message/plugin.go @@ -106,6 +106,8 @@ func GetMessage(c echo.Context) (err error) { } if deps.Tangle.Storage.Message(messageID).Consume(func(message *tangle.Message) { + var payloadBytes []byte + payloadBytes, err = message.Payload().Bytes() err = c.JSON(http.StatusOK, jsonmodels.Message{ ID: message.ID().Base58(), StrongParents: message.ParentsByType(tangle.StrongParentType).Base58(), @@ -127,7 +129,7 @@ func GetMessage(c echo.Context) (err error) { return "" }(), - Payload: message.Payload().Bytes(), + Payload: payloadBytes, Signature: message.Signature().String(), }) }) { diff --git a/tools/double-spend/double-spend.go b/tools/double-spend/double-spend.go index facf6ae6a0..d8ff174595 100644 --- a/tools/double-spend/double-spend.go +++ b/tools/double-spend/double-spend.go @@ -6,6 +6,8 @@ import ( "sync" "time" + "github.com/iotaledger/hive.go/generics/lo" + "github.com/iotaledger/goshimmer/packages/consensus/gof" "github.com/iotaledger/goshimmer/packages/ledger/utxo" "github.com/iotaledger/goshimmer/packages/ledger/vm/devnetvm" @@ -97,13 +99,13 @@ func main() { }), destAddr.Address()) txEssence := devnetvm.NewTransactionEssence(0, time.Now(), identity.ID{}, identity.ID{}, devnetvm.NewInputs(devnetvm.NewUTXOInput(out)), devnetvm.NewOutputs(output)) kp := *mySeed.KeyPair(0) - sig := devnetvm.NewED25519Signature(kp.PublicKey, kp.PrivateKey.Sign(txEssence.Bytes())) + sig := devnetvm.NewED25519Signature(kp.PublicKey, kp.PrivateKey.Sign(lo.PanicOnErr(txEssence.Bytes()))) unlockBlock := devnetvm.NewSignatureUnlockBlock(sig) tx := devnetvm.NewTransaction(txEssence, devnetvm.UnlockBlocks{unlockBlock}) conflictingTxs[i] = tx // issue the tx - resp, err2 := clients[i].PostTransaction(tx.Bytes()) + resp, err2 := clients[i].PostTransaction(lo.PanicOnErr(tx.Bytes())) if err2 != nil { panic(err2) } diff --git a/tools/integration-tests/tester/go.mod b/tools/integration-tests/tester/go.mod index e8c772d947..e3398286c8 100644 --- a/tools/integration-tests/tester/go.mod +++ b/tools/integration-tests/tester/go.mod @@ -7,7 +7,7 @@ require ( github.com/docker/docker v1.13.1 github.com/docker/go-connections v0.4.0 github.com/iotaledger/goshimmer v0.1.3 - github.com/iotaledger/hive.go v0.0.0-20220604100743-40898337a727 + github.com/iotaledger/hive.go v0.0.0-20220607150119-1be29e962175 github.com/mr-tron/base58 v1.2.0 github.com/stretchr/testify v1.7.1 golang.org/x/crypto v0.0.0-20220214200702-86341886e292 diff --git a/tools/integration-tests/tester/go.sum b/tools/integration-tests/tester/go.sum index cdf0731dc8..ff87a649e5 100644 --- a/tools/integration-tests/tester/go.sum +++ b/tools/integration-tests/tester/go.sum @@ -461,12 +461,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -github.com/iotaledger/hive.go v0.0.0-20220602132800-47d5e0419264 h1:BFO3PiYpYa3tOleosCow99+hjUFzw2hYffV24Q84x/w= -github.com/iotaledger/hive.go v0.0.0-20220602132800-47d5e0419264/go.mod h1:8f9U7qHFby0W3cxv/nKnz9LHn9BbwWU0tMsWDnfqzRI= -github.com/iotaledger/hive.go v0.0.0-20220603121653-149d5f740312 h1:nIFqz2F4bKrJ1Kqiyts2rdNUwms87ocahKHHDr0nLJI= -github.com/iotaledger/hive.go v0.0.0-20220603121653-149d5f740312/go.mod h1:8f9U7qHFby0W3cxv/nKnz9LHn9BbwWU0tMsWDnfqzRI= -github.com/iotaledger/hive.go v0.0.0-20220604100743-40898337a727 h1:NDNgpftyrWboDyKHgcbISPdYKRVqeXw+mBYomCax+qw= -github.com/iotaledger/hive.go v0.0.0-20220604100743-40898337a727/go.mod h1:8f9U7qHFby0W3cxv/nKnz9LHn9BbwWU0tMsWDnfqzRI= +github.com/iotaledger/hive.go v0.0.0-20220607150119-1be29e962175 h1:IgXxiPx51WJglOL5EtIurlMbujnrLP4vLYQqyfmR0zg= +github.com/iotaledger/hive.go v0.0.0-20220607150119-1be29e962175/go.mod h1:8f9U7qHFby0W3cxv/nKnz9LHn9BbwWU0tMsWDnfqzRI= github.com/ipfs/go-cid v0.0.1/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= github.com/ipfs/go-cid v0.0.2/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= github.com/ipfs/go-cid v0.0.3/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= diff --git a/tools/integration-tests/tester/tests/consensus/consensus_conflict_spam_test.go b/tools/integration-tests/tester/tests/consensus/consensus_conflict_spam_test.go index c3ff0e7cbc..4625e9dfff 100644 --- a/tools/integration-tests/tester/tests/consensus/consensus_conflict_spam_test.go +++ b/tools/integration-tests/tester/tests/consensus/consensus_conflict_spam_test.go @@ -7,6 +7,7 @@ import ( "time" "github.com/iotaledger/hive.go/crypto/ed25519" + "github.com/iotaledger/hive.go/generics/lo" "github.com/stretchr/testify/require" "github.com/iotaledger/goshimmer/packages/consensus/gof" @@ -176,7 +177,7 @@ func postTransactions(t *testing.T, peers []*framework.Node, peerIndex int, atta for i, tx := range txs { newPeerIndex := (peerIndex + i) % len(peers) log.Printf("%s: post tx %s on peer %s", attackName, tx.ID().Base58(), peers[newPeerIndex].Name()) - resp, err := peers[newPeerIndex].PostTransaction(tx.Bytes()) + resp, err := peers[newPeerIndex].PostTransaction(lo.PanicOnErr(tx.Bytes())) require.NoError(t, err, "%s: There was an error posting transaction %s to peer %s", attackName, tx.ID().Base58(), peers[newPeerIndex].Name()) require.Empty(t, resp.Error, "%s: There was an error in the response while posting transaction %s to peer %s", @@ -213,7 +214,7 @@ func getOutputsControlledBy(t *testing.T, node *framework.Node, addresses ...dev func splitToAddresses(t *testing.T, node *framework.Node, output devnetvm.Output, keyPairs map[string]*ed25519.KeyPair, addresses ...devnetvm.Address) devnetvm.Outputs { transaction := tests.CreateTransactionFromOutputs(t, node.ID(), addresses, keyPairs, output) - _, err := node.PostTransaction(transaction.Bytes()) + _, err := node.PostTransaction(lo.PanicOnErr(transaction.Bytes())) require.NoError(t, err, "Error occured while trying to split addresses") return transaction.Essence().Outputs() } diff --git a/tools/integration-tests/tester/tests/testutil.go b/tools/integration-tests/tester/tests/testutil.go index 561b414ab0..51b528645e 100644 --- a/tools/integration-tests/tester/tests/testutil.go +++ b/tools/integration-tests/tester/tests/testutil.go @@ -8,6 +8,7 @@ import ( "time" "github.com/iotaledger/hive.go/datastructure/walker" + "github.com/iotaledger/hive.go/generics/lo" "github.com/iotaledger/hive.go/crypto/ed25519" @@ -260,7 +261,7 @@ func CreateTransactionFromOutputs(t *testing.T, manaPledgeID identity.ID, target addressKey := utxos[i].Address().String() keyPair := keyPairs[addressKey] require.NotNilf(t, keyPair, "missing key pair for address %s", addressKey) - sig := devnetvm.NewED25519Signature(keyPair.PublicKey, keyPair.PrivateKey.Sign(txEssence.Bytes())) + sig := devnetvm.NewED25519Signature(keyPair.PublicKey, keyPair.PrivateKey.Sign(lo.PanicOnErr(txEssence.Bytes()))) unlockBlocks[i] = devnetvm.NewSignatureUnlockBlock(sig) } @@ -278,7 +279,7 @@ func SendDataMessage(t *testing.T, node *framework.Node, data []byte, number int number: number, id: id, // save payload to be able to compare API response - data: payload.NewGenericDataPayload(data).Bytes(), + data: lo.PanicOnErr(payload.NewGenericDataPayload(data).Bytes()), issuerPublicKey: node.Identity.PublicKey().String(), } return id, sent @@ -357,7 +358,7 @@ func SendTransaction(t *testing.T, from *framework.Node, to *framework.Node, col } txEssence := devnetvm.NewTransactionEssence(0, time.Now(), txConfig.AccessManaPledgeID, txConfig.ConsensusManaPledgeID, devnetvm.NewInputs(input), devnetvm.NewOutputs(outputs...)) - sig := devnetvm.NewED25519Signature(from.KeyPair(txConfig.FromAddressIndex).PublicKey, from.KeyPair(txConfig.FromAddressIndex).PrivateKey.Sign(txEssence.Bytes())) + sig := devnetvm.NewED25519Signature(from.KeyPair(txConfig.FromAddressIndex).PublicKey, from.KeyPair(txConfig.FromAddressIndex).PrivateKey.Sign(lo.PanicOnErr(txEssence.Bytes()))) unlockBlock := devnetvm.NewSignatureUnlockBlock(sig) txn := devnetvm.NewTransaction(txEssence, devnetvm.UnlockBlocks{unlockBlock}) @@ -368,7 +369,7 @@ func SendTransaction(t *testing.T, from *framework.Node, to *framework.Node, col } // send transaction - resp, err := from.PostTransaction(txn.Bytes()) + resp, err := from.PostTransaction(lo.PanicOnErr(txn.Bytes())) if err != nil { return "", err } diff --git a/tools/integration-tests/tester/tests/value/value_test.go b/tools/integration-tests/tester/tests/value/value_test.go index dd6daf2789..dbacfbbc9c 100644 --- a/tools/integration-tests/tester/tests/value/value_test.go +++ b/tools/integration-tests/tester/tests/value/value_test.go @@ -288,7 +288,7 @@ func TestValueAliasDelegation(t *testing.T) { devnetvm.NewInputs(devnetvm.NewUTXOInput(delegatedAliasOutputID)), devnetvm.NewOutputs(nextOutput)) tx := devnetvm.NewTransaction(essence, dumbWallet.unlockBlocks(essence)) - _, err = nonFaucetPeers[0].PostTransaction(tx.Bytes()) + _, err = nonFaucetPeers[0].PostTransaction(lo.PanicOnErr(tx.Bytes())) require.NoError(t, err) tests.RequireGradeOfFinalityEqual(t, n.Peers(), map[string]tests.ExpectedState{ @@ -357,7 +357,7 @@ func createWallets(n int) []simpleWallet { } func (s simpleWallet) sign(txEssence *devnetvm.TransactionEssence) *devnetvm.ED25519Signature { - return devnetvm.NewED25519Signature(s.publicKey(), s.privateKey().Sign(txEssence.Bytes())) + return devnetvm.NewED25519Signature(s.publicKey(), s.privateKey().Sign(lo.PanicOnErr(txEssence.Bytes()))) } func (s simpleWallet) unlockBlocks(txEssence *devnetvm.TransactionEssence) []devnetvm.UnlockBlock { diff --git a/tools/rand-seed/go.mod b/tools/rand-seed/go.mod deleted file mode 100644 index ed1ff9615c..0000000000 --- a/tools/rand-seed/go.mod +++ /dev/null @@ -1,16 +0,0 @@ -module rand-seed - -go 1.18 - -require ( - github.com/iotaledger/hive.go v0.0.0-20220604100743-40898337a727 - github.com/mr-tron/base58 v1.2.0 -) - -require ( - github.com/oasisprotocol/ed25519 v0.0.0-20210505154701-76d8c688d86e // indirect - github.com/pkg/errors v0.9.1 // indirect - golang.org/x/crypto v0.0.0-20220214200702-86341886e292 // indirect - golang.org/x/sys v0.0.0-20210909193231-528a39cd75f3 // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect -) diff --git a/tools/rand-seed/go.sum b/tools/rand-seed/go.sum deleted file mode 100644 index 8e5d0af682..0000000000 --- a/tools/rand-seed/go.sum +++ /dev/null @@ -1,26 +0,0 @@ -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/iotaledger/hive.go v0.0.0-20220601101729-54f8761ea542 h1:pD2ZHEDMlD/VvxP3G7r2NFnkpmpi4ys5K/ohEJbsKek= -github.com/iotaledger/hive.go v0.0.0-20220601101729-54f8761ea542/go.mod h1:8f9U7qHFby0W3cxv/nKnz9LHn9BbwWU0tMsWDnfqzRI= -github.com/iotaledger/hive.go v0.0.0-20220604100743-40898337a727 h1:NDNgpftyrWboDyKHgcbISPdYKRVqeXw+mBYomCax+qw= -github.com/iotaledger/hive.go v0.0.0-20220604100743-40898337a727/go.mod h1:8f9U7qHFby0W3cxv/nKnz9LHn9BbwWU0tMsWDnfqzRI= -github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= -github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= -github.com/oasisprotocol/ed25519 v0.0.0-20210505154701-76d8c688d86e h1:pHDo+QVA9a72j08pr99Zh91vkQibH0CiNNSp36sOflA= -github.com/oasisprotocol/ed25519 v0.0.0-20210505154701-76d8c688d86e/go.mod h1:IZbb50w3AB72BVobEF6qG93NNSrTw/V2QlboxqSu3Xw= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191119213627-4f8c1d86b1ba/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20220214200702-86341886e292 h1:f+lwQ+GtmgoY+A2YaQxlSOnDjXcQ7ZRLWOHbC6HtRqE= -golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210909193231-528a39cd75f3 h1:3Ad41xy2WCESpufXwgs7NpDSu+vjxqLt2UFqUV+20bI= -golang.org/x/sys v0.0.0-20210909193231-528a39cd75f3/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=