Skip to content
Snippets Groups Projects
Commit 453479bf authored by Hans Moog's avatar Hans Moog
Browse files

Feat: finished bundle model

parent cc038578
Branches
No related tags found
No related merge requests found
...@@ -18,7 +18,7 @@ func TestApprovers_SettersGetters(t *testing.T) { ...@@ -18,7 +18,7 @@ func TestApprovers_SettersGetters(t *testing.T) {
assert.Equal(t, approversTest.GetHashes()[0], hashB, "hashes") assert.Equal(t, approversTest.GetHashes()[0], hashB, "hashes")
} }
func TestApprovers_MarshalUnmarshalGetters(t *testing.T) { func TestApprovers_MarshalUnmarshal(t *testing.T) {
hashA := ternary.Trytes("A9999999999999999999999999999999999999999999999999999999999999999999999999999999F") hashA := ternary.Trytes("A9999999999999999999999999999999999999999999999999999999999999999999999999999999F")
hashB := ternary.Trytes("B9999999999999999999999999999999999999999999999999999999999999999999999999999999F") hashB := ternary.Trytes("B9999999999999999999999999999999999999999999999999999999999999999999999999999999F")
approversTest := New(hashA) approversTest := New(hashA)
......
package bundle package bundle
import ( import (
"encoding/binary"
"strconv"
"sync" "sync"
"unsafe"
"github.com/iotaledger/goshimmer/packages/bitutils"
"github.com/iotaledger/goshimmer/packages/errors" "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" "github.com/iotaledger/goshimmer/packages/ternary"
) )
type Bundle struct { type Bundle struct {
hash ternary.Trytes hash ternary.Trytes
transactionHashes []ternary.Trytes hashMutex sync.RWMutex
isValueBundle bool transactionHashes []ternary.Trytes
isValueBundleMutex sync.RWMutex transactionHashesMutex sync.RWMutex
bundleEssenceHash ternary.Trytes isValueBundle bool
modified bool isValueBundleMutex sync.RWMutex
modifiedMutex sync.RWMutex bundleEssenceHash ternary.Trytes
bundleEssenceHashMutex sync.RWMutex
modified bool
modifiedMutex sync.RWMutex
} }
func New(headTransactionHash ternary.Trytes) (result *Bundle) { func New(headTransactionHash ternary.Trytes) (result *Bundle) {
...@@ -27,12 +34,32 @@ func New(headTransactionHash ternary.Trytes) (result *Bundle) { ...@@ -27,12 +34,32 @@ func New(headTransactionHash ternary.Trytes) (result *Bundle) {
return return
} }
func (bundle *Bundle) GetTransactionHashes() []ternary.Trytes { func (bundle *Bundle) GetHash() (result ternary.Trytes) {
return bundle.transactionHashes 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 { func (bundle *Bundle) SetTransactionHashes(transactionHashes []ternary.Trytes) {
return bundle.hash bundle.transactionHashesMutex.Lock()
bundle.transactionHashes = transactionHashes
bundle.transactionHashesMutex.Unlock()
} }
func (bundle *Bundle) IsValueBundle() (result bool) { func (bundle *Bundle) IsValueBundle() (result bool) {
...@@ -49,6 +76,20 @@ func (bundle *Bundle) SetValueBundle(valueBundle bool) { ...@@ -49,6 +76,20 @@ func (bundle *Bundle) SetValueBundle(valueBundle bool) {
bundle.isValueBundleMutex.Unlock() 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) { func (bundle *Bundle) GetModified() (result bool) {
bundle.modifiedMutex.RLock() bundle.modifiedMutex.RLock()
result = bundle.modified result = bundle.modified
...@@ -63,14 +104,81 @@ func (bundle *Bundle) SetModified(modified bool) { ...@@ -63,14 +104,81 @@ func (bundle *Bundle) SetModified(modified bool) {
bundle.modifiedMutex.Unlock() bundle.modifiedMutex.Unlock()
} }
func (bundle *Bundle) Marshal() []byte { func (bundle *Bundle) Marshal() (result []byte) {
return nil 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 { i++
return nil }
bundle.transactionHashesMutex.RUnlock()
bundle.isValueBundleMutex.RUnlock()
bundle.bundleEssenceHashMutex.RUnlock()
bundle.hashMutex.RUnlock()
return
} }
func CalculateBundleHash(transactions []*value_transaction.ValueTransaction) ternary.Trytes { func (bundle *Bundle) Unmarshal(data []byte) (err errors.IdentifiableError) {
return (<-Hasher.Hash(transactions[0].GetBundleEssence())).ToTrytes() 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
} }
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")
}
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
)
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")
)
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)
)
...@@ -31,8 +31,8 @@ func ProcessSolidBundleHead(headTransaction *value_transaction.ValueTransaction) ...@@ -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") return nil, ErrProcessBundleFailed.Derive(errors.New("invalid parameter"), "transaction needs to be head of bundle")
} }
// initialize result variables // initialize event variables
processedBundle := bundle.New(headTransactionHash) newBundle := bundle.New(headTransactionHash)
bundleTransactions := make([]*value_transaction.ValueTransaction, 0) bundleTransactions := make([]*value_transaction.ValueTransaction, 0)
// iterate through trunk transactions until we reach the tail // iterate through trunk transactions until we reach the tail
...@@ -40,9 +40,9 @@ func ProcessSolidBundleHead(headTransaction *value_transaction.ValueTransaction) ...@@ -40,9 +40,9 @@ func ProcessSolidBundleHead(headTransaction *value_transaction.ValueTransaction)
for { for {
// abort if we reached a previous head // abort if we reached a previous head
if currentTransaction.IsHead() && currentTransaction != headTransaction { 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") return nil, ErrProcessBundleFailed.Derive(errors.New("invalid bundle found"), "missing bundle tail")
} }
...@@ -58,21 +58,21 @@ func ProcessSolidBundleHead(headTransaction *value_transaction.ValueTransaction) ...@@ -58,21 +58,21 @@ func ProcessSolidBundleHead(headTransaction *value_transaction.ValueTransaction)
currentTransactionMetadata.SetBundleHeadHash(headTransactionHash) currentTransactionMetadata.SetBundleHeadHash(headTransactionHash)
// update value bundle flag // update value bundle flag
if !processedBundle.IsValueBundle() && currentTransaction.GetValue() != 0 { if !newBundle.IsValueBundle() && currentTransaction.GetValue() != 0 {
processedBundle.SetValueBundle(true) newBundle.SetValueBundle(true)
} }
// if we are done -> trigger events // if we are done -> trigger events
if currentTransaction.IsTail() { if currentTransaction.IsTail() {
processedBundle.SetTransactionHashes(mapTransactionsToTransactionHashes(bundleTransactions)) newBundle.SetTransactionHashes(mapTransactionsToTransactionHashes(bundleTransactions))
if processedBundle.IsValueBundle() { if newBundle.IsValueBundle() {
Events.ValueBundleReceived.Trigger(processedBundle, bundleTransactions) Events.ValueBundleReceived.Trigger(newBundle, bundleTransactions)
} else { } else {
Events.DataBundleReceived.Trigger(processedBundle, bundleTransactions) Events.DataBundleReceived.Trigger(newBundle, bundleTransactions)
} }
return processedBundle, nil return newBundle, nil
} }
// try to iterate to next turn // try to iterate to next turn
...@@ -85,12 +85,11 @@ func ProcessSolidBundleHead(headTransaction *value_transaction.ValueTransaction) ...@@ -85,12 +85,11 @@ func ProcessSolidBundleHead(headTransaction *value_transaction.ValueTransaction)
}) })
} }
func mapTransactionsToTransactionHashes(transactions []*value_transaction.ValueTransaction) []ternary.Trytes { func mapTransactionsToTransactionHashes(transactions []*value_transaction.ValueTransaction) (result []ternary.Trytes) {
result := make([]ternary.Trytes, len(transactions)) result = make([]ternary.Trytes, len(transactions))
for k, v := range transactions {
for i, v := range transactions { result[k] = v.GetHash()
result[i] = v.GetHash()
} }
return result return
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment