From f15c8f0a599dd09ffbbadcb524381e9181200076 Mon Sep 17 00:00:00 2001 From: Arnab Ghose Date: Mon, 12 Aug 2024 14:37:40 +0530 Subject: [PATCH] fixed transfer of rbt between DIDs when either is present on a Quorum node --- core/ping.go | 45 +++++++++--- core/quorum.go | 20 ++++-- core/quorum_initiator.go | 25 +++++-- core/quorum_recv.go | 26 +++++-- tests/scenarios/rbt_transfer.py | 123 ++++++++++++++++++++++++++++++-- 5 files changed, 207 insertions(+), 32 deletions(-) diff --git a/core/ping.go b/core/ping.go index eebfb071..7c66f9df 100644 --- a/core/ping.go +++ b/core/ping.go @@ -5,6 +5,7 @@ import ( "net/http" "strconv" "time" + "strings" "github.com/rubixchain/rubixgoplatform/core/ipfsport" "github.com/rubixchain/rubixgoplatform/core/model" @@ -99,7 +100,10 @@ func (c *Core) CheckQuorumStatusResponse(req *ensweb.Request) *ensweb.Result { / func (c *Core) CheckQuorumStatus(peerID string, did string) (string, bool, error) { // q := make(map[string]string) if peerID == "" { - peerID = c.qm.GetPeerID(did) + peerID = c.qm.GetPeerID(did, c.peerID) + } + if peerID == "" { + return "Quorum Connection Error", false, fmt.Errorf("unable to find Quorum DID info and peer for %v", did) } p, err := c.pm.OpenPeerConn(peerID, "", c.getCoreAppName(peerID)) if err != nil { @@ -202,20 +206,39 @@ func (c *Core) GetPeerInfoResponse(req *ensweb.Request) *ensweb.Result { //PingR pInfo.PeerID = c.w.GetPeerID(peerDID) if pInfo.PeerID == "" { - c.log.Error("sender does not have prev pledged quorum in DIDPeerTable", peerDID) - resp.Message = "Couldn't fetch peer id for did: " + peerDID - resp.Status = false - return c.l.RenderJSON(req, &resp, http.StatusOK) + _, err := c.w.GetDID(peerDID) + if err != nil { + c.log.Error("sender does not have prev pledged quorum in DIDPeerTable", peerDID) + resp.Message = "Couldn't fetch peer id for did: " + peerDID + resp.Status = false + return c.l.RenderJSON(req, &resp, http.StatusOK) + } else { + pInfo.PeerID = c.peerID + } } qDidType, err := c.w.GetPeerDIDType(peerDID) if err != nil || qDidType == -1 { - c.log.Error("could not fetch did type for quorum:", peerDID, "error", err) - pInfo.DIDType = nil - resp.PeerInfo = pInfo - resp.Status = true - resp.Message = "could not fetch did type, only sharing peerId" - return c.l.RenderJSON(req, &resp, http.StatusOK) + if strings.Contains(err.Error(), "no records found") { + didInfo, err := c.w.GetDID(peerDID) + if err != nil { + c.log.Error("unable to find DID in DIDTable, could not fetch did type for quorum:", peerDID, "error", err) + pInfo.DIDType = nil + resp.PeerInfo = pInfo + resp.Status = true + resp.Message = "could not fetch did type, only sharing peerId" + return c.l.RenderJSON(req, &resp, http.StatusOK) + } else { + pInfo.DIDType = &didInfo.Type + } + } else { + c.log.Error("could not fetch did type for quorum:", peerDID, "error", err) + pInfo.DIDType = nil + resp.PeerInfo = pInfo + resp.Status = true + resp.Message = "could not fetch did type, only sharing peerId" + return c.l.RenderJSON(req, &resp, http.StatusOK) + } } else { pInfo.DIDType = &qDidType } diff --git a/core/quorum.go b/core/quorum.go index 2f82bcd8..b09982d5 100644 --- a/core/quorum.go +++ b/core/quorum.go @@ -95,7 +95,7 @@ func NewQuorumManager(s storage.Storage, log logger.Logger) (*QuorumManager, err } // GetQuorum will get the configured or available quorum -func (qm *QuorumManager) GetQuorum(t int, lastChar string) []string { +func (qm *QuorumManager) GetQuorum(t int, lastChar string, selfPeer string) []string { //QuorumTypeOne is to select quorums from the public pool of quorums instead of a private subnet. //Once a new node is created, it will create a DID. Using the command "registerdid", the peerID and DID will be //published in the network, and all the nodes listening to the subscription will have the DID added on the DIDPeerTable @@ -130,7 +130,7 @@ func (qm *QuorumManager) GetQuorum(t int, lastChar string) []string { var quorumAddrList []string quorumAddrCount := 0 for _, q := range qm.ql { - peerID := qm.GetPeerID(q) + peerID := qm.GetPeerID(q, selfPeer) addr := string(peerID + "." + q) quorumAddrList = append(quorumAddrList, addr) quorumAddrCount = quorumAddrCount + 1 @@ -165,11 +165,19 @@ func (qm *QuorumManager) RemoveAllQuorum(t int) error { return err } -func (qm *QuorumManager) GetPeerID(did string) string { +func (qm *QuorumManager) GetPeerID(did string, selfPeer string) string { var dm QuorumDIDPeerMap err := qm.s.Read(wallet.DIDPeerStorage, &dm, "did=?", did) - if err != nil { - return "" + if err != nil && strings.Contains(err.Error(), "no records found") { + // Check if the Quorum DID is part of the same node by looking in DIDTable + var dt wallet.DIDType + err2 := qm.s.Read(wallet.DIDStorage, &dt, "did=?", did) + if err2 != nil { + return "" + } else { + return selfPeer + } + } else { + return dm.PeerID } - return dm.PeerID } diff --git a/core/quorum_initiator.go b/core/quorum_initiator.go index 5f5ac90d..3a249693 100644 --- a/core/quorum_initiator.go +++ b/core/quorum_initiator.go @@ -252,7 +252,7 @@ func (c *Core) SetupQuorum(didStr string, pwd string, pvtKeyPwd string) error { } func (c *Core) GetAllQuorum() []string { - return c.qm.GetQuorum(QuorumTypeTwo, "") + return c.qm.GetQuorum(QuorumTypeTwo, "", c.peerID) } func (c *Core) AddQuorum(ql []QuorumData) error { @@ -347,7 +347,7 @@ func (c *Core) initiateConsensus(cr *ConensusRequest, sc *contract.Contract, dc lastCharTID := string(tid[len(tid)-1]) cr.TransactionID = tid - ql := c.qm.GetQuorum(cr.Type, lastCharTID) //passing lastCharTID as a parameter. Made changes in GetQuorum function to take 2 arguments + ql := c.qm.GetQuorum(cr.Type, lastCharTID, c.peerID) //passing lastCharTID as a parameter. Made changes in GetQuorum function to take 2 arguments if ql == nil || len(ql) < MinQuorumRequired { c.log.Error("Failed to get required quorums") return nil, nil, fmt.Errorf("failed to get required quorums") @@ -474,12 +474,30 @@ func (c *Core) initiateConsensus(cr *ConensusRequest, sc *contract.Contract, dc if qpid == "" { qpid = c.w.GetPeerID(qdid) } + // Initiatitor is part of Quorum Node + if qpid == "" { + _, err := c.w.GetDID(qdid) + if err != nil { + return nil, nil, fmt.Errorf("unable to fetch peerID for quorum DID: %v which fetching quorum information", qdid) + } else { + qpid = c.peerID + } + } var qrmInfo QuorumDIDPeerMap //fetch did type of the quorum qDidType, err := c.w.GetPeerDIDType(qdid) if err != nil { - c.log.Error("could not fetch did type for quorum:", qdid, "error", err) + if strings.Contains(err.Error(), "no records found") { + didInfo, err := c.w.GetDID(qdid) + if err != nil { + return nil, nil, err + } else { + qDidType = didInfo.Type + } + } else { + c.log.Error(fmt.Sprintf("could not fetch did type for quorum: %v while gathering quorum information, err: %v", qdid, err)) + } } if qDidType == -1 { c.log.Info("did type is empty for quorum:", qdid, "connecting & fetching from quorum") @@ -1268,7 +1286,6 @@ func (c *Core) finishConsensus(id string, qt int, p *ipfsport.Peer, status bool, } func (c *Core) connectQuorum(cr *ConensusRequest, addr string, qt int, sc *contract.Contract) { - defer c.w.ReleaseAllLockedTokens() c.startConsensus(cr.ReqID, qt) var p *ipfsport.Peer var err error diff --git a/core/quorum_recv.go b/core/quorum_recv.go index d22f89ad..93916b9c 100644 --- a/core/quorum_recv.go +++ b/core/quorum_recv.go @@ -906,12 +906,26 @@ func (c *Core) updatePledgeToken(req *ensweb.Request) *ensweb.Result { ctcb := make(map[string]*block.Block) tsb := make([]block.TransTokens, 0) - for _, t := range tks { - err = c.w.AddTokenBlock(t, b) - if err != nil { - c.log.Error("Failed to add token block", "token", t) - crep.Message = "Failed to add token block" - return c.l.RenderJSON(req, &crep, http.StatusOK) + // Generally, addition of a token block happens on Sender, Receiver + // and Quorum's end. + // + // If both sender and receiver happen to be on a Non-Quorum server, this is + // not an issue since we skip TokensTable and Token chain update, if the reciever + // and sender peer as seem. Thus, multiple update of same block to the Token's tokenchain + // is avoided + // + // However in case both sender and receiver happen to be a Quorum server, even though the above + // scenario is covered, but since the token block is also added on Quorum's end, we end up in a + // situation where update of same block happens twice. Hence the following check ensures that we + // skip the addition of block here, if sender and receiver happen to be on a Quoeum node. + if !c.w.IsDIDExist(b.GetReceiverDID()) && !c.w.IsDIDExist(b.GetSenderDID()) { + for _, t := range tks { + err = c.w.AddTokenBlock(t, b) + if err != nil { + c.log.Error("Failed to add token block", "token", t) + crep.Message = "Failed to add token block" + return c.l.RenderJSON(req, &crep, http.StatusOK) + } } } for _, t := range ur.PledgedTokens { diff --git a/tests/scenarios/rbt_transfer.py b/tests/scenarios/rbt_transfer.py index b1190f3a..8142af5e 100644 --- a/tests/scenarios/rbt_transfer.py +++ b/tests/scenarios/rbt_transfer.py @@ -15,13 +15,27 @@ def setup(): config_A = node_config["node9"] config_B = node_config["node10"] + + config_quorum = get_quorum_config() + config_quorum_node4 = config_quorum["node4"] + config_quorum_node5 = config_quorum["node5"] create_and_register_did(config_A, "did_a", register_did=False) + + # Sender and Receiver on same Non-Quorum server Scenario create_and_register_did(config_A, "did_a1", register_did=False) create_and_register_did(config_A, "did_a2", register_did=False) + + # Sender and Receiver on same Quorum server Scenario + create_and_register_did(config_quorum_node4, "did_quorum_a1_node4", register_did=False) + create_and_register_did(config_quorum_node4, "did_quorum_a2_node4", register_did=False) + create_and_register_did(config_quorum_node5, "did_quorum_a1_node5", register_did=False) + create_and_register_did(config_A, "did_nonquorum_a1_node9", register_did=False) + create_and_register_did(config_B, "did_b", register_did=False) save_to_config_file(__node_config_path, node_config) + save_to_config_file("./quorum_config.json", config_quorum) run_quorum_nodes(False, False, "quorum2", "./quorum_config2.json", "quorumlist2.json") @@ -49,6 +63,7 @@ def run(skip_setup: bool = False): shuttle_transfer(node_config) did_on_same_server_transfer(node_config) + did_on_same_quorum_server_transfer(node_config) insufficient_balance_transfer(node_config) max_decimal_place_transfer(node_config) @@ -56,24 +71,122 @@ def run(skip_setup: bool = False): def did_on_same_server_transfer(config): node_A_info = config["node9"] + server_port_A, grpc_port_A = node_A_info["server"], node_A_info["grpcPort"] did_A1, did_A2 = get_did_by_alias(node_A_info, "did_a1"), get_did_by_alias(node_A_info, "did_a2") print("------ Test Case (PASS): Transfer between DID's on same server ------\n") - print("\n1. Generating 2 whole RBT for A") + print("\n1. Generating 2 whole RBT for A1") expect_success(fund_did_with_rbt)(node_A_info, did_A1, 2) print("Funded node A with 2 RBT") - print("\n2. Transferring 1 RBT from A to B (both present on same node)....") + print("\n2. Transferring 0.5 RBT from A1 to A2....") + expect_success(rbt_transfer)(did_A1, did_A2, 0.5, server_port_A, grpc_port_A) + print("Transfer Complete") + + print("\n3. Transferring 1.499 RBT from A1 to A2....") + expect_success(rbt_transfer)(did_A1, did_A2, 1.499, server_port_A, grpc_port_A) + print("Transfer Complete") + + print("\n4. Transferring 0.25 RBT from A2 to A1....") + expect_success(rbt_transfer)(did_A2, did_A1, 0.25, server_port_A, grpc_port_A) + print("Transfer Complete") + + print("\n5. Transferring 0.25 RBT from A2 to A1....") + expect_success(rbt_transfer)(did_A2, did_A1, 0.25, server_port_A, grpc_port_A) + print("Transfer Complete") + + print("\n6. Transferring 0.25 RBT from A2 to A1....") + expect_success(rbt_transfer)(did_A2, did_A1, 0.25, server_port_A, grpc_port_A) + print("Transfer Complete") + + print("\n7. Transferring 0.25 RBT from A2 to A1....") + expect_success(rbt_transfer)(did_A2, did_A1, 0.25, server_port_A, grpc_port_A) + print("Transfer Complete") + + print("\n8. Transferring 1 RBT from A1 to A2....") expect_success(rbt_transfer)(did_A1, did_A2, 1, server_port_A, grpc_port_A) + print("Transfer Complete") + + print("\n9. Generating 2 whole RBT for A1") + expect_success(fund_did_with_rbt)(node_A_info, did_A1, 2) + print("Transfer Complete") + + print("\n10. Transferring 2 RBT from A1 to A2....") + expect_success(rbt_transfer)(did_A1, did_A2, 2, server_port_A, grpc_port_A) + print("Transfer Complete") + + print("\n11. Transferring 0.001 RBT from A1 to A2....") + expect_success(rbt_transfer)(did_A1, did_A2, 0.001, server_port_A, grpc_port_A) + print("Transfer Complete") + + print("\n------ Test Case (PASS): Transfer between DID's on same server completed ------\n") + +def did_on_same_quorum_server_transfer(config): + quorum_config = get_quorum_config() + + node_4_info = quorum_config["node4"] + node_5_info = quorum_config["node5"] + node_9_info = config["node9"] + + server_port_4, grpc_port_4 = node_4_info["server"], node_4_info["grpcPort"] + did_4_A, did_4_B = get_did_by_alias(node_4_info, "did_quorum_a1_node4"), get_did_by_alias(node_4_info, "did_quorum_a2_node4") + + server_port_5, grpc_port_5 = node_5_info["server"], node_5_info["grpcPort"] + did_5_A = get_did_by_alias(node_5_info, "did_quorum_a1_node5") + + server_port_9, grpc_port_9 = node_9_info["server"], node_9_info["grpcPort"] + did_9_A = get_did_by_alias(node_9_info, "did_nonquorum_a1_node9") + + for _, val in quorum_config.items(): + add_peer_details(val["peerId"], val["dids"]["did_quorum"], 4, server_port_4, grpc_port_4) + add_peer_details(val["peerId"], val["dids"]["did_quorum"], 4, server_port_5, grpc_port_5) + add_peer_details(val["peerId"], val["dids"]["did_quorum"], 4, server_port_9, grpc_port_9) + + print("------ Test Case (PASS): Transfer between DID's where either the Sender or Receiver are on a Quorum node ------\n") + + print("\n1. Generating 2 whole RBT for A on node4 (node4 is a Quorum node)") + expect_success(fund_did_with_rbt)(node_4_info, did_4_A, 2) + print("Funded node A with 2 RBT") + + print("\n2. Transferring 1 RBT from A to B on node4 (node4 is a Quorum node)") + expect_success(rbt_transfer)(did_4_A, did_4_B, 1, server_port_4, grpc_port_4) print("Transferred 1 RBT from A to B") - print("\n3. Transferring 1 RBT from B to A (both present on same node)....") - expect_success(rbt_transfer)(did_A2, did_A1, 1, server_port_A, grpc_port_A) + print("\n3. Transferring 1 RBT from B to A on node4 (node4 is a Quorum node)") + expect_success(rbt_transfer)(did_4_B, did_4_A, 1, server_port_4, grpc_port_4) print("Transferred 1 RBT from A to B") - print("\n------ Test Case (PASS): Transfer between DID's on same server completed ------\n") + print("\n4. Transferring 0.555 RBT from A to B on node4 (node4 is a Quorum node)") + expect_success(rbt_transfer)(did_4_A, did_4_B, 0.555, server_port_4, grpc_port_4) + print("Transferred 1 RBT from A to B") + + print("\n5. Transferring 0.555 RBT from B to A on node4 (node4 is a Quorum node)") + expect_success(rbt_transfer)(did_4_B, did_4_B, 0.555, server_port_4, grpc_port_4) + print("Transferred 1 RBT from A to B") + + print("\n6. Transferring 0.445 RBT from A of node4 to A of node5 (node4 and node5 are Quorum nodes)") + add_peer_details(node_5_info["peerId"], did_5_A, 4, server_port_4, grpc_port_4) + expect_success(rbt_transfer)(did_4_A, did_5_A, 0.445, server_port_4, grpc_port_4) + print("Transferred 1 RBT from A to B") + + print("\n7. Transferring 0.445 RBT from A of node5 to A of node4 (node4 and node5 are Quorum nodes)") + add_peer_details(node_4_info["peerId"], did_4_A, 4, server_port_5, grpc_port_5) + expect_success(rbt_transfer)(did_5_A, did_4_A, 0.445, server_port_5, grpc_port_5) + print("Transferred 1 RBT from A to B") + + print("\n8. Transferring 1 RBT from A of node4 to A of node9 (node4 is a Quorum node, node9 is a Non-Quorum node)") + add_peer_details(node_9_info["peerId"], did_9_A, 4, server_port_4, grpc_port_4) + expect_success(rbt_transfer)(did_4_A, did_9_A, 1, server_port_4, grpc_port_4) + print("Transferred 1 RBT from A to B") + + print("\n9. Transferring 1 RBT from A of node9 to A of node4 (node4 is a Quorum node, node9 is a Non-Quorum node)") + add_peer_details(node_4_info["peerId"], did_4_A, 4, server_port_9, grpc_port_9) + expect_success(rbt_transfer)(did_9_A, did_4_A, 1, server_port_9, grpc_port_9) + print("Transferred 1 RBT from A to B") + + print("------ Test Case (PASS): Transfer between DID's where either the Sender or Receiver are on a Quorum node completed------\n") def max_decimal_place_transfer(config): node_A_info, node_B_info = config["node9"], config["node10"]