From 65a3642f5650338daadd9e79f512d613c0037ede Mon Sep 17 00:00:00 2001 From: Pranav Shriram <46643914+PranavShriram@users.noreply.github.com> Date: Tue, 30 Mar 2021 15:05:23 +0530 Subject: [PATCH] [Fix] : Add comments for cheat detection algorithms (#354) * [Fix] : Add comments for cheat detection algorithms * [Sync]: frontend Co-authored-by: abesheknarayan --- frontend | 2 +- models/InspectComponents.go | 26 ++++++++++++++++++++++---- models/InspectUserDegree.go | 22 ++++++++++++++++++---- 3 files changed, 41 insertions(+), 9 deletions(-) diff --git a/frontend b/frontend index e92d38f7..e12907bb 160000 --- a/frontend +++ b/frontend @@ -1 +1 @@ -Subproject commit e92d38f7ccced44906429b1e45e0f77c9266adc0 +Subproject commit e12907bb3109f2cc05c8d978469671998b700954 diff --git a/models/InspectComponents.go b/models/InspectComponents.go index e636513d..2dd99007 100644 --- a/models/InspectComponents.go +++ b/models/InspectComponents.go @@ -9,7 +9,7 @@ import ( ) - +//Converts user cluster object to proto object func (c *InspectComponentResult) ToProto() *models_pb.Cluster { pCluster:= &models_pb.Cluster{ Members: c.members, @@ -18,6 +18,7 @@ func (c *InspectComponentResult) ToProto() *models_pb.Cluster { return pCluster } +//Stack for DFS type Stack []int32 func (s Stack) Push(v int32) Stack { @@ -44,13 +45,14 @@ type AdjacencyList struct{ edges map[int32][]int32 } -//Funcion to build graph +//Funcion to build graph and get components func getComponents(nnodes int32)(res1[] InspectComponentResult){ l := logger.WithFields(logrus.Fields{ "method": "buildGraph", }) + //Initialise weights of the graph var weights[2001][2001] int64 var transDetails[] TransactionGraph @@ -59,11 +61,16 @@ func getComponents(nnodes int32)(res1[] InspectComponentResult){ db := getDB() + //Query to get transactions made between users + //While making edges cash flows between bidding user and Asking user + //This determines the direction of edge in the graph err := db.Raw("SELECT b.userId as fromid, a.userId as toid, t.total as volume FROM OrderFills o, Transactions t, Asks a, Bids b WHERE o.transactionId = t.id AND o.bidId = b.id AND o.askId = a.id").Scan(&transDetails).Error var users[] UserId - //Get user ids + //Get user ids + //This is necessary as in db user ids are not sequential and hence a mapping + //is required between user number and user id err = db.Raw("SELECT id from Users").Scan(&users).Error //For userId to number mapping @@ -71,25 +78,30 @@ func getComponents(nnodes int32)(res1[] InspectComponentResult){ userMap = make(map[int32]int32) + //Map from userId -> user number for i := 0;i < len(users);i++{ userMap[users[i].Id] = int32(i+1) } + //Update the weights using transactions with help of user id map for i := 0;i < len(transDetails);i++{ weights[userMap[transDetails[i].Fromid]][userMap[transDetails[i].Toid]] += transDetails[i].Volume } + //As Kosarajus algorithm is used to find strongly connected components we need the original graph and reversed graph var listGraph AdjacencyList var reversedGraph AdjacencyList var i, j, k int32 + //Initialise graph structs listGraph.nodes = nnodes reversedGraph.nodes = nnodes listGraph.edges = make(map[int32][]int32) reversedGraph.edges = make(map[int32][]int32) + //Create graphs as adjacency lists for i = 1;i <= nnodes;i++{ for j = 1;j <= nnodes;j++{ if weights[i][j] > 0{ @@ -105,16 +117,18 @@ func getComponents(nnodes int32)(res1[] InspectComponentResult){ var nodeStack Stack + //Array for keeping track of visited nodes var visited[] bool for i = 1;i <= nnodes + 1;i++{ visited = append(visited, false) } - + //Array to get the order of nodes for performing second pass using reversed graph var order[] int32 var top int32 + //Perform dfs for first pass for i = 1;i <= nnodes;i++{ if !visited[i]{ @@ -144,10 +158,13 @@ func getComponents(nnodes int32)(res1[] InspectComponentResult){ visited[i] = false } + //Reverse order obtained using dfs to get the right order for i, j := 0, len(order)-1; i < j; i, j = i+1, j-1 { order[i], order[j] = order[j], order[i] } + //Perform second pass of kosaraju + //All nodes reachable from given node are part of the same component for i = 1;i <= nnodes;i++{ if !visited[order[i - 1]]{ var cur[] int32 @@ -176,6 +193,7 @@ func getComponents(nnodes int32)(res1[] InspectComponentResult){ } } + //Find volume of each component for i = 0;i < int32(len(res));i++{ for j = 0;j < int32(len(res[i].members));j++{ diff --git a/models/InspectUserDegree.go b/models/InspectUserDegree.go index 7295cce0..4de4583a 100644 --- a/models/InspectUserDegree.go +++ b/models/InspectUserDegree.go @@ -41,6 +41,7 @@ type UserId struct{ Id int32 } +//Function to get number of users from db func getNumberOfUsers() int32{ l := logger.WithFields(logrus.Fields{ @@ -63,24 +64,31 @@ func getNumberOfUsers() int32{ } -//Funcion to build graph +//Funcion to build graph and volume of transaction of each user +//with nodes of degree 1 func buildGraph(nnodes int32)(res1 InspectDegreeDetails){ l := logger.WithFields(logrus.Fields{ "method": "buildGraph", }) + //Adjacency matrix storing weight of each edge var weights[2001][2001] int64 var transDetails[] TransactionGraph db := getDB() + //Query to get transactions made between users + //While making edges cash flows between bidding user and Asking user + //This determines the direction of edge in the graph err := db.Raw("SELECT b.userId as fromid, a.userId as toid, t.total as volume FROM OrderFills o, Transactions t, Asks a, Bids b WHERE o.transactionId = t.id AND o.bidId = b.id AND o.askId = a.id").Scan(&transDetails).Error var users[] UserId - //Get user ids + //Get user ids + //This is necessary as in db user ids are not sequential and hence a mapping + //is required between user number and user id err = db.Raw("SELECT id from Users").Scan(&users).Error //For userId to number mapping @@ -88,17 +96,22 @@ func buildGraph(nnodes int32)(res1 InspectDegreeDetails){ userMap = make(map[int32]int32) + //Map from userId -> user number for i := 0;i < len(users);i++{ userMap[users[i].Id] = int32(i+1) } + //Update the weights using transactions with help of user id map for i := 0;i < len(transDetails);i++{ weights[userMap[transDetails[i].Fromid]][userMap[transDetails[i].Toid] ]+= transDetails[i].Volume } + //First all nodes which are of degree one + //are found and the state is updated in the boolean array var isDegreeOne[2001] bool var i, j int32 + //Direction of transaction is immaterial so both directions are summed up for i = 1; i <= nnodes;i++{ for j = 1;j <= nnodes;j++{ if i > j{ @@ -108,8 +121,7 @@ func buildGraph(nnodes int32)(res1 InspectDegreeDetails){ } } - - + //Find degree one nodes for i = 1;i <= nnodes;i++{ count := 0 @@ -128,6 +140,7 @@ func buildGraph(nnodes int32)(res1 InspectDegreeDetails){ var volumeVals VolumeSortArray + //Find volume of transaction from every node to degree one nodes for i = 1;i <= nnodes;i++{ var temp int64 = 0 for j = 1;j <= nnodes;j++{ @@ -142,6 +155,7 @@ func buildGraph(nnodes int32)(res1 InspectDegreeDetails){ volumeVals.elements = append(volumeVals.elements, curVol) } + //Sort by volume in descending order sort.Slice(volumeVals.elements, func(i, j int) bool { return volumeVals.elements[i].volume > volumeVals.elements[j].volume })