diff --git a/packages/model/approvers/approvers_test.go b/packages/model/approvers/approvers_test.go index 61cfef5076fff6a2358c004f3a7ae3354bf93a73..5436330c4136f7b6cbbb8e561e520c55d087a490 100644 --- a/packages/model/approvers/approvers_test.go +++ b/packages/model/approvers/approvers_test.go @@ -18,7 +18,7 @@ func TestApprovers_SettersGetters(t *testing.T) { assert.Equal(t, approversTest.GetHashes()[0], hashB, "hashes") } -func TestApprovers_MarshalUnmarshalGetters(t *testing.T) { +func TestApprovers_MarshalUnmarshal(t *testing.T) { hashA := ternary.Trytes("A9999999999999999999999999999999999999999999999999999999999999999999999999999999F") hashB := ternary.Trytes("B9999999999999999999999999999999999999999999999999999999999999999999999999999999F") approversTest := New(hashA) diff --git a/packages/model/bundle/bundle.go b/packages/model/bundle/bundle.go index 36c50cb995fee7780add92cb9f280302cc2871ae..88ff6cb17b22808765334d6041fb6b61d67d4f50 100644 --- a/packages/model/bundle/bundle.go +++ b/packages/model/bundle/bundle.go @@ -1,22 +1,29 @@ package bundle import ( + "encoding/binary" + "strconv" "sync" + "unsafe" + "github.com/iotaledger/goshimmer/packages/bitutils" "github.com/iotaledger/goshimmer/packages/errors" + "github.com/iotaledger/goshimmer/packages/typeutils" - "github.com/iotaledger/goshimmer/packages/model/value_transaction" "github.com/iotaledger/goshimmer/packages/ternary" ) type Bundle struct { - hash ternary.Trytes - transactionHashes []ternary.Trytes - isValueBundle bool - isValueBundleMutex sync.RWMutex - bundleEssenceHash ternary.Trytes - modified bool - modifiedMutex sync.RWMutex + hash ternary.Trytes + hashMutex sync.RWMutex + transactionHashes []ternary.Trytes + transactionHashesMutex sync.RWMutex + isValueBundle bool + isValueBundleMutex sync.RWMutex + bundleEssenceHash ternary.Trytes + bundleEssenceHashMutex sync.RWMutex + modified bool + modifiedMutex sync.RWMutex } func New(headTransactionHash ternary.Trytes) (result *Bundle) { @@ -27,12 +34,32 @@ func New(headTransactionHash ternary.Trytes) (result *Bundle) { return } -func (bundle *Bundle) GetTransactionHashes() []ternary.Trytes { - return bundle.transactionHashes +func (bundle *Bundle) GetHash() (result ternary.Trytes) { + bundle.hashMutex.RLock() + result = bundle.hash + bundle.hashMutex.RUnlock() + + return +} + +func (bundle *Bundle) SetHash(hash ternary.Trytes) { + bundle.hashMutex.Lock() + bundle.hash = hash + bundle.hashMutex.Unlock() +} + +func (bundle *Bundle) GetTransactionHashes() (result []ternary.Trytes) { + bundle.bundleEssenceHashMutex.RLock() + result = bundle.transactionHashes + bundle.bundleEssenceHashMutex.RUnlock() + + return } -func (bundle *Bundle) GetHash() ternary.Trytes { - return bundle.hash +func (bundle *Bundle) SetTransactionHashes(transactionHashes []ternary.Trytes) { + bundle.transactionHashesMutex.Lock() + bundle.transactionHashes = transactionHashes + bundle.transactionHashesMutex.Unlock() } func (bundle *Bundle) IsValueBundle() (result bool) { @@ -49,6 +76,20 @@ func (bundle *Bundle) SetValueBundle(valueBundle bool) { bundle.isValueBundleMutex.Unlock() } +func (bundle *Bundle) GetBundleEssenceHash() (result ternary.Trytes) { + bundle.bundleEssenceHashMutex.RLock() + result = bundle.bundleEssenceHash + bundle.bundleEssenceHashMutex.RUnlock() + + return +} + +func (bundle *Bundle) SetBundleEssenceHash(bundleEssenceHash ternary.Trytes) { + bundle.bundleEssenceHashMutex.Lock() + bundle.bundleEssenceHash = bundleEssenceHash + bundle.bundleEssenceHashMutex.Unlock() +} + func (bundle *Bundle) GetModified() (result bool) { bundle.modifiedMutex.RLock() result = bundle.modified @@ -63,14 +104,81 @@ func (bundle *Bundle) SetModified(modified bool) { bundle.modifiedMutex.Unlock() } -func (bundle *Bundle) Marshal() []byte { - return nil -} +func (bundle *Bundle) Marshal() (result []byte) { + bundle.hashMutex.RLock() + bundle.bundleEssenceHashMutex.RLock() + bundle.isValueBundleMutex.RLock() + bundle.transactionHashesMutex.RLock() + + result = make([]byte, MARSHALED_MIN_SIZE+len(bundle.transactionHashes)*MARSHALED_TRANSACTION_HASH_SIZE) + + binary.BigEndian.PutUint64(result[MARSHALED_TRANSACTIONS_COUNT_START:MARSHALED_TRANSACTIONS_COUNT_END], uint64(len(bundle.transactionHashes))) + + copy(result[MARSHALED_HASH_START:MARSHALED_HASH_END], bundle.hash.CastToBytes()) + copy(result[MARSHALED_BUNDLE_ESSENCE_HASH_START:MARSHALED_BUNDLE_ESSENCE_HASH_END], bundle.bundleEssenceHash.CastToBytes()) + + var flags bitutils.BitMask + if bundle.isValueBundle { + flags = flags.SetFlag(0) + } + result[MARSHALED_FLAGS_START] = *(*byte)(unsafe.Pointer(&flags)) + + i := 0 + for _, hash := range bundle.transactionHashes { + var HASH_START = MARSHALED_APPROVERS_HASHES_START + i*(MARSHALED_TRANSACTION_HASH_SIZE) + var HASH_END = HASH_START + MARSHALED_TRANSACTION_HASH_SIZE + + copy(result[HASH_START:HASH_END], hash.CastToBytes()) -func (bundle *Bundle) Unmarshal(data []byte) errors.IdentifiableError { - return nil + i++ + } + + bundle.transactionHashesMutex.RUnlock() + bundle.isValueBundleMutex.RUnlock() + bundle.bundleEssenceHashMutex.RUnlock() + bundle.hashMutex.RUnlock() + + return } -func CalculateBundleHash(transactions []*value_transaction.ValueTransaction) ternary.Trytes { - return (<-Hasher.Hash(transactions[0].GetBundleEssence())).ToTrytes() +func (bundle *Bundle) Unmarshal(data []byte) (err errors.IdentifiableError) { + dataLen := len(data) + + if dataLen < MARSHALED_MIN_SIZE { + return ErrMarshallFailed.Derive(errors.New("unmarshall failed"), "marshaled bundle is too short") + } + + hashesCount := binary.BigEndian.Uint64(data[MARSHALED_TRANSACTIONS_COUNT_START:MARSHALED_TRANSACTIONS_COUNT_END]) + + if dataLen < MARSHALED_MIN_SIZE+int(hashesCount)*MARSHALED_TRANSACTION_HASH_SIZE { + return ErrMarshallFailed.Derive(errors.New("unmarshall failed"), "marshaled bundle is too short for "+strconv.FormatUint(hashesCount, 10)+" transactions") + } + + bundle.hashMutex.Lock() + bundle.bundleEssenceHashMutex.Lock() + bundle.isValueBundleMutex.Lock() + bundle.transactionHashesMutex.Lock() + + bundle.hash = ternary.Trytes(typeutils.BytesToString(data[MARSHALED_HASH_START:MARSHALED_HASH_END])) + bundle.bundleEssenceHash = ternary.Trytes(typeutils.BytesToString(data[MARSHALED_BUNDLE_ESSENCE_HASH_START:MARSHALED_BUNDLE_ESSENCE_HASH_END])) + + flags := bitutils.BitMask(data[MARSHALED_FLAGS_START]) + if flags.HasFlag(0) { + bundle.isValueBundle = true + } + + bundle.transactionHashes = make([]ternary.Trytes, hashesCount) + for i := uint64(0); i < hashesCount; i++ { + var HASH_START = MARSHALED_APPROVERS_HASHES_START + i*(MARSHALED_TRANSACTION_HASH_SIZE) + var HASH_END = HASH_START + MARSHALED_TRANSACTION_HASH_SIZE + + bundle.transactionHashes[i] = ternary.Trytes(typeutils.BytesToString(data[HASH_START:HASH_END])) + } + + bundle.transactionHashesMutex.Unlock() + bundle.isValueBundleMutex.Unlock() + bundle.bundleEssenceHashMutex.Unlock() + bundle.hashMutex.Unlock() + + return } diff --git a/packages/model/bundle/bundle_test.go b/packages/model/bundle/bundle_test.go new file mode 100644 index 0000000000000000000000000000000000000000..ee1b0e328e9a085872fcb3b593d0a2b2b2d4061f --- /dev/null +++ b/packages/model/bundle/bundle_test.go @@ -0,0 +1,56 @@ +package bundle + +import ( + "testing" + + "github.com/iotaledger/goshimmer/packages/ternary" + "github.com/magiconair/properties/assert" +) + +func TestBundle_SettersGetters(t *testing.T) { + bundleHash := ternary.Trytes("A9999999999999999999999999999999999999999999999999999999999999999999999999999999F") + bundleEssenceHash := ternary.Trytes("B9999999999999999999999999999999999999999999999999999999999999999999999999999999F") + transactions := []ternary.Trytes{ + bundleHash, + ternary.Trytes("C9999999999999999999999999999999999999999999999999999999999999999999999999999999F"), + } + + testBundle := New(bundleHash) + testBundle.SetTransactionHashes(transactions) + testBundle.SetBundleEssenceHash(bundleEssenceHash) + testBundle.SetValueBundle(true) + + assert.Equal(t, testBundle.GetHash(), bundleHash, "hash of source") + assert.Equal(t, testBundle.GetBundleEssenceHash(), bundleEssenceHash, "bundle essence hash of source") + assert.Equal(t, testBundle.IsValueBundle(), true, "value bundle of source") + assert.Equal(t, len(testBundle.GetTransactionHashes()), len(transactions), "# of transactions of source") + assert.Equal(t, testBundle.GetTransactionHashes()[0], transactions[0], "transaction[0] of source") + assert.Equal(t, testBundle.GetTransactionHashes()[1], transactions[1], "transaction[1] of source") +} + +func TestBundle_SettersGettersMarshalUnmarshal(t *testing.T) { + bundleHash := ternary.Trytes("A9999999999999999999999999999999999999999999999999999999999999999999999999999999F") + bundleEssenceHash := ternary.Trytes("B9999999999999999999999999999999999999999999999999999999999999999999999999999999F") + transactions := []ternary.Trytes{ + bundleHash, + ternary.Trytes("C9999999999999999999999999999999999999999999999999999999999999999999999999999999F"), + } + + testBundle := New(bundleHash) + testBundle.SetTransactionHashes(transactions) + testBundle.SetBundleEssenceHash(bundleEssenceHash) + testBundle.SetValueBundle(true) + + var bundleUnmarshaled Bundle + err := bundleUnmarshaled.Unmarshal(testBundle.Marshal()) + if err != nil { + t.Error(err) + } + + assert.Equal(t, bundleUnmarshaled.GetHash(), testBundle.GetHash(), "hash of target") + assert.Equal(t, bundleUnmarshaled.GetBundleEssenceHash(), testBundle.GetBundleEssenceHash(), "bundle essence hash of target") + assert.Equal(t, bundleUnmarshaled.IsValueBundle(), true, "value bundle of target") + assert.Equal(t, len(bundleUnmarshaled.GetTransactionHashes()), len(transactions), "# of transactions of target") + assert.Equal(t, bundleUnmarshaled.GetTransactionHashes()[0], transactions[0], "transaction[0] of target") + assert.Equal(t, bundleUnmarshaled.GetTransactionHashes()[1], transactions[1], "transaction[1] of target") +} diff --git a/packages/model/bundle/constants.go b/packages/model/bundle/constants.go new file mode 100644 index 0000000000000000000000000000000000000000..1575f582e69874f4cc852600bf8ea29d5389ec89 --- /dev/null +++ b/packages/model/bundle/constants.go @@ -0,0 +1,20 @@ +package bundle + +const ( + MARSHALED_TRANSACTIONS_COUNT_START = 0 + MARSHALED_HASH_START = MARSHALED_TRANSACTIONS_COUNT_END + MARSHALED_BUNDLE_ESSENCE_HASH_START = MARSHALED_HASH_END + MARSHALED_FLAGS_START = MARSHALED_BUNDLE_ESSENCE_HASH_END + MARSHALED_APPROVERS_HASHES_START = MARSHALED_FLAGS_END + + MARSHALED_TRANSACTIONS_COUNT_END = MARSHALED_TRANSACTIONS_COUNT_START + MARSHALED_TRANSACTIONS_COUNT_SIZE + MARSHALED_HASH_END = MARSHALED_HASH_START + MARSHALED_TRANSACTION_HASH_SIZE + MARSHALED_BUNDLE_ESSENCE_HASH_END = MARSHALED_BUNDLE_ESSENCE_HASH_START + MARSHALED_BUNDLE_ESSENCE_HASH_SIZE + MARSHALED_FLAGS_END = MARSHALED_FLAGS_START + MARSHALED_FLAGS_SIZE + + MARSHALED_TRANSACTIONS_COUNT_SIZE = 8 + MARSHALED_TRANSACTION_HASH_SIZE = 81 + MARSHALED_BUNDLE_ESSENCE_HASH_SIZE = 81 + MARSHALED_FLAGS_SIZE = 1 + MARSHALED_MIN_SIZE = MARSHALED_TRANSACTIONS_COUNT_SIZE + MARSHALED_TRANSACTION_HASH_SIZE + MARSHALED_BUNDLE_ESSENCE_HASH_SIZE + MARSHALED_FLAGS_SIZE +) diff --git a/packages/model/bundle/errors.go b/packages/model/bundle/errors.go new file mode 100644 index 0000000000000000000000000000000000000000..c4805e6bb6c70d420a1cccc764c97a8a2d5cd036 --- /dev/null +++ b/packages/model/bundle/errors.go @@ -0,0 +1,10 @@ +package bundle + +import ( + "github.com/iotaledger/goshimmer/packages/errors" +) + +var ( + ErrUnmarshalFailed = errors.Wrap(errors.New("unmarshall failed"), "input data is corrupted") + ErrMarshallFailed = errors.Wrap(errors.New("marshal failed"), "the source object contains invalid values") +) diff --git a/packages/model/bundle/hasher.go b/packages/model/bundle/hasher.go deleted file mode 100644 index e1eccd1e1d7320436b9b26c57a4e0c8294f97ea7..0000000000000000000000000000000000000000 --- a/packages/model/bundle/hasher.go +++ /dev/null @@ -1,14 +0,0 @@ -package bundle - -import ( - "github.com/iotaledger/goshimmer/packages/curl" -) - -const ( - CURLP81_HASH_LENGTH = 243 - CURLP81_ROUNDS = 81 -) - -var ( - Hasher = curl.NewBatchHasher(CURLP81_HASH_LENGTH, CURLP81_ROUNDS) -) diff --git a/plugins/bundleprocessor/plugin.go b/plugins/bundleprocessor/plugin.go index 4139f2336fe4d7cd9e1f79ecfcd76ab6e14825e0..304772311684bbbdd9da88ad31b897d328d5628a 100644 --- a/plugins/bundleprocessor/plugin.go +++ b/plugins/bundleprocessor/plugin.go @@ -31,8 +31,8 @@ func ProcessSolidBundleHead(headTransaction *value_transaction.ValueTransaction) return nil, ErrProcessBundleFailed.Derive(errors.New("invalid parameter"), "transaction needs to be head of bundle") } - // initialize result variables - processedBundle := bundle.New(headTransactionHash) + // initialize event variables + newBundle := bundle.New(headTransactionHash) bundleTransactions := make([]*value_transaction.ValueTransaction, 0) // iterate through trunk transactions until we reach the tail @@ -40,9 +40,9 @@ func ProcessSolidBundleHead(headTransaction *value_transaction.ValueTransaction) for { // abort if we reached a previous head if currentTransaction.IsHead() && currentTransaction != headTransaction { - processedBundle.SetTransactionHashes(mapTransactionsToTransactionHashes(bundleTransactions)) + newBundle.SetTransactionHashes(mapTransactionsToTransactionHashes(bundleTransactions)) - Events.InvalidBundleReceived.Trigger(processedBundle, bundleTransactions) + Events.InvalidBundleReceived.Trigger(newBundle, bundleTransactions) return nil, ErrProcessBundleFailed.Derive(errors.New("invalid bundle found"), "missing bundle tail") } @@ -58,21 +58,21 @@ func ProcessSolidBundleHead(headTransaction *value_transaction.ValueTransaction) currentTransactionMetadata.SetBundleHeadHash(headTransactionHash) // update value bundle flag - if !processedBundle.IsValueBundle() && currentTransaction.GetValue() != 0 { - processedBundle.SetValueBundle(true) + if !newBundle.IsValueBundle() && currentTransaction.GetValue() != 0 { + newBundle.SetValueBundle(true) } // if we are done -> trigger events if currentTransaction.IsTail() { - processedBundle.SetTransactionHashes(mapTransactionsToTransactionHashes(bundleTransactions)) + newBundle.SetTransactionHashes(mapTransactionsToTransactionHashes(bundleTransactions)) - if processedBundle.IsValueBundle() { - Events.ValueBundleReceived.Trigger(processedBundle, bundleTransactions) + if newBundle.IsValueBundle() { + Events.ValueBundleReceived.Trigger(newBundle, bundleTransactions) } else { - Events.DataBundleReceived.Trigger(processedBundle, bundleTransactions) + Events.DataBundleReceived.Trigger(newBundle, bundleTransactions) } - return processedBundle, nil + return newBundle, nil } // try to iterate to next turn @@ -85,12 +85,11 @@ func ProcessSolidBundleHead(headTransaction *value_transaction.ValueTransaction) }) } -func mapTransactionsToTransactionHashes(transactions []*value_transaction.ValueTransaction) []ternary.Trytes { - result := make([]ternary.Trytes, len(transactions)) - - for i, v := range transactions { - result[i] = v.GetHash() +func mapTransactionsToTransactionHashes(transactions []*value_transaction.ValueTransaction) (result []ternary.Trytes) { + result = make([]ternary.Trytes, len(transactions)) + for k, v := range transactions { + result[k] = v.GetHash() } - return result + return }