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

Feat: finished bundle model

parent cc038578
No related branches found
No related tags found
No related merge requests found
......@@ -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)
......
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
}
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)
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
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment