diff --git a/packages/binary/tangle/tangle.go b/packages/binary/tangle/tangle.go new file mode 100644 index 0000000000000000000000000000000000000000..2d6210fc7f80559cdf879af4ca652c34a1474db3 --- /dev/null +++ b/packages/binary/tangle/tangle.go @@ -0,0 +1,22 @@ +package tangle + +import ( + "github.com/iotaledger/goshimmer/packages/binary/transaction" + "github.com/iotaledger/hive.go/objectstorage" +) + +type Tangle struct { + transactionStorage *objectstorage.ObjectStorage +} + +func New(storageId string) *Tangle { + return &Tangle{ + transactionStorage: objectstorage.New(storageId+"TANGLE_TRANSACTION_STORAGE", transactionFactory), + } +} + +func transactionFactory(key []byte) objectstorage.StorableObject { + result := transaction.FromStorage(key) + + return result +} diff --git a/packages/binary/transaction/id.go b/packages/binary/transaction/id.go new file mode 100644 index 0000000000000000000000000000000000000000..be29a8b8c95e09ad30ed5e0a1f5704ef5cba8dd6 --- /dev/null +++ b/packages/binary/transaction/id.go @@ -0,0 +1,5 @@ +package transaction + +type Id [transactionIdLength]byte + +const transactionIdLength = 64 diff --git a/packages/binary/transaction/payload.go b/packages/binary/transaction/payload.go new file mode 100644 index 0000000000000000000000000000000000000000..13cb0d6160cf0cc45163fc70ff4f60c30bcc4884 --- /dev/null +++ b/packages/binary/transaction/payload.go @@ -0,0 +1,12 @@ +package transaction + +import ( + "encoding" +) + +type Payload interface { + encoding.BinaryMarshaler + encoding.BinaryUnmarshaler + + GetType() int +} diff --git a/packages/binary/transaction/payload_id.go b/packages/binary/transaction/payload_id.go new file mode 100644 index 0000000000000000000000000000000000000000..ca8edef87c597ee853b9fcb1d304dd94d2c28777 --- /dev/null +++ b/packages/binary/transaction/payload_id.go @@ -0,0 +1,5 @@ +package transaction + +type PayloadId [payloadIdLength]byte + +const payloadIdLength = 64 diff --git a/packages/binary/transaction/transaction.go b/packages/binary/transaction/transaction.go new file mode 100644 index 0000000000000000000000000000000000000000..06a0e56ced017f453f19d489ad639bccce80669b --- /dev/null +++ b/packages/binary/transaction/transaction.go @@ -0,0 +1,164 @@ +package transaction + +import ( + "sync" + + "github.com/iotaledger/hive.go/objectstorage" + + "golang.org/x/crypto/blake2b" +) + +type Transaction struct { + // base functionality of StorableObject + objectstorage.StorableObjectFlags + + // core properties (they are part of the transaction when being sent) + trunkTransactionId Id + branchTransactionId Id + payload Payload + + // derived properties + id *Id + idMutex sync.RWMutex + payloadId *PayloadId + payloadIdMutex sync.RWMutex + bytes []byte + bytesMutex sync.RWMutex +} + +func FromStorage(id []byte) (result *Transaction) { + var transactionId Id + copy(transactionId[:], id) + + result = &Transaction{ + id: &transactionId, + } + + return +} + +func (transaction *Transaction) GetId() (result Id) { + transaction.idMutex.RLock() + if transaction.id == nil { + transaction.idMutex.RLock() + + transaction.idMutex.Lock() + if transaction.id == nil { + result = transaction.calculateTransactionId() + + transaction.id = &result + } else { + result = *transaction.id + } + transaction.idMutex.Unlock() + } else { + result = *transaction.id + + transaction.idMutex.RLock() + } + + return +} + +func (transaction *Transaction) GetPayloadId() (result PayloadId) { + transaction.payloadIdMutex.RLock() + if transaction.payloadId == nil { + transaction.payloadIdMutex.RLock() + + transaction.payloadIdMutex.Lock() + if transaction.payloadId == nil { + result = transaction.calculatePayloadId() + + transaction.payloadId = &result + } else { + result = *transaction.payloadId + } + transaction.payloadIdMutex.Unlock() + } else { + result = *transaction.payloadId + + transaction.payloadIdMutex.RLock() + } + + return +} + +func (transaction *Transaction) GetBytes() (result []byte) { + transaction.bytesMutex.RLock() + if transaction.bytes == nil { + transaction.bytesMutex.RLock() + + transaction.bytesMutex.Lock() + if transaction.bytes == nil { + var err error + + if result, err = transaction.MarshalBinary(); err != nil { + // this should never happen + panic(err) + } + + transaction.bytes = result + } else { + result = transaction.bytes + } + transaction.bytesMutex.Unlock() + } else { + result = transaction.bytes + + transaction.bytesMutex.RLock() + } + + return +} + +func (transaction *Transaction) calculateTransactionId() Id { + payloadId := transaction.GetPayloadId() + + hashBase := make([]byte, transactionIdLength+transactionIdLength+payloadIdLength) + offset := 0 + + copy(hashBase[offset:], transaction.trunkTransactionId[:]) + offset += transactionIdLength + + copy(hashBase[offset:], transaction.branchTransactionId[:]) + offset += transactionIdLength + + copy(hashBase[offset:], payloadId[:]) + offset += payloadIdLength + + return blake2b.Sum512(hashBase) +} + +func (transaction *Transaction) calculatePayloadId() PayloadId { + bytes := transaction.GetBytes() + + return blake2b.Sum512(bytes[2*transactionIdLength:]) +} + +func (transaction *Transaction) MarshalBinary() (result []byte, err error) { + result = make([]byte, 2*transactionIdLength) + + if serializedPayload, serializationErr := transaction.payload.MarshalBinary(); serializationErr != nil { + err = serializationErr + + return + } else { + result = append(result, serializedPayload...) + } + + return +} + +// TODO: FINISH +func (transaction *Transaction) UnmarshalBinary(date []byte) (err error) { + return +} + +func (transaction *Transaction) GetStorageKey() []byte { + transactionId := transaction.GetId() + + return transactionId[:] +} + +// TODO: FINISH +func (transaction *Transaction) Update(other objectstorage.StorableObject) {} diff --git a/packages/bitutils/offset.go b/packages/bitutils/offset.go new file mode 100644 index 0000000000000000000000000000000000000000..9ecd43c274407a5aa9022939dce996855a995fff --- /dev/null +++ b/packages/bitutils/offset.go @@ -0,0 +1,21 @@ +package bitutils + +import ( + "unsafe" +) + +type Offset int + +func NewOffset() *Offset { + offset := Offset(0) + + return &offset +} + +func (offset *Offset) Inc(delta int) (newOffset int) { + newOffset = *(*int)(unsafe.Pointer(offset)) + delta + + *offset = *(*Offset)(unsafe.Pointer(&newOffset)) + + return +} diff --git a/packages/bitutils/offset_test.go b/packages/bitutils/offset_test.go new file mode 100644 index 0000000000000000000000000000000000000000..345df662a3558eb12ca9de67d906755d8e70d46f --- /dev/null +++ b/packages/bitutils/offset_test.go @@ -0,0 +1,38 @@ +package bitutils + +import ( + "fmt" + "testing" +) + +func BenchmarkOffset_Inc(b *testing.B) { + var x Offset + + for i := 0; i < b.N; i++ { + x.Inc(1) + } +} + +func Benchmark_Inc(b *testing.B) { + x := 0 + + for i := 0; i < b.N; i++ { + x++ + } +} + +func TestOffset_Inc(t *testing.T) { + q := 3 + + j := &q + + *j = 12 + + fmt.Println(q) + + var x Offset + + x.Inc(4) + + fmt.Println(x) +} diff --git a/packages/ledgerstate/ledgerstate_test.go b/packages/ledgerstate/ledgerstate_test.go index be5cdf81d3f91ae00ae2acb3b83a8d93b6e50f57..84dbfd10b923ce21a36cd549436bd1fc0ebb8179 100644 --- a/packages/ledgerstate/ledgerstate_test.go +++ b/packages/ledgerstate/ledgerstate_test.go @@ -222,8 +222,8 @@ func TestAggregateAggregatedRealities(t *testing.T) { outputs1 := multiSpend(ledgerState, 2, multiSpend(ledgerState, 1, transferOutputs[1])[0]) multiSpend(ledgerState, 1, transferOutputs[1]) - multiSpend(ledgerState, 1, transferOutputs[2]) outputs2 := multiSpend(ledgerState, 2, multiSpend(ledgerState, 1, transferOutputs[2])[0]) + multiSpend(ledgerState, 1, transferOutputs[2]) aggregatedOutputs0 := multiSpend(ledgerState, 2, outputs0[0], outputs1[0]) aggregatedOutputs1 := multiSpend(ledgerState, 2, outputs1[1], outputs2[1]) diff --git a/packages/ledgerstate/outputs1.png b/packages/ledgerstate/outputs1.png index dfd470d7176c3bbe1ba5a22a683236185ada77c0..3113c75833e458ab2bcf84e6596befc8c8e46a30 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 1091bb3948e28c5185f490646c85590d67c257aa..7b2d13e78682f509b9c425714dbee0bc3dc068fc 100644 Binary files a/packages/ledgerstate/outputs2.png and b/packages/ledgerstate/outputs2.png differ diff --git a/packages/ledgerstate/realities1.png b/packages/ledgerstate/realities1.png index 632f38af62b17e2feb2609e6e40df9b14e7555ab..9506a61b894d1193dd33faa6375a3182b042bc4c 100644 Binary files a/packages/ledgerstate/realities1.png and b/packages/ledgerstate/realities1.png differ diff --git a/packages/ledgerstate/realities2.png b/packages/ledgerstate/realities2.png index 51d0e54970a4ad8e61be95e328557918dc07a4cc..cd86247171dbc3800c20dc35df1b532eef937ede 100644 Binary files a/packages/ledgerstate/realities2.png and b/packages/ledgerstate/realities2.png differ diff --git a/packages/ledgerstate/reality.objectstorage.go b/packages/ledgerstate/reality.objectstorage.go index a43ae432b8edf747726eaf19567b0233022d8888..fac42376ff3104ca79619217c4686232bcb442be 100644 --- a/packages/ledgerstate/reality.objectstorage.go +++ b/packages/ledgerstate/reality.objectstorage.go @@ -3,6 +3,8 @@ package ledgerstate import ( "encoding/binary" + "github.com/iotaledger/hive.go/bitmask" + "github.com/iotaledger/hive.go/objectstorage" ) @@ -26,11 +28,21 @@ func (reality *Reality) MarshalBinary() ([]byte, error) { parentRealityCount := len(reality.parentRealityIds) subRealityCount := len(reality.subRealityIds) - marshaledReality := make([]byte, 4+4+4+1+parentRealityCount*realityIdLength+subRealityCount*realityIdLength) + marshaledReality := make([]byte, 1+4+4+4+parentRealityCount*realityIdLength+subRealityCount*realityIdLength) offset := 0 - binary.LittleEndian.PutUint32(marshaledReality, uint32(reality.GetTransferOutputCount())) + var flags bitmask.BitMask + if reality.IsPreferred() { + flags = flags.SetFlag(0) + } + if reality.IsLiked() { + flags = flags.SetFlag(1) + } + marshaledReality[offset] = byte(flags) + offset += 1 + + binary.LittleEndian.PutUint32(marshaledReality[offset:], reality.GetTransferOutputCount()) offset += 4 binary.LittleEndian.PutUint32(marshaledReality[offset:], uint32(parentRealityCount)) @@ -49,59 +61,87 @@ func (reality *Reality) MarshalBinary() ([]byte, error) { offset += realityIdLength } - if reality.liked { - marshaledReality[offset] = 1 - } else { - marshaledReality[offset] = 0 - } - //offset += 1 - reality.parentRealityIdsMutex.RUnlock() return marshaledReality, nil } -func (reality *Reality) UnmarshalBinary(serializedObject []byte) error { - if err := reality.id.UnmarshalBinary(reality.storageKey[:realityIdLength]); err != nil { - return err +func (reality *Reality) UnmarshalBinary(serializedObject []byte) (err error) { + if err = reality.id.UnmarshalBinary(reality.storageKey[:realityIdLength]); err != nil { + return } - reality.parentRealityIds = NewRealityIdSet() - reality.subRealityIds = NewRealityIdSet() - offset := 0 - reality.transferOutputCount = binary.LittleEndian.Uint32(serializedObject) - offset += 4 + reality.unmarshalBinaryFlags(serializedObject, &offset) - parentRealityCount := int(binary.LittleEndian.Uint32(serializedObject[offset:])) - offset += 4 + reality.unmarshalBinaryTransferOutputCount(serializedObject, &offset) + + if err = reality.unmarshalBinaryParentRealities(serializedObject, &offset); err != nil { + return + } + + if err = reality.unmarshalBinarySubRealities(serializedObject, &offset); err != nil { + return + } + + return nil +} + +func (reality *Reality) unmarshalBinaryFlags(serializedObject []byte, offset *int) { + var flags = bitmask.BitMask(serializedObject[*offset]) + + if flags.HasFlag(0) { + reality.preferred = true + } + + if flags.HasFlag(1) { + reality.liked = true + } + + *offset += 1 +} + +func (reality *Reality) unmarshalBinaryTransferOutputCount(serializedObject []byte, offset *int) { + reality.transferOutputCount = binary.LittleEndian.Uint32(serializedObject[*offset:]) + + *offset = *offset + 4 +} + +func (reality *Reality) unmarshalBinaryParentRealities(serializedObject []byte, offset *int) (err error) { + reality.parentRealityIds = NewRealityIdSet() + + parentRealityCount := int(binary.LittleEndian.Uint32(serializedObject[*offset:])) + *offset += 4 for i := 0; i < parentRealityCount; i++ { var restoredRealityId RealityId - if err := restoredRealityId.UnmarshalBinary(serializedObject[offset:]); err != nil { - return err + if err = restoredRealityId.UnmarshalBinary(serializedObject[*offset:]); err != nil { + return } - offset += realityIdLength + *offset += realityIdLength reality.parentRealityIds[restoredRealityId] = void } - subRealityCount := int(binary.LittleEndian.Uint32(serializedObject[offset:])) - offset += 4 + return +} + +func (reality *Reality) unmarshalBinarySubRealities(serializedObject []byte, offset *int) (err error) { + reality.subRealityIds = NewRealityIdSet() + + subRealityCount := int(binary.LittleEndian.Uint32(serializedObject[*offset:])) + *offset += 4 for i := 0; i < subRealityCount; i++ { var restoredRealityId RealityId - if err := restoredRealityId.UnmarshalBinary(serializedObject[offset:]); err != nil { - return err + if err = restoredRealityId.UnmarshalBinary(serializedObject[*offset:]); err != nil { + return } - offset += realityIdLength + *offset += realityIdLength reality.subRealityIds[restoredRealityId] = void } - reality.liked = serializedObject[offset] == 1 - //offset += 1 - - return nil + return }