Skip to content
Snippets Groups Projects
Unverified Commit 99da8900 authored by Hans Moog's avatar Hans Moog Committed by GitHub
Browse files

Feat: Adding the ability to book ValueObjects into their corresponding branch (#414)

* Feat: added valueobjectbooking model

* Docs: adjusted docs

* Docs: updated another comment

* Feat: added first function to book ValueObjects

* Feat: intermediary commit so nothing gets lost

* Feat: nearly done with the refactor

* Feat: nearly finished the tangle refactor

* Feat: finished refactor

* Fix: fixed unreleased object

* Refactor: removed comment

* Feat: added LedgerState.Balances

* Feat: first successful test for valuetransfer

* Feat: refactor

* Fix: fixed bug in BranchesConflicting

* Fix: fixed annotations from cilint

* Feat: commit so we can merge :D
parent 6026d479
No related branches found
No related tags found
No related merge requests found
Showing
with 1261 additions and 334 deletions
......@@ -9,11 +9,9 @@ import (
"github.com/iotaledger/hive.go/node"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/branchmanager"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/ledgerstate"
valuepayload "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/payload"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/tangle"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/utxodag"
"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
messageTangle "github.com/iotaledger/goshimmer/packages/binary/messagelayer/tangle"
"github.com/iotaledger/goshimmer/packages/database"
......@@ -37,11 +35,8 @@ var (
// Tangle represents the value tangle that is used to express votes on value transactions.
Tangle *tangle.Tangle
// UTXODAG represents the flow of funds that is derived from the value tangle.
UTXODAG *utxodag.UTXODAG
// LedgerState represents the ledger state, that keeps track of the liked branches and offers an API to access funds.
LedgerState *ledgerstate.LedgerState
LedgerState *tangle.LedgerState
// log holds a reference to the logger used by this app.
log *logger.Logger
......@@ -54,14 +49,13 @@ func configure(_ *node.Plugin) {
// create instances
Tangle = tangle.New(database.GetBadgerInstance())
UTXODAG = utxodag.New(database.GetBadgerInstance(), Tangle)
// subscribe to message-layer
messagelayer.Tangle.Events.MessageSolid.Attach(events.NewClosure(onReceiveMessageFromMessageLayer))
// setup behavior of package instances
UTXODAG.Events.TransactionBooked.Attach(events.NewClosure(onTransactionBooked))
UTXODAG.Events.Fork.Attach(events.NewClosure(onForkOfFirstConsumer))
Tangle.Events.TransactionBooked.Attach(events.NewClosure(onTransactionBooked))
Tangle.Events.Fork.Attach(events.NewClosure(onForkOfFirstConsumer))
configureFPC()
// TODO: DECIDE WHAT WE SHOULD DO IF FPC FAILS -> cry
......@@ -76,12 +70,12 @@ func configure(_ *node.Plugin) {
switch opinion {
case vote.Like:
if _, err := UTXODAG.BranchManager().SetBranchPreferred(branchID, true); err != nil {
if _, err := Tangle.BranchManager().SetBranchPreferred(branchID, true); err != nil {
panic(err)
}
// TODO: merge branch mutations into the parent branch
case vote.Dislike:
if _, err := UTXODAG.BranchManager().SetBranchPreferred(branchID, false); err != nil {
if _, err := Tangle.BranchManager().SetBranchPreferred(branchID, false); err != nil {
panic(err)
}
// TODO: merge branch mutations into the parent branch / cleanup
......@@ -93,7 +87,6 @@ func run(*node.Plugin) {
_ = daemon.BackgroundWorker("Tangle", func(shutdownSignal <-chan struct{}) {
<-shutdownSignal
Tangle.Shutdown()
UTXODAG.Shutdown()
}, shutdown.PriorityTangle)
runFPC()
......@@ -127,7 +120,7 @@ func onReceiveMessageFromMessageLayer(cachedMessage *message.CachedMessage, cach
Tangle.AttachPayload(valuePayload)
}
func onTransactionBooked(cachedTransaction *transaction.CachedTransaction, cachedTransactionMetadata *utxodag.CachedTransactionMetadata, cachedBranch *branchmanager.CachedBranch, conflictingInputs []transaction.OutputID, decisionPending bool) {
func onTransactionBooked(cachedTransaction *transaction.CachedTransaction, cachedTransactionMetadata *tangle.CachedTransactionMetadata, cachedBranch *branchmanager.CachedBranch, conflictingInputs []transaction.OutputID, decisionPending bool) {
defer cachedTransaction.Release()
defer cachedTransactionMetadata.Release()
defer cachedBranch.Release()
......@@ -174,7 +167,7 @@ func onTransactionBooked(cachedTransaction *transaction.CachedTransaction, cache
}
// TODO: clarify what we do here
func onForkOfFirstConsumer(cachedTransaction *transaction.CachedTransaction, cachedTransactionMetadata *utxodag.CachedTransactionMetadata, cachedBranch *branchmanager.CachedBranch, conflictingInputs []transaction.OutputID) {
func onForkOfFirstConsumer(cachedTransaction *transaction.CachedTransaction, cachedTransactionMetadata *tangle.CachedTransactionMetadata, cachedBranch *branchmanager.CachedBranch, conflictingInputs []transaction.OutputID) {
defer cachedTransaction.Release()
defer cachedTransactionMetadata.Release()
defer cachedBranch.Release()
......@@ -197,7 +190,7 @@ func onForkOfFirstConsumer(cachedTransaction *transaction.CachedTransaction, cac
return
}
if _, err := UTXODAG.BranchManager().SetBranchPreferred(branch.ID(), true); err != nil {
if _, err := Tangle.BranchManager().SetBranchPreferred(branch.ID(), true); err != nil {
log.Error(err)
}
......
......@@ -108,7 +108,7 @@ func runFPC() {
return vote.Unknown
}
cachedBranch := UTXODAG.BranchManager().Branch(branchID)
cachedBranch := Tangle.BranchManager().Branch(branchID)
defer cachedBranch.Release()
branch := cachedBranch.Unwrap()
......
......@@ -184,9 +184,9 @@ func (branchManager *BranchManager) ElevateConflictBranch(branchToElevate Branch
// BranchesConflicting returns true if the given Branches are part of the same Conflicts and can therefore not be
// merged.
func (branchManager *BranchManager) BranchesConflicting(branchIds ...BranchID) (branchesConflicting bool, err error) {
// iterate through parameters and collect conflicting branches
conflictingBranches := make(map[BranchID]types.Empty)
processedBranches := make(map[BranchID]types.Empty)
// iterate through branches and collect conflicting branches
traversedBranches := make(map[BranchID]types.Empty)
blacklistedBranches := make(map[BranchID]types.Empty)
for _, branchID := range branchIds {
// add the current branch to the stack of branches to check
ancestorStack := list.New()
......@@ -196,40 +196,44 @@ func (branchManager *BranchManager) BranchesConflicting(branchIds ...BranchID) (
for ancestorStack.Len() >= 1 {
// retrieve branch from stack
firstElement := ancestorStack.Front()
ancestorBranchID := firstElement.Value.(BranchID)
currentBranchID := firstElement.Value.(BranchID)
ancestorStack.Remove(firstElement)
// abort if we have seen this branch already
if _, processedAlready := processedBranches[ancestorBranchID]; processedAlready {
if _, traversedAlready := traversedBranches[currentBranchID]; traversedAlready {
continue
}
processedBranches[ancestorBranchID] = types.Void
// abort if this branch was blacklisted by another branch already
if _, branchesConflicting = blacklistedBranches[currentBranchID]; branchesConflicting {
return
}
// unpack the branch and abort if we failed to load it
cachedBranch := branchManager.Branch(branchID)
branch := cachedBranch.Unwrap()
if branch == nil {
err = fmt.Errorf("failed to load branch '%s'", ancestorBranchID)
currentCachedBranch := branchManager.Branch(currentBranchID)
currentBranch := currentCachedBranch.Unwrap()
if currentBranch == nil {
err = fmt.Errorf("failed to load branch '%s'", currentBranchID)
cachedBranch.Release()
currentCachedBranch.Release()
return
}
// add the parents of the current branch to the list of branches to check
for _, parentBranchID := range branch.ParentBranches() {
for _, parentBranchID := range currentBranch.ParentBranches() {
ancestorStack.PushBack(parentBranchID)
}
// abort the following checks if the branch is aggregated (aggregated branches have no own conflicts)
if branch.IsAggregated() {
cachedBranch.Release()
if currentBranch.IsAggregated() {
currentCachedBranch.Release()
continue
}
// iterate through the conflicts and take note of its member branches
for conflictID := range branch.Conflicts() {
for conflictID := range currentBranch.Conflicts() {
for _, cachedConflictMember := range branchManager.ConflictMembers(conflictID) {
// unwrap the current ConflictMember
conflictMember := cachedConflictMember.Unwrap()
......@@ -239,22 +243,28 @@ func (branchManager *BranchManager) BranchesConflicting(branchIds ...BranchID) (
continue
}
if conflictMember.BranchID() == currentBranchID {
continue
}
// abort if this branch was found as a conflict of another branch already
if _, branchesConflicting = conflictingBranches[conflictMember.BranchID()]; branchesConflicting {
if _, branchesConflicting = traversedBranches[conflictMember.BranchID()]; branchesConflicting {
cachedConflictMember.Release()
cachedBranch.Release()
currentCachedBranch.Release()
return
}
// store the current conflict in the list of seen conflicting branches
conflictingBranches[conflictMember.BranchID()] = types.Void
blacklistedBranches[conflictMember.BranchID()] = types.Void
cachedConflictMember.Release()
}
}
cachedBranch.Release()
currentCachedBranch.Release()
traversedBranches[currentBranchID] = types.Void
}
}
......
package branchmanager
import (
"fmt"
"testing"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
"github.com/iotaledger/goshimmer/packages/binary/testutil"
)
func TestSomething(t *testing.T) {
branchManager := New(testutil.DB(t))
cachedBranch1, _ := branchManager.Fork(BranchID{2}, []BranchID{MasterBranchID}, []ConflictID{transaction.OutputID{4}})
defer cachedBranch1.Release()
_ = cachedBranch1.Unwrap()
cachedBranch2, _ := branchManager.Fork(BranchID{3}, []BranchID{MasterBranchID}, []ConflictID{transaction.OutputID{4}})
defer cachedBranch2.Release()
branch2 := cachedBranch2.Unwrap()
fmt.Println(branchManager.BranchesConflicting(MasterBranchID, branch2.ID()))
}
package ledgerstate
import (
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/utxodag"
)
// LedgerState represents a struct, that allows us to read the balances from the UTXODAG by filtering the existing
// unspent Outputs depending on the liked branches.
type LedgerState struct {
utxoDAG *utxodag.UTXODAG
}
// New is the constructor of the LedgerState. It creates a new instance with the given UTXODAG.
func New(utxoDAG *utxodag.UTXODAG) *LedgerState {
return &LedgerState{
utxoDAG: utxoDAG,
}
}
package utxodag
package tangle
import (
"github.com/iotaledger/hive.go/marshalutil"
......
package utxodag
package tangle
import (
"github.com/iotaledger/hive.go/marshalutil"
......
......@@ -3,7 +3,9 @@ package tangle
import (
"github.com/iotaledger/hive.go/events"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/branchmanager"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/payload"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
)
// Events is a container for the different kind of events of the Tangle.
......@@ -14,6 +16,18 @@ type Events struct {
MissingPayloadReceived *events.Event
PayloadMissing *events.Event
PayloadUnsolidifiable *events.Event
// TransactionReceived gets triggered whenever a transaction was received for the first time (not solid yet).
TransactionReceived *events.Event
// TransactionBooked gets triggered whenever a transactions becomes solid and gets booked into a particular branch.
TransactionBooked *events.Event
// Fork gets triggered when a previously un-conflicting transaction get's some of its inputs double spend, so that a
// new Branch is created.
Fork *events.Event
Error *events.Event
}
func newEvents() *Events {
......@@ -23,6 +37,10 @@ func newEvents() *Events {
MissingPayloadReceived: events.NewEvent(cachedPayloadEvent),
PayloadMissing: events.NewEvent(payloadIDEvent),
PayloadUnsolidifiable: events.NewEvent(payloadIDEvent),
TransactionReceived: events.NewEvent(cachedTransactionEvent),
TransactionBooked: events.NewEvent(transactionBookedEvent),
Fork: events.NewEvent(forkEvent),
Error: events.NewEvent(events.ErrorCaller),
}
}
......@@ -36,3 +54,30 @@ func cachedPayloadEvent(handler interface{}, params ...interface{}) {
params[1].(*CachedPayloadMetadata).Retain(),
)
}
func transactionBookedEvent(handler interface{}, params ...interface{}) {
handler.(func(*transaction.CachedTransaction, *CachedTransactionMetadata, *branchmanager.CachedBranch, []transaction.OutputID, bool))(
params[0].(*transaction.CachedTransaction).Retain(),
params[1].(*CachedTransactionMetadata).Retain(),
params[2].(*branchmanager.CachedBranch).Retain(),
params[3].([]transaction.OutputID),
params[4].(bool),
)
}
func forkEvent(handler interface{}, params ...interface{}) {
handler.(func(*transaction.CachedTransaction, *CachedTransactionMetadata, *branchmanager.CachedBranch, []transaction.OutputID))(
params[0].(*transaction.CachedTransaction).Retain(),
params[1].(*CachedTransactionMetadata).Retain(),
params[2].(*branchmanager.CachedBranch).Retain(),
params[3].([]transaction.OutputID),
)
}
func cachedTransactionEvent(handler interface{}, params ...interface{}) {
handler.(func(*transaction.CachedTransaction, *CachedTransactionMetadata, *CachedAttachment))(
params[0].(*transaction.CachedTransaction).Retain(),
params[1].(*CachedTransactionMetadata).Retain(),
params[2].(*CachedAttachment).Retain(),
)
}
package tangle
import (
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/balance"
)
// LedgerState represents a struct, that allows us to read the balances from the UTXODAG by filtering the existing
// unspent Outputs depending on the liked branches.
type LedgerState struct {
tangle *Tangle
}
// NewLedgerState is the constructor of the LedgerState. It creates a new instance with the given UTXODAG.
func NewLedgerState(tangle *Tangle) *LedgerState {
return &LedgerState{
tangle: tangle,
}
}
// Balances returns a map containing the balances of the different colors that are unspent on a certain address.
func (ledgerState *LedgerState) Balances(address address.Address) (coloredBalances map[balance.Color]int64) {
coloredBalances = make(map[balance.Color]int64)
ledgerState.tangle.OutputsOnAddress(address).Consume(func(output *Output) {
if output.ConsumerCount() == 0 {
for _, coloredBalance := range output.Balances() {
coloredBalances[coloredBalance.Color()] += coloredBalance.Value()
}
}
})
return
}
package utxodag
package tangle
import (
"time"
......
package tangle
import (
"time"
"github.com/iotaledger/hive.go/objectstorage"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/payload"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
)
const (
......@@ -15,6 +18,18 @@ const (
osPayloadMetadata
osMissingPayload
osApprover
osTransaction
osTransactionMetadata
osAttachment
osOutput
osConsumer
)
var (
osLeakDetectionOption = objectstorage.LeakDetectionEnabled(true, objectstorage.LeakDetectionOptions{
MaxConsumersPerObject: 10,
MaxConsumerHoldTime: 10 * time.Second,
})
)
func osPayloadFactory(key []byte) (objectstorage.StorableObject, int, error) {
......@@ -32,3 +47,23 @@ func osMissingPayloadFactory(key []byte) (objectstorage.StorableObject, int, err
func osPayloadApproverFactory(key []byte) (objectstorage.StorableObject, int, error) {
return PayloadApproverFromStorageKey(key)
}
func osTransactionFactory(key []byte) (objectstorage.StorableObject, int, error) {
return transaction.FromStorageKey(key)
}
func osTransactionMetadataFactory(key []byte) (objectstorage.StorableObject, int, error) {
return TransactionMetadataFromStorageKey(key)
}
func osAttachmentFactory(key []byte) (objectstorage.StorableObject, int, error) {
return AttachmentFromStorageKey(key)
}
func osOutputFactory(key []byte) (objectstorage.StorableObject, int, error) {
return OutputFromStorageKey(key)
}
func osConsumerFactory(key []byte) (objectstorage.StorableObject, int, error) {
return ConsumerFromStorageKey(key)
}
package utxodag
package tangle
import (
"sync"
......@@ -212,12 +212,21 @@ func (output *Output) RegisterConsumer(consumer transaction.ID) (consumerCount i
output.firstConsumer = consumer
}
output.consumerCount++
output.SetModified()
firstConsumerID = output.firstConsumer
return
}
// ConsumerCount returns the number of transactions that have spent this Output.
func (output *Output) ConsumerCount() int {
output.consumerMutex.RLock()
defer output.consumerMutex.RUnlock()
return output.consumerCount
}
// Balances returns the colored balances (color + balance) that this output contains.
func (output *Output) Balances() []*balance.Balance {
return output.balances
......@@ -247,10 +256,12 @@ func (output *Output) ObjectStorageValue() []byte {
balanceCount := len(output.balances)
// initialize helper
marshalUtil := marshalutil.New(branchmanager.BranchIDLength + marshalutil.BOOL_SIZE + marshalutil.TIME_SIZE + marshalutil.UINT32_SIZE + balanceCount*balance.Length)
marshalUtil := marshalutil.New(branchmanager.BranchIDLength + marshalutil.BOOL_SIZE + marshalutil.TIME_SIZE + transaction.IDLength + marshalutil.UINT32_SIZE + marshalutil.UINT32_SIZE + balanceCount*balance.Length)
marshalUtil.WriteBytes(output.branchID.Bytes())
marshalUtil.WriteBool(output.solid)
marshalUtil.WriteTime(output.solidificationTime)
marshalUtil.WriteBytes(output.firstConsumer.Bytes())
marshalUtil.WriteUint32(uint32(output.consumerCount))
marshalUtil.WriteUint32(uint32(balanceCount))
for _, balanceToMarshal := range output.balances {
marshalUtil.WriteBytes(balanceToMarshal.Bytes())
......@@ -272,8 +283,16 @@ func (output *Output) UnmarshalObjectStorageValue(data []byte) (consumedBytes in
if output.solidificationTime, err = marshalUtil.ReadTime(); err != nil {
return
}
var balanceCount uint32
if balanceCount, err = marshalUtil.ReadUint32(); err != nil {
if output.firstConsumer, err = transaction.ParseID(marshalUtil); err != nil {
return
}
consumerCount, err := marshalUtil.ReadUint32()
if err != nil {
return
}
output.consumerCount = int(consumerCount)
balanceCount, err := marshalUtil.ReadUint32()
if err != nil {
return
}
output.balances = make([]*balance.Balance, balanceCount)
......
......@@ -8,6 +8,7 @@ import (
"github.com/iotaledger/hive.go/objectstorage"
"github.com/iotaledger/hive.go/stringify"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/branchmanager"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/payload"
)
......@@ -19,9 +20,11 @@ type PayloadMetadata struct {
payloadID payload.ID
solid bool
solidificationTime time.Time
branchID branchmanager.BranchID
solidMutex sync.RWMutex
solidificationTimeMutex sync.RWMutex
branchIDMutex sync.RWMutex
}
// NewPayloadMetadata creates an empty container for the metadata of a value transfer payload.
......@@ -136,9 +139,41 @@ func (payloadMetadata *PayloadMetadata) SoldificationTime() time.Time {
return payloadMetadata.solidificationTime
}
// BranchID returns the identifier of the Branch that this Payload was booked into.
func (payloadMetadata *PayloadMetadata) BranchID() branchmanager.BranchID {
payloadMetadata.branchIDMutex.RLock()
defer payloadMetadata.branchIDMutex.RUnlock()
return payloadMetadata.branchID
}
// SetBranchID is the setter for the BranchID that the corresponding Payload is booked into.
func (payloadMetadata *PayloadMetadata) SetBranchID(branchID branchmanager.BranchID) (modified bool) {
payloadMetadata.branchIDMutex.RLock()
if branchID == payloadMetadata.branchID {
payloadMetadata.branchIDMutex.RUnlock()
return
}
payloadMetadata.branchIDMutex.RUnlock()
payloadMetadata.branchIDMutex.Lock()
defer payloadMetadata.branchIDMutex.Unlock()
if branchID == payloadMetadata.branchID {
return
}
payloadMetadata.branchID = branchID
payloadMetadata.SetModified()
modified = true
return
}
// Bytes marshals the metadata into a sequence of bytes.
func (payloadMetadata *PayloadMetadata) Bytes() []byte {
return marshalutil.New(payload.IDLength + marshalutil.TIME_SIZE + marshalutil.BOOL_SIZE).
return marshalutil.New(payload.IDLength + marshalutil.TIME_SIZE + marshalutil.BOOL_SIZE + branchmanager.BranchIDLength).
WriteBytes(payloadMetadata.ObjectStorageKey()).
WriteBytes(payloadMetadata.ObjectStorageValue()).
Bytes()
......@@ -170,6 +205,7 @@ func (payloadMetadata *PayloadMetadata) ObjectStorageValue() []byte {
return marshalutil.New(marshalutil.TIME_SIZE + marshalutil.BOOL_SIZE).
WriteTime(payloadMetadata.solidificationTime).
WriteBool(payloadMetadata.solid).
WriteBytes(payloadMetadata.branchID.Bytes()).
Bytes()
}
......@@ -182,6 +218,9 @@ func (payloadMetadata *PayloadMetadata) UnmarshalObjectStorageValue(data []byte)
if payloadMetadata.solid, err = marshalUtil.ReadBool(); err != nil {
return
}
if payloadMetadata.branchID, err = branchmanager.ParseBranchID(marshalUtil); err != nil {
return
}
consumedBytes = marshalUtil.ReadOffset()
return
......
This diff is collapsed.
package utxodag
package tangle
import (
"io/ioutil"
"os"
"testing"
"time"
"github.com/iotaledger/hive.go/crypto/ed25519"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/balance"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/branchmanager"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/payload"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/tangle"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
"github.com/iotaledger/goshimmer/packages/database"
"github.com/iotaledger/goshimmer/plugins/config"
)
func TestNewOutput(t *testing.T) {
......@@ -71,72 +64,3 @@ func TestAttachment(t *testing.T) {
assert.Equal(t, transactionID, clonedAttachment.TransactionID())
assert.Equal(t, payloadID, clonedAttachment.PayloadID())
}
func TestTangle_AttachPayload(t *testing.T) {
dir, err := ioutil.TempDir("", t.Name())
require.NoError(t, err)
defer os.Remove(dir)
config.Node.Set(database.CFG_DIRECTORY, dir)
valueTangle := tangle.New(database.GetBadgerInstance())
if err := valueTangle.Prune(); err != nil {
t.Error(err)
return
}
utxoDAG := New(database.GetBadgerInstance(), valueTangle)
addressKeyPair1 := ed25519.GenerateKeyPair()
addressKeyPair2 := ed25519.GenerateKeyPair()
transferID1, _ := transaction.IDFromBase58("8opHzTAnfzRpPEx21XtnrVTX28YQuCpAjcn1PczScKh")
transferID2, _ := transaction.IDFromBase58("4uQeVj5tqViQh7yWWGStvkEG1Zmhx6uasJtWCJziofM")
input1 := NewOutput(address.FromED25519PubKey(addressKeyPair1.PublicKey), transferID1, branchmanager.MasterBranchID, []*balance.Balance{
balance.New(balance.ColorIOTA, 337),
})
input1.SetSolid(true)
input2 := NewOutput(address.FromED25519PubKey(addressKeyPair2.PublicKey), transferID2, branchmanager.MasterBranchID, []*balance.Balance{
balance.New(balance.ColorIOTA, 1000),
})
input2.SetSolid(true)
utxoDAG.outputStorage.Store(input1).Release()
utxoDAG.outputStorage.Store(input2).Release()
outputAddress1 := address.Random()
outputAddress2 := address.Random()
// attach first spend
valueTangle.AttachPayload(payload.New(payload.GenesisID, payload.GenesisID, transaction.New(
transaction.NewInputs(
input1.ID(),
input2.ID(),
),
transaction.NewOutputs(map[address.Address][]*balance.Balance{
outputAddress1: {
balance.New(balance.ColorNew, 1337),
},
}),
)))
// attach double spend
valueTangle.AttachPayload(payload.New(payload.GenesisID, payload.GenesisID, transaction.New(
transaction.NewInputs(
input1.ID(),
input2.ID(),
),
transaction.NewOutputs(map[address.Address][]*balance.Balance{
outputAddress2: {
balance.New(balance.ColorNew, 1337),
},
}),
)))
valueTangle.Shutdown()
utxoDAG.Shutdown()
}
package utxodag
package tangle
import (
"sync"
......
package test
import (
"testing"
"time"
"github.com/iotaledger/hive.go/crypto/ed25519"
"github.com/stretchr/testify/assert"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/balance"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/payload"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/tangle"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
"github.com/iotaledger/goshimmer/packages/binary/testutil"
)
func TestTangle_ValueTransfer(t *testing.T) {
// initialize tangle + ledgerstate
valueTangle := tangle.New(testutil.DB(t))
if err := valueTangle.Prune(); err != nil {
t.Error(err)
return
}
ledgerState := tangle.NewLedgerState(valueTangle)
//
addressKeyPair1 := ed25519.GenerateKeyPair()
addressKeyPair2 := ed25519.GenerateKeyPair()
address1 := address.FromED25519PubKey(addressKeyPair1.PublicKey)
address2 := address.FromED25519PubKey(addressKeyPair2.PublicKey)
// check if ledger empty first
assert.Equal(t, map[balance.Color]int64{}, ledgerState.Balances(address1))
assert.Equal(t, map[balance.Color]int64{}, ledgerState.Balances(address2))
// load snapshot
valueTangle.LoadSnapshot(map[transaction.ID]map[address.Address][]*balance.Balance{
transaction.GenesisID: {
address1: []*balance.Balance{
balance.New(balance.ColorIOTA, 337),
},
address2: []*balance.Balance{
balance.New(balance.ColorIOTA, 1000),
},
},
})
// check if balance exists after loading snapshot
assert.Equal(t, map[balance.Color]int64{balance.ColorIOTA: 337}, ledgerState.Balances(address1))
assert.Equal(t, map[balance.Color]int64{balance.ColorIOTA: 1000}, ledgerState.Balances(address2))
// attach first spend
outputAddress1 := address.Random()
valueTangle.AttachPayload(payload.New(payload.GenesisID, payload.GenesisID, transaction.New(
transaction.NewInputs(
transaction.NewOutputID(address1, transaction.GenesisID),
transaction.NewOutputID(address2, transaction.GenesisID),
),
transaction.NewOutputs(map[address.Address][]*balance.Balance{
outputAddress1: {
balance.New(balance.ColorIOTA, 1337),
},
}),
)))
// wait for async task to run (TODO: REPLACE TIME BASED APPROACH WITH A WG)
time.Sleep(500 * time.Millisecond)
// check if old addresses are empty
assert.Equal(t, map[balance.Color]int64{}, ledgerState.Balances(address1))
assert.Equal(t, map[balance.Color]int64{}, ledgerState.Balances(address2))
// check if new addresses are filled
assert.Equal(t, map[balance.Color]int64{balance.ColorIOTA: 1337}, ledgerState.Balances(outputAddress1))
// attach double spend
outputAddress2 := address.Random()
valueTangle.AttachPayload(payload.New(payload.GenesisID, payload.GenesisID, transaction.New(
transaction.NewInputs(
transaction.NewOutputID(address1, transaction.GenesisID),
transaction.NewOutputID(address2, transaction.GenesisID),
),
transaction.NewOutputs(map[address.Address][]*balance.Balance{
outputAddress2: {
balance.New(balance.ColorNew, 1337),
},
}),
)))
// shutdown tangle
valueTangle.Shutdown()
}
......@@ -82,5 +82,7 @@ func (id ID) String() string {
return base58.Encode(id[:])
}
var GenesisID ID
// IDLength contains the amount of bytes that a marshaled version of the ID contains.
const IDLength = 32
package utxodag
import (
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/branchmanager"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
"github.com/iotaledger/hive.go/events"
)
// Events is a container for the different kind of events of the UTXODAG.
type Events struct {
// TransactionReceived gets triggered whenever a transaction was received for the first time (not solid yet).
TransactionReceived *events.Event
// TransactionBooked gets triggered whenever a transactions becomes solid and gets booked into a particular branch.
TransactionBooked *events.Event
// Fork gets triggered when a previously un-conflicting transaction get's some of its inputs double spend, so that a
// new Branch is created.
Fork *events.Event
Error *events.Event
}
func newEvents() *Events {
return &Events{
TransactionReceived: events.NewEvent(cachedTransactionEvent),
TransactionBooked: events.NewEvent(transactionBookedEvent),
Fork: events.NewEvent(forkEvent),
Error: events.NewEvent(events.ErrorCaller),
}
}
func transactionBookedEvent(handler interface{}, params ...interface{}) {
handler.(func(*transaction.CachedTransaction, *CachedTransactionMetadata, *branchmanager.CachedBranch, []transaction.OutputID, bool))(
params[0].(*transaction.CachedTransaction).Retain(),
params[1].(*CachedTransactionMetadata).Retain(),
params[2].(*branchmanager.CachedBranch).Retain(),
params[3].([]transaction.OutputID),
params[4].(bool),
)
}
func forkEvent(handler interface{}, params ...interface{}) {
handler.(func(*transaction.CachedTransaction, *CachedTransactionMetadata, *branchmanager.CachedBranch, []transaction.OutputID))(
params[0].(*transaction.CachedTransaction).Retain(),
params[1].(*CachedTransactionMetadata).Retain(),
params[2].(*branchmanager.CachedBranch).Retain(),
params[3].([]transaction.OutputID),
)
}
func cachedTransactionEvent(handler interface{}, params ...interface{}) {
handler.(func(*transaction.CachedTransaction, *CachedTransactionMetadata, *CachedAttachment))(
params[0].(*transaction.CachedTransaction).Retain(),
params[1].(*CachedTransactionMetadata).Retain(),
params[2].(*CachedAttachment).Retain(),
)
}
package utxodag
import (
"time"
"github.com/iotaledger/hive.go/objectstorage"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
)
const (
// the following values are a list of prefixes defined as an enum
_ byte = iota
// prefixes used for the objectstorage
osTransaction
osTransactionMetadata
osAttachment
osOutput
osConsumer
)
var (
osLeakDetectionOption = objectstorage.LeakDetectionEnabled(true, objectstorage.LeakDetectionOptions{
MaxConsumersPerObject: 10,
MaxConsumerHoldTime: 10 * time.Second,
})
)
func osTransactionFactory(key []byte) (objectstorage.StorableObject, int, error) {
return transaction.FromStorageKey(key)
}
func osTransactionMetadataFactory(key []byte) (objectstorage.StorableObject, int, error) {
return TransactionMetadataFromStorageKey(key)
}
func osAttachmentFactory(key []byte) (objectstorage.StorableObject, int, error) {
return AttachmentFromStorageKey(key)
}
func osOutputFactory(key []byte) (objectstorage.StorableObject, int, error) {
return OutputFromStorageKey(key)
}
func osConsumerFactory(key []byte) (objectstorage.StorableObject, int, error) {
return ConsumerFromStorageKey(key)
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment