diff --git a/packages/binary/tangle/snapshot.go b/packages/binary/tangle/snapshot.go new file mode 100644 index 0000000000000000000000000000000000000000..1c1d560921bfa6ebf3a55f4d0f74242afa305ec4 --- /dev/null +++ b/packages/binary/tangle/snapshot.go @@ -0,0 +1,11 @@ +package tangle + +import ( + "github.com/iotaledger/goshimmer/packages/binary/address" + "github.com/iotaledger/goshimmer/packages/binary/transaction" + "github.com/iotaledger/goshimmer/packages/ledgerstate/coloredcoins" +) + +type Snapshot struct { + SolidEntryPoints map[transaction.Id]map[address.Address]*coloredcoins.ColoredBalance +} diff --git a/packages/binary/tangle/tangle.go b/packages/binary/tangle/tangle.go index 7827f7e1764a290f7899513c056d6671f8cdc910..0e8d062bf4e39827ae00cae600ee10657ef68176 100644 --- a/packages/binary/tangle/tangle.go +++ b/packages/binary/tangle/tangle.go @@ -4,9 +4,12 @@ import ( "container/list" "time" + "github.com/iotaledger/goshimmer/packages/binary/transaction/payload/valuetransfer" + "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/data" "github.com/iotaledger/goshimmer/packages/binary/transactionmetadata" "github.com/iotaledger/goshimmer/packages/storageprefix" "github.com/iotaledger/hive.go/async" @@ -50,6 +53,31 @@ func New(storageId []byte) (result *Tangle) { return } +func (tangle *Tangle) LoadSnapshot(snapshot *Snapshot) { + fakeTransactionId := func(tx *transaction.Transaction, id transaction.Id) *transaction.Transaction { + fakedTransaction := transaction.FromStorage(id[:]) + if err := fakedTransaction.UnmarshalBinary(tx.GetBytes()); err != nil { + panic(err) + } + + return fakedTransaction.(*transaction.Transaction) + } + + for transactionId, addresses := range snapshot.SolidEntryPoints { + if addresses == nil { + tangle.AttachTransaction(fakeTransactionId(transaction.New(transaction.EmptyId, transaction.EmptyId, nil, data.New(nil)), transactionId)) + } else { + valueTransfer := valuetransfer.New() + + for address, coloredBalance := range addresses { + valueTransfer.AddOutput(address, coloredBalance) + } + + tangle.AttachTransaction(fakeTransactionId(transaction.New(transaction.EmptyId, transaction.EmptyId, nil, valueTransfer), transactionId)) + } + } +} + // 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 @@ -157,7 +185,7 @@ func (tangle *Tangle) storeTransactionWorker(tx *transaction.Transaction) { transactionId := tx.GetId() - cachedTransactionMetadata := &transactionmetadata.CachedTransactionMetadata{CachedObject: tangle.transactionMetadataStorage.Store(transactionmetadata.New(tx.GetId()))} + cachedTransactionMetadata := &transactionmetadata.CachedTransactionMetadata{CachedObject: tangle.transactionMetadataStorage.Store(transactionmetadata.New(transactionId))} addTransactionToApprovers(transactionId, tx.GetTrunkTransactionId()) addTransactionToApprovers(transactionId, tx.GetBranchTransactionId()) diff --git a/packages/binary/transaction/id.go b/packages/binary/transaction/id.go index 1f39029e5305d6f0c2e6f83fd39dae2d16f95312..1c0c5165f19ffa69249cdb8d99c1cd9022eaffba 100644 --- a/packages/binary/transaction/id.go +++ b/packages/binary/transaction/id.go @@ -1,7 +1,17 @@ package transaction +import ( + "github.com/mr-tron/base58" +) + type Id [IdLength]byte +func NewId(id []byte) (result Id) { + copy(result[:], id) + + return +} + func (id *Id) MarshalBinary() (result []byte, err error) { result = make([]byte, IdLength) copy(result, id[:]) @@ -15,6 +25,10 @@ func (id *Id) UnmarshalBinary(data []byte) (err error) { return } +func (id *Id) String() string { + return base58.Encode(id[:]) +} + 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 411b2f156dccc6841a581b88e22e172c4b400229..d9fa48ceca6a0e761e25dfdef8a77ac84d04e0bc 100644 --- a/packages/binary/transaction/payload/valuetransfer/valuetransfer.go +++ b/packages/binary/transaction/payload/valuetransfer/valuetransfer.go @@ -79,6 +79,14 @@ func (valueTransfer *ValueTransfer) AddOutput(address address.Address, balance * return valueTransfer } +func (valueTransfer *ValueTransfer) GetOutputs() (result map[address.Address][]*coloredcoins.ColoredBalance) { + valueTransfer.outputsMutex.RLock() + result = valueTransfer.outputs + valueTransfer.outputsMutex.RUnlock() + + return +} + func (valueTransfer *ValueTransfer) Sign(keyPair ed25119.KeyPair) *ValueTransfer { payloadBytes := valueTransfer.marshalPayloadBytes() diff --git a/packages/binary/transaction/transaction.go b/packages/binary/transaction/transaction.go index 4ec441045a3151ce806ef021668da5239df8b03a..5b36af5881a2aa9e3109d498fe15b49175c61829 100644 --- a/packages/binary/transaction/transaction.go +++ b/packages/binary/transaction/transaction.go @@ -191,7 +191,9 @@ func (transaction *Transaction) MarshalBinary() (result []byte, err error) { copy(result[offset:], transaction.branchTransactionId[:]) offset += IdLength - copy(result[offset:], transaction.issuer.PublicKey) + if transaction.issuer != nil { + copy(result[offset:], transaction.issuer.PublicKey) + } offset += identity.PublicKeySize binary.LittleEndian.PutUint32(result[offset:], transaction.payload.GetType()) @@ -202,10 +204,12 @@ func (transaction *Transaction) MarshalBinary() (result []byte, err error) { offset += serializedPayloadLength } - transaction.signatureMutex.Lock() - copy(transaction.signature[:], transaction.issuer.Sign(result[:offset])) - transaction.signatureMutex.Unlock() - copy(result[offset:], transaction.signature[:]) + if transaction.issuer != nil { + transaction.signatureMutex.Lock() + copy(transaction.signature[:], transaction.issuer.Sign(result[:offset])) + transaction.signatureMutex.Unlock() + copy(result[offset:], transaction.signature[:]) + } // offset += identity.SignatureSize transaction.bytes = result diff --git a/packages/binary/valuetangle/events.go b/packages/binary/valuetangle/events.go index 2c0352c1a74afe58d8688220e644e4886a70c8a4..5d5758f8e22da26a39c4ac10d21559c108483ce1 100644 --- a/packages/binary/valuetangle/events.go +++ b/packages/binary/valuetangle/events.go @@ -1,6 +1,8 @@ package valuetangle import ( + "github.com/iotaledger/goshimmer/packages/binary/valuetangle/model" + "github.com/iotaledger/goshimmer/packages/ledgerstate/transfer" "github.com/iotaledger/hive.go/events" ) @@ -11,3 +13,29 @@ type Events struct { MissingTransferReceived *events.Event TransferRemoved *events.Event } + +func newEvents() *Events { + return &Events{ + TransferAttached: events.NewEvent(cachedTransferEvent), + TransferSolid: events.NewEvent(cachedTransferEvent), + TransferMissing: events.NewEvent(transferIdEvent), + MissingTransferReceived: events.NewEvent(transferIdEvent), + TransferRemoved: events.NewEvent(transferIdEvent), + } +} + +func transferIdEvent(handler interface{}, params ...interface{}) { + missingTransactionId := params[0].(transfer.Id) + + handler.(func(transfer.Id))(missingTransactionId) +} + +func cachedTransferEvent(handler interface{}, params ...interface{}) { + cachedTransfer := params[0].(*model.CachedValueTransfer) + cachedTransferMetadata := params[1].(*model.CachedTransferMetadata) + + cachedTransfer.RegisterConsumer() + cachedTransferMetadata.RegisterConsumer() + + handler.(func(*model.CachedValueTransfer, *model.CachedTransferMetadata))(cachedTransfer, cachedTransferMetadata) +} diff --git a/packages/binary/valuetangle/model/cached_consumers.go b/packages/binary/valuetangle/model/cached_consumers.go new file mode 100644 index 0000000000000000000000000000000000000000..c570571a73ccde07771dbd9235330b3def6b9a02 --- /dev/null +++ b/packages/binary/valuetangle/model/cached_consumers.go @@ -0,0 +1,21 @@ +package model + +import ( + "github.com/iotaledger/hive.go/objectstorage" +) + +type CachedConsumers struct { + *objectstorage.CachedObject +} + +func (cachedObject *CachedConsumers) Unwrap() *Consumers { + if untypedObject := cachedObject.Get(); untypedObject == nil { + return nil + } else { + if typedObject := untypedObject.(*Consumers); typedObject == nil || typedObject.IsDeleted() { + return nil + } else { + return typedObject + } + } +} diff --git a/packages/binary/valuetangle/model/cached_transfermetadata.go b/packages/binary/valuetangle/model/cached_transfermetadata.go new file mode 100644 index 0000000000000000000000000000000000000000..bce48511c388337e3cb14d8580a1e7bc2c023d42 --- /dev/null +++ b/packages/binary/valuetangle/model/cached_transfermetadata.go @@ -0,0 +1,21 @@ +package model + +import ( + "github.com/iotaledger/hive.go/objectstorage" +) + +type CachedTransferMetadata struct { + *objectstorage.CachedObject +} + +func (cachedObject *CachedTransferMetadata) Unwrap() *TransferMetadata { + if untypedObject := cachedObject.Get(); untypedObject == nil { + return nil + } else { + if typedObject := untypedObject.(*TransferMetadata); 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 index 0d8fdeba193911223f6f0a5905fbe57d6e7e6390..7a6ade84636baf9e234857aedf7a5acf62ee3228 100644 --- a/packages/binary/valuetangle/model/cached_valuetransfer.go +++ b/packages/binary/valuetangle/model/cached_valuetransfer.go @@ -3,23 +3,20 @@ 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) { +func (cachedValueTransfer *CachedValueTransfer) Unwrap() *ValueTransfer { if untypedTransaction := cachedValueTransfer.Get(); untypedTransaction == nil { - return nil, transfer.EmptyId + return nil } else { if typeCastedTransaction := untypedTransaction.(*transaction.Transaction); typeCastedTransaction == nil || typeCastedTransaction.IsDeleted() || typeCastedTransaction.GetPayload().GetType() != valuetransfer.Type { - return nil, transfer.EmptyId + return nil } else { - transactionId := typeCastedTransaction.GetId() - - return typeCastedTransaction.GetPayload().(*valuetransfer.ValueTransfer), transfer.NewId(transactionId[:]) + return NewValueTransfer(typeCastedTransaction) } } } diff --git a/packages/binary/valuetangle/model/consumers.go b/packages/binary/valuetangle/model/consumers.go index 3f8edf84f405da27fd263f424dedfa71c3a93d33..4bd073599d38c9f25aec68a176eaaed08b0e8f1e 100644 --- a/packages/binary/valuetangle/model/consumers.go +++ b/packages/binary/valuetangle/model/consumers.go @@ -26,15 +26,11 @@ func NewConsumers(transferId transfer.Id) *Consumers { // 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) +func ConsumersFromStorage(id []byte) objectstorage.StorableObject { + result := &Consumers{} + copy(result.transferId[:], id) - result = &Consumers{ - transferId: transferId, - } - - return + return result } func (consumers *Consumers) GetTransferId() transfer.Id { diff --git a/packages/binary/valuetangle/model/missingtransfer.go b/packages/binary/valuetangle/model/missingtransfer.go index f9772bbf3e598d09e7837a22173db41cb85eac9d..2f58ee8f1810e7d1131d7c342262df8c6684d836 100644 --- a/packages/binary/valuetangle/model/missingtransfer.go +++ b/packages/binary/valuetangle/model/missingtransfer.go @@ -15,7 +15,7 @@ type MissingTransfer struct { missingSince time.Time } -func New(transferId transfer.Id) *MissingTransfer { +func NewMissingTransfer(transferId transfer.Id) *MissingTransfer { return &MissingTransfer{ transferId: transferId, missingSince: time.Now(), diff --git a/packages/binary/valuetangle/model/transfermetadata.go b/packages/binary/valuetangle/model/transfermetadata.go new file mode 100644 index 0000000000000000000000000000000000000000..1cce70cb5c00d001840ef226d08b83293e255ff9 --- /dev/null +++ b/packages/binary/valuetangle/model/transfermetadata.go @@ -0,0 +1,81 @@ +package model + +import ( + "sync" + "time" + + "github.com/iotaledger/goshimmer/packages/ledgerstate/transfer" + + "github.com/iotaledger/hive.go/objectstorage" +) + +type TransferMetadata struct { + objectstorage.StorableObjectFlags + + transferId transfer.Id + receivedTime time.Time + solid bool + solidificationTime time.Time + + solidMutex sync.RWMutex +} + +func NewTransferMetadata(transferId transfer.Id) *TransferMetadata { + return &TransferMetadata{ + transferId: transferId, + receivedTime: time.Now(), + } +} + +func TransferMetadataFromStorage(id []byte) objectstorage.StorableObject { + result := &TransferMetadata{} + copy(result.transferId[:], id) + + return result +} + +func (transferMetadata *TransferMetadata) IsSolid() (result bool) { + transferMetadata.solidMutex.RLock() + result = transferMetadata.solid + transferMetadata.solidMutex.RUnlock() + + return +} + +func (transferMetadata *TransferMetadata) SetSolid(solid bool) (modified bool) { + transferMetadata.solidMutex.RLock() + if transferMetadata.solid != solid { + transferMetadata.solidMutex.RUnlock() + + transferMetadata.solidMutex.Lock() + if transferMetadata.solid != solid { + transferMetadata.solid = solid + + transferMetadata.SetModified() + + modified = true + } + transferMetadata.solidMutex.Unlock() + + } else { + transferMetadata.solidMutex.RUnlock() + } + + return +} + +func (transferMetadata *TransferMetadata) GetStorageKey() []byte { + return transferMetadata.transferId[:] +} + +func (transferMetadata *TransferMetadata) Update(other objectstorage.StorableObject) { + panic("TransferMetadata should never be overwritten") +} + +func (transferMetadata *TransferMetadata) MarshalBinary() ([]byte, error) { + return nil, nil +} + +func (transferMetadata *TransferMetadata) UnmarshalBinary([]byte) error { + return nil +} diff --git a/packages/binary/valuetangle/model/transferoutput/transferoutput.go b/packages/binary/valuetangle/model/transferoutput/transferoutput.go new file mode 100644 index 0000000000000000000000000000000000000000..ae825b77c3578698565211693a1adf6d7dff55f6 --- /dev/null +++ b/packages/binary/valuetangle/model/transferoutput/transferoutput.go @@ -0,0 +1,270 @@ +package transferoutput + +import ( + "encoding/binary" + "sync" + + "github.com/iotaledger/goshimmer/packages/binary/address" + "github.com/iotaledger/goshimmer/packages/binary/transaction" + "github.com/iotaledger/goshimmer/packages/binary/types" + "github.com/iotaledger/goshimmer/packages/ledgerstate/coloredcoins" + "github.com/iotaledger/goshimmer/packages/ledgerstate/reality" + "github.com/iotaledger/goshimmer/packages/stringify" + "github.com/iotaledger/hive.go/objectstorage" +) + +type TransferOutput struct { + objectstorage.StorableObjectFlags + + transactionId transaction.Id + address address.Address + realityId reality.Id + balances []*coloredcoins.ColoredBalance + consumers map[transaction.Id]types.Empty + + realityIdMutex sync.RWMutex + consumersMutex sync.RWMutex +} + +func NewTransferOutput(transactionId transaction.Id, address address.Address, balances ...*coloredcoins.ColoredBalance) *TransferOutput { + return &TransferOutput{ + transactionId: transactionId, + address: address, + balances: balances, + realityId: reality.EmptyId, + consumers: make(map[transaction.Id]types.Empty), + } +} + +func FromStorage(key []byte) objectstorage.StorableObject { + result := &TransferOutput{} + offset := 0 + + if err := result.transactionId.UnmarshalBinary(key[offset:]); err != nil { + panic(err) + } + offset += transaction.IdLength + + if err := result.address.UnmarshalBinary(key[offset:]); err != nil { + panic(err) + } + + return result +} + +func (transferOutput *TransferOutput) GetTransactionId() (transactionId transaction.Id) { + transactionId = transferOutput.transactionId + + return +} + +func (transferOutput *TransferOutput) GetAddress() (address address.Address) { + return transferOutput.address +} + +func (transferOutput *TransferOutput) GetRealityId() (realityId reality.Id) { + transferOutput.realityIdMutex.RLock() + realityId = transferOutput.realityId + transferOutput.realityIdMutex.RUnlock() + + return +} + +func (transferOutput *TransferOutput) SetRealityId(realityId reality.Id) (modified bool) { + transferOutput.realityIdMutex.RLock() + if transferOutput.realityId != realityId { + transferOutput.realityIdMutex.RUnlock() + + transferOutput.realityIdMutex.Lock() + if transferOutput.realityId != realityId { + transferOutput.realityId = realityId + + transferOutput.SetModified() + + modified = true + } + transferOutput.realityIdMutex.Unlock() + } else { + transferOutput.realityIdMutex.RUnlock() + } + + return +} + +func (transferOutput *TransferOutput) GetBalances() []*coloredcoins.ColoredBalance { + return transferOutput.balances +} + +func (transferOutput *TransferOutput) GetConsumers() (consumers map[transaction.Id]types.Empty) { + consumers = make(map[transaction.Id]types.Empty) + + transferOutput.consumersMutex.RLock() + for transferHash := range transferOutput.consumers { + consumers[transferHash] = types.Void + } + transferOutput.consumersMutex.RUnlock() + + return +} + +func (transferOutput *TransferOutput) IsSpent() (result bool) { + transferOutput.consumersMutex.RLock() + result = len(transferOutput.consumers) >= 1 + transferOutput.consumersMutex.RUnlock() + + return +} + +func (transferOutput *TransferOutput) AddConsumer(consumer transaction.Id) (previousConsumers map[transaction.Id]types.Empty) { + transferOutput.consumersMutex.RLock() + if _, exist := transferOutput.consumers[consumer]; !exist { + transferOutput.consumersMutex.RUnlock() + + transferOutput.consumersMutex.Lock() + if _, exist := transferOutput.consumers[consumer]; !exist { + previousConsumers = make(map[transaction.Id]types.Empty) + for transactionId := range transferOutput.consumers { + previousConsumers[transactionId] = types.Void + } + + transferOutput.consumers[consumer] = types.Void + + transferOutput.SetModified() + } + transferOutput.consumersMutex.Unlock() + } else { + transferOutput.consumersMutex.RUnlock() + } + + return +} + +func (transferOutput *TransferOutput) String() string { + return stringify.Struct("TransferOutput", + stringify.StructField("transactionId", transferOutput.GetTransactionId().String()), + stringify.StructField("address", transferOutput.GetAddress().String()), + stringify.StructField("realityId", transferOutput.GetRealityId().String()), + stringify.StructField("balances", transferOutput.GetBalances()), + stringify.StructField("spent", len(transferOutput.GetConsumers()) >= 1), + ) +} + +func (transferOutput *TransferOutput) GetStorageKey() []byte { + return append(transferOutput.transactionId[:], transferOutput.address[:]...) +} + +func (transferOutput *TransferOutput) Update(other objectstorage.StorableObject) { + panic("TransferOutput should never be overwritten / updated") +} + +func (transferOutput *TransferOutput) MarshalBinary() ([]byte, error) { + transferOutput.realityIdMutex.RLock() + transferOutput.consumersMutex.RLock() + + balanceCount := len(transferOutput.balances) + consumerCount := len(transferOutput.consumers) + + result := make([]byte, reality.IdLength+4+balanceCount*coloredcoins.BalanceLength+4+consumerCount*transaction.IdLength) + offset := 0 + + copy(result[0:], transferOutput.realityId[:]) + offset += reality.IdLength + + binary.LittleEndian.PutUint32(result[offset:], uint32(balanceCount)) + offset += 4 + + for i := 0; i < balanceCount; i++ { + if marshaledColoredBalance, err := transferOutput.balances[i].MarshalBinary(); err != nil { + return nil, err + } else { + copy(result[offset:], marshaledColoredBalance) + offset += coloredcoins.BalanceLength + } + } + + binary.LittleEndian.PutUint32(result[offset:], uint32(consumerCount)) + offset += 4 + + for transactionId := range transferOutput.consumers { + copy(result[offset:], transactionId[:]) + offset += transaction.IdLength + } + + transferOutput.consumersMutex.RUnlock() + transferOutput.realityIdMutex.RUnlock() + + return result, nil +} + +func (transferOutput *TransferOutput) UnmarshalBinary(serializedObject []byte) error { + offset := 0 + + if err := transferOutput.realityId.UnmarshalBinary(serializedObject[offset:]); err != nil { + return err + } + offset += reality.IdLength + + if balances, err := transferOutput.unmarshalBalances(serializedObject, &offset); err != nil { + return err + } else { + transferOutput.balances = balances + } + + if consumers, err := transferOutput.unmarshalConsumers(serializedObject, &offset); err != nil { + return err + } else { + transferOutput.consumers = consumers + } + + return nil +} + +func (transferOutput *TransferOutput) unmarshalBalances(serializedBalances []byte, offset *int) ([]*coloredcoins.ColoredBalance, error) { + balanceCount := int(binary.LittleEndian.Uint32(serializedBalances)) + *offset += 4 + + balances := make([]*coloredcoins.ColoredBalance, balanceCount) + for i := 0; i < balanceCount; i++ { + coloredBalance := coloredcoins.ColoredBalance{} + if err := coloredBalance.UnmarshalBinary(serializedBalances[4+i*coloredcoins.BalanceLength:]); err != nil { + return nil, err + } + *offset += coloredcoins.BalanceLength + + balances[i] = &coloredBalance + } + + return balances, nil +} + +func (transferOutput *TransferOutput) unmarshalConsumers(serializedConsumers []byte, offset *int) (map[transaction.Id]types.Empty, error) { + consumerCount := int(binary.LittleEndian.Uint32(serializedConsumers[*offset:])) + *offset += 4 + + consumers := make(map[transaction.Id]types.Empty, consumerCount) + for i := 0; i < consumerCount; i++ { + var transactionId transaction.Id + if err := transactionId.UnmarshalBinary(serializedConsumers[*offset:]); err != nil { + return nil, err + } + *offset += transaction.IdLength + } + + return consumers, nil +} + +type CachedTransferOutput struct { + *objectstorage.CachedObject +} + +func (cachedObject *CachedTransferOutput) Unwrap() *TransferOutput { + if untypedObject := cachedObject.Get(); untypedObject == nil { + return nil + } else { + if typedObject := untypedObject.(*TransferOutput); typedObject == nil || typedObject.IsDeleted() { + return nil + } else { + return typedObject + } + } +} diff --git a/packages/binary/valuetangle/model/valuetransfer.go b/packages/binary/valuetangle/model/valuetransfer.go new file mode 100644 index 0000000000000000000000000000000000000000..fa614d48361b3a6b4d5299bd41d86bc0aad34d97 --- /dev/null +++ b/packages/binary/valuetangle/model/valuetransfer.go @@ -0,0 +1,64 @@ +package model + +import ( + "sync" + + "github.com/iotaledger/goshimmer/packages/binary/address" + "github.com/iotaledger/goshimmer/packages/binary/transaction" + "github.com/iotaledger/goshimmer/packages/binary/transaction/payload/valuetransfer" + "github.com/iotaledger/goshimmer/packages/binary/types" + "github.com/iotaledger/goshimmer/packages/ledgerstate/transfer" +) + +type ValueTransfer struct { + *transaction.Transaction + *valuetransfer.ValueTransfer + + inputs map[transfer.Id]map[address.Address]types.Empty + inputsMutex sync.RWMutex +} + +func NewValueTransfer(transaction *transaction.Transaction) *ValueTransfer { + return &ValueTransfer{ + Transaction: transaction, + ValueTransfer: transaction.GetPayload().(*valuetransfer.ValueTransfer), + } +} + +func (valueTransfer *ValueTransfer) GetId() transfer.Id { + transactionId := valueTransfer.Transaction.GetId() + + return transfer.NewId(transactionId[:]) +} + +func (valueTransfer *ValueTransfer) GetInputs() (result map[transfer.Id]map[address.Address]types.Empty) { + valueTransfer.inputsMutex.RLock() + if valueTransfer.inputs == nil { + valueTransfer.inputsMutex.RUnlock() + + valueTransfer.inputsMutex.Lock() + if valueTransfer.inputs == nil { + result = make(map[transfer.Id]map[address.Address]types.Empty) + + for _, transferOutputReference := range valueTransfer.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 + } + + valueTransfer.inputs = result + } else { + result = valueTransfer.inputs + } + valueTransfer.inputsMutex.Unlock() + } else { + result = valueTransfer.inputs + } + + return +} diff --git a/packages/binary/valuetangle/valuetangle.go b/packages/binary/valuetangle/valuetangle.go index e5b107d0025ea7514f60866f864ca461c02fd6a1..e8e16a9a9d3b8da0a52e32c5f448fca81999e14f 100644 --- a/packages/binary/valuetangle/valuetangle.go +++ b/packages/binary/valuetangle/valuetangle.go @@ -2,15 +2,19 @@ package valuetangle import ( "container/list" + "fmt" + "time" "github.com/iotaledger/goshimmer/packages/binary/address" + + "github.com/iotaledger/goshimmer/packages/binary/types" + + "github.com/iotaledger/goshimmer/packages/storageprefix" + "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" @@ -18,7 +22,12 @@ import ( "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 +const ( + MaxMissingTimeBeforeCleanup = 30 * time.Second + MissingCheckInterval = 5 * time.Second +) + +// 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 @@ -29,7 +38,7 @@ type ValueTangle struct { storeTransactionsWorkerPool async.WorkerPool solidifierWorkerPool async.WorkerPool - // cleanupWorkerPool async.WorkerPool + cleanupWorkerPool async.WorkerPool Events Events } @@ -37,8 +46,16 @@ type ValueTangle struct { func New(tangle *tangle.Tangle) (valueTangle *ValueTangle) { valueTangle = &ValueTangle{ tangle: tangle, + + transferMetadataStorage: objectstorage.New(append(tangle.GetStorageId(), storageprefix.ValueTangleTransferMetadata...), model.TransferMetadataFromStorage), + consumersStorage: objectstorage.New(append(tangle.GetStorageId(), storageprefix.ValueTangleConsumers...), model.ConsumersFromStorage), + missingTransferStorage: objectstorage.New(append(tangle.GetStorageId(), storageprefix.TangleMissingTransaction...), model.MissingTransferFromStorage), + + Events: *newEvents(), } + valueTangle.solidifierWorkerPool.Tune(1024) + tangle.Events.TransactionSolid.Attach(events.NewClosure(valueTangle.attachTransaction)) tangle.Events.TransactionRemoved.Attach(events.NewClosure(valueTangle.deleteTransfer)) @@ -51,36 +68,60 @@ func (valueTangle *ValueTangle) attachTransaction(cachedTransaction *transaction }) } -// Retrieves a transaction from the tangle. -func (valueTangle *ValueTangle) GetTransfer(transactionId transaction.Id) *model.CachedValueTransfer { - cachedTransaction := valueTangle.tangle.GetTransaction(transactionId) +// Retrieves a transfer from the tangle. +func (valueTangle *ValueTangle) GetTransfer(transferId transfer.Id) *model.CachedValueTransfer { + cachedTransaction := valueTangle.tangle.GetTransaction(transaction.NewId(transferId[:])) - // return an empty result if the transaction is no value transaction + // return an empty result if the transfer is no value transfer 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: &transaction.CachedTransaction{CachedObject: objectstorage.NewEmptyCachedObject(transferId[:])}} } 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[:])} +// Retrieves the metadata of a transfer from the tangle. +func (valueTangle *ValueTangle) GetTransferMetadata(transferId transfer.Id) *model.CachedTransferMetadata { + return &model.CachedTransferMetadata{CachedObject: valueTangle.transferMetadataStorage.Load(transferId[:])} +} + +// Retrieves the approvers of a transfer from the tangle. +func (valueTangle *ValueTangle) GetConsumers(transferId transfer.Id) *model.CachedConsumers { + return &model.CachedConsumers{CachedObject: valueTangle.consumersStorage.Load(transferId[:])} } -func (valueTangle *ValueTangle) deleteTransfer(transactionId transaction.Id) { +func (valueTangle *ValueTangle) deleteTransfer(transferIf transfer.Id) { } -// Marks the tangle as stopped, so it will not accept any new transactions (waits for all backgroundTasks to finish. +// Marks the tangle as stopped, so it will not accept any new transfers (waits for all backgroundTasks to finish. func (valueTangle *ValueTangle) Shutdown() *ValueTangle { + valueTangle.tangle.Shutdown() + valueTangle.storeTransactionsWorkerPool.ShutdownGracefully() + valueTangle.solidifierWorkerPool.ShutdownGracefully() + valueTangle.cleanupWorkerPool.ShutdownGracefully() return valueTangle } +// Resets the database and deletes all objects (good for testing or "node resets"). +func (valueTangle *ValueTangle) Prune() error { + for _, storage := range []*objectstorage.ObjectStorage{ + valueTangle.transferMetadataStorage, + valueTangle.consumersStorage, + valueTangle.missingTransferStorage, + } { + if err := storage.Prune(); err != nil { + return err + } + } + + return nil +} + 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 { @@ -107,113 +148,129 @@ func (valueTangle *ValueTangle) storeTransactionWorker(cachedTx *transaction.Cac cachedValueTransfer := &model.CachedValueTransfer{CachedTransaction: cachedTx} - valueTransfer, transferId := cachedValueTransfer.Unwrap() + valueTransfer := cachedValueTransfer.Unwrap() if valueTransfer == nil { cachedValueTransfer.Release() return } - for transferId := range valueTangle.getInputsMap(valueTransfer) { - addTransferToConsumers(transferId, transferId) + transferId := valueTransfer.GetId() + + cachedTransferMetadata := &model.CachedTransferMetadata{CachedObject: valueTangle.transferMetadataStorage.Store(model.NewTransferMetadata(transferId))} + for referencedTransferId := range valueTransfer.GetInputs() { + addTransferToConsumers(transferId, referencedTransferId) } if valueTangle.missingTransferStorage.DeleteIfPresent(transferId[:]) { valueTangle.Events.MissingTransferReceived.Trigger(transferId) } - valueTangle.Events.TransferAttached.Trigger(cachedValueTransfer) + valueTangle.Events.TransferAttached.Trigger(cachedValueTransfer, cachedTransferMetadata) valueTangle.solidifierWorkerPool.Submit(func() { - valueTangle.solidifyTransferWorker(cachedValueTransfer, transferId) + valueTangle.solidifyTransferWorker(cachedValueTransfer, cachedTransferMetadata) }) } -// 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 { +// Worker that solidifies the transfers (recursively from past to present). +func (valueTangle *ValueTangle) solidifyTransferWorker(cachedValueTransfer *model.CachedValueTransfer, cachedTransferMetadata *model.CachedTransferMetadata) { + areInputsSolid := func(transferId transfer.Id, addresses map[address.Address]types.Empty) bool { + transferMetadataCached := valueTangle.GetTransferMetadata(transferId) + if transferMetadata := transferMetadataCached.Unwrap(); transferMetadata == 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 { + // if transfer is missing and was not reported as missing, yet + if cachedMissingTransfer, missingTransactionStored := valueTangle.missingTransferStorage.StoreIfAbsent(transferId[:], model.NewMissingTransfer(transferId)); missingTransactionStored { cachedMissingTransfer.Consume(func(object objectstorage.StorableObject) { - valueTangle.monitorMissingTransactionWorker(object.(*missingtransaction.MissingTransaction).GetTransactionId()) + valueTangle.monitorMissingTransactionWorker(object.(*model.MissingTransfer).GetTransferId()) }) } return false - } else if !transactionMetadata.IsSolid() { + } else if !transferMetadata.IsSolid() { transferMetadataCached.Release() return false } transferMetadataCached.Release() + cachedTransfer := valueTangle.GetTransfer(transferId) + if valueTransfer := cachedTransfer.Unwrap(); valueTransfer == nil { + cachedTransfer.Release() + + return false + } else { + outputs := valueTransfer.GetOutputs() + for address := range outputs { + if _, addressExists := outputs[address]; !addressExists { + cachedTransfer.Release() + + fmt.Println("INVALID TX DETECTED") + + return false + } + } + } + cachedTransfer.Release() + return true } - isTransactionSolid := func(transaction *transaction.Transaction, transactionMetadata *transactionmetadata.TransactionMetadata) bool { - if transaction == nil || transaction.IsDeleted() { + isTransferSolid := func(transfer *model.ValueTransfer, transferMetadata *model.TransferMetadata) bool { + if transfer == nil || transfer.IsDeleted() { return false } - if transactionMetadata == nil || transactionMetadata.IsDeleted() { + if transferMetadata == nil || transferMetadata.IsDeleted() { return false } - if transactionMetadata.IsSolid() { + if transferMetadata.IsSolid() { return true } // 1. check tangle solidity - isTrunkSolid := isTransferMarkedAsSolid(transaction.GetTrunkTransactionId()) - isBranchSolid := isTransferMarkedAsSolid(transaction.GetBranchTransactionId()) - if isTrunkSolid && isBranchSolid { - // 2. check payload solidity - return true + inputsSolid := true + for inputTransferId, addresses := range transfer.GetInputs() { + inputsSolid = inputsSolid && areInputsSolid(inputTransferId, addresses) } - return false + return inputsSolid } - popElementsFromStack := func(stack *list.List) (*transaction.CachedTransaction, *transactionmetadata.CachedTransactionMetadata) { + popElementsFromStack := func(stack *list.List) (*model.CachedValueTransfer, *model.CachedTransferMetadata) { currentSolidificationEntry := stack.Front() - currentCachedTransaction := currentSolidificationEntry.Value.([2]interface{})[0] - currentCachedTransactionMetadata := currentSolidificationEntry.Value.([2]interface{})[1] + currentCachedTransfer := currentSolidificationEntry.Value.([2]interface{})[0] + currentCachedTransferMetadata := currentSolidificationEntry.Value.([2]interface{})[1] stack.Remove(currentSolidificationEntry) - return currentCachedTransaction.(*transaction.CachedTransaction), currentCachedTransactionMetadata.(*transactionmetadata.CachedTransactionMetadata) + return currentCachedTransfer.(*model.CachedValueTransfer), currentCachedTransferMetadata.(*model.CachedTransferMetadata) } // initialize the stack solidificationStack := list.New() - solidificationStack.PushBack([2]interface{}{cachedValueTransfer, transferId}) + solidificationStack.PushBack([2]interface{}{cachedValueTransfer, cachedTransferMetadata}) - // process transactions that are supposed to be checked for solidity recursively + // process transfers that are supposed to be checked for solidity recursively for solidificationStack.Len() > 0 { - currentCachedTransaction, currentCachedTransactionMetadata := popElementsFromStack(solidificationStack) + currentCachedTransfer, currentCachedTransferMetadata := popElementsFromStack(solidificationStack) - currentTransaction := currentCachedTransaction.Unwrap() - currentTransactionMetadata := currentCachedTransactionMetadata.Unwrap() - if currentTransaction == nil || currentTransactionMetadata == nil { - currentCachedTransaction.Release() - currentCachedTransactionMetadata.Release() + currentTransfer := currentCachedTransfer.Unwrap() + currentTransferMetadata := currentCachedTransferMetadata.Unwrap() + if currentTransfer == nil || currentTransferMetadata == nil { + currentCachedTransfer.Release() + currentCachedTransferMetadata.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) + // if current transfer is solid and was not marked as solid before: mark as solid and propagate + if isTransferSolid(currentTransfer, currentTransferMetadata) && currentTransferMetadata.SetSolid(true) { + valueTangle.Events.TransferSolid.Trigger(currentCachedTransfer, currentCachedTransferMetadata) - valueTangle.GetConsumers(currentTransaction.GetId()).Consume(func(object objectstorage.StorableObject) { - for approverTransactionId := range object.(*approvers.Approvers).Get() { + valueTangle.GetConsumers(currentTransfer.GetId()).Consume(func(object objectstorage.StorableObject) { + for approverTransactionId := range object.(*model.Consumers).Get() { solidificationStack.PushBack([2]interface{}{ valueTangle.GetTransfer(approverTransactionId), valueTangle.GetTransferMetadata(approverTransactionId), @@ -223,24 +280,50 @@ func (valueTangle *ValueTangle) solidifyTransferWorker(cachedValueTransfer *mode } // release cached results - currentCachedTransaction.Release() - currentCachedTransactionMetadata.Release() + currentCachedTransfer.Release() + currentCachedTransferMetadata.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) +// Worker that Monitors the missing transfers (by scheduling regular checks). +func (valueTangle *ValueTangle) monitorMissingTransactionWorker(transferId transfer.Id) { + var scheduleNextMissingCheck func(transferId transfer.Id) + scheduleNextMissingCheck = func(transferId transfer.Id) { + time.AfterFunc(MissingCheckInterval, func() { + valueTangle.missingTransferStorage.Load(transferId[:]).Consume(func(object objectstorage.StorableObject) { + missingTransfer := object.(*model.MissingTransfer) - for _, transferOutputReference := range valueTransfer.GetInputs() { - addressMap, addressMapExists := result[transferOutputReference.GetTransferHash()] - if !addressMapExists { - addressMap = make(map[address.Address]types.Empty) + if time.Since(missingTransfer.GetMissingSince()) >= MaxMissingTimeBeforeCleanup { + valueTangle.cleanupWorkerPool.Submit(func() { valueTangle.cleanupWorker(missingTransfer.GetTransferId()) }) + } else { + valueTangle.Events.TransferMissing.Trigger(transferId) - result[transferOutputReference.GetTransferHash()] = addressMap - } - - addressMap[transferOutputReference.GetAddress()] = types.Void + scheduleNextMissingCheck(transferId) + } + }) + }) } + valueTangle.Events.TransferMissing.Trigger(transferId) - return + scheduleNextMissingCheck(transferId) +} + +// Worker that recursively cleans up the approvers of a unsolidifiable missing transfer. +func (valueTangle *ValueTangle) cleanupWorker(transferId transfer.Id) { + cleanupStack := list.New() + cleanupStack.PushBack(transferId) + + for cleanupStack.Len() >= 1 { + currentStackEntry := cleanupStack.Front() + currentTransferId := currentStackEntry.Value.(transfer.Id) + cleanupStack.Remove(currentStackEntry) + + valueTangle.GetConsumers(currentTransferId).Consume(func(object objectstorage.StorableObject) { + for approverTransactionId := range object.(*model.Consumers).Get() { + valueTangle.deleteTransfer(currentTransferId) + + cleanupStack.PushBack(approverTransactionId) + } + }) + } } diff --git a/packages/binary/valuetangle/valuetangle_test.go b/packages/binary/valuetangle/valuetangle_test.go new file mode 100644 index 0000000000000000000000000000000000000000..d4e954bbc2e457fe9e44d37aaa9b056bbb0eef73 --- /dev/null +++ b/packages/binary/valuetangle/valuetangle_test.go @@ -0,0 +1,101 @@ +package valuetangle + +import ( + "fmt" + "testing" + "time" + + "github.com/mr-tron/base58" + + "github.com/iotaledger/goshimmer/packages/binary/address" + "github.com/iotaledger/goshimmer/packages/binary/transactionmetadata" + "github.com/iotaledger/goshimmer/packages/ledgerstate/coloredcoins" + "github.com/iotaledger/goshimmer/packages/ledgerstate/transfer" + + "github.com/iotaledger/goshimmer/packages/binary/signature/ed25119" + + "github.com/iotaledger/goshimmer/packages/binary/transaction/payload/valuetransfer" + + "github.com/iotaledger/goshimmer/packages/binary/identity" + "github.com/iotaledger/goshimmer/packages/binary/transaction" + + "github.com/iotaledger/goshimmer/packages/binary/tangle" + "github.com/iotaledger/goshimmer/packages/binary/valuetangle/model" + "github.com/iotaledger/hive.go/events" +) + +var localSnapshot *tangle.Snapshot +var keyPairOfAddress1 = ed25119.GenerateKeyPair() +var keyPairOfAddress2 = ed25119.GenerateKeyPair() + +func getLocalSnapshot() *tangle.Snapshot { + if localSnapshot == nil { + localSnapshot = &tangle.Snapshot{ + SolidEntryPoints: map[transaction.Id]map[address.Address]*coloredcoins.ColoredBalance{ + transaction.NewId([]byte("tx0")): nil, + transaction.NewId([]byte("tx1")): { + address.FromPublicKey(keyPairOfAddress1.PublicKey): coloredcoins.NewColoredBalance(coloredcoins.NewColor("IOTA"), 1337), + address.FromPublicKey(keyPairOfAddress2.PublicKey): coloredcoins.NewColoredBalance(coloredcoins.NewColor("IOTA"), 1337), + }, + transaction.NewId([]byte("tx2")): { + address.New([]byte("address3")): coloredcoins.NewColoredBalance(coloredcoins.NewColor("IOTA"), 1337), + address.New([]byte("address4")): coloredcoins.NewColoredBalance(coloredcoins.NewColor("IOTA"), 1337), + }, + }, + } + } + + return localSnapshot +} + +func TestValueTangle(t *testing.T) { + transactionTangle := tangle.New([]byte("testtangle")) + transactionTangle.Prune() + + valueTangle := New(transactionTangle) + valueTangle.Prune() + + transactionTangle.Events.TransactionAttached.Attach(events.NewClosure(func(cachedTransaction *transaction.CachedTransaction, cachedTransactionMetadata *transactionmetadata.CachedTransactionMetadata) { + transactionId := cachedTransaction.Unwrap().GetId() + + if cachedTransaction.Unwrap().GetPayload().GetType() != valuetransfer.Type { + fmt.Println("[TRANSACTION TANGLE] Data transaction attached: ", base58.Encode(transactionId[:])) + } else { + fmt.Println("[TRANSACTION TANGLE] Value Transaction attached:", base58.Encode(transactionId[:])) + } + })) + + transactionTangle.Events.TransactionSolid.Attach(events.NewClosure(func(cachedTransaction *transaction.CachedTransaction, cachedTransactionMetadata *transactionmetadata.CachedTransactionMetadata) { + transactionId := cachedTransaction.Unwrap().GetId() + + if cachedTransaction.Unwrap().GetPayload().GetType() != valuetransfer.Type { + fmt.Println("[TRANSACTION TANGLE] Data transaction solid: ", base58.Encode(transactionId[:])) + } else { + fmt.Println("[TRANSACTION TANGLE] Value Transaction solid: ", base58.Encode(transactionId[:])) + } + })) + + valueTangle.Events.TransferAttached.Attach(events.NewClosure(func(cachedValueTransfer *model.CachedValueTransfer, cachedTransferMetadata *model.CachedTransferMetadata) { + transactionId := cachedValueTransfer.Unwrap().GetId() + + fmt.Println("[VALUE TANGLE] Value Transaction attached:", base58.Encode(transactionId[:])) + })) + valueTangle.Events.TransferSolid.Attach(events.NewClosure(func(cachedValueTransfer *model.CachedValueTransfer, cachedTransferMetadata *model.CachedTransferMetadata) { + transactionId := cachedValueTransfer.Unwrap().GetId() + + fmt.Println("[VALUE TANGLE] Value Transaction solid: ", base58.Encode(transactionId[:])) + })) + + transactionTangle.LoadSnapshot(getLocalSnapshot()) + + myIdentity := identity.Generate() + + transactionTangle.AttachTransaction(transaction.New(transaction.EmptyId, transaction.EmptyId, myIdentity, valuetransfer.New(). + AddInput(transfer.NewId([]byte("tx1")), address.FromPublicKey(keyPairOfAddress1.PublicKey)). + AddOutput(address.FromPublicKey(keyPairOfAddress2.PublicKey), coloredcoins.NewColoredBalance(coloredcoins.NewColor("IOTA"), 12)). + Sign(keyPairOfAddress1))) + + time.Sleep(1 * time.Second) + + valueTangle.Shutdown() +} diff --git a/packages/ledgerstate/transfer/id.go b/packages/ledgerstate/transfer/id.go index 8431ea04dc7358f49bb1e49727f30c9321789461..124439db880248516d0a0eefc0e74f18a53a90e1 100644 --- a/packages/ledgerstate/transfer/id.go +++ b/packages/ledgerstate/transfer/id.go @@ -44,4 +44,4 @@ func (transferId Id) String() string { var EmptyId = Id{} -const IdLength = 32 +const IdLength = 64 diff --git a/packages/storageprefix/storageprefix.go b/packages/storageprefix/storageprefix.go index 45bdcbfeab14e5b0577c4a04fa508233690223bb..c491a24e5d6adfeae617211e290b148a1f5738b3 100644 --- a/packages/storageprefix/storageprefix.go +++ b/packages/storageprefix/storageprefix.go @@ -6,6 +6,10 @@ var ( TangleApprovers = []byte{1} TangleMissingTransaction = []byte{7} + ValueTangleTransferMetadata = []byte{8} + ValueTangleConsumers = []byte{9} + ValueTangleMissingTransfers = []byte{10} + LedgerStateTransferOutput = []byte{2} LedgerStateTransferOutputBooking = []byte{3} LedgerStateReality = []byte{4}