diff --git a/go.mod b/go.mod index 9c32ac67638309de873941c5581050b082cce044..8b68a9a7cf9eb1382ed9c028e6ab22776996ffcf 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/golang/protobuf v1.3.2 // indirect github.com/google/open-location-code/go v0.0.0-20190903173953-119bc96a3a51 github.com/gorilla/websocket v1.4.1 - github.com/iotaledger/hive.go v0.0.0-20200106003337-d78637b86bf2 + github.com/iotaledger/hive.go v0.0.0-20200107105553-d35f44074aa2 github.com/iotaledger/iota.go v1.0.0-beta.9 github.com/kr/text v0.1.0 github.com/labstack/echo v3.3.10+incompatible diff --git a/go.sum b/go.sum index 88cdee3fd97107a92c0efec2feb589b3c9eb8b83..867562cddd4cdf3925b5dc7a1ee90f9c242231fe 100644 --- a/go.sum +++ b/go.sum @@ -105,6 +105,8 @@ github.com/iotaledger/hive.go v0.0.0-20200105155739-b36b4405762f h1:HqEFrjhbUB4u github.com/iotaledger/hive.go v0.0.0-20200105155739-b36b4405762f/go.mod h1:vrZrvGaWT1o5kz3Jj2B/PcUtqsFzZnLWrO3zEsGSuwk= github.com/iotaledger/hive.go v0.0.0-20200106003337-d78637b86bf2 h1:HBQKEP6PnaEhG7j3HNjZTkpALvek8QYPeoJXyDzBDpo= github.com/iotaledger/hive.go v0.0.0-20200106003337-d78637b86bf2/go.mod h1:vrZrvGaWT1o5kz3Jj2B/PcUtqsFzZnLWrO3zEsGSuwk= +github.com/iotaledger/hive.go v0.0.0-20200107105553-d35f44074aa2 h1:o4jOpCuNXSRhbiSJHPR2SuN/qGkKt/LpX+DIQ0rUMSY= +github.com/iotaledger/hive.go v0.0.0-20200107105553-d35f44074aa2/go.mod h1:vrZrvGaWT1o5kz3Jj2B/PcUtqsFzZnLWrO3zEsGSuwk= github.com/iotaledger/iota.go v1.0.0-beta.9 h1:c654s9pkdhMBkABUvWg+6k91MEBbdtmZXP1xDfQpajg= github.com/iotaledger/iota.go v1.0.0-beta.9/go.mod h1:F6WBmYd98mVjAmmPVYhnxg8NNIWCjjH8VWT9qvv3Rc8= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= diff --git a/packages/binary/tangle/approvers/approvers.go b/packages/binary/tangle/approvers/approvers.go index adf7af3a099ea1dd50714f2719fe24bf5fc46c49..90014e143ddb071753f2cf9aadb42ffe33166b56 100644 --- a/packages/binary/tangle/approvers/approvers.go +++ b/packages/binary/tangle/approvers/approvers.go @@ -1,8 +1,11 @@ package approvers import ( + "encoding/binary" "sync" + "github.com/iotaledger/goshimmer/packages/binary/types" + "github.com/iotaledger/goshimmer/packages/binary/transaction" "github.com/iotaledger/hive.go/objectstorage" ) @@ -11,14 +14,14 @@ type Approvers struct { objectstorage.StorableObjectFlags transactionId transaction.Id - approvers map[transaction.Id]empty + approvers map[transaction.Id]types.Empty approversMutex sync.RWMutex } func New(transactionId transaction.Id) *Approvers { return &Approvers{ transactionId: transactionId, - approvers: make(map[transaction.Id]empty), + approvers: make(map[transaction.Id]types.Empty), } } @@ -39,11 +42,11 @@ func (approvers *Approvers) GetTransactionId() transaction.Id { return approvers.transactionId } -func (approvers *Approvers) Get() (result map[transaction.Id]empty) { +func (approvers *Approvers) Get() (result map[transaction.Id]types.Empty) { approvers.approversMutex.RLock() - result = make(map[transaction.Id]empty, len(approvers.approvers)) + result = make(map[transaction.Id]types.Empty, len(approvers.approvers)) for approverId := range approvers.approvers { - result[approverId] = void + result[approverId] = types.Void } approvers.approversMutex.RUnlock() @@ -57,7 +60,7 @@ func (approvers *Approvers) Add(transactionId transaction.Id) (modified bool) { approvers.approversMutex.Lock() if _, exists := approvers.approvers[transactionId]; !exists { - approvers.approvers[transactionId] = void + approvers.approvers[transactionId] = types.Void modified = true @@ -111,11 +114,50 @@ func (approvers *Approvers) Update(other objectstorage.StorableObject) { } func (approvers *Approvers) MarshalBinary() (result []byte, err error) { + approvers.approversMutex.RLock() + + approversCount := len(approvers.approvers) + result = make([]byte, 4+approversCount*transaction.IdLength) + offset := 0 + + binary.LittleEndian.PutUint32(result[offset:], uint32(approversCount)) + offset += 4 + + for approverId := range approvers.approvers { + marshaledBytes, marshalErr := approverId.MarshalBinary() + if marshalErr != nil { + err = marshalErr + + approvers.approversMutex.RUnlock() + + return + } + + copy(result[offset:], marshaledBytes) + offset += len(marshaledBytes) + } + + approvers.approversMutex.RUnlock() + return } func (approvers *Approvers) UnmarshalBinary(data []byte) (err error) { - approvers.approvers = make(map[transaction.Id]empty) + approvers.approvers = make(map[transaction.Id]types.Empty) + offset := 0 + + approversCount := int(binary.LittleEndian.Uint32(data[offset:])) + offset += 4 + + for i := 0; i < approversCount; i++ { + var approverId transaction.Id + if err = approverId.UnmarshalBinary(data[offset:]); err != nil { + return + } + offset += transaction.IdLength + + approvers.approvers[approverId] = types.Void + } return } diff --git a/packages/binary/tangle/approvers/types.go b/packages/binary/tangle/approvers/types.go deleted file mode 100644 index 99c78a42ede49d7a627262262dd0fc16a16154f6..0000000000000000000000000000000000000000 --- a/packages/binary/tangle/approvers/types.go +++ /dev/null @@ -1,5 +0,0 @@ -package approvers - -type empty struct{} - -var void empty diff --git a/packages/binary/tangle/events.go b/packages/binary/tangle/events.go index 91c7013b0fee1136b8073708cfe1fea27399e4f4..23df13656ff31bbaf9d7561bd6d10c912558c4cd 100644 --- a/packages/binary/tangle/events.go +++ b/packages/binary/tangle/events.go @@ -10,7 +10,7 @@ type Events struct { TransactionAttached *events.Event TransactionSolid *events.Event TransactionMissing *events.Event - MissingTransactionAttached *events.Event + MissingTransactionReceived *events.Event TransactionRemoved *events.Event } @@ -19,7 +19,7 @@ func newEvents() *Events { TransactionAttached: events.NewEvent(cachedTransactionEvent), TransactionSolid: events.NewEvent(cachedTransactionEvent), TransactionMissing: events.NewEvent(transactionIdEvent), - MissingTransactionAttached: events.NewEvent(transactionIdEvent), + MissingTransactionReceived: events.NewEvent(transactionIdEvent), TransactionRemoved: events.NewEvent(transactionIdEvent), } } diff --git a/packages/binary/tangle/tangle.go b/packages/binary/tangle/tangle.go index ecf60caa9fa673b055a05590685532b920115400..7827f7e1764a290f7899513c056d6671f8cdc910 100644 --- a/packages/binary/tangle/tangle.go +++ b/packages/binary/tangle/tangle.go @@ -19,6 +19,8 @@ const ( ) type Tangle struct { + storageId []byte + transactionStorage *objectstorage.ObjectStorage transactionMetadataStorage *objectstorage.ObjectStorage approversStorage *objectstorage.ObjectStorage @@ -34,6 +36,7 @@ type Tangle struct { // Constructor for the tangle. func New(storageId []byte) (result *Tangle) { result = &Tangle{ + storageId: storageId, transactionStorage: objectstorage.New(append(storageId, storageprefix.TangleTransaction...), transaction.FromStorage), transactionMetadataStorage: objectstorage.New(append(storageId, storageprefix.TangleTransactionMetadata...), transactionmetadata.FromStorage), approversStorage: objectstorage.New(append(storageId, storageprefix.TangleApprovers...), approvers.FromStorage), @@ -47,16 +50,29 @@ func New(storageId []byte) (result *Tangle) { return } +// Returns the storage id of this tangle (can be used to create ontologies that follow the storage of the main tangle). +func (tangle *Tangle) GetStorageId() []byte { + return tangle.storageId +} + // Attaches a new transaction to the tangle. func (tangle *Tangle) AttachTransaction(transaction *transaction.Transaction) { tangle.storeTransactionsWorkerPool.Submit(func() { tangle.storeTransactionWorker(transaction) }) } -// Triggers the solidification of a given transaction. -func (tangle *Tangle) SolidifyTransaction(transactionId transaction.Id) { - tangle.solidifierWorkerPool.Submit(func() { - tangle.solidifyTransactionWorker(tangle.GetTransaction(transactionId), tangle.GetTransactionMetadata(transactionId)) - }) +// Retrieves a transaction from the tangle. +func (tangle *Tangle) GetTransaction(transactionId transaction.Id) *transaction.CachedTransaction { + return &transaction.CachedTransaction{CachedObject: tangle.transactionStorage.Load(transactionId[:])} +} + +// Retrieves the metadata of a transaction from the tangle. +func (tangle *Tangle) GetTransactionMetadata(transactionId transaction.Id) *transactionmetadata.CachedTransactionMetadata { + return &transactionmetadata.CachedTransactionMetadata{CachedObject: tangle.transactionMetadataStorage.Load(transactionId[:])} +} + +// Retrieves the approvers of a transaction from the tangle. +func (tangle *Tangle) GetApprovers(transactionId transaction.Id) *approvers.CachedApprovers { + return &approvers.CachedApprovers{CachedObject: tangle.approversStorage.Load(transactionId[:])} } // Deletes a transaction from the tangle (i.e. for local snapshots) @@ -84,21 +100,6 @@ func (tangle *Tangle) DeleteTransaction(transactionId transaction.Id) { tangle.Events.TransactionRemoved.Trigger(transactionId) } -// Retrieves a transaction from the tangle. -func (tangle *Tangle) GetTransaction(transactionId transaction.Id) *transaction.CachedTransaction { - return &transaction.CachedTransaction{CachedObject: tangle.transactionStorage.Load(transactionId[:])} -} - -// Retrieves the metadata of a transaction from the tangle. -func (tangle *Tangle) GetTransactionMetadata(transactionId transaction.Id) *transactionmetadata.CachedTransactionMetadata { - return &transactionmetadata.CachedTransactionMetadata{CachedObject: tangle.transactionMetadataStorage.Load(transactionId[:])} -} - -// Retrieves the approvers of a transaction from the tangle. -func (tangle *Tangle) GetApprovers(transactionId transaction.Id) *approvers.CachedApprovers { - return &approvers.CachedApprovers{CachedObject: tangle.approversStorage.Load(transactionId[:])} -} - // Marks the tangle as stopped, so it will not accept any new transactions (waits for all backgroundTasks to finish. func (tangle *Tangle) Shutdown() *Tangle { tangle.storeTransactionsWorkerPool.ShutdownGracefully() @@ -161,7 +162,7 @@ func (tangle *Tangle) storeTransactionWorker(tx *transaction.Transaction) { addTransactionToApprovers(transactionId, tx.GetBranchTransactionId()) if tangle.missingTransactionsStorage.DeleteIfPresent(transactionId[:]) { - tangle.Events.MissingTransactionAttached.Trigger(transactionId) + tangle.Events.MissingTransactionReceived.Trigger(transactionId) } tangle.Events.TransactionAttached.Trigger(cachedTransaction, cachedTransactionMetadata) diff --git a/packages/binary/transaction/id.go b/packages/binary/transaction/id.go index c940bf23ebd2e58e1175731b401b5a0749b1cdca..1f39029e5305d6f0c2e6f83fd39dae2d16f95312 100644 --- a/packages/binary/transaction/id.go +++ b/packages/binary/transaction/id.go @@ -2,6 +2,19 @@ package transaction type Id [IdLength]byte +func (id *Id) MarshalBinary() (result []byte, err error) { + result = make([]byte, IdLength) + copy(result, id[:]) + + return +} + +func (id *Id) UnmarshalBinary(data []byte) (err error) { + copy(id[:], data) + + return +} + var EmptyId = Id{} const IdLength = 64 diff --git a/packages/binary/transaction/payload/valuetransfer/valuetransfer.go b/packages/binary/transaction/payload/valuetransfer/valuetransfer.go index 6f369dc8883edb5259d6078de9192f5801c822a0..411b2f156dccc6841a581b88e22e172c4b400229 100644 --- a/packages/binary/transaction/payload/valuetransfer/valuetransfer.go +++ b/packages/binary/transaction/payload/valuetransfer/valuetransfer.go @@ -43,18 +43,26 @@ func (valueTransfer *ValueTransfer) GetType() payload.Type { return Type } -func (valueTransfer *ValueTransfer) AddInput(transferHash transfer.Hash, address address.Address) *ValueTransfer { +func (valueTransfer *ValueTransfer) AddInput(transferId transfer.Id, address address.Address) *ValueTransfer { if valueTransfer.isFinalized() { panic("you can not add inputs after you have started finalizing (sign / marshal) the ValueTransfer") } valueTransfer.inputsMutex.Lock() - valueTransfer.inputs = append(valueTransfer.inputs, transfer.NewOutputReference(transferHash, address)) + valueTransfer.inputs = append(valueTransfer.inputs, transfer.NewOutputReference(transferId, address)) valueTransfer.inputsMutex.Unlock() return valueTransfer } +func (valueTransfer *ValueTransfer) GetInputs() (result []*transfer.OutputReference) { + valueTransfer.inputsMutex.RLock() + result = valueTransfer.inputs + valueTransfer.inputsMutex.RUnlock() + + return +} + func (valueTransfer *ValueTransfer) AddOutput(address address.Address, balance *coloredcoins.ColoredBalance) *ValueTransfer { if valueTransfer.isFinalized() { panic("you can not add outputs after you have started finalizing (sign / marshal) the ValueTransfer") @@ -180,7 +188,7 @@ func (valueTransfer *ValueTransfer) marshalPayloadBytes() (result []byte) { func (valueTransfer *ValueTransfer) marshalInputs() (result []byte) { inputCount := len(valueTransfer.inputs) - marshaledTransferOutputReferenceLength := transfer.HashLength + address.Length + marshaledTransferOutputReferenceLength := transfer.IdLength + address.Length result = make([]byte, 4+inputCount*marshaledTransferOutputReferenceLength) offset := 0 @@ -281,7 +289,7 @@ func (valueTransfer *ValueTransfer) unmarshalInputs(bytes []byte, offset *int) ( *offset += 4 valueTransfer.inputs = make([]*transfer.OutputReference, inputCount) - marshaledTransferOutputReferenceLength := transfer.HashLength + address.Length + marshaledTransferOutputReferenceLength := transfer.IdLength + address.Length for i := 0; i < inputCount; i++ { var transferOutputReference transfer.OutputReference if err = transferOutputReference.UnmarshalBinary(bytes[*offset:]); err != nil { diff --git a/packages/binary/transaction/test/transaction_test.go b/packages/binary/transaction/test/transaction_test.go index 15a4be905482fb370a11b1f75f134e0cd4095f41..d94c14b09388d51d11345a435c1a3ad85c82826d 100644 --- a/packages/binary/transaction/test/transaction_test.go +++ b/packages/binary/transaction/test/transaction_test.go @@ -6,9 +6,8 @@ import ( "sync" "testing" - "github.com/iotaledger/goshimmer/packages/binary/async" - "github.com/iotaledger/goshimmer/packages/binary/signature/ed25119" + "github.com/iotaledger/hive.go/async" "github.com/iotaledger/goshimmer/packages/ledgerstate/coloredcoins" @@ -66,7 +65,7 @@ func BenchmarkVerifyValueTransactions(b *testing.B) { transactions := make([][]byte, b.N) for i := 0; i < b.N; i++ { tx := transaction.New(transaction.EmptyId, transaction.EmptyId, identity.Generate(), valuetransfer.New(). - AddInput(transfer.NewHash("test"), address.FromPublicKey(keyPairOfSourceAddress.PublicKey)). + AddInput(transfer.NewId([]byte("test")), address.FromPublicKey(keyPairOfSourceAddress.PublicKey)). AddOutput(address.FromPublicKey(keyPairOfTargetAddress.PublicKey), coloredcoins.NewColoredBalance(coloredcoins.NewColor("IOTA"), 12)). Sign(keyPairOfSourceAddress)) @@ -134,7 +133,7 @@ func TestNew(t *testing.T) { newValueTransaction1 := transaction.New(transaction.EmptyId, transaction.EmptyId, identity.Generate(), valuetransfer.New(). - AddInput(transfer.NewHash("test"), address.FromPublicKey(keyPairOfSourceAddress.PublicKey)). + AddInput(transfer.NewId("test"), address.FromPublicKey(keyPairOfSourceAddress.PublicKey)). AddOutput(address.FromPublicKey(keyPairOfTargetAddress.PublicKey), coloredcoins.NewColoredBalance(coloredcoins.NewColor("IOTA"), 12)). Sign(keyPairOfSourceAddress), ) diff --git a/packages/binary/valuetangle/events.go b/packages/binary/valuetangle/events.go new file mode 100644 index 0000000000000000000000000000000000000000..2c0352c1a74afe58d8688220e644e4886a70c8a4 --- /dev/null +++ b/packages/binary/valuetangle/events.go @@ -0,0 +1,13 @@ +package valuetangle + +import ( + "github.com/iotaledger/hive.go/events" +) + +type Events struct { + TransferAttached *events.Event + TransferSolid *events.Event + TransferMissing *events.Event + MissingTransferReceived *events.Event + TransferRemoved *events.Event +} diff --git a/packages/binary/valuetangle/model/cached_missing_transfer.go b/packages/binary/valuetangle/model/cached_missing_transfer.go new file mode 100644 index 0000000000000000000000000000000000000000..6e885c2bf0fccfcf7432644c1b8b796ca4599318 --- /dev/null +++ b/packages/binary/valuetangle/model/cached_missing_transfer.go @@ -0,0 +1,21 @@ +package model + +import ( + "github.com/iotaledger/hive.go/objectstorage" +) + +type CachedMissingTransfer struct { + *objectstorage.CachedObject +} + +func (cachedObject *CachedMissingTransfer) Unwrap() *MissingTransfer { + if untypedObject := cachedObject.Get(); untypedObject == nil { + return nil + } else { + if typedObject := untypedObject.(*MissingTransfer); typedObject == nil || typedObject.IsDeleted() { + return nil + } else { + return typedObject + } + } +} diff --git a/packages/binary/valuetangle/model/cached_valuetransfer.go b/packages/binary/valuetangle/model/cached_valuetransfer.go new file mode 100644 index 0000000000000000000000000000000000000000..0d8fdeba193911223f6f0a5905fbe57d6e7e6390 --- /dev/null +++ b/packages/binary/valuetangle/model/cached_valuetransfer.go @@ -0,0 +1,25 @@ +package model + +import ( + "github.com/iotaledger/goshimmer/packages/binary/transaction" + "github.com/iotaledger/goshimmer/packages/binary/transaction/payload/valuetransfer" + "github.com/iotaledger/goshimmer/packages/ledgerstate/transfer" +) + +type CachedValueTransfer struct { + *transaction.CachedTransaction +} + +func (cachedValueTransfer *CachedValueTransfer) Unwrap() (*valuetransfer.ValueTransfer, transfer.Id) { + if untypedTransaction := cachedValueTransfer.Get(); untypedTransaction == nil { + return nil, transfer.EmptyId + } else { + if typeCastedTransaction := untypedTransaction.(*transaction.Transaction); typeCastedTransaction == nil || typeCastedTransaction.IsDeleted() || typeCastedTransaction.GetPayload().GetType() != valuetransfer.Type { + return nil, transfer.EmptyId + } else { + transactionId := typeCastedTransaction.GetId() + + return typeCastedTransaction.GetPayload().(*valuetransfer.ValueTransfer), transfer.NewId(transactionId[:]) + } + } +} diff --git a/packages/binary/valuetangle/model/consumers.go b/packages/binary/valuetangle/model/consumers.go new file mode 100644 index 0000000000000000000000000000000000000000..3f8edf84f405da27fd263f424dedfa71c3a93d33 --- /dev/null +++ b/packages/binary/valuetangle/model/consumers.go @@ -0,0 +1,162 @@ +package model + +import ( + "encoding/binary" + "sync" + + "github.com/iotaledger/goshimmer/packages/binary/types" + "github.com/iotaledger/goshimmer/packages/ledgerstate/transfer" + "github.com/iotaledger/hive.go/objectstorage" +) + +type Consumers struct { + objectstorage.StorableObjectFlags + + transferId transfer.Id + consumers map[transfer.Id]types.Empty + consumersMutex sync.RWMutex +} + +func NewConsumers(transferId transfer.Id) *Consumers { + return &Consumers{ + transferId: transferId, + consumers: make(map[transfer.Id]types.Empty), + } +} + +// Get's called when we restore the approvers from storage. The bytes and the content will be unmarshaled by an external +// caller (the objectStorage factory). +func FromStorage(id []byte) (result objectstorage.StorableObject) { + var transferId transfer.Id + copy(transferId[:], id) + + result = &Consumers{ + transferId: transferId, + } + + return +} + +func (consumers *Consumers) GetTransferId() transfer.Id { + return consumers.transferId +} + +func (consumers *Consumers) Get() (result map[transfer.Id]types.Empty) { + consumers.consumersMutex.RLock() + result = make(map[transfer.Id]types.Empty, len(consumers.consumers)) + for approverId := range consumers.consumers { + result[approverId] = types.Void + } + consumers.consumersMutex.RUnlock() + + return +} + +func (consumers *Consumers) Add(transferId transfer.Id) (modified bool) { + consumers.consumersMutex.RLock() + if _, exists := consumers.consumers[transferId]; !exists { + consumers.consumersMutex.RUnlock() + + consumers.consumersMutex.Lock() + if _, exists := consumers.consumers[transferId]; !exists { + consumers.consumers[transferId] = types.Void + + modified = true + + consumers.SetModified() + } + consumers.consumersMutex.Unlock() + } else { + consumers.consumersMutex.RUnlock() + } + + return +} + +func (consumers *Consumers) Remove(transferId transfer.Id) (modified bool) { + consumers.consumersMutex.RLock() + if _, exists := consumers.consumers[transferId]; exists { + consumers.consumersMutex.RUnlock() + + consumers.consumersMutex.Lock() + if _, exists := consumers.consumers[transferId]; exists { + delete(consumers.consumers, transferId) + + modified = true + + consumers.SetModified() + } + consumers.consumersMutex.Unlock() + } else { + consumers.consumersMutex.RUnlock() + } + + return +} + +func (consumers *Consumers) Size() (result int) { + consumers.consumersMutex.RLock() + result = len(consumers.consumers) + consumers.consumersMutex.RUnlock() + + return +} + +func (consumers *Consumers) GetStorageKey() []byte { + transferId := consumers.GetTransferId() + + return transferId[:] +} + +func (consumers *Consumers) Update(other objectstorage.StorableObject) { + panic("approvers should never be overwritten and only stored once to optimize IO") +} + +func (consumers *Consumers) MarshalBinary() (result []byte, err error) { + consumers.consumersMutex.RLock() + + approversCount := len(consumers.consumers) + result = make([]byte, 4+approversCount*transfer.IdLength) + offset := 0 + + binary.LittleEndian.PutUint32(result[offset:], uint32(approversCount)) + offset += 4 + + for approverId := range consumers.consumers { + marshaledBytes, marshalErr := approverId.MarshalBinary() + if marshalErr != nil { + err = marshalErr + + consumers.consumersMutex.RUnlock() + + return + } + + copy(result[offset:], marshaledBytes) + offset += len(marshaledBytes) + } + + consumers.consumersMutex.RUnlock() + + return +} + +func (consumers *Consumers) UnmarshalBinary(data []byte) (err error) { + consumers.consumers = make(map[transfer.Id]types.Empty) + offset := 0 + + approversCount := int(binary.LittleEndian.Uint32(data[offset:])) + offset += 4 + + for i := 0; i < approversCount; i++ { + var approverId transfer.Id + if err = approverId.UnmarshalBinary(data[offset:]); err != nil { + return + } + offset += transfer.IdLength + + consumers.consumers[approverId] = types.Void + } + + return +} diff --git a/packages/binary/valuetangle/model/missingtransfer.go b/packages/binary/valuetangle/model/missingtransfer.go new file mode 100644 index 0000000000000000000000000000000000000000..f9772bbf3e598d09e7837a22173db41cb85eac9d --- /dev/null +++ b/packages/binary/valuetangle/model/missingtransfer.go @@ -0,0 +1,54 @@ +package model + +import ( + "time" + + "github.com/iotaledger/goshimmer/packages/ledgerstate/transfer" + + "github.com/iotaledger/hive.go/objectstorage" +) + +type MissingTransfer struct { + objectstorage.StorableObjectFlags + + transferId transfer.Id + missingSince time.Time +} + +func New(transferId transfer.Id) *MissingTransfer { + return &MissingTransfer{ + transferId: transferId, + missingSince: time.Now(), + } +} + +func MissingTransferFromStorage(key []byte) objectstorage.StorableObject { + result := &MissingTransfer{} + copy(result.transferId[:], key) + + return result +} + +func (missingTransfer *MissingTransfer) GetTransferId() transfer.Id { + return missingTransfer.transferId +} + +func (missingTransfer *MissingTransfer) GetMissingSince() time.Time { + return missingTransfer.missingSince +} + +func (missingTransfer *MissingTransfer) GetStorageKey() []byte { + return missingTransfer.transferId[:] +} + +func (missingTransfer *MissingTransfer) Update(other objectstorage.StorableObject) { + panic("missing transfer should never be overwritten and only stored once to optimize IO") +} + +func (missingTransfer *MissingTransfer) MarshalBinary() (result []byte, err error) { + return missingTransfer.missingSince.MarshalBinary() +} + +func (missingTransfer *MissingTransfer) UnmarshalBinary(data []byte) (err error) { + return missingTransfer.missingSince.UnmarshalBinary(data) +} diff --git a/packages/binary/valuetangle/valuetangle.go b/packages/binary/valuetangle/valuetangle.go new file mode 100644 index 0000000000000000000000000000000000000000..e5b107d0025ea7514f60866f864ca461c02fd6a1 --- /dev/null +++ b/packages/binary/valuetangle/valuetangle.go @@ -0,0 +1,246 @@ +package valuetangle + +import ( + "container/list" + + "github.com/iotaledger/goshimmer/packages/binary/address" + "github.com/iotaledger/goshimmer/packages/binary/tangle" + "github.com/iotaledger/goshimmer/packages/binary/tangle/approvers" + "github.com/iotaledger/goshimmer/packages/binary/tangle/missingtransaction" + "github.com/iotaledger/goshimmer/packages/binary/transaction" + "github.com/iotaledger/goshimmer/packages/binary/transaction/payload/valuetransfer" + "github.com/iotaledger/goshimmer/packages/binary/transactionmetadata" + "github.com/iotaledger/goshimmer/packages/binary/types" + "github.com/iotaledger/goshimmer/packages/binary/valuetangle/model" + "github.com/iotaledger/goshimmer/packages/ledgerstate/transfer" + "github.com/iotaledger/hive.go/async" + "github.com/iotaledger/hive.go/events" + "github.com/iotaledger/hive.go/objectstorage" +) + +// The value tangle defines an "ontology" on top of the tangle that "sees"" the value transfers as a hidden tangle in +// the tangle. +type ValueTangle struct { + tangle *tangle.Tangle + + transferMetadataStorage *objectstorage.ObjectStorage + consumersStorage *objectstorage.ObjectStorage + missingTransferStorage *objectstorage.ObjectStorage + + storeTransactionsWorkerPool async.WorkerPool + solidifierWorkerPool async.WorkerPool + // cleanupWorkerPool async.WorkerPool + + Events Events +} + +func New(tangle *tangle.Tangle) (valueTangle *ValueTangle) { + valueTangle = &ValueTangle{ + tangle: tangle, + } + + tangle.Events.TransactionSolid.Attach(events.NewClosure(valueTangle.attachTransaction)) + tangle.Events.TransactionRemoved.Attach(events.NewClosure(valueTangle.deleteTransfer)) + + return +} + +func (valueTangle *ValueTangle) attachTransaction(cachedTransaction *transaction.CachedTransaction, cachedTransactionMetadata *transactionmetadata.CachedTransactionMetadata) { + valueTangle.storeTransactionsWorkerPool.Submit(func() { + valueTangle.storeTransactionWorker(cachedTransaction, cachedTransactionMetadata) + }) +} + +// Retrieves a transaction from the tangle. +func (valueTangle *ValueTangle) GetTransfer(transactionId transaction.Id) *model.CachedValueTransfer { + cachedTransaction := valueTangle.tangle.GetTransaction(transactionId) + + // return an empty result if the transaction is no value transaction + if tx := cachedTransaction.Unwrap(); tx != nil && tx.GetPayload().GetType() != valuetransfer.Type { + cachedTransaction.Release() + + return &model.CachedValueTransfer{CachedTransaction: &transaction.CachedTransaction{CachedObject: objectstorage.NewEmptyCachedObject(transactionId[:])}} + } + + return &model.CachedValueTransfer{CachedTransaction: cachedTransaction} +} + +// Retrieves the metadata of a transaction from the tangle. +func (valueTangle *ValueTangle) GetTransferMetadata(transactionId transaction.Id) *transactionmetadata.CachedTransactionMetadata { + return &transactionmetadata.CachedTransactionMetadata{CachedObject: valueTangle.transferMetadataStorage.Load(transactionId[:])} +} + +func (valueTangle *ValueTangle) deleteTransfer(transactionId transaction.Id) { + +} + +// Marks the tangle as stopped, so it will not accept any new transactions (waits for all backgroundTasks to finish. +func (valueTangle *ValueTangle) Shutdown() *ValueTangle { + valueTangle.storeTransactionsWorkerPool.ShutdownGracefully() + + return valueTangle +} + +func (valueTangle *ValueTangle) storeTransactionWorker(cachedTx *transaction.CachedTransaction, cachedTxMetadata *transactionmetadata.CachedTransactionMetadata) { + addTransferToConsumers := func(transferId transfer.Id, consumedTransferHash transfer.Id) { + cachedConsumers := valueTangle.consumersStorage.ComputeIfAbsent(consumedTransferHash[:], func([]byte) objectstorage.StorableObject { + result := model.NewConsumers(consumedTransferHash) + + result.SetModified() + + return result + }) + + if _tmp := cachedConsumers.Get(); _tmp != nil { + if consumersObject := _tmp.(*model.Consumers); consumersObject != nil { + consumersObject.Add(transferId) + + // if the approvers got "cleaned up" while being in cache, we make sure the object gets persisted again + consumersObject.Persist() + } + } + + cachedConsumers.Release() + } + + cachedTxMetadata.Release() + + cachedValueTransfer := &model.CachedValueTransfer{CachedTransaction: cachedTx} + + valueTransfer, transferId := cachedValueTransfer.Unwrap() + if valueTransfer == nil { + cachedValueTransfer.Release() + + return + } + + for transferId := range valueTangle.getInputsMap(valueTransfer) { + addTransferToConsumers(transferId, transferId) + } + + if valueTangle.missingTransferStorage.DeleteIfPresent(transferId[:]) { + valueTangle.Events.MissingTransferReceived.Trigger(transferId) + } + + valueTangle.Events.TransferAttached.Trigger(cachedValueTransfer) + + valueTangle.solidifierWorkerPool.Submit(func() { + valueTangle.solidifyTransferWorker(cachedValueTransfer, transferId) + }) +} + +// Worker that solidifies the transactions (recursively from past to present). +func (valueTangle *ValueTangle) solidifyTransferWorker(cachedValueTransfer *model.CachedValueTransfer, transferId transfer.Id) { + isTransferMarkedAsSolid := func(transactionId transaction.Id) bool { + if transactionId == transaction.EmptyId { + return true + } + + transferMetadataCached := valueTangle.GetTransferMetadata(transactionId) + if transactionMetadata := transferMetadataCached.Unwrap(); transactionMetadata == nil { + transferMetadataCached.Release() + + // if transaction is missing and was not reported as missing, yet + if cachedMissingTransfer, missingTransactionStored := valueTangle.missingTransferStorage.StoreIfAbsent(transactionId[:], missingtransaction.New(transactionId)); missingTransactionStored { + cachedMissingTransfer.Consume(func(object objectstorage.StorableObject) { + valueTangle.monitorMissingTransactionWorker(object.(*missingtransaction.MissingTransaction).GetTransactionId()) + }) + } + + return false + } else if !transactionMetadata.IsSolid() { + transferMetadataCached.Release() + + return false + } + transferMetadataCached.Release() + + return true + } + + isTransactionSolid := func(transaction *transaction.Transaction, transactionMetadata *transactionmetadata.TransactionMetadata) bool { + if transaction == nil || transaction.IsDeleted() { + return false + } + + if transactionMetadata == nil || transactionMetadata.IsDeleted() { + return false + } + + if transactionMetadata.IsSolid() { + return true + } + + // 1. check tangle solidity + isTrunkSolid := isTransferMarkedAsSolid(transaction.GetTrunkTransactionId()) + isBranchSolid := isTransferMarkedAsSolid(transaction.GetBranchTransactionId()) + if isTrunkSolid && isBranchSolid { + // 2. check payload solidity + return true + } + + return false + } + + popElementsFromStack := func(stack *list.List) (*transaction.CachedTransaction, *transactionmetadata.CachedTransactionMetadata) { + currentSolidificationEntry := stack.Front() + currentCachedTransaction := currentSolidificationEntry.Value.([2]interface{})[0] + currentCachedTransactionMetadata := currentSolidificationEntry.Value.([2]interface{})[1] + stack.Remove(currentSolidificationEntry) + + return currentCachedTransaction.(*transaction.CachedTransaction), currentCachedTransactionMetadata.(*transactionmetadata.CachedTransactionMetadata) + } + + // initialize the stack + solidificationStack := list.New() + solidificationStack.PushBack([2]interface{}{cachedValueTransfer, transferId}) + + // process transactions that are supposed to be checked for solidity recursively + for solidificationStack.Len() > 0 { + currentCachedTransaction, currentCachedTransactionMetadata := popElementsFromStack(solidificationStack) + + currentTransaction := currentCachedTransaction.Unwrap() + currentTransactionMetadata := currentCachedTransactionMetadata.Unwrap() + if currentTransaction == nil || currentTransactionMetadata == nil { + currentCachedTransaction.Release() + currentCachedTransactionMetadata.Release() + + continue + } + + // if current transaction is solid and was not marked as solid before: mark as solid and propagate + if isTransactionSolid(currentTransaction, currentTransactionMetadata) && currentTransactionMetadata.SetSolid(true) { + valueTangle.Events.TransferSolid.Trigger(currentCachedTransaction, currentCachedTransactionMetadata) + + valueTangle.GetConsumers(currentTransaction.GetId()).Consume(func(object objectstorage.StorableObject) { + for approverTransactionId := range object.(*approvers.Approvers).Get() { + solidificationStack.PushBack([2]interface{}{ + valueTangle.GetTransfer(approverTransactionId), + valueTangle.GetTransferMetadata(approverTransactionId), + }) + } + }) + } + + // release cached results + currentCachedTransaction.Release() + currentCachedTransactionMetadata.Release() + } +} + +func (valueTangle *ValueTangle) getInputsMap(valueTransfer *valuetransfer.ValueTransfer) (result map[transfer.Id]map[address.Address]types.Empty) { + result = make(map[transfer.Id]map[address.Address]types.Empty) + + for _, transferOutputReference := range valueTransfer.GetInputs() { + addressMap, addressMapExists := result[transferOutputReference.GetTransferHash()] + if !addressMapExists { + addressMap = make(map[address.Address]types.Empty) + + result[transferOutputReference.GetTransferHash()] = addressMap + } + + addressMap[transferOutputReference.GetAddress()] = types.Void + } + + return +} diff --git a/packages/ledgerstate/conflict/id.go b/packages/ledgerstate/conflict/id.go index 8ceabbcef8fbb6537f78070ebb88c39c26f20ac4..377b3e45d5d0f648f3c82f1892c5a7d2c9d57063 100644 --- a/packages/ledgerstate/conflict/id.go +++ b/packages/ledgerstate/conflict/id.go @@ -16,19 +16,19 @@ type Id [IdLength]byte func NewId(conflictBytes ...interface{}) (result Id) { switch len(conflictBytes) { case 2: - transferHash, ok := conflictBytes[0].(transfer.Hash) + transferHash, ok := conflictBytes[0].(transfer.Id) if !ok { panic("expected first parameter of NewId to be a TransferHash") } - addressHash, ok := conflictBytes[0].(transfer.Hash) + addressHash, ok := conflictBytes[0].(transfer.Id) if !ok { panic("expected second parameter of NewId to be a AddressHash") } - fullConflictSetIdentifier := make([]byte, transfer.HashLength+address.Length) + fullConflictSetIdentifier := make([]byte, transfer.IdLength+address.Length) copy(fullConflictSetIdentifier, transferHash[:]) - copy(fullConflictSetIdentifier[transfer.HashLength:], addressHash[:]) + copy(fullConflictSetIdentifier[transfer.IdLength:], addressHash[:]) result = blake2b.Sum256(fullConflictSetIdentifier) case 1: diff --git a/packages/ledgerstate/ledgerstate.go b/packages/ledgerstate/ledgerstate.go index 48d48147ddb2cc3b58bf664442d9c7d793b2d807..b017becee8589bbe2b353398b323f1117524890c 100644 --- a/packages/ledgerstate/ledgerstate.go +++ b/packages/ledgerstate/ledgerstate.go @@ -50,7 +50,7 @@ func NewLedgerState(storageId []byte) *LedgerState { return result } -func (ledgerState *LedgerState) AddTransferOutput(transferHash transfer.Hash, addressHash address.Address, balances ...*coloredcoins.ColoredBalance) *LedgerState { +func (ledgerState *LedgerState) AddTransferOutput(transferHash transfer.Id, addressHash address.Address, balances ...*coloredcoins.ColoredBalance) *LedgerState { ledgerState.GetReality(reality.MAIN_ID).Consume(func(object objectstorage.StorableObject) { mainReality := object.(*Reality) @@ -153,7 +153,7 @@ func (ledgerState *LedgerState) BookTransfer(transfer *transfer.Transfer) (err e ledgerState.getTargetReality(inputs).Consume(func(object objectstorage.StorableObject) { targetReality := object.(*Reality) - if err = targetReality.bookTransfer(transfer.GetHash(), inputs, transfer.GetOutputs()); err != nil { + if err = targetReality.bookTransfer(transfer.GetId(), inputs, transfer.GetOutputs()); err != nil { return } @@ -388,7 +388,7 @@ func (ledgerState *LedgerState) Prune() *LedgerState { func (ledgerState *LedgerState) generateFilterPrefixes(filters []interface{}) ([][]byte, bool) { filteredRealities := make([]reality.Id, 0) filteredAddresses := make([]address.Address, 0) - filteredTransfers := make([]transfer.Hash, 0) + filteredTransfers := make([]transfer.Id, 0) filterSpent := false filterUnspent := false @@ -398,7 +398,7 @@ func (ledgerState *LedgerState) generateFilterPrefixes(filters []interface{}) ([ filteredRealities = append(filteredRealities, typeCastedValue) case address.Address: filteredAddresses = append(filteredAddresses, typeCastedValue) - case transfer.Hash: + case transfer.Id: filteredTransfers = append(filteredTransfers, typeCastedValue) case transfer.SpentIndicator: switch typeCastedValue { diff --git a/packages/ledgerstate/ledgerstate_test.go b/packages/ledgerstate/ledgerstate_test.go index bcac9f2fe5ae44a49594fe11e191a23640705eae..62c5c975404d649087f1dcbfeb69a2d065fa25e2 100644 --- a/packages/ledgerstate/ledgerstate_test.go +++ b/packages/ledgerstate/ledgerstate_test.go @@ -17,12 +17,12 @@ import ( var ( iota_ = coloredcoins.NewColor("IOTA") eth = coloredcoins.NewColor("ETH") - transferHash1 = transfer.NewHash("TRANSFER1") - transferHash2 = transfer.NewHash("TRANSFER2") - transferHash3 = transfer.NewHash("TRANSFER3") - transferHash4 = transfer.NewHash("TRANSFER4") - transferHash5 = transfer.NewHash("TRANSFER5") - transferHash6 = transfer.NewHash("TRANSFER6") + transferHash1 = transfer.NewId([]byte("TRANSFER1")) + transferHash2 = transfer.NewId([]byte("TRANSFER2")) + transferHash3 = transfer.NewId([]byte("TRANSFER3")) + transferHash4 = transfer.NewId([]byte("TRANSFER4")) + transferHash5 = transfer.NewId([]byte("TRANSFER5")) + transferHash6 = transfer.NewId([]byte("TRANSFER6")) addressHash1 = address.New([]byte("ADDRESS1")) addressHash2 = address.New([]byte("ADDRESS2")) addressHash3 = address.New([]byte("ADDRESS3")) @@ -42,7 +42,7 @@ func Benchmark(b *testing.B) { lastTransferHash := transferHash1 for i := 0; i < b.N; i++ { - newTransferHash := transfer.NewHash(strconv.Itoa(i)) + newTransferHash := transfer.NewId([]byte(strconv.Itoa(i))) if err := ledgerState.BookTransfer(transfer.NewTransfer(newTransferHash).AddInput( transfer.NewOutputReference(lastTransferHash, addressHash1), @@ -108,10 +108,10 @@ func Test(t *testing.T) { var transferHashCounter = 0 -func generateRandomTransferHash() transfer.Hash { +func generateRandomTransferHash() transfer.Id { transferHashCounter++ - return transfer.NewHash("TRANSFER" + strconv.Itoa(transferHashCounter)) + return transfer.NewId([]byte("TRANSFER" + strconv.Itoa(transferHashCounter))) } var addressHashCounter = 0 diff --git a/packages/ledgerstate/outputs.png b/packages/ledgerstate/outputs.png index fbc282cfbfb8ee744230710f46f058f8ff961ccd..c1b424939d22f696b0484d0840cb43566bcee6af 100644 Binary files a/packages/ledgerstate/outputs.png and b/packages/ledgerstate/outputs.png differ diff --git a/packages/ledgerstate/outputs1.png b/packages/ledgerstate/outputs1.png index 6da15b92f4a01206aa38ff8b10439123feb22e41..abb2351f713fe9acff448e2fef9411e4e45dc300 100644 Binary files a/packages/ledgerstate/outputs1.png and b/packages/ledgerstate/outputs1.png differ diff --git a/packages/ledgerstate/outputs2.png b/packages/ledgerstate/outputs2.png index 4628b918f78a8d522080837b68b29fb28159a0d8..e05b9dc06842dff660319bcf21edd8f4d9da0707 100644 Binary files a/packages/ledgerstate/outputs2.png and b/packages/ledgerstate/outputs2.png differ diff --git a/packages/ledgerstate/realities.png b/packages/ledgerstate/realities.png index 9415fdb9f6fee591eada13e24e86fb98a923a16b..049e17a2f46861b937f81d3400acf808ca4ac833 100644 Binary files a/packages/ledgerstate/realities.png and b/packages/ledgerstate/realities.png differ diff --git a/packages/ledgerstate/reality.go b/packages/ledgerstate/reality.go index 09bb808a7df694f9c15af51fb5752fe77573fa89..efbc72c629496297eb427f213fbaef82387ae767 100644 --- a/packages/ledgerstate/reality.go +++ b/packages/ledgerstate/reality.go @@ -424,7 +424,7 @@ func (mreality *Reality) CreateReality(id reality.Id) *objectstorage.CachedObjec // Books a transfer into this reality (wrapper for the private bookTransfer function). func (mreality *Reality) BookTransfer(transfer *transfer.Transfer) (err error) { - err = mreality.bookTransfer(transfer.GetHash(), mreality.ledgerState.getTransferInputs(transfer), transfer.GetOutputs()) + err = mreality.bookTransfer(transfer.GetId(), mreality.ledgerState.getTransferInputs(transfer), transfer.GetOutputs()) return } @@ -450,7 +450,7 @@ func (mreality *Reality) String() (result string) { } // Books a transfer into this reality (contains the dispatcher for the actual tasks). -func (mreality *Reality) bookTransfer(transferHash transfer.Hash, inputs objectstorage.CachedObjects, outputs map[address.Address][]*coloredcoins.ColoredBalance) (err error) { +func (mreality *Reality) bookTransfer(transferHash transfer.Id, inputs objectstorage.CachedObjects, outputs map[address.Address][]*coloredcoins.ColoredBalance) (err error) { if err = mreality.verifyTransfer(inputs, outputs); err != nil { return } @@ -511,7 +511,7 @@ func (mreality *Reality) verifyTransfer(inputs []*objectstorage.CachedObject, ou // Internal utility function that marks the consumed inputs as spent and returns the corresponding conflicts if the // inputs have been consumed before. -func (mreality *Reality) consumeInputs(inputs objectstorage.CachedObjects, transferHash transfer.Hash, outputs map[address.Address][]*coloredcoins.ColoredBalance) (conflicts objectstorage.CachedObjects, err error) { +func (mreality *Reality) consumeInputs(inputs objectstorage.CachedObjects, transferHash transfer.Id, outputs map[address.Address][]*coloredcoins.ColoredBalance) (conflicts objectstorage.CachedObjects, err error) { conflicts = make(objectstorage.CachedObjects, 0) for _, input := range inputs { @@ -539,7 +539,7 @@ func (mreality *Reality) consumeInputs(inputs objectstorage.CachedObjects, trans // // If the inputs have been used before and we consequently have a non-empty list of conflicts, we first create a new // reality for the inputs and then book the transfer outputs into the correct reality. -func (mreality *Reality) createTransferOutputs(transferHash transfer.Hash, outputs map[address.Address][]*coloredcoins.ColoredBalance, conflicts objectstorage.CachedObjects) (err error) { +func (mreality *Reality) createTransferOutputs(transferHash transfer.Id, outputs map[address.Address][]*coloredcoins.ColoredBalance, conflicts objectstorage.CachedObjects) (err error) { if len(conflicts) >= 1 { targetRealityId := transferHash.ToRealityId() @@ -588,7 +588,7 @@ func (mreality *Reality) collectParentConflictRealities(parentConflictRealities // Utility function that processes a conflicting input by retrieving the corresponding conflict. // If there is a non-empty list of consumers to elevate, we elevate them. -func (mreality *Reality) processConflictingInput(input *transfer.Output, consumersToElevate map[transfer.Hash][]address.Address) (cachedConflict *objectstorage.CachedObject, err error) { +func (mreality *Reality) processConflictingInput(input *transfer.Output, consumersToElevate map[transfer.Id][]address.Address) (cachedConflict *objectstorage.CachedObject, err error) { conflictId := conflict.NewId(input.GetTransferHash(), input.GetAddressHash()) if len(consumersToElevate) >= 1 { @@ -603,7 +603,7 @@ func (mreality *Reality) processConflictingInput(input *transfer.Output, consume } // Creates a Reality for the consumers of the conflicting inputs and registers it as part of the corresponding Conflict. -func (mreality *Reality) createRealityForPreviouslyUnconflictingConsumers(consumersOfConflictingInput map[transfer.Hash][]address.Address, conflict *conflict.Conflict) (err error) { +func (mreality *Reality) createRealityForPreviouslyUnconflictingConsumers(consumersOfConflictingInput map[transfer.Id][]address.Address, conflict *conflict.Conflict) (err error) { for transferHash, addressHashes := range consumersOfConflictingInput { elevatedRealityId := transferHash.ToRealityId() diff --git a/packages/ledgerstate/transfer/constants.go b/packages/ledgerstate/transfer/constants.go index 03b144466dda030547b996bf1188c3259bdb86b6..c3a690bd4eaf5c859933112a0d5893acf5224f2f 100644 --- a/packages/ledgerstate/transfer/constants.go +++ b/packages/ledgerstate/transfer/constants.go @@ -16,7 +16,7 @@ const ( marshalTransferOutputBookingSpentStart = marshalTransferOutputBookingAddressHashEnd marshalTransferOutputBookingSpentEnd = marshalTransferOutputBookingSpentStart + 1 marshalTransferOutputBookingTransferHashStart = marshalTransferOutputBookingSpentEnd - marshalTransferOutputBookingTransferHashEnd = marshalTransferOutputBookingTransferHashStart + HashLength + marshalTransferOutputBookingTransferHashEnd = marshalTransferOutputBookingTransferHashStart + IdLength marshalTransferOutputBookingTotalLength = marshalTransferOutputBookingTransferHashEnd ) diff --git a/packages/ledgerstate/transfer/hash.go b/packages/ledgerstate/transfer/hash.go deleted file mode 100644 index ed95f24839d491af50f291e8528132db62659cf3..0000000000000000000000000000000000000000 --- a/packages/ledgerstate/transfer/hash.go +++ /dev/null @@ -1,38 +0,0 @@ -package transfer - -import ( - "unicode/utf8" - - "github.com/iotaledger/goshimmer/packages/ledgerstate/reality" - "github.com/iotaledger/goshimmer/packages/stringify" -) - -type Hash [HashLength]byte - -func NewHash(transferHash string) (result Hash) { - copy(result[:], transferHash) - - return -} - -func (transferHash Hash) ToRealityId() (realityId reality.Id) { - copy(realityId[:], transferHash[:]) - - return -} - -func (transferHash *Hash) UnmarshalBinary(data []byte) error { - copy(transferHash[:], data[:HashLength]) - - return nil -} - -func (transferHash Hash) String() string { - if utf8.Valid(transferHash[:]) { - return string(transferHash[:]) - } else { - return stringify.SliceOfBytes(transferHash[:]) - } -} - -const HashLength = 32 diff --git a/packages/ledgerstate/transfer/id.go b/packages/ledgerstate/transfer/id.go new file mode 100644 index 0000000000000000000000000000000000000000..8431ea04dc7358f49bb1e49727f30c9321789461 --- /dev/null +++ b/packages/ledgerstate/transfer/id.go @@ -0,0 +1,47 @@ +package transfer + +import ( + "unicode/utf8" + + "github.com/iotaledger/goshimmer/packages/ledgerstate/reality" + "github.com/iotaledger/goshimmer/packages/stringify" +) + +type Id [IdLength]byte + +func NewId(id []byte) (result Id) { + copy(result[:], id) + + return +} + +func (transferId Id) ToRealityId() (realityId reality.Id) { + copy(realityId[:], transferId[:]) + + return +} + +func (transferId *Id) MarshalBinary() (result []byte, err error) { + result = make([]byte, IdLength) + copy(result, transferId[:]) + + return +} + +func (transferId *Id) UnmarshalBinary(data []byte) error { + copy(transferId[:], data[:IdLength]) + + return nil +} + +func (transferId Id) String() string { + if utf8.Valid(transferId[:]) { + return string(transferId[:]) + } else { + return stringify.SliceOfBytes(transferId[:]) + } +} + +var EmptyId = Id{} + +const IdLength = 32 diff --git a/packages/ledgerstate/transfer/output.go b/packages/ledgerstate/transfer/output.go index 9afeefb67a0fc12618199b078a5cb32dfbc11f2a..8c7f5b06b981bfa13a94d7e9429dddd4b80ffab4 100644 --- a/packages/ledgerstate/transfer/output.go +++ b/packages/ledgerstate/transfer/output.go @@ -15,11 +15,11 @@ import ( type Output struct { objectstorage.StorableObjectFlags - transferHash Hash + transferHash Id addressHash address.Address balances []*coloredcoins.ColoredBalance realityId reality.Id - consumers map[Hash][]address.Address + consumers map[Id][]address.Address storageKey []byte OutputBookings *objectstorage.ObjectStorage @@ -29,20 +29,20 @@ type Output struct { bookingMutex sync.Mutex } -func NewTransferOutput(outputBookings *objectstorage.ObjectStorage, realityId reality.Id, transferHash Hash, addressHash address.Address, balances ...*coloredcoins.ColoredBalance) *Output { +func NewTransferOutput(outputBookings *objectstorage.ObjectStorage, realityId reality.Id, transferHash Id, addressHash address.Address, balances ...*coloredcoins.ColoredBalance) *Output { return &Output{ transferHash: transferHash, addressHash: addressHash, balances: balances, realityId: realityId, - consumers: make(map[Hash][]address.Address), + consumers: make(map[Id][]address.Address), storageKey: append(transferHash[:], addressHash[:]...), OutputBookings: outputBookings, } } -func (transferOutput *Output) GetTransferHash() (transferHash Hash) { +func (transferOutput *Output) GetTransferHash() (transferHash Id) { transferHash = transferOutput.transferHash return @@ -81,8 +81,8 @@ func (transferOutput *Output) GetBalances() []*coloredcoins.ColoredBalance { return transferOutput.balances } -func (transferOutput *Output) GetConsumers() (consumers map[Hash][]address.Address) { - consumers = make(map[Hash][]address.Address) +func (transferOutput *Output) GetConsumers() (consumers map[Id][]address.Address) { + consumers = make(map[Id][]address.Address) transferOutput.consumersMutex.RLock() for transferHash, addresses := range transferOutput.consumers { @@ -94,7 +94,7 @@ func (transferOutput *Output) GetConsumers() (consumers map[Hash][]address.Addre return } -func (transferOutput *Output) AddConsumer(consumer Hash, outputs map[address.Address][]*coloredcoins.ColoredBalance) (consumersToElevate map[Hash][]address.Address, err error) { +func (transferOutput *Output) AddConsumer(consumer Id, outputs map[address.Address][]*coloredcoins.ColoredBalance) (consumersToElevate map[Id][]address.Address, err error) { transferOutput.consumersMutex.RLock() if _, exist := transferOutput.consumers[consumer]; exist { transferOutput.consumersMutex.RUnlock() @@ -107,13 +107,13 @@ func (transferOutput *Output) AddConsumer(consumer Hash, outputs map[address.Add consumersToElevate = nil err = transferOutput.markAsSpent() case 1: - consumersToElevate = make(map[Hash][]address.Address, 1) + consumersToElevate = make(map[Id][]address.Address, 1) for transferHash, addresses := range transferOutput.consumers { consumersToElevate[transferHash] = addresses } err = nil default: - consumersToElevate = make(map[Hash][]address.Address) + consumersToElevate = make(map[Id][]address.Address) err = nil } consumers := make([]address.Address, len(outputs)) @@ -174,7 +174,7 @@ func (transferOutput *Output) MarshalBinary() ([]byte, error) { balanceCount := len(transferOutput.balances) consumerCount := len(transferOutput.consumers) - serializedLength := reality.IdLength + 4 + balanceCount*coloredcoins.BalanceLength + 4 + consumerCount*HashLength + serializedLength := reality.IdLength + 4 + balanceCount*coloredcoins.BalanceLength + 4 + consumerCount*IdLength for _, addresses := range transferOutput.consumers { serializedLength += 4 for range addresses { @@ -198,8 +198,8 @@ func (transferOutput *Output) MarshalBinary() ([]byte, error) { binary.LittleEndian.PutUint32(result[offset:], uint32(consumerCount)) offset += 4 for transferHash, addresses := range transferOutput.consumers { - copy(result[offset:], transferHash[:HashLength]) - offset += HashLength + copy(result[offset:], transferHash[:IdLength]) + offset += IdLength binary.LittleEndian.PutUint32(result[offset:], uint32(len(addresses))) offset += 4 @@ -217,11 +217,11 @@ func (transferOutput *Output) MarshalBinary() ([]byte, error) { } func (transferOutput *Output) UnmarshalBinary(serializedObject []byte) error { - if err := transferOutput.transferHash.UnmarshalBinary(transferOutput.storageKey[:HashLength]); err != nil { + if err := transferOutput.transferHash.UnmarshalBinary(transferOutput.storageKey[:IdLength]); err != nil { return err } - if err := transferOutput.addressHash.UnmarshalBinary(transferOutput.storageKey[HashLength:]); err != nil { + if err := transferOutput.addressHash.UnmarshalBinary(transferOutput.storageKey[IdLength:]); err != nil { return err } @@ -268,19 +268,19 @@ func (transferOutput *Output) unmarshalBalances(serializedBalances []byte) ([]*c return balances, nil } -func (transferOutput *Output) unmarshalConsumers(serializedConsumers []byte) (map[Hash][]address.Address, error) { +func (transferOutput *Output) unmarshalConsumers(serializedConsumers []byte) (map[Id][]address.Address, error) { offset := 0 consumerCount := int(binary.LittleEndian.Uint32(serializedConsumers[offset:])) offset += 4 - consumers := make(map[Hash][]address.Address, consumerCount) + consumers := make(map[Id][]address.Address, consumerCount) for i := 0; i < consumerCount; i++ { - transferHash := Hash{} + transferHash := Id{} if err := transferHash.UnmarshalBinary(serializedConsumers[offset:]); err != nil { return nil, err } - offset += HashLength + offset += IdLength addressHashCount := int(binary.LittleEndian.Uint32(serializedConsumers[offset:])) offset += 4 diff --git a/packages/ledgerstate/transfer/output_booking.go b/packages/ledgerstate/transfer/output_booking.go index e18a77c1d807bde5afd0e86869345731090b0c5a..e268015151c3eb1437bd584fb729aa86262981d8 100644 --- a/packages/ledgerstate/transfer/output_booking.go +++ b/packages/ledgerstate/transfer/output_booking.go @@ -8,8 +8,8 @@ import ( // region private utility methods ////////////////////////////////////////////////////////////////////////////////////// -func GenerateOutputBookingStorageKey(realityId reality.Id, addressHash address.Address, spent bool, transferHash Hash) (storageKey []byte) { - storageKey = make([]byte, reality.IdLength+address.Length+1+HashLength) +func GenerateOutputBookingStorageKey(realityId reality.Id, addressHash address.Address, spent bool, transferHash Id) (storageKey []byte) { + storageKey = make([]byte, reality.IdLength+address.Length+1+IdLength) copy(storageKey[marshalTransferOutputBookingRealityIdStart:marshalTransferOutputBookingRealityIdEnd], realityId[:reality.IdLength]) copy(storageKey[marshalTransferOutputBookingAddressHashStart:marshalTransferOutputBookingAddressHashEnd], addressHash[:address.Length]) @@ -18,7 +18,7 @@ func GenerateOutputBookingStorageKey(realityId reality.Id, addressHash address.A } else { storageKey[marshalTransferOutputBookingSpentStart] = byte(UNSPENT) } - copy(storageKey[marshalTransferOutputBookingTransferHashStart:marshalTransferOutputBookingTransferHashEnd], transferHash[:HashLength]) + copy(storageKey[marshalTransferOutputBookingTransferHashStart:marshalTransferOutputBookingTransferHashEnd], transferHash[:IdLength]) return } diff --git a/packages/ledgerstate/transfer/output_reference.go b/packages/ledgerstate/transfer/output_reference.go index b061af8fb30545da6772b0be535943be65d9a33c..d990f7bedcbbd5e6ef3da11eb8e412a519c2a404 100644 --- a/packages/ledgerstate/transfer/output_reference.go +++ b/packages/ledgerstate/transfer/output_reference.go @@ -7,11 +7,11 @@ import ( type OutputReference struct { storageKey []byte - transferHash Hash + transferHash Id addressHash address.Address } -func NewOutputReference(transferHash Hash, addressHash address.Address) *OutputReference { +func NewOutputReference(transferHash Id, addressHash address.Address) *OutputReference { return &OutputReference{ storageKey: append(transferHash[:], addressHash[:]...), transferHash: transferHash, @@ -19,16 +19,20 @@ func NewOutputReference(transferHash Hash, addressHash address.Address) *OutputR } } +func (reference *OutputReference) GetTransferHash() Id { + return reference.transferHash +} + func (reference *OutputReference) GetAddress() address.Address { return reference.addressHash } func (reference *OutputReference) MarshalBinary() (result []byte, err error) { - result = make([]byte, HashLength+address.Length) + result = make([]byte, IdLength+address.Length) offset := 0 copy(result[offset:], reference.transferHash[:]) - offset += HashLength + offset += IdLength copy(result[offset:], reference.addressHash[:]) @@ -39,7 +43,7 @@ func (reference *OutputReference) UnmarshalBinary(bytes []byte) (err error) { offset := 0 copy(reference.transferHash[:], bytes[offset:]) - offset += HashLength + offset += IdLength copy(reference.addressHash[:], bytes[offset:]) diff --git a/packages/ledgerstate/transfer/output_test.go b/packages/ledgerstate/transfer/output_test.go index 5bf1746964ca8579ed29a09b199b1b9a6de78d73..3166683ca56e960590e95693fd5c000deba1cda2 100644 --- a/packages/ledgerstate/transfer/output_test.go +++ b/packages/ledgerstate/transfer/output_test.go @@ -13,10 +13,10 @@ import ( ) func TestTransferOutput_MarshalUnmarshal(t *testing.T) { - transferOutput := NewTransferOutput(nil, reality.NewId("REALITY"), NewHash("RECEIVE"), address.New([]byte("ADDRESS1")), coloredcoins.NewColoredBalance(coloredcoins.NewColor("IOTA"), 44), coloredcoins.NewColoredBalance(coloredcoins.NewColor("BTC"), 88)) - transferOutput.consumers = make(map[Hash][]address.Address) + transferOutput := NewTransferOutput(nil, reality.NewId("REALITY"), NewId([]byte("RECEIVE")), address.New([]byte("ADDRESS1")), coloredcoins.NewColoredBalance(coloredcoins.NewColor("IOTA"), 44), coloredcoins.NewColoredBalance(coloredcoins.NewColor("BTC"), 88)) + transferOutput.consumers = make(map[Id][]address.Address) - spendTransferHash := NewHash("SPEND") + spendTransferHash := NewId([]byte("SPEND")) transferOutput.consumers[spendTransferHash] = make([]address.Address, 2) transferOutput.consumers[spendTransferHash][0] = address.New([]byte("ADDRESS2")) transferOutput.consumers[spendTransferHash][1] = address.New([]byte("ADDRESS3")) diff --git a/packages/ledgerstate/transfer/transfer.go b/packages/ledgerstate/transfer/transfer.go index ee91c1ebf67db1a3fa27172d1eb44f370ea617b7..70c27381af06daed26523fc53f4e25e120f24e0f 100644 --- a/packages/ledgerstate/transfer/transfer.go +++ b/packages/ledgerstate/transfer/transfer.go @@ -6,21 +6,21 @@ import ( ) type Transfer struct { - hash Hash + id Id inputs []*OutputReference outputs map[address.Address][]*coloredcoins.ColoredBalance } -func NewTransfer(transferHash Hash) *Transfer { +func NewTransfer(id Id) *Transfer { return &Transfer{ - hash: transferHash, + id: id, inputs: make([]*OutputReference, 0), outputs: make(map[address.Address][]*coloredcoins.ColoredBalance), } } -func (transfer *Transfer) GetHash() Hash { - return transfer.hash +func (transfer *Transfer) GetId() Id { + return transfer.id } func (transfer *Transfer) GetInputs() []*OutputReference { diff --git a/packages/ledgerstate/transfer/transfer_output_booking.go b/packages/ledgerstate/transfer/transfer_output_booking.go index 663f1aa99f0d27b2f6d2f60665d75188b1faf458..01238c133c0f6843d097bb362d61305417f17b9c 100644 --- a/packages/ledgerstate/transfer/transfer_output_booking.go +++ b/packages/ledgerstate/transfer/transfer_output_booking.go @@ -16,12 +16,12 @@ type OutputBooking struct { realityId reality.Id addressHash address.Address spent bool - transferHash Hash + transferHash Id storageKey []byte } -func NewTransferOutputBooking(realityId reality.Id, addressHash address.Address, spent bool, transferHash Hash) (result *OutputBooking) { +func NewTransferOutputBooking(realityId reality.Id, addressHash address.Address, spent bool, transferHash Id) (result *OutputBooking) { result = &OutputBooking{ realityId: realityId, addressHash: addressHash, @@ -46,7 +46,7 @@ func (booking *OutputBooking) IsSpent() bool { return booking.spent } -func (booking *OutputBooking) GetTransferHash() Hash { +func (booking *OutputBooking) GetTransferHash() Id { return booking.transferHash } diff --git a/packages/ledgerstate/visualizer.go b/packages/ledgerstate/visualizer.go index b4a086971bd1fd4498846da1d2140693e17496d8..3820a60ae0d4549354692b0e1b09ab84e660ea7f 100644 --- a/packages/ledgerstate/visualizer.go +++ b/packages/ledgerstate/visualizer.go @@ -11,7 +11,7 @@ import ( "github.com/iotaledger/hive.go/objectstorage" ) -type transferOutputId [transfer.HashLength + address.Length]byte +type transferOutputId [transfer.IdLength + address.Length]byte type Visualizer struct { ledgerState *LedgerState @@ -79,7 +79,7 @@ func (visualizer *Visualizer) generateTransferOutputId(transferOutput *transfer. addressHash := transferOutput.GetAddressHash() copy(result[:], transferHash[:]) - copy(result[transfer.HashLength:], addressHash[:]) + copy(result[transfer.IdLength:], addressHash[:]) return }