diff --git a/packages/unbreakable_consensus/otv/approvers.go b/packages/unbreakable_consensus/otv/approvers.go new file mode 100644 index 0000000000000000000000000000000000000000..cb89154c13b01fad24274576cd69ddd578406b7b --- /dev/null +++ b/packages/unbreakable_consensus/otv/approvers.go @@ -0,0 +1,57 @@ +package social_consensus + +import ( + "sync" +) + +type Approvers struct { + transactionID int + transactionLikers map[int]*Transaction + transactionDislikers map[int]*Transaction + realityLikers map[int]*Transaction + realityDislikers map[int]*Transaction + + mutex sync.RWMutex +} + +func NewApprovers(transactionID int) *Approvers { + return &Approvers{ + transactionID: transactionID, + transactionLikers: make(map[int]*Transaction), + transactionDislikers: make(map[int]*Transaction), + realityLikers: make(map[int]*Transaction), + realityDislikers: make(map[int]*Transaction), + } +} + +func (approvers *Approvers) GetTransactionID() int { + return approvers.transactionID +} + +func (approvers *Approvers) AddTransactionLiker(transaction *Transaction) { + approvers.mutex.Lock() + defer approvers.mutex.Unlock() + + approvers.transactionLikers[transaction.GetID()] = transaction +} + +func (approvers *Approvers) AddTransactionDisliker(transaction *Transaction) { + approvers.mutex.Lock() + defer approvers.mutex.Unlock() + + approvers.transactionDislikers[transaction.GetID()] = transaction +} + +func (approvers *Approvers) AddRealityLiker(transaction *Transaction) { + approvers.mutex.Lock() + defer approvers.mutex.Unlock() + + approvers.realityLikers[transaction.GetID()] = transaction +} + +func (approvers *Approvers) AddRealityDisliker(transaction *Transaction) { + approvers.mutex.Lock() + defer approvers.mutex.Unlock() + + approvers.realityDislikers[transaction.GetID()] = transaction +} diff --git a/packages/unbreakable_consensus/otv/approvers_database.go b/packages/unbreakable_consensus/otv/approvers_database.go new file mode 100644 index 0000000000000000000000000000000000000000..7fa0385916f96e3b1efe9002a5a5546b4f1b79b1 --- /dev/null +++ b/packages/unbreakable_consensus/otv/approvers_database.go @@ -0,0 +1,31 @@ +package social_consensus + +import ( + "sync" +) + +type ApproversDatabase struct { + approvers map[int]*Approvers + + mutex sync.RWMutex +} + +func NewApproversDatabase() *ApproversDatabase { + return &ApproversDatabase{ + approvers: make(map[int]*Approvers), + } +} + +func (approversDatabase *ApproversDatabase) StoreApprovers(approvers *Approvers) { + approversDatabase.mutex.Lock() + defer approversDatabase.mutex.Unlock() + + approversDatabase.approvers[approvers.GetTransactionID()] = approvers +} + +func (approversDatabase *ApproversDatabase) LoadApprovers(transactionID int) *Approvers { + approversDatabase.mutex.RLock() + defer approversDatabase.mutex.RUnlock() + + return approversDatabase.approvers[transactionID] +} diff --git a/packages/unbreakable_consensus/otv/conflict_set.go b/packages/unbreakable_consensus/otv/conflict_set.go new file mode 100644 index 0000000000000000000000000000000000000000..c6201f85cfb93de0dda3ea2379f80f478d6142f3 --- /dev/null +++ b/packages/unbreakable_consensus/otv/conflict_set.go @@ -0,0 +1,29 @@ +package social_consensus + +type ConflictSet struct { + realities map[int]*Reality +} + +func NewConflictSet() *ConflictSet { + return &ConflictSet{ + realities: make(map[int]*Reality), + } +} + +func (conflictSet *ConflictSet) GetRealities() map[int]*Reality { + return conflictSet.realities +} + +func (conflictSet *ConflictSet) GetReality(id int) *Reality { + return conflictSet.realities[id] +} + +func (conflictSet *ConflictSet) AddReality(id int) (result *Reality) { + if _, exists := conflictSet.realities[id]; !exists { + result = NewReality(conflictSet, id) + + conflictSet.realities[id] = result + } + + return +} diff --git a/packages/unbreakable_consensus/otv/elder_mask.go b/packages/unbreakable_consensus/otv/elder_mask.go new file mode 100644 index 0000000000000000000000000000000000000000..8a251de0fa661555f8e70f22bd3fe910679b6158 --- /dev/null +++ b/packages/unbreakable_consensus/otv/elder_mask.go @@ -0,0 +1,19 @@ +package social_consensus + +import ( + "fmt" +) + +type ElderMask uint64 + +func (elderMask ElderMask) Union(otherElderMask ElderMask) ElderMask { + return elderMask | otherElderMask +} + +func (elderMask ElderMask) Contains(otherMask ElderMask) bool { + return elderMask&otherMask != 0 +} + +func (elderMask ElderMask) String() string { + return "ElderMask(" + fmt.Sprintf("%064b", elderMask) + ")" +} diff --git a/packages/unbreakable_consensus/otv/node.go b/packages/unbreakable_consensus/otv/node.go new file mode 100644 index 0000000000000000000000000000000000000000..488c2b296503c4b7000da777e2ddaff81e5717fc --- /dev/null +++ b/packages/unbreakable_consensus/otv/node.go @@ -0,0 +1,164 @@ +package social_consensus + +import ( + "math/rand" + "time" + + "github.com/iotaledger/goshimmer/packages/events" +) + +type Node struct { + id int + weight int + transactionCounter int + tipSelector *TipSelector + solidifier *Solidifier + approversDatabase *ApproversDatabase + transactionDatabase *TransactionDatabase + gossipNeighbors []*Node + conflictSet *ConflictSet + Events NodeEvents + + favoredReality int +} + +var nodeIdCounter = 0 + +func NewNode(weight int) *Node { + node := &Node{ + id: nodeIdCounter, + weight: weight, + approversDatabase: NewApproversDatabase(), + transactionDatabase: NewTransactionDatabase(), + gossipNeighbors: make([]*Node, 0), + conflictSet: NewConflictSet(), + Events: NodeEvents{ + TransactionSolid: events.NewEvent(TransactionEventHandler), + }, + } + + node.Events.TransactionSolid.Attach(events.NewClosure(node.ClassifyTransaction)) + + node.tipSelector = NewTipSelector(node) + node.solidifier = NewSolidifier(node) + + nodeIdCounter++ + + return node +} + +func (node *Node) GetWeight() int { + return node.weight +} + +func (node *Node) GetID() int { + return node.id +} + +func (node *Node) Peer(nodes ...*Node) { + node.gossipNeighbors = nodes +} + +func (node *Node) GossipTransaction(transaction *Transaction) { + node.ReceiveTransaction(transaction) + + for _, neighbor := range node.gossipNeighbors { + go func(neighbor *Node, transaction *Transaction) { + time.Sleep(time.Duration(100+rand.Intn(200)) * time.Millisecond) + + neighbor.ReceiveTransaction(transaction.Clone()) + }(neighbor, transaction) + } +} + +func (node *Node) StoreTransaction(transaction *Transaction) { + if transaction != nil { + node.transactionDatabase.StoreTransaction(transaction) + } +} + +func (node *Node) GetTransaction(id int) *Transaction { + return node.transactionDatabase.LoadTransaction(id) +} + +func (node *Node) CreateTransaction() *Transaction { + trunk, branch, transactionLiked, realityLiked := node.tipSelector.GetTipsToApprove() + + newTransaction := NewTransaction() + newTransaction.SetCounter(node.transactionCounter) + newTransaction.SetNodeID(node.GetID()) + newTransaction.SetTrunkID(trunk.GetID()) + newTransaction.SetBranchID(branch.GetID()) + newTransaction.SetBranchTransactionLiked(transactionLiked) + newTransaction.SetBranchRealityLiked(realityLiked) + + node.transactionCounter++ + + return newTransaction +} + +func (node *Node) IssueTransaction() { + node.GossipTransaction(node.CreateTransaction()) +} + +func (node *Node) updateApprovers(transaction *Transaction) { + if trunkID := transaction.GetTrunkID(); trunkID != -1 { + approvers := node.approversDatabase.LoadApprovers(trunkID) + if approvers == nil { + approvers = NewApprovers(trunkID) + + node.approversDatabase.StoreApprovers(approvers) + } + + approvers.AddTransactionLiker(transaction) + approvers.AddRealityLiker(transaction) + } + + if branchID := transaction.GetBranchID(); branchID != -1 { + approvers := node.approversDatabase.LoadApprovers(branchID) + if approvers == nil { + approvers = NewApprovers(branchID) + + node.approversDatabase.StoreApprovers(approvers) + } + + if transaction.IsBranchTransactionLiked() { + approvers.AddTransactionLiker(transaction) + } else { + approvers.AddTransactionDisliker(transaction) + } + + if transaction.IsBranchRealityLiked() { + approvers.AddRealityLiker(transaction) + } else { + approvers.AddRealityDisliker(transaction) + } + } +} + +func (node *Node) ReceiveTransaction(transaction *Transaction) { + if node.transactionDatabase.LoadTransaction(transaction.GetID()) == nil { + if transaction.claimedReality != 0 { + node.conflictSet.AddReality(transaction.claimedReality) + + if node.favoredReality == 0 { + node.favoredReality = transaction.claimedReality + } + } + + node.StoreTransaction(transaction) + node.updateApprovers(transaction) + + node.solidifier.ProcessTransaction(transaction) + } +} + +func (node *Node) ClassifyTransaction(transaction *Transaction) { + transactionLocallyLiked := transaction.claimedReality == node.favoredReality || transaction.claimedReality == 0 + realityLocallyLiked := transactionLocallyLiked && (transaction.GetMetadata().GetReality() == 0 || transaction.GetMetadata().GetReality() == node.favoredReality) + + transaction.GetMetadata().SetTransactionLocallyLiked(transactionLocallyLiked) + transaction.GetMetadata().SetRealityLocallyLiked(realityLocallyLiked) + + node.tipSelector.ProcessClassifiedTransaction(transaction) +} diff --git a/packages/unbreakable_consensus/otv/node_events.go b/packages/unbreakable_consensus/otv/node_events.go new file mode 100644 index 0000000000000000000000000000000000000000..e1a67a09d1a80f0f7f31a40839db95a656cd4139 --- /dev/null +++ b/packages/unbreakable_consensus/otv/node_events.go @@ -0,0 +1,13 @@ +package social_consensus + +import ( + "github.com/iotaledger/goshimmer/packages/events" +) + +type NodeEvents struct { + TransactionSolid *events.Event +} + +func TransactionEventHandler(handler interface{}, params ...interface{}) { + handler.(func(*Transaction))(params[0].(*Transaction)) +} diff --git a/packages/unbreakable_consensus/otv/otv_test.go b/packages/unbreakable_consensus/otv/otv_test.go new file mode 100644 index 0000000000000000000000000000000000000000..68c625a80ccf2b2f5fc380b5634d63139079661c --- /dev/null +++ b/packages/unbreakable_consensus/otv/otv_test.go @@ -0,0 +1,138 @@ +package social_consensus + +import ( + "sync" + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "github.com/iotaledger/goshimmer/packages/events" +) + +func GetRealityFLC(node *Node, transaction *Transaction) (result []*Transaction) { + result = make([]*Transaction, 1) + result[0] = transaction + + traversalStack := make([]*Transaction, 0) + seenTransactions := make(map[int]bool) + + if approvers := node.approversDatabase.LoadApprovers(transaction.GetID()); approvers != nil { + for _, approver := range approvers.transactionLikers { + if _, exists := seenTransactions[approver.GetID()]; !exists { + traversalStack = append(traversalStack, approver) + seenTransactions[approver.GetID()] = true + } + } + + for _, approver := range approvers.realityLikers { + if _, exists := seenTransactions[approver.GetID()]; !exists { + traversalStack = append(traversalStack, approver) + seenTransactions[approver.GetID()] = true + } + } + } + + for len(traversalStack) != 0 { + currentTransaction := traversalStack[0] + + result = append(result, currentTransaction) + + if approvers := node.approversDatabase.LoadApprovers(currentTransaction.GetID()); approvers != nil { + for _, approver := range approvers.realityLikers { + if _, seen := seenTransactions[approver.GetID()]; !seen { + traversalStack = append(traversalStack, approver) + seenTransactions[approver.GetID()] = true + } + } + } + + traversalStack = traversalStack[1:] + } + + return result +} + +func BenchmarkTPS(b *testing.B) { + transactionIdCounter = 0 + + genesisTransaction := NewTransaction() + genesisTransaction.SetTrunkID(-1) + genesisTransaction.SetBranchID(-1) + genesisTransaction.GetMetadata().SetSolid(true) + + node := NewNode(100) + node.StoreTransaction(genesisTransaction) + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + node.IssueTransaction() + } +} + +func TestTipSelector_GetTipsToApprove(t *testing.T) { + // region initialize nodes ///////////////////////////////////////////////////////////////////////////////////////// + + genesisTransaction := NewTransaction() + genesisTransaction.SetTrunkID(-1) + genesisTransaction.SetBranchID(-1) + genesisTransaction.GetMetadata().SetSolid(true) + + node0 := NewNode(15) + node0.StoreTransaction(genesisTransaction) + + node1 := NewNode(17) + node1.StoreTransaction(genesisTransaction) + + node2 := NewNode(19) + node2.StoreTransaction(genesisTransaction) + + node0.Peer(node1, node2) + node1.Peer(node0, node2) + node2.Peer(node0, node1) + + // endregion /////////////////////////////////////////////////////////////////////////////////////////////////////// + + var wg sync.WaitGroup + + node0.Events.TransactionSolid.Attach(events.NewClosure(func(transaction *Transaction) { + wg.Done() + })) + + // region initialize consensus test conditions ///////////////////////////////////////////////////////////////////// + + // create first conflicting transaction + wg.Add(1) + conflictingTransaction0 := node0.CreateTransaction() + conflictingTransaction0.claimedReality = 1 + + // create second conflicting transaction + wg.Add(1) + conflictingTransaction1 := node1.CreateTransaction() + conflictingTransaction1.claimedReality = 2 + + // make nodes see the conflicts in different order + node0.GossipTransaction(conflictingTransaction0) + node1.GossipTransaction(conflictingTransaction1) + node2.GossipTransaction(conflictingTransaction0) + + // endregion /////////////////////////////////////////////////////////////////////////////////////////////////////// + + // issue a few transactions + for i := 0; i < 30; i++ { + wg.Add(3) + + node0.IssueTransaction() + node1.IssueTransaction() + node2.IssueTransaction() + + time.Sleep(50 * time.Millisecond) + } + + wg.Wait() + + // check if consensus reached + assert.Equal(t, node0.favoredReality, node1.favoredReality) + assert.Equal(t, node1.favoredReality, node2.favoredReality) +} diff --git a/packages/unbreakable_consensus/otv/reality.go b/packages/unbreakable_consensus/otv/reality.go new file mode 100644 index 0000000000000000000000000000000000000000..b069111f3e6ce4bdc8423cab2c32e31c3fb83a37 --- /dev/null +++ b/packages/unbreakable_consensus/otv/reality.go @@ -0,0 +1,71 @@ +package social_consensus + +import ( + "sync" +) + +type Reality struct { + id int + conflictSet *ConflictSet + supporters map[int]int + weight int + mutex sync.RWMutex +} + +func NewReality(conflictSet *ConflictSet, id int) *Reality { + return &Reality{ + id: id, + conflictSet: conflictSet, + supporters: make(map[int]int), + } +} + +func (reality *Reality) GetId() int { + return reality.id +} + +func (reality *Reality) GetConflictSet() *ConflictSet { + return reality.conflictSet +} + +func (reality *Reality) ContainsSupporter(nodeId int) (transactionCounter int) { + if currentTransactionCounter, supporterExists := reality.supporters[nodeId]; supporterExists { + transactionCounter = currentTransactionCounter + } + + return +} + +func (reality *Reality) AddSupporter(nodeId int, transactionCounter int) { + reality.mutex.Lock() + defer reality.mutex.Unlock() + + if oldTransactionCounter, supporterExists := reality.supporters[nodeId]; !supporterExists { + // remove supporter from the alternative reality + for otherRealityId, otherReality := range reality.conflictSet.GetRealities() { + if otherTransactionCounter := otherReality.ContainsSupporter(nodeId); otherRealityId != reality.id && otherTransactionCounter != 0 { + otherReality.RemoveSupporter(nodeId, transactionCounter) + } + } + + // add supporter to new reality + reality.supporters[nodeId] = transactionCounter + + // update weight + reality.weight += 20 + } else if oldTransactionCounter < transactionCounter { + // update transaction counter + reality.supporters[nodeId] = transactionCounter + } +} + +func (reality *Reality) RemoveSupporter(nodeId int, transactionCounter int) { + reality.mutex.Lock() + defer reality.mutex.Unlock() + + if oldTransactionCounter, supporterExists := reality.supporters[nodeId]; supporterExists && oldTransactionCounter < transactionCounter { + delete(reality.supporters, nodeId) + + reality.weight -= 20 + } +} diff --git a/packages/unbreakable_consensus/otv/solidifier.go b/packages/unbreakable_consensus/otv/solidifier.go new file mode 100644 index 0000000000000000000000000000000000000000..93620cb351e65b5b76147ba09d0f563bdfd3c77d --- /dev/null +++ b/packages/unbreakable_consensus/otv/solidifier.go @@ -0,0 +1,92 @@ +package social_consensus + +type Solidifier struct { + node *Node +} + +func NewSolidifier(node *Node) *Solidifier { + return &Solidifier{ + node: node, + } +} + +func (solidifier *Solidifier) IsTransactionSolid(transaction *Transaction) (isSolid bool) { + isSolid = true + + if trunkID := transaction.GetTrunkID(); trunkID != -1 { + if trunk := solidifier.node.GetTransaction(trunkID); trunk == nil { + isSolid = false + } else if !trunk.GetMetadata().IsSolid() { + isSolid = false + } + } + + if branchID := transaction.GetBranchID(); branchID != -1 { + if trunk := solidifier.node.GetTransaction(branchID); trunk == nil { + isSolid = false + } else if !trunk.GetMetadata().IsSolid() { + isSolid = false + } + } + + return +} + +func (solidifier *Solidifier) PropagateSolidity(transaction *Transaction) { + if !transaction.GetMetadata().IsSolid() { + transaction.GetMetadata().SetSolid(true) + + if transaction.claimedReality != 0 { + transaction.GetMetadata().SetReality(transaction.claimedReality) + } else { + trunk := solidifier.node.GetTransaction(transaction.GetTrunkID()) + trunkReality := trunk.GetMetadata().GetReality() + branch := solidifier.node.GetTransaction(transaction.GetBranchID()) + branchReality := branch.GetMetadata().GetReality() + + if transaction.IsBranchRealityLiked() && branchReality != 0 { + transaction.GetMetadata().SetReality(branchReality) + + solidifier.node.conflictSet.GetReality(branchReality).AddSupporter(transaction.GetNodeID(), transaction.counter) + + var heaviestReality *Reality + heaviestRealityWeight := 0 + for _, reality := range solidifier.node.conflictSet.GetRealities() { + if reality.weight > heaviestRealityWeight { + heaviestReality = reality + heaviestRealityWeight = reality.weight + } + } + + if solidifier.node.favoredReality != heaviestReality.id { + solidifier.node.favoredReality = heaviestReality.id + } + } else { + transaction.GetMetadata().SetReality(trunkReality) + } + } + + solidifier.node.Events.TransactionSolid.Trigger(transaction) + + if approvers := solidifier.node.approversDatabase.LoadApprovers(transaction.GetID()); approvers != nil { + for _, approver := range approvers.transactionLikers { + solidifier.PropagateSolidity(approver) + } + for _, approver := range approvers.transactionDislikers { + solidifier.PropagateSolidity(approver) + } + for _, approver := range approvers.realityLikers { + solidifier.PropagateSolidity(approver) + } + for _, approver := range approvers.realityDislikers { + solidifier.PropagateSolidity(approver) + } + } + } +} + +func (solidifier *Solidifier) ProcessTransaction(transaction *Transaction) { + if solidifier.IsTransactionSolid(transaction) { + solidifier.PropagateSolidity(transaction) + } +} diff --git a/packages/unbreakable_consensus/otv/tip_selector.go b/packages/unbreakable_consensus/otv/tip_selector.go new file mode 100644 index 0000000000000000000000000000000000000000..b2cd8efd039f894ed69df1fbfa639c7270e47396 --- /dev/null +++ b/packages/unbreakable_consensus/otv/tip_selector.go @@ -0,0 +1,118 @@ +package social_consensus + +import ( + "sync" + + "github.com/iotaledger/goshimmer/packages/datastructure" +) + +type TipSelector struct { + node *Node + likedTips *datastructure.RandomMap // like transaction / like reality + semiLikedTips *datastructure.RandomMap // like transaction / dislike reality + dislikedTips *datastructure.RandomMap // dislike transaction / dislike reality + // semiDislikedTips *datastructure.RandomMap // dislike transaction / like reality (doesn't occur: the only reason + // to dislike a transaction is if it is conflicting. This however means it spawns a reality we can not both like and + // dislike it at the same time. + + mutex sync.RWMutex +} + +func NewTipSelector(node *Node) *TipSelector { + return &TipSelector{ + node: node, + likedTips: datastructure.NewRandomMap(), + semiLikedTips: datastructure.NewRandomMap(), + dislikedTips: datastructure.NewRandomMap(), + } +} + +func (tipSelector *TipSelector) GetTipsToApprove() (trunkTransaction *Transaction, branchTransaction *Transaction, transactionLiked bool, realityLiked bool) { + tipSelector.mutex.RLock() + defer tipSelector.mutex.RUnlock() + + trunkTransaction = tipSelector.getRandomLikedTip() + + if branchTransaction = tipSelector.getRandomSemiLikedTip(); branchTransaction != nil { + transactionLiked = true + realityLiked = false + + return + } + + if branchTransaction = tipSelector.getRandomDislikedTip(); branchTransaction != nil { + transactionLiked = false + realityLiked = false + + return + } + + branchTransaction = tipSelector.getRandomLikedTip() + transactionLiked = true + realityLiked = true + + return +} + +func (tipSelector *TipSelector) ProcessClassifiedTransaction(transaction *Transaction) { + tipSelector.mutex.Lock() + defer tipSelector.mutex.Unlock() + + transactionMetadata := transaction.GetMetadata() + + if transactionLiked := transactionMetadata.IsTransactionLocallyLiked(); transactionLiked { + if realityLiked := transactionMetadata.IsRealityLocallyLiked(); realityLiked { + tipSelector.likedTips.Set(transaction.GetID(), transaction) + + if trunk := tipSelector.node.GetTransaction(transaction.GetTrunkID()); trunk != nil { + trunkId := trunk.GetID() + + tipSelector.likedTips.Delete(trunkId) + tipSelector.semiLikedTips.Delete(trunkId) + tipSelector.dislikedTips.Delete(trunkId) + } + + if branch := tipSelector.node.GetTransaction(transaction.GetBranchID()); branch != nil { + branchId := branch.GetID() + + tipSelector.likedTips.Delete(branchId) + tipSelector.semiLikedTips.Delete(branchId) + tipSelector.dislikedTips.Delete(branchId) + } + } else { + tipSelector.semiLikedTips.Set(transaction.GetID(), transaction) + } + } else { + if realityLiked := transactionMetadata.IsRealityLocallyLiked(); realityLiked { + panic("we should never dislike a transaction and at the same time like its reality") + } else { + tipSelector.dislikedTips.Set(transaction.GetID(), transaction) + } + } + + //fmt.Println("TIP COUNT:", tipSelector.dislikedTips.Size()+tipSelector.semiLikedTips.Size()+tipSelector.likedTips.Size()) +} + +func (tipSelector *TipSelector) getRandomLikedTip() *Transaction { + if result := tipSelector.likedTips.RandomEntry(); result == nil { + return tipSelector.node.GetTransaction(0) + } else { + return result.(*Transaction) + } +} + +func (tipSelector *TipSelector) getRandomSemiLikedTip() *Transaction { + if randomSemiLikedTip := tipSelector.semiLikedTips.RandomEntry(); randomSemiLikedTip != nil { + return randomSemiLikedTip.(*Transaction) + } else { + return nil + } +} + +func (tipSelector *TipSelector) getRandomDislikedTip() *Transaction { + if randomDislikedTip := tipSelector.dislikedTips.RandomEntry(); randomDislikedTip != nil { + return randomDislikedTip.(*Transaction) + } else { + return nil + } +} diff --git a/packages/unbreakable_consensus/otv/transaction.go b/packages/unbreakable_consensus/otv/transaction.go new file mode 100644 index 0000000000000000000000000000000000000000..67d360e3b61e0198da893d542094b5018b18e042 --- /dev/null +++ b/packages/unbreakable_consensus/otv/transaction.go @@ -0,0 +1,119 @@ +package social_consensus + +import ( + "github.com/iotaledger/goshimmer/packages/stringify" +) + +type Transaction struct { + id int + nodeId int + trunkId int + branchId int + counter int + branchRealityLiked bool + branchTransactionLiked bool + + metadata *TransactionMetadata + claimedReality int +} + +var transactionIdCounter = 0 + +func NewTransaction() *Transaction { + transaction := &Transaction{ + id: transactionIdCounter, + } + + transactionIdCounter++ + + return transaction +} + +func (transaction *Transaction) SetNodeID(nodeID int) { + transaction.nodeId = nodeID +} + +func (transaction *Transaction) GetNodeID() int { + return transaction.nodeId +} + +func (transaction *Transaction) GetID() int { + return transaction.id +} + +func (transaction *Transaction) SetTrunkID(id int) { + transaction.trunkId = id +} + +func (transaction *Transaction) GetTrunkID() int { + return transaction.trunkId +} + +func (transaction *Transaction) SetBranchID(id int) { + transaction.branchId = id +} + +func (transaction *Transaction) GetBranchID() int { + return transaction.branchId +} + +func (transaction *Transaction) SetCounter(counter int) { + transaction.counter = counter +} + +func (transaction *Transaction) GetCounter() int { + return transaction.counter +} + +func (transaction *Transaction) SetBranchRealityLiked(liked bool) { + transaction.branchRealityLiked = liked +} + +func (transaction *Transaction) IsBranchRealityLiked() bool { + return transaction.branchRealityLiked +} + +func (transaction *Transaction) SetBranchTransactionLiked(liked bool) { + transaction.branchTransactionLiked = liked +} + +func (transaction *Transaction) IsBranchTransactionLiked() bool { + return transaction.branchTransactionLiked +} + +func (transaction *Transaction) GetMetadata() *TransactionMetadata { + if transaction.metadata == nil { + transaction.metadata = NewTransactionMetadata() + } + + return transaction.metadata +} + +func (transaction *Transaction) Clone() *Transaction { + return &Transaction{ + id: transaction.id, + nodeId: transaction.nodeId, + trunkId: transaction.GetTrunkID(), + branchId: transaction.GetBranchID(), + counter: transaction.counter, + branchRealityLiked: transaction.branchRealityLiked, + branchTransactionLiked: transaction.branchTransactionLiked, + metadata: nil, + claimedReality: transaction.claimedReality, + } +} + +func (transaction *Transaction) String() string { + return stringify.Struct("Transaction", + stringify.StructField("id", transaction.GetID()), + stringify.StructField("nodeID", transaction.GetNodeID()), + stringify.StructField("trunkID", transaction.GetTrunkID()), + stringify.StructField("branchID", transaction.GetBranchID()), + stringify.StructField("branchTransactionLiked", transaction.IsBranchTransactionLiked()), + stringify.StructField("branchRealityLiked", transaction.IsBranchRealityLiked()), + stringify.StructField("claimedReality", transaction.claimedReality), + stringify.StructField("reality", transaction.GetMetadata().GetReality()), + stringify.StructField("isTransactionLocallyLiked", transaction.GetMetadata().IsTransactionLocallyLiked()), + stringify.StructField("isRealityLocallyLiked", transaction.GetMetadata().IsRealityLocallyLiked()), + ) +} diff --git a/packages/unbreakable_consensus/otv/transaction_database.go b/packages/unbreakable_consensus/otv/transaction_database.go new file mode 100644 index 0000000000000000000000000000000000000000..a17ca59bdde2a580147929eccf97be6718b880ed --- /dev/null +++ b/packages/unbreakable_consensus/otv/transaction_database.go @@ -0,0 +1,30 @@ +package social_consensus + +import ( + "sync" +) + +type TransactionDatabase struct { + transactions map[int]*Transaction + mutex sync.RWMutex +} + +func NewTransactionDatabase() *TransactionDatabase { + return &TransactionDatabase{ + transactions: make(map[int]*Transaction), + } +} + +func (transactionDatabase *TransactionDatabase) StoreTransaction(transaction *Transaction) { + transactionDatabase.mutex.Lock() + defer transactionDatabase.mutex.Unlock() + + transactionDatabase.transactions[transaction.GetID()] = transaction +} + +func (transactionDatabase *TransactionDatabase) LoadTransaction(id int) *Transaction { + transactionDatabase.mutex.RLock() + defer transactionDatabase.mutex.RUnlock() + + return transactionDatabase.transactions[id] +} diff --git a/packages/unbreakable_consensus/otv/transaction_metadata.go b/packages/unbreakable_consensus/otv/transaction_metadata.go new file mode 100644 index 0000000000000000000000000000000000000000..ab285cd6cde8228a0d4271f512edccf09dc4b537 --- /dev/null +++ b/packages/unbreakable_consensus/otv/transaction_metadata.go @@ -0,0 +1,44 @@ +package social_consensus + +type TransactionMetadata struct { + solid bool + transactionLocallyLiked bool + realityLocallyLiked bool + reality int +} + +func NewTransactionMetadata() *TransactionMetadata { + return &TransactionMetadata{} +} + +func (transactionMetadata *TransactionMetadata) SetSolid(solid bool) { + transactionMetadata.solid = solid +} + +func (transactionMetadata *TransactionMetadata) IsSolid() bool { + return transactionMetadata.solid +} + +func (transactionMetadata *TransactionMetadata) SetTransactionLocallyLiked(liked bool) { + transactionMetadata.transactionLocallyLiked = liked +} + +func (transactionMetadata *TransactionMetadata) IsTransactionLocallyLiked() bool { + return transactionMetadata.transactionLocallyLiked +} + +func (transactionMetadata *TransactionMetadata) SetRealityLocallyLiked(liked bool) { + transactionMetadata.realityLocallyLiked = liked +} + +func (transactionMetadata *TransactionMetadata) IsRealityLocallyLiked() bool { + return transactionMetadata.realityLocallyLiked +} + +func (transactionMetadata *TransactionMetadata) SetReality(reality int) { + transactionMetadata.reality = reality +} + +func (transactionMetadata *TransactionMetadata) GetReality() int { + return transactionMetadata.reality +}