Repository: skalenetwork/IMA (language: python)

Issue:
Ticket 1515/ima agent stuck on re discover
Fixed un-reasonable awaiting for more than 11 of 16 nodes on S-Chain discovery


Discussion / hints:

Fix patch:
diff --git a/agent/bls.mjs b/agent/bls.mjs
index 0feaa2174..fa0f7059e 100644
--- a/agent/bls.mjs
+++ b/agent/bls.mjs
@@ -2800,7 +2800,7 @@ export async function handleSkaleImaVerifyAndSign( joCallData ) {
         if( ! joAccount.strURL ) {
             joAccount = optsHandleVerifyAndSign.imaState.chainProperties.mn.joAccount;
             if( ! joAccount.strSgxURL )
-                throw new Error( "SGX URL is unknown, cannot sign IMA message(s)" );
+                throw new Error( "SGX URL is unknown, cannot sign(handle) IMA message(s)" );
             if( ! joAccount.strBlsKeyName )
                 throw new Error( "BLS keys name is unknown, cannot sign IMA message(s)" );
         }
diff --git a/agent/cli.mjs b/agent/cli.mjs
index 644941296..12058a7ff 100644
--- a/agent/cli.mjs
+++ b/agent/cli.mjs
@@ -475,10 +475,11 @@ function printHelpUserAccount1( soi ) {
     console.log( soi + cc.debug( "--" ) +
         cc.bright( "sgx-url-t-chain" ) + cc.sunny( "=" ) + cc.attention( "URL" ) +
         cc.debug( "..................." ) + cc.sunny( "SGX server" ) +
-        cc.notice( " URL for " ) + cc.note( "S<->S Target S-chain" ) +
-        cc.notice( ". Value is automatically loaded from the " ) +
-        cc.warning( "SGX_URL_S_CHAIN_TARGET" ) +
-        cc.notice( " environment variable if not specified." ) );
+        cc.notice( " URL for " ) + cc.note( "S<->S Target S-chain." ) );
+    console.log( soi + cc.debug( "--" ) +
+        cc.bright( "sgx-url" ) + cc.sunny( "=" ) + cc.attention( "URL" ) +
+        cc.debug( "..................." ) + cc.sunny( "SGX server" ) +
+        cc.notice( " URL for all chains." ) );
     console.log( soi + cc.debug( "--" ) +
         cc.bright( "sgx-ecdsa-key-main-net" ) + cc.sunny( "=" ) + cc.error( "name" ) +
         cc.debug( "..........." ) + cc.attention( "SGX/ECDSA key name" ) +
@@ -500,6 +501,10 @@ function printHelpUserAccount1( soi ) {
         cc.notice( ". Value is automatically loaded from the " ) +
         cc.warning( "SGX_KEY_S_CHAIN_TARGET" ) +
         cc.notice( " environment variable if not specified." ) );
+    console.log( soi + cc.debug( "--" ) +
+        cc.bright( "sgx-ecdsa-key" ) + cc.sunny( "=" ) + cc.error( "name" ) +
+        cc.debug( "............" ) + cc.attention( "SGX/ECDSA key name" ) +
+        cc.notice( " for all chains." ) );
     console.log( soi + cc.debug( "--" ) +
         cc.bright( "sgx-bls-key-main-net" ) + cc.sunny( "=" ) + cc.error( "name" ) +
         cc.debug( "............." ) + cc.attention( "SGX/BLS key name" ) +
@@ -521,6 +526,10 @@ function printHelpUserAccount1( soi ) {
         cc.notice( ". Value is automatically loaded from the " ) +
         cc.warning( "BLS_KEY_S_CHAIN_TARGET" ) +
         cc.notice( " environment variable if not specified." ) );
+    console.log( soi + cc.debug( "--" ) +
+        cc.bright( "sgx-bls-key" ) + cc.sunny( "=" ) + cc.error( "name" ) +
+        cc.debug( ".............." ) + cc.attention( "SGX/BLS key name" ) +
+        cc.notice( " for all chains." ) );
     console.log( soi + cc.debug( "--" ) +
         cc.bright( "sgx-ssl-key-main-net" ) + cc.sunny( "=" ) + cc.attention( "path" ) +
         cc.debug( "............." ) + cc.notice( "Path to " ) + cc.note( "SSL key file" ) +
@@ -544,6 +553,10 @@ function printHelpUserAccount1( soi ) {
         cc.notice( ". Value is automatically loaded from the " ) +
         cc.warning( "SGX_SSL_KEY_FILE_S_CHAIN_TARGET" ) +
         cc.notice( " environment variable if not specified." ) );
+    console.log( soi + cc.debug( "--" ) +
+        cc.bright( "sgx-ssl-key" ) + cc.sunny( "=" ) + cc.attention( "path" ) +
+        cc.debug( ".............." ) + cc.notice( "Path to " ) + cc.note( "SSL key file" ) +
+        cc.notice( " for " ) + cc.bright( "SGX wallet" ) + cc.notice( " of all chains." ) );
     console.log( soi + cc.debug( "--" ) +
         cc.bright( "sgx-ssl-cert-main-net" ) + cc.sunny( "=" ) + cc.attention( "path" ) +
         cc.debug( "............" ) + cc.notice( "Path to " ) +
@@ -569,6 +582,10 @@ function printHelpUserAccount1( soi ) {
         cc.notice( ". Value is automatically loaded from the " ) +
         cc.warning( "SGX_SSL_CERT_FILE_S_CHAIN_TARGET" ) +
         cc.notice( " environment variable if not specified." ) );
+    console.log( soi + cc.debug( "--" ) +
+        cc.bright( "sgx-ssl-cert" ) + cc.sunny( "=" ) + cc.attention( "path" ) +
+        cc.debug( "............." ) + cc.notice( "Path to " ) +
+        cc.note( "SSL certificate file" ) + cc.notice( " for all chains." ) );
     console.log( soi + cc.debug( "--" ) +
         cc.bright( "address-main-net" ) + cc.sunny( "=" ) + cc.warning( "value" ) +
         cc.debug( "................" ) + cc.note( "Main-net" ) + " " +
@@ -1182,6 +1199,12 @@ function printHelpMonitoring( soi ) {
         cc.sunny( "0" ) + cc.debug( " to " ) + cc.error( "disable" ) +
         cc.notice( "." ) + cc.debug( " By default monitoring server is " ) +
         cc.error( "disabled" ) + cc.notice( "." ) );
+    console.log( soi + cc.debug( "--" ) +
+        cc.bright( "monitoring-log" ) +
+        cc.debug( "........................" ) + cc.notice( "Enable logging on " ) +
+        cc.note( "monitoring web socket RPC server" ) +
+        cc.notice( ". " ) + cc.debug( " By default these log messages are " ) +
+        cc.error( "disabled" ) + cc.notice( "." ) );
 }
 
 function printHelpGasReimbursement( soi ) {
@@ -1564,6 +1587,14 @@ function parseSgxArgs( imaState, joArg ) {
         imaState.chainProperties.tc.joAccount.strSgxURL = joArg.value;
         return true;
     }
+    if( joArg.name == "sgx-url" ) {
+        owaspUtils.verifyArgumentIsURL( joArg );
+        imaState.chainProperties.mn.joAccount.strSgxURL =
+            imaState.chainProperties.sc.joAccount.strSgxURL =
+            imaState.chainProperties.tc.joAccount.strSgxURL =
+            joArg.value;
+        return true;
+    }
     if( joArg.name == "sgx-ecdsa-key-main-net" ) {
         owaspUtils.verifyArgumentWithNonEmptyValue( joArg );
         imaState.chainProperties.mn.joAccount.strSgxKeyName = joArg.value;
@@ -1579,6 +1610,14 @@ function parseSgxArgs( imaState, joArg ) {
         imaState.chainProperties.tc.joAccount.strSgxKeyName = joArg.value;
         return true;
     }
+    if( joArg.name == "sgx-ecdsa-key" ) {
+        owaspUtils.verifyArgumentWithNonEmptyValue( joArg );
+        imaState.chainProperties.mn.joAccount.strSgxKeyName =
+            imaState.chainProperties.sc.joAccount.strSgxKeyName =
+            imaState.chainProperties.tc.joAccount.strSgxKeyName =
+            joArg.value;
+        return true;
+    }
     if( joArg.name == "sgx-bls-key-main-net" ) {
         owaspUtils.verifyArgumentWithNonEmptyValue( joArg );
         imaState.chainProperties.mn.joAccount.strBlsKeyName = joArg.value;
@@ -1594,6 +1633,14 @@ function parseSgxArgs( imaState, joArg ) {
         imaState.chainProperties.tc.joAccount.strBlsKeyName = joArg.value;
         return true;
     }
+    if( joArg.name == "sgx-bls-key" ) {
+        owaspUtils.verifyArgumentWithNonEmptyValue( joArg );
+        imaState.chainProperties.mn.joAccount.strBlsKeyName =
+            imaState.chainProperties.sc.joAccount.strBlsKeyName =
+            imaState.chainProperties.tc.joAccount.strBlsKeyName =
+            joArg.value;
+        return true;
+    }
     if( joArg.name == "sgx-ssl-key-main-net" ) {
         owaspUtils.verifyArgumentIsPathToExistingFile( joArg );
         imaState.chainProperties.mn.joAccount.strPathSslKey =
@@ -1612,6 +1659,14 @@ function parseSgxArgs( imaState, joArg ) {
             imaUtils.normalizePath( joArg.value );
         return true;
     }
+    if( joArg.name == "sgx-ssl-key" ) {
+        owaspUtils.verifyArgumentIsPathToExistingFile( joArg );
+        imaState.chainProperties.mn.joAccount.strPathSslKey =
+            imaState.chainProperties.sc.joAccount.strPathSslKey =
+            imaState.chainProperties.tc.joAccount.strPathSslKey =
+            imaUtils.normalizePath( joArg.value );
+        return true;
+    }
     if( joArg.name == "sgx-ssl-cert-main-net" ) {
         owaspUtils.verifyArgumentIsPathToExistingFile( joArg );
         imaState.chainProperties.mn.joAccount.strPathSslCert =
@@ -1630,6 +1685,14 @@ function parseSgxArgs( imaState, joArg ) {
             imaUtils.normalizePath( joArg.value );
         return true;
     }
+    if( joArg.name == "sgx-ssl-cert" ) {
+        owaspUtils.verifyArgumentIsPathToExistingFile( joArg );
+        imaState.chainProperties.mn.joAccount.strPathSslCert =
+            imaState.chainProperties.sc.joAccount.strPathSslCert =
+            imaState.chainProperties.tc.joAccount.strPathSslCert =
+            imaUtils.normalizePath( joArg.value );
+        return true;
+    }
     return false;
 }
 
@@ -2235,6 +2298,11 @@ function parseMonitoringArgs( imaState, joArg ) {
         imaState.nMonitoringPort = owaspUtils.toInteger( joArg.value );
         return true;
     }
+    if( joArg.name == "monitoring-log" ) {
+        owaspUtils.verifyArgumentIsIntegerIpPortNumber( joArg, true );
+        imaState.bLogMonitoringServer = true;
+        return true;
+    }
     return false;
 }
 
diff --git a/agent/discoveryTools.mjs b/agent/discoveryTools.mjs
index 04935f9a2..d8f5428bc 100644
--- a/agent/discoveryTools.mjs
+++ b/agent/discoveryTools.mjs
@@ -207,7 +207,8 @@ let gFlagIsInSChainDiscovery = false;
 
 export async function continueSChainDiscoveryInBackgroundIfNeeded( isSilent ) {
     const imaState = state.get();
-    const cntNodes = getSChainNodesCount( imaState.joSChainNetworkInfo );
+    const cntNodesOnChain = getSChainNodesCount( imaState.joSChainNetworkInfo );
+    const cntNodes = Math.min( Math.ceil( cntNodesOnChain * 2 / 3 + 1 ), cntNodesOnChain );
     const cntDiscovered = getSChainDiscoveredNodesCount( imaState.joSChainNetworkInfo );
     if( cntDiscovered >= cntNodes ) {
         if( gTimerSChainDiscovery != null ) {
diff --git a/agent/main.mjs b/agent/main.mjs
index 1abbc6dc9..d7a994e9a 100644
--- a/agent/main.mjs
+++ b/agent/main.mjs
@@ -54,10 +54,6 @@ function parseCommandLine() {
     strPrintedArguments = imaUtils.replaceAll( strPrintedArguments, "=", cc.sunny( "=" ) );
     strPrintedArguments = imaUtils.replaceAll( strPrintedArguments, "/", cc.info( "/" ) );
     strPrintedArguments = imaUtils.replaceAll( strPrintedArguments, ":", cc.info( ":" ) );
-    if( log.verboseGet() >= log.verboseReversed().information ) {
-        log.write( cc.debug( "Agent was started with " ) + cc.info( process.argv.length ) +
-            cc.debug( " command line argument(s) as: " ) + strPrintedArguments + "\n" );
-    }
     imaCLI.parse( {
         "register": clpTools.commandLineTaskRegister,
         "register1": clpTools.commandLineTaskRegister1,
@@ -141,6 +137,10 @@ function parseCommandLine() {
             imaState.strLogFilePath, imaState.nLogMaxSizeBeforeRotation,
             imaState.nLogMaxFilesCount );
     }
+    if( imaState.isPrintSecurityValues && log.verboseGet() >= log.verboseReversed().information ) {
+        log.write( cc.debug( "Agent was started with " ) + cc.info( process.argv.length ) +
+            cc.debug( " command line argument(s) as: " ) + strPrintedArguments + "\n" );
+    }
     if( imaState.bIsNeededCommonInit ) {
         imaCLI.commonInit();
         imaCLI.initContracts();
@@ -158,14 +158,21 @@ function initMonitoringServer() {
     if( imaState.nMonitoringPort <= 0 )
         return;
     const strLogPrefix = cc.attention( "Monitoring:" ) + " ";
-    if( log.verboseGet() >= log.verboseReversed().trace ) {
+    if( imaState.bLogMonitoringServer && log.verboseGet() >= log.verboseReversed().trace ) {
         log.write( strLogPrefix + cc.normal( "Will start monitoring WS server on port " ) +
             cc.info( imaState.nMonitoringPort ) + "\n" );
     }
     gServerMonitoringWS = new ws.WebSocketServer( { port: 0 + imaState.nMonitoringPort } );
     gServerMonitoringWS.on( "connection", function( wsPeer, req ) {
-        const ip = req.socket.remoteAddress;
-        if( log.verboseGet() >= log.verboseReversed().debug )
+        let ip = req.socket.remoteAddress;
+        if( "headers" in req && req.headers && typeof req.headers == "object" &&
+            "x-forwarded-for" in req.headers && req.headers["x-forwarded-for"] )
+            ip = req.headers["x-forwarded-for"]; // better under NGINX
+        if( ( !ip ) && "_socket" in req && req._socket && "remoteAddress" in req._socket )
+            ip = req._socket.remoteAddress;
+        if( !ip )
+            ip = "N/A";
+        if( imaState.bLogMonitoringServer && log.verboseGet() >= log.verboseReversed().debug )
             log.write( strLogPrefix + cc.normal( "New connection from " ) + cc.info( ip ) + "\n" );
         wsPeer.on( "message", function( message ) {
             const joAnswer = {
@@ -175,7 +182,9 @@ function initMonitoringServer() {
             };
             try {
                 const joMessage = JSON.parse( message );
-                if( log.verboseGet() >= log.verboseReversed().trace ) {
+                if( imaState.bLogMonitoringServer &&
+                    log.verboseGet() >= log.verboseReversed().trace
+                ) {
                     log.write( strLogPrefix + cc.sunny( "<<<" ) + " " +
                         cc.normal( "message from " ) + cc.info( ip ) + cc.normal( ": " ) +
                         cc.j( joMessage ) + "\n" );
@@ -255,7 +264,9 @@ function initMonitoringServer() {
                 }
             }
             try {
-                if( log.verboseGet() >= log.verboseReversed().trace ) {
+                if( imaState.bLogMonitoringServer &&
+                    log.verboseGet() >= log.verboseReversed().trace
+                ) {
                     log.write( strLogPrefix + cc.sunny( ">>>" ) + " " + cc.normal( "answer to " ) +
                         cc.info( ip ) + cc.normal( ": " ) + cc.j( joAnswer ) + "\n" );
                 }
diff --git a/agent/run.sh b/agent/run.sh
index 39e0a1469..e4bd86bfe 100644
--- a/agent/run.sh
+++ b/agent/run.sh
@@ -19,6 +19,7 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
 : "${NODE_NUMBER?Need to set NODE_NUMBER}"
 : "${NODES_COUNT?Need to set NODES_COUNT}"
 
+: "${RPC_PORT?Need to set RPC_PORT}"
 : "${MONITORING_PORT?Need to set MONITORING_PORT}"
 
 : "${TM_URL_MAIN_NET?Need to set TM_URL_MAIN_NET}"
@@ -70,6 +71,7 @@ echo "NODES_COUNT: $NODES_COUNT"
 BASE_OPTIONS="--gas-price-multiplier=$GAS_PRICE_MULTIPLIER \
 --gas-multiplier=$GAS_MULTIPLIER \
 --verbose=$VERBOSE \
+--json-rpc-port=$RPC_PORT \
 --s2s-enable \
 --abi-skale-manager=$MANAGER_ABI_PATH \
 --url-main-net=$MAINNET_RPC_URL \
@@ -80,10 +82,10 @@ BASE_OPTIONS="--gas-price-multiplier=$GAS_PRICE_MULTIPLIER \
 --cid-s-chain=$CID_SCHAIN \
 --abi-main-net=$MAINNET_PROXY_PATH \
 --abi-s-chain=$SCHAIN_PROXY_PATH \
---sgx-url-s-chain=$SGX_URL \
---sgx-ecdsa-key-s-chain=$ECDSA_KEY_NAME \
---sgx-ssl-key-s-chain=$SGX_SSL_KEY_PATH \
---sgx-ssl-cert-s-chain=$SGX_SSL_CERT_PATH \
+--sgx-url=$SGX_URL \
+--sgx-ecdsa-key=$ECDSA_KEY_NAME \
+--sgx-ssl-key=$SGX_SSL_KEY_PATH \
+--sgx-ssl-cert=$SGX_SSL_CERT_PATH \
 --address-main-net=$NODE_ADDRESS \
 --address-s-chain=$NODE_ADDRESS \
 --sign-messages \
diff --git a/agent/state.mjs b/agent/state.mjs
index f6ddcd263..01467adb7 100644
--- a/agent/state.mjs
+++ b/agent/state.mjs
@@ -205,7 +205,7 @@ export function get() {
         "nTimeFrameSeconds": 0, // 0-disable, 60-recommended
         "nNextFrameGap": 10,
 
-        "nAutoExitAfterSeconds": 3600, // 0-disable
+        "nAutoExitAfterSeconds": 0, // 0-disable
 
         "joCommunityPool": null, // only main net
         "joDepositBoxETH": null, // only main net
@@ -256,6 +256,7 @@ export function get() {
         "nTimeoutSecondsPWA": 60,
 
         "nMonitoringPort": 0, // 0 - default, means monitoring server is disabled
+        "bLogMonitoringServer": false,
 
         "strReimbursementChain": "",
         "isShowReimbursementBalance": false,


Test patch:
diff --git a/test/agent-test.mjs b/test/agent-test.mjs
index 4b00d00c6..25c983f2e 100644
--- a/test/agent-test.mjs
+++ b/test/agent-test.mjs
@@ -70,7 +70,7 @@ const imaState = {
     "nLogMaxSizeBeforeRotation": -1,
     "nLogMaxFilesCount": -1,
     "isPrintGathered": true,
-    "isPrintSecurityValues": true,
+    "isPrintSecurityValues": false,
     "isPrintPWA": false,
     "isDynamicLogInDoTransfer": true,
     "isDynamicLogInBlsSigner": false,