11"use strict" ;
2- const { types : utilTypes } = require ( "util" ) ;
32const { arr2hex, calcErgReward, calcEthReward } = require ( "../helpers.js" ) ;
43const COIN_PROFILE_SYMBOL = Symbol . for ( "nodejs-pool.coinProfile" ) ;
54const XTM_T_MINING_HASH_OFFSET = 3 ;
@@ -12,7 +11,6 @@ const XTM_T_POW_DATA_OFFSET = XTM_T_POW_ALGO_OFFSET + 1;
1211const XTM_T_POW_DATA_SIZE = 32 ;
1312const XTM_T_RANDOMXT_POW_ALGO = 2 ;
1413const XTM_T_POOL_RESERVED_OFFSET = XTM_T_POW_DATA_OFFSET ;
15- const ORIGINAL_BUFFER_READ_UINT32_BE = Buffer . prototype . readUInt32BE ;
1614
1715function cloneValue ( value ) {
1816 if ( Array . isArray ( value ) ) return value . slice ( ) ;
@@ -812,163 +810,26 @@ const pow = {
812810 c29s : createCyclePowFactory ( "c29s" , "c29s_packed_edges" )
813811} ;
814812
815- function errorMessage ( error ) {
816- return error && error . message ? error . message : String ( error ) ;
817- }
818-
819- function safeInspect ( value ) {
820- if ( typeof value === "function" ) return "[Function " + ( value . name || "anonymous" ) + "]" ;
821- if ( typeof value === "bigint" ) return value . toString ( ) + "n" ;
822- if ( typeof value === "undefined" ) return "undefined" ;
823- try {
824- return JSON . stringify ( value ) ;
825- } catch ( _error ) {
826- try {
827- return String ( value ) ;
828- } catch ( _error2 ) {
829- return "[uninspectable]" ;
830- }
831- }
832- }
833-
834- function safeGet ( value , key ) {
835- try {
836- return value ? value [ key ] : undefined ;
837- } catch ( error ) {
838- return "[threw " + errorMessage ( error ) + "]" ;
839- }
840- }
841-
842- function ctorName ( value ) {
843- const ctor = safeGet ( value , "constructor" ) ;
844- return ctor && ctor . name ? ctor . name : typeof value ;
845- }
846-
847- function protoCtorName ( value ) {
848- try {
849- const proto = Object . getPrototypeOf ( value ) ;
850- return proto && proto . constructor && proto . constructor . name ? proto . constructor . name : safeInspect ( proto ) ;
851- } catch ( error ) {
852- return "[threw " + errorMessage ( error ) + "]" ;
853- }
854- }
855-
856- function describeIndexedValues ( value , offset ) {
857- const items = [ ] ;
858- for ( let index = offset ; index < offset + 8 ; index ++ ) {
859- const byte = safeGet ( value , index ) ;
860- items . push ( index + ":" + typeof byte + ":" + safeInspect ( byte ) + ":integer=" + Number . isInteger ( byte ) ) ;
861- }
862- return items . join ( "," ) ;
863- }
864-
865- function describeBufferBacking ( value , offset ) {
866- if ( ! Buffer . isBuffer ( value ) ) return "" ;
867- try {
868- const backing = Buffer . from ( value . buffer , value . byteOffset , value . length ) ;
869- const view = new DataView ( backing . buffer , backing . byteOffset , backing . byteLength ) ;
870- return " backingDataViewHi=" + view . getUint32 ( offset , false ) +
871- " backingDataViewLo=" + view . getUint32 ( offset + 4 , false ) +
872- " backingBytes=" + Array . from ( backing . subarray ( offset , offset + 8 ) ) . join ( "," ) +
873- " backingHex=" + backing . toString ( "hex" ) ;
874- } catch ( error ) {
875- return " backing=[threw " + errorMessage ( error ) + "]" ;
876- }
877- }
878-
879- function readUInt32IndexedBE ( value , offset ) {
880- return value [ offset ] * 0x1000000 +
881- value [ offset + 1 ] * 0x10000 +
882- value [ offset + 2 ] * 0x100 +
883- value [ offset + 3 ] ;
884- }
885-
886- function describeLegacyNonceReadMethods ( value , offset ) {
887- let indexedHi = "[unread]" ;
888- let indexedLo = "[unread]" ;
889- try {
890- indexedHi = readUInt32IndexedBE ( value , offset ) ;
891- indexedLo = readUInt32IndexedBE ( value , offset + 4 ) ;
892- } catch ( error ) {
893- indexedHi = indexedLo = "[threw " + errorMessage ( error ) + "]" ;
894- }
895- return " methods={" +
896- "readUInt32BEIsOriginal:" + ( safeGet ( value , "readUInt32BE" ) === ORIGINAL_BUFFER_READ_UINT32_BE ) +
897- ",bufferProtoReadUInt32BEIsOriginal:" + ( Buffer . prototype . readUInt32BE === ORIGINAL_BUFFER_READ_UINT32_BE ) +
898- ",readUInt32BEName:" + safeInspect ( safeGet ( safeGet ( value , "readUInt32BE" ) , "name" ) ) +
899- ",indexedCalcHi:" + indexedHi +
900- ",indexedCalcLo:" + indexedLo +
901- "}" ;
902- }
903-
904- function describeLegacyNonceRead ( value , offset ) {
905- return "receiver={" +
906- "isBuffer:" + Buffer . isBuffer ( value ) +
907- ",isProxy:" + utilTypes . isProxy ( value ) +
908- ",instanceofBuffer:" + ( value instanceof Buffer ) +
909- ",ctor:" + ctorName ( value ) +
910- ",protoCtor:" + protoCtorName ( value ) +
911- ",length:" + safeInspect ( safeGet ( value , "length" ) ) +
912- ",byteOffset:" + safeInspect ( safeGet ( value , "byteOffset" ) ) +
913- ",byteLength:" + safeInspect ( safeGet ( value , "byteLength" ) ) +
914- ",readUInt32BEOwn:" + Object . prototype . hasOwnProperty . call ( value || { } , "readUInt32BE" ) +
915- ",readUInt32BEIsBufferProto:" + ( safeGet ( value , "readUInt32BE" ) === Buffer . prototype . readUInt32BE ) +
916- "} indexed=[" + describeIndexedValues ( value , offset ) + "]" +
917- describeLegacyNonceReadMethods ( value , offset ) +
918- describeBufferBacking ( value , offset ) ;
919- }
920-
921- function reportLegacyNonceReadDiagnostic ( prefix , report ) {
922- console . warn ( report ) ;
923- try {
924- if ( ! global . support || typeof global . support . sendAdminFyi !== "function" ) return ;
925- global . support . sendAdminFyi (
926- "coins:xtm-legacy-nonce-read:" + prefix ,
927- "FYI: XTM legacy nonce read mismatch" ,
928- "The pool server: " + ( global . config && global . config . hostname ? global . config . hostname : "unknown" ) +
929- " saw an XTM legacy nonce Buffer/DataView mismatch.\n" + report ,
930- { cooldownMs : 60 * 60 * 1000 }
931- ) ;
932- } catch ( _error ) {
933- // Diagnostics must never block a valid block submit.
934- }
935- }
936-
937- function readUInt64BufferBE ( buf , offset = 0 , label ) {
938- const prefix = "XTM legacy nonce read" + ( label ? " " + label : "" ) ;
939- if ( ! Buffer . isBuffer ( buf ) ) {
940- throw new TypeError ( prefix + " expected Buffer; " + describeLegacyNonceRead ( buf , offset ) ) ;
941- }
942- let hiNumber ;
943- let loNumber ;
944- let hi ;
945- let lo ;
946- try {
947- const view = new DataView ( buf . buffer , buf . byteOffset , buf . byteLength ) ;
948- hiNumber = view . getUint32 ( offset , false ) ;
949- loNumber = view . getUint32 ( offset + 4 , false ) ;
950- hi = BigInt ( hiNumber ) ;
951- lo = BigInt ( loNumber ) ;
952- } catch ( error ) {
953- throw new RangeError ( prefix + " failed: " + errorMessage ( error ) + "; " + describeLegacyNonceRead ( buf , offset ) ) ;
954- }
955- try {
956- const legacyHi1 = buf . readUInt32BE ( offset ) ;
957- const legacyLo1 = buf . readUInt32BE ( offset + 4 ) ;
958- const legacyHi2 = buf . readUInt32BE ( offset ) ;
959- const legacyLo2 = buf . readUInt32BE ( offset + 4 ) ;
960- if ( legacyHi1 !== hiNumber || legacyLo1 !== loNumber ||
961- legacyHi2 !== hiNumber || legacyLo2 !== loNumber ||
962- ! Number . isInteger ( legacyHi1 ) || ! Number . isInteger ( legacyLo1 ) ||
963- ! Number . isInteger ( legacyHi2 ) || ! Number . isInteger ( legacyLo2 ) ) {
964- reportLegacyNonceReadDiagnostic ( prefix , prefix + " mismatch" +
965- " legacyHi1=" + legacyHi1 + " legacyLo1=" + legacyLo1 +
966- " legacyHi2=" + legacyHi2 + " legacyLo2=" + legacyLo2 +
967- " dataViewHi=" + hiNumber + " dataViewLo=" + loNumber + "; " + describeLegacyNonceRead ( buf , offset ) ) ;
968- }
969- } catch ( error ) {
970- reportLegacyNonceReadDiagnostic ( prefix , prefix + " legacy check failed: " + errorMessage ( error ) + "; " + describeLegacyNonceRead ( buf , offset ) ) ;
971- }
813+ function readUInt64BufferBE ( buf , offset = 0 ) {
814+ if ( ! Buffer . isBuffer ( buf ) ) throw new TypeError ( "XTM nonce read expected Buffer" ) ;
815+
816+ /*
817+ * Do not use Buffer.readUInt32BE here.
818+ *
819+ * Production XTM-T submits on Node v24/V8 13.6 have twice observed
820+ * Buffer.readUInt32BE returning a fractional uint32 value such as
821+ * 2200978431.9999986 or 3447195903.9999986 while the same Buffer's
822+ * indexed bytes and DataView backing-store reads were the correct
823+ * integers. That made a valid block fail before it reached the daemon.
824+ *
825+ * DataView#getUint32 reads the Buffer's ArrayBuffer backing store
826+ * directly and matched the actual bytes in those incidents, so this
827+ * block-critical nonce conversion intentionally avoids Buffer's uint32
828+ * helper.
829+ */
830+ const view = new DataView ( buf . buffer , buf . byteOffset , buf . byteLength ) ;
831+ const hi = BigInt ( view . getUint32 ( offset , false ) ) ;
832+ const lo = BigInt ( view . getUint32 ( offset + 4 , false ) ) ;
972833 return ( ( hi << 32n ) | lo ) . toString ( 10 ) ;
973834}
974835
@@ -1412,15 +1273,15 @@ function submitXtmRxBlock(ctx) {
14121273 const xtmBlock = cloneRpcTemplate ( ctx . blockTemplate . xtm_block ) ;
14131274 const powData = ctx . blockData . slice ( XTM_T_POW_DATA_OFFSET ) ;
14141275
1415- xtmBlock . header . nonce = readUInt64BufferBE ( ctx . blockData , XTM_T_NONCE_OFFSET , "XTM-T" ) ;
1276+ xtmBlock . header . nonce = readUInt64BufferBE ( ctx . blockData , XTM_T_NONCE_OFFSET ) ;
14161277 xtmBlock . header . pow . pow_data = powData . every ( ( byte ) => byte === 0 ) ? [ ] : [ ...powData ] ;
14171278 ctx . support . rpcPortDaemon ( ctx . blockTemplate . port , "SubmitBlock" , xtmBlock , ctx . replyFn , true ) ;
14181279}
14191280
14201281function submitXtmCBlock ( ctx ) {
14211282 const xtmBlock = cloneRpcTemplate ( ctx . blockTemplate . xtm_block ) ;
14221283
1423- xtmBlock . header . nonce = readUInt64BufferBE ( ctx . blockData , 0 , "XTM-C" ) ;
1284+ xtmBlock . header . nonce = readUInt64BufferBE ( ctx . blockData , 0 ) ;
14241285 xtmBlock . header . pow . pow_data = ctx . job . c29_packed_edges ;
14251286 ctx . support . rpcPortDaemon ( ctx . blockTemplate . port , "SubmitBlock" , xtmBlock , ctx . replyFn , true ) ;
14261287}
0 commit comments