@tradelayerprotocol/tradelayer 1.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +13 -0
- package/.claude/skills/tl-algo/SKILL.md +255 -0
- package/.gitattributes +2 -0
- package/.github/workflows/publish.yaml +26 -0
- package/4mm.js +163 -0
- package/LICENSE +21 -0
- package/NPMSwapRefactor.zip +0 -0
- package/README.md +217 -0
- package/address.sh +26 -0
- package/algoAPI.js +581 -0
- package/analyzepsbt.js +92 -0
- package/apiEx.js +99 -0
- package/bb_hyperscalper.js +290 -0
- package/bbo_demo.js +111 -0
- package/buyer.js +622 -0
- package/client.js +50 -0
- package/createTxTest.js +26 -0
- package/createWallet.js +75 -0
- package/daytrader.js +531 -0
- package/decodeTest.js +69 -0
- package/fundingManager.js +144 -0
- package/index.js +4 -0
- package/listener.js +27 -0
- package/litecoreTxBuilder.js +1128 -0
- package/mmEx.js +356 -0
- package/networks.js +51 -0
- package/orderbook.js +200 -0
- package/package.json +34 -0
- package/perTradeQueue.js +36 -0
- package/projectsTLNPMTLNPM/package-lock.json +162 -0
- package/projectsTLNPMTLNPM/package.json +5 -0
- package/quick.js +32 -0
- package/quickFut.js +37 -0
- package/quickSell.js +37 -0
- package/relayerClient.js +117 -0
- package/run4mm.js +80 -0
- package/run_bbo_tracker.js +241 -0
- package/seller.js +443 -0
- package/session.js +45 -0
- package/setup-lin-ltc.sh +139 -0
- package/setup-lin.sh +203 -0
- package/setup-win-ltc.bat +108 -0
- package/setup-win.bat +167 -0
- package/spam_screamer_futures.js +222 -0
- package/tradelayer.js/.gitattributes +2 -0
- package/tradelayer.js/README.md +2 -0
- package/tradelayer.js/oldTests/activationTest.js +6 -0
- package/tradelayer.js/oldTests/base58.test.js +23 -0
- package/tradelayer.js/oldTests/base64Decode.test.js +16 -0
- package/tradelayer.js/oldTests/blocksRefactor.js +140 -0
- package/tradelayer.js/oldTests/checkVestBalance.js +25 -0
- package/tradelayer.js/oldTests/consensusHashProto.js +151 -0
- package/tradelayer.js/oldTests/contractOrderbook.js +243 -0
- package/tradelayer.js/oldTests/createPayload.js +0 -0
- package/tradelayer.js/oldTests/createTestnetAddr.js +43 -0
- package/tradelayer.js/oldTests/decode.js +205 -0
- package/tradelayer.js/oldTests/decodeTest.js +50 -0
- package/tradelayer.js/oldTests/displayTallyMap.js +19 -0
- package/tradelayer.js/oldTests/encodeDecode.js +340 -0
- package/tradelayer.js/oldTests/expressTest.js +29 -0
- package/tradelayer.js/oldTests/extractBlocksVanilla.js +214 -0
- package/tradelayer.js/oldTests/extractBlocksVanillaa.js +179 -0
- package/tradelayer.js/oldTests/extractPubkeyTest.js +60 -0
- package/tradelayer.js/oldTests/fillInputCacheProto.js +111 -0
- package/tradelayer.js/oldTests/getRawTxTest.js +22 -0
- package/tradelayer.js/oldTests/indexTest.js +26 -0
- package/tradelayer.js/oldTests/initTokensTest.js +32 -0
- package/tradelayer.js/oldTests/interfaceChild.js +129 -0
- package/tradelayer.js/oldTests/listenerChild.js +112 -0
- package/tradelayer.js/oldTests/opdecode.js +26 -0
- package/tradelayer.js/oldTests/options.js +79 -0
- package/tradelayer.js/oldTests/optxtest.js +116 -0
- package/tradelayer.js/oldTests/optxtest1.js +64 -0
- package/tradelayer.js/oldTests/oracle.test.js +32 -0
- package/tradelayer.js/oldTests/orderbook.test.js +36 -0
- package/tradelayer.js/oldTests/parsing.js +93 -0
- package/tradelayer.js/oldTests/payload.js +13 -0
- package/tradelayer.js/oldTests/persistenceUnitTest.js +23 -0
- package/tradelayer.js/oldTests/property.test.js +53 -0
- package/tradelayer.js/oldTests/propertyLevel.js +75 -0
- package/tradelayer.js/oldTests/propertyTest.js +32 -0
- package/tradelayer.js/oldTests/queryAddressTest.js +17 -0
- package/tradelayer.js/oldTests/salter.js +14 -0
- package/tradelayer.js/oldTests/tally.js +81 -0
- package/tradelayer.js/oldTests/tally.test.js +48 -0
- package/tradelayer.js/oldTests/tally2.js +124 -0
- package/tradelayer.js/oldTests/tally3.js +142 -0
- package/tradelayer.js/oldTests/tallyDiag.js +38 -0
- package/tradelayer.js/oldTests/testGetRaw.js +40 -0
- package/tradelayer.js/oldTests/testHexConvert.js +47 -0
- package/tradelayer.js/oldTests/testNewEncoding.js +96 -0
- package/tradelayer.js/oldTests/testNewEncoding2.js +113 -0
- package/tradelayer.js/oldTests/testNewEncoding3 +112 -0
- package/tradelayer.js/oldTests/testNewEncoding3.js +168 -0
- package/tradelayer.js/oldTests/testOPReturn.js +102 -0
- package/tradelayer.js/oldTests/testPayload.js +23 -0
- package/tradelayer.js/oldTests/testRaw.js +50 -0
- package/tradelayer.js/oldTests/testSendTooMuch.js +20 -0
- package/tradelayer.js/oldTests/testTxBuild +28 -0
- package/tradelayer.js/oldTests/testTxBuild.js +42 -0
- package/tradelayer.js/oldTests/tokenOrderbook.js +243 -0
- package/tradelayer.js/oldTests/txUtilsA.js +515 -0
- package/tradelayer.js/oldTests/validityUnitTest.js +53 -0
- package/tradelayer.js/oldTests/vaults.js +72 -0
- package/tradelayer.js/oldTests/volumeIndex.js +117 -0
- package/tradelayer.js/oldTests/volumeIndex2.js +88 -0
- package/tradelayer.js/output_base64.txt +1 -0
- package/tradelayer.js/package-lock.json +9967 -0
- package/tradelayer.js/package.json +61 -0
- package/tradelayer.js/server/index.js +88 -0
- package/tradelayer.js/server/litecoind.exe +0 -0
- package/tradelayer.js/src/activation.js +303 -0
- package/tradelayer.js/src/adjuster.js +77 -0
- package/tradelayer.js/src/amm.js +400 -0
- package/tradelayer.js/src/base256.js +55 -0
- package/tradelayer.js/src/base94.js +79 -0
- package/tradelayer.js/src/channels.js +1163 -0
- package/tradelayer.js/src/clearing.js +3109 -0
- package/tradelayer.js/src/clearlist.js +364 -0
- package/tradelayer.js/src/client.js +295 -0
- package/tradelayer.js/src/consensus.js +613 -0
- package/tradelayer.js/src/contractRegistry.js +964 -0
- package/tradelayer.js/src/db.js +89 -0
- package/tradelayer.js/src/init.js +24 -0
- package/tradelayer.js/src/insurance.js +347 -0
- package/tradelayer.js/src/interface.js +218 -0
- package/tradelayer.js/src/interfaceExpress.js +178 -0
- package/tradelayer.js/src/iou.js +509 -0
- package/tradelayer.js/src/listener.js +226 -0
- package/tradelayer.js/src/logic.js +1702 -0
- package/tradelayer.js/src/main.js +927 -0
- package/tradelayer.js/src/marginMap.js +2165 -0
- package/tradelayer.js/src/options.js +126 -0
- package/tradelayer.js/src/oracle.js +394 -0
- package/tradelayer.js/src/orderbook.js +4123 -0
- package/tradelayer.js/src/persistence.js +554 -0
- package/tradelayer.js/src/property.js +411 -0
- package/tradelayer.js/src/reOrg.js +41 -0
- package/tradelayer.js/src/scaling.js +145 -0
- package/tradelayer.js/src/tally.js +1275 -0
- package/tradelayer.js/src/tradeHistoryManager.js +552 -0
- package/tradelayer.js/src/txDecoder.js +584 -0
- package/tradelayer.js/src/txEncoder.js +610 -0
- package/tradelayer.js/src/txIndex.js +502 -0
- package/tradelayer.js/src/txUtils.js +1392 -0
- package/tradelayer.js/src/types.js +429 -0
- package/tradelayer.js/src/validity.js +3077 -0
- package/tradelayer.js/src/vaults.js +430 -0
- package/tradelayer.js/src/vesting.js +491 -0
- package/tradelayer.js/src/volumeIndex.js +618 -0
- package/tradelayer.js/src/walletInterface.js +220 -0
- package/tradelayer.js/src/walletListener.js +665 -0
- package/tradelayer.js/tests/256decode.js +82 -0
- package/tradelayer.js/tests/UTXOracle.js +205 -0
- package/tradelayer.js/tests/base94test.js +23 -0
- package/tradelayer.js/tests/cancelTxTest.js +62 -0
- package/tradelayer.js/tests/contractInterfaceTest.js +48 -0
- package/tradelayer.js/tests/decimalTest.js +65 -0
- package/tradelayer.js/tests/decoderTest.js +100 -0
- package/tradelayer.js/tests/deltaCount.js +47 -0
- package/tradelayer.js/tests/deltaCount2.js +60 -0
- package/tradelayer.js/tests/interfaceTest.js +37 -0
- package/tradelayer.js/tests/mainTest.js +53 -0
- package/tradelayer.js/tests/makeActivationTest.js +24 -0
- package/tradelayer.js/tests/maxHeightTest.js +49 -0
- package/tradelayer.js/tests/reverseHash.js +72 -0
- package/tradelayer.js/tests/sensitiveConsoleOutput.txt +267 -0
- package/tradelayer.js/tests/tallyTest.js +40 -0
- package/tradelayer.js/tests/testBuybacks.js +46 -0
- package/tradelayer.js/tests/testCodeHash.js +49 -0
- package/tradelayer.js/tests/testConsensusHash.js +91 -0
- package/tradelayer.js/tests/testDecode.js +30 -0
- package/tradelayer.js/tests/testEncodingLengths.js +129 -0
- package/tradelayer.js/tests/testGetTx +32 -0
- package/tradelayer.js/tests/testGetTx.js +32 -0
- package/tradelayer.js/tests/testHexHash.js +32 -0
- package/tradelayer.js/tests/testIndexHash.js +35 -0
- package/tradelayer.js/tests/testInitContracts.js +38 -0
- package/tradelayer.js/tests/testMaxConsensus.js +12 -0
- package/tradelayer.js/tests/testMaxSynth.js +44 -0
- package/tradelayer.js/tests/testMint.js +21 -0
- package/tradelayer.js/tests/testNetwork.js +33 -0
- package/tradelayer.js/tests/testOrderbookLoad.js +62 -0
- package/tradelayer.js/tests/testRebates.js +32 -0
- package/tradelayer.js/tests/testRedeem.js +22 -0
- package/tradelayer.js/tests/testTokenTrade.js +39 -0
- package/tradelayer.js/tests/testTxBuild.js +42 -0
- package/tradelayer.js/tests/testUTXOTrade.js +27 -0
- package/tradelayer.js/tests/tokenTradeHistory.js +27 -0
- package/tradelayer.js/tests/tradeFutures.js +40 -0
- package/tradelayer.js/tests/tradeHistoryExample.js +35 -0
- package/tradelayer.js/tests/tradeHistoryLoad.js +15 -0
- package/tradelayer.js/tests/txScanTest.js +134 -0
- package/tradelayer.js/tests/validateTest.js +136 -0
- package/tradelayer.js/tests/vestingTest.js +37 -0
- package/tradelayer.js/utils/activateMainnet.js +59 -0
- package/tradelayer.js/utils/activateMainnetDoge.js +63 -0
- package/tradelayer.js/utils/autocompactdb.js +23 -0
- package/tradelayer.js/utils/base64toHex.js +32 -0
- package/tradelayer.js/utils/broadcastDoge.js +38 -0
- package/tradelayer.js/utils/calcRedeem.js +19 -0
- package/tradelayer.js/utils/checkNetwork.js +27 -0
- package/tradelayer.js/utils/createAddress.js +48 -0
- package/tradelayer.js/utils/createAttestation.js +133 -0
- package/tradelayer.js/utils/createContract.js +118 -0
- package/tradelayer.js/utils/createOracle.js +94 -0
- package/tradelayer.js/utils/createwallet.js +20 -0
- package/tradelayer.js/utils/crossFuturesTrades.js +57 -0
- package/tradelayer.js/utils/crossTokenTrades.js +62 -0
- package/tradelayer.js/utils/dumpPriv.js +29 -0
- package/tradelayer.js/utils/generateChannel.js +34 -0
- package/tradelayer.js/utils/getInfo.js +21 -0
- package/tradelayer.js/utils/hardWipe.js +20 -0
- package/tradelayer.js/utils/hexTo64.js +16 -0
- package/tradelayer.js/utils/importAddress.js +28 -0
- package/tradelayer.js/utils/importpriv.js +20 -0
- package/tradelayer.js/utils/issueOracleContract.js +67 -0
- package/tradelayer.js/utils/issueTokens.js +41 -0
- package/tradelayer.js/utils/listunspent.js +66 -0
- package/tradelayer.js/utils/litecoinClient.js +30 -0
- package/tradelayer.js/utils/loadwallet.js +20 -0
- package/tradelayer.js/utils/publishOracle.js +113 -0
- package/tradelayer.js/utils/sendActivation.js +21 -0
- package/tradelayer.js/utils/sendChannelContractTrade.js +34 -0
- package/tradelayer.js/utils/sendChannelTokenTrade.js +34 -0
- package/tradelayer.js/utils/sendCommit.js +24 -0
- package/tradelayer.js/utils/sendDoge.js +62 -0
- package/tradelayer.js/utils/sendDogeMain.js +67 -0
- package/tradelayer.js/utils/sendDogeTx.js +46 -0
- package/tradelayer.js/utils/sendLTC.js +63 -0
- package/tradelayer.js/utils/sendMainnet.js +62 -0
- package/tradelayer.js/utils/sendTransfer.js +19 -0
- package/tradelayer.js/utils/sendVestTest.js +88 -0
- package/tradelayer.js/utils/sendWithdrawal.js +26 -0
- package/tradelayer.js/utils/simpleStart.js +8 -0
- package/tradelayer.js/utils/startStop.js +27 -0
- package/tradelayer.js/utils/structuredTrades.js +136 -0
- package/tradelayer.js/utils/verifySignature.js +90 -0
- package/tradelayer.js/utils/verifyWitnessAndScriptPubkey.js +41 -0
- package/tradelayer.js/utils/walletCache.js +172 -0
- package/tradelayer.js/utils/walletContractInterface.js +48 -0
- package/tradelayer.js/utils/walletFetchTxs.js +66 -0
- package/tradelayer.js/utils/walletUtils.js +97 -0
- package/tradelayer.js/utils/wipeDB.js +55 -0
- package/tradelayer.js/utils/wipeDBNotTx.js +50 -0
- package/txEncoder.js +529 -0
- package/utility.js +28 -0
- package/verifymessage.js +38 -0
- package/ws-transport.js +311 -0
|
@@ -0,0 +1,927 @@
|
|
|
1
|
+
// Define a global shutdown event
|
|
2
|
+
const EventEmitter = require('events');
|
|
3
|
+
class ShutdownEmitter extends EventEmitter {}
|
|
4
|
+
const shutdownEmitter = new ShutdownEmitter();
|
|
5
|
+
//const fetch = require('node-fetch'); // For HTTP requests (e.g., price lookups)
|
|
6
|
+
const util = require('util')
|
|
7
|
+
const ReOrgChecker = require('./reOrg.js');
|
|
8
|
+
// main.js
|
|
9
|
+
const initialize = require('./init');
|
|
10
|
+
let client
|
|
11
|
+
let db
|
|
12
|
+
(async () => {
|
|
13
|
+
try {
|
|
14
|
+
console.log('initializing from the top')
|
|
15
|
+
const { Client, Db } = await initialize();
|
|
16
|
+
client = Client
|
|
17
|
+
db = Db
|
|
18
|
+
console.log('Client and Database initialized successfully.');
|
|
19
|
+
|
|
20
|
+
// Now proceed with the rest of your app setup
|
|
21
|
+
/*const Main = require('./main-logic'); // Adjust based on your main logic setup
|
|
22
|
+
const app = new Main(client, db); // Pass the initialized instances if needed
|
|
23
|
+
app.start(); // Start your app logic*/
|
|
24
|
+
|
|
25
|
+
} catch (error) {
|
|
26
|
+
console.error('Failed to initialize client or database.', error);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
})();
|
|
30
|
+
|
|
31
|
+
const fs = require('fs'); // File system module
|
|
32
|
+
|
|
33
|
+
const Validity = require('./validity.js'); // Module for checking transaction validity
|
|
34
|
+
const TxUtils = require('./txUtils.js'); // Utility functions for transactions
|
|
35
|
+
const TxIndex = require('./txIndex.js') // Indexes TradeLayer transactions
|
|
36
|
+
const TradeChannel = require('./channels.js'); // Manages Trade Channels
|
|
37
|
+
const TallyMap = require('./tally.js'); // Manages Tally Mapping
|
|
38
|
+
const MarginMap = require('./marginMap.js'); // Manages Margin Mapping
|
|
39
|
+
const Clearing = require('./clearing.js')
|
|
40
|
+
const Channels = require('./channels.js')
|
|
41
|
+
const PropertyManager = require('./property.js'); // Manages properties
|
|
42
|
+
const ContractsRegistry = require('./contractRegistry.js'); // Registry for contracts
|
|
43
|
+
const VolumeIndex = require('./volumeIndex.js')
|
|
44
|
+
const TradeLayerManager = require('./vesting.js')
|
|
45
|
+
const Consensus = require('./consensus.js'); // Functions for handling consensus
|
|
46
|
+
const Oracles = require('./oracle.js')
|
|
47
|
+
const Activation = require('./activation.js')
|
|
48
|
+
const Orderbook = require('./orderbook.js')
|
|
49
|
+
const Persistence = require('./persistence.js');
|
|
50
|
+
let activation
|
|
51
|
+
|
|
52
|
+
(async () => {
|
|
53
|
+
activation = Activation.getInstance()
|
|
54
|
+
await activation.init();
|
|
55
|
+
console.log(`App initialized with Chain: ${activation.chain}, Testnet: ${activation.test}, Admin Address: ${activation.adminAddress}`);
|
|
56
|
+
|
|
57
|
+
// Continue with the rest of your application setup
|
|
58
|
+
// Initialize other components or start the server, etc.
|
|
59
|
+
})();
|
|
60
|
+
|
|
61
|
+
const Encode = require('./txEncoder.js'); // Encodes transactions
|
|
62
|
+
const Types = require('./types.js'); // Defines different types used in the system
|
|
63
|
+
const Logic = require('./logic.js')
|
|
64
|
+
const AMM = require('./amm.js')
|
|
65
|
+
const Decode = require('./txDecoder.js'); // Decodes transactionsconst db = require('./db.js'); // Adjust the path if necessary
|
|
66
|
+
const genesisBlock = 3082500
|
|
67
|
+
const COIN = 100000000
|
|
68
|
+
const pause = false
|
|
69
|
+
|
|
70
|
+
const GENESIS_BLOCK_HEIGHTS = {
|
|
71
|
+
BTC: {
|
|
72
|
+
test: 3520000, // Replace with actual testnet genesis block height
|
|
73
|
+
main: 880000, // Replace with actual mainnet genesis block height
|
|
74
|
+
},
|
|
75
|
+
DOGE: {
|
|
76
|
+
test: 6815000, // Replace with actual testnet genesis block height
|
|
77
|
+
main: 5520000, // Replace with actual mainnet genesis block height
|
|
78
|
+
},
|
|
79
|
+
LTC: {
|
|
80
|
+
test: 3082500, // Replace with actual testnet genesis block height
|
|
81
|
+
main: 2819000, // Replace with actual mainnet genesis block height
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
class Main {
|
|
86
|
+
static instance;
|
|
87
|
+
static isInitializing = false; // Add a flag to track initialization
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
constructor() {
|
|
91
|
+
console.log('inside main constructor '+Boolean(Main.instance))
|
|
92
|
+
if (Main.instance) {
|
|
93
|
+
console.log('main already initialized')
|
|
94
|
+
return Main.instance;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
this.client=client// Use the already initialized clientInstance // Initialize the client with the specified chain
|
|
98
|
+
|
|
99
|
+
console.log('client in main ' +this.client)
|
|
100
|
+
//this.tradeLayerManager = new TradeLayerManager();
|
|
101
|
+
this.txIndex = TxIndex.getInstance();
|
|
102
|
+
this.getBlockCountAsync = () => this.client.getBlockCount();
|
|
103
|
+
this.getNetworkInfoAsync = () => this.client.getNetworkInfo();
|
|
104
|
+
this.genesisBlock = 600000
|
|
105
|
+
this.parseBlock = 0
|
|
106
|
+
console.log(this.genesisBlock)
|
|
107
|
+
Main.instance = this;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
static async getInstance() {
|
|
111
|
+
if (!Main.instance && !Main.isInitializing) {
|
|
112
|
+
Main.isInitializing = true;
|
|
113
|
+
console.log('Initializing Main instance...');
|
|
114
|
+
|
|
115
|
+
// 1. Construct Main first
|
|
116
|
+
Main.instance = new Main();
|
|
117
|
+
|
|
118
|
+
// 2. Detect network from the initialized client instance
|
|
119
|
+
const net = await Main.instance.client.getChain();
|
|
120
|
+
const test = await Main.instance.client.getTest();
|
|
121
|
+
console.log("[Main] Detected network:", net, "test:", test);
|
|
122
|
+
|
|
123
|
+
// 3. Initialize Persistence ONCE only via its own singleton
|
|
124
|
+
Main.instance.blockchainPersistence = await Persistence.getInstance({
|
|
125
|
+
network: net,
|
|
126
|
+
test: test,
|
|
127
|
+
snapshotInterval: 1000
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
Main.persistenceInitialized = true;
|
|
131
|
+
Main.isInitializing = false;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return Main.instance;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
async delay(ms) {
|
|
139
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async initialize() {
|
|
143
|
+
await this.delay(1500)
|
|
144
|
+
console.log('db status '+db)
|
|
145
|
+
if(!db&&this.client){
|
|
146
|
+
console.log('have client, awaiting db')
|
|
147
|
+
await db.init(this.client.chain)
|
|
148
|
+
await this.delay(300)
|
|
149
|
+
console.log('db status recheck '+db.initialized)
|
|
150
|
+
}else if(!db.initialized&&!this.client.chain){
|
|
151
|
+
console.log('no client no db, trying init again')
|
|
152
|
+
const { Client, Db } = await initialize();
|
|
153
|
+
await this.delay(300)
|
|
154
|
+
console.log('db+client status recheck '+db.initialized+this.client.chain)
|
|
155
|
+
}
|
|
156
|
+
const txIndex = await TxIndex.getInstance();
|
|
157
|
+
this.test = await this.client.getTest()
|
|
158
|
+
this.chain = await this.client.getChain()
|
|
159
|
+
console.log('this.test '+this.test+' '+this.chain)
|
|
160
|
+
console.log('block heights '+GENESIS_BLOCK_HEIGHTS)
|
|
161
|
+
if (this.test !== undefined && this.chain !== undefined) {
|
|
162
|
+
const networkType = this.test ? 'test' : 'main'; // Map boolean to 'test' or 'main'
|
|
163
|
+
const genesisConfig = GENESIS_BLOCK_HEIGHTS?.[this.chain];
|
|
164
|
+
|
|
165
|
+
if (genesisConfig && genesisConfig[networkType] !== undefined) {
|
|
166
|
+
this.genesisBlock = genesisConfig[networkType];
|
|
167
|
+
console.log(`Genesis Block for ${this.chain} (${networkType}): ${this.genesisBlock}`);
|
|
168
|
+
} else {
|
|
169
|
+
console.error(`Genesis block not configured for chain: ${this.chain} (${networkType})`);
|
|
170
|
+
}
|
|
171
|
+
} else {
|
|
172
|
+
console.error('Chain or test flag is undefined. Cannot set genesis block.');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
console.log('genesis block in tx index init '+this.genesisBlock)
|
|
176
|
+
try {
|
|
177
|
+
await txIndex.initializeOrLoadDB(this.genesisBlock);
|
|
178
|
+
// Proceed with further operations after successful initialization
|
|
179
|
+
} catch (error) {
|
|
180
|
+
console.log('boop '+error)
|
|
181
|
+
}
|
|
182
|
+
console.log('about to check for Index')
|
|
183
|
+
const indexExists = await TxIndex.checkForIndex();
|
|
184
|
+
console.log('indexExists' + indexExists);
|
|
185
|
+
if (!indexExists) {
|
|
186
|
+
console.log('building txIndex');
|
|
187
|
+
await this.initOrLoadTxIndex()
|
|
188
|
+
//await TxIndex.initializeIndex(this.genesisBlock);
|
|
189
|
+
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Construct consensus from index, or load from Persistence if available
|
|
193
|
+
console.log('constructing consensus state')
|
|
194
|
+
const consensus = await this.constructOrLoadConsensus();
|
|
195
|
+
|
|
196
|
+
// Start processing incoming blocks
|
|
197
|
+
//await this.processIncomingBlocks(consensus);
|
|
198
|
+
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async getCurrentBlockHeight() {
|
|
202
|
+
try {
|
|
203
|
+
const blockchainInfo = await this.getBlockCountAsync();
|
|
204
|
+
console.log(blockchainInfo)
|
|
205
|
+
return blockchainInfo;
|
|
206
|
+
} catch (error) {
|
|
207
|
+
console.error('Error fetching current block height:', error);
|
|
208
|
+
throw error; // or handle error as needed
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async initOrLoadTxIndex() {
|
|
213
|
+
// Check if the txIndex exists by trying to find the max indexed block
|
|
214
|
+
var maxIndexedBlock = await TxIndex.findMaxIndexedBlock();
|
|
215
|
+
console.log('max Indexed Block ' + JSON.stringify(maxIndexedBlock))
|
|
216
|
+
if (maxIndexedBlock === 0 || maxIndexedBlock === null) {
|
|
217
|
+
// Initialize the txIndex if it doesn't exist
|
|
218
|
+
console.log('about to init index with ' +this.genesisBlock)
|
|
219
|
+
await TxIndex.initializeIndex(this.genesisBlock);
|
|
220
|
+
maxIndexedBlock= this.genesisBlock
|
|
221
|
+
}
|
|
222
|
+
// Proceed to synchronize the index
|
|
223
|
+
await this.syncIndex(maxIndexedBlock);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
async syncIndex(maxIndexedBlock) {
|
|
227
|
+
console.log('sync Index maxIndexedBlock '+maxIndexedBlock)
|
|
228
|
+
try {
|
|
229
|
+
// Find the maximum indexed block in the database
|
|
230
|
+
if(maxIndexedBlock===null){this.initOrLoadTxIndex()}
|
|
231
|
+
// Fetch the current chain tip (latest block number) from the blockchain
|
|
232
|
+
const chainTip = await this.getBlockCountAsync()
|
|
233
|
+
console.log('sync index retrieved chaintip '+chainTip)
|
|
234
|
+
// If the chain tip is greater than the max indexed block, sync the index
|
|
235
|
+
if (chainTip > maxIndexedBlock && (maxIndexedBlock !=0 || maxIndexedBlock != {})){
|
|
236
|
+
// Loop through each block starting from maxIndexedBlock + 1 to chainTip
|
|
237
|
+
console.log('building tx index '+maxIndexedBlock)
|
|
238
|
+
return await TxIndex.extractBlockData(maxIndexedBlock)
|
|
239
|
+
} else if(maxIndexedBlock==0|| maxIndexedBlock == {}){
|
|
240
|
+
console.log('building txIndex from genesis')
|
|
241
|
+
return await TxIndex.extractBlockData(this.genesisBlock)
|
|
242
|
+
}else if(maxIndexedBlock==chainTip){
|
|
243
|
+
|
|
244
|
+
console.log("TxIndex is already up to date.");
|
|
245
|
+
return this.constructOrLoadConsensus(maxIndexedBlock)
|
|
246
|
+
}
|
|
247
|
+
} catch (error) {
|
|
248
|
+
console.error("Error during syncIndex:", error);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
async constructOrLoadConsensus() {
|
|
253
|
+
let consensusState;
|
|
254
|
+
|
|
255
|
+
try {
|
|
256
|
+
//const lastSavedHeight = await persistenceDB.get('lastSavedHeight');
|
|
257
|
+
const startHeight = /*lastSavedHeight ||*/ this.genesisBlock;
|
|
258
|
+
return this.constructConsensusFromIndex(startHeight, false);
|
|
259
|
+
} catch (error) {
|
|
260
|
+
if (error.type === 'NotFoundError') {
|
|
261
|
+
// If no saved state, start constructing consensus from genesis block
|
|
262
|
+
console.log("no consensus found")
|
|
263
|
+
return this.constructConsensusFromIndex(genesisBlockHeight, false);
|
|
264
|
+
} else {
|
|
265
|
+
console.error('Error loading consensus state:', error);
|
|
266
|
+
throw error;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/*
|
|
272
|
+
Most important function, has two modes, realtime==false means we're catching up and constructing consensus,
|
|
273
|
+
from the txIndex, from genesis until chaintip.
|
|
274
|
+
Real-time==true means we're looping in a delayed timer to check for new blocks and include any new ones in the
|
|
275
|
+
txIndex then apply them to this to update the db and consensus.
|
|
276
|
+
*/
|
|
277
|
+
async constructConsensusFromIndex(startHeight) {
|
|
278
|
+
let lastIndexBlock = await TxIndex.findMaxIndexedBlock();
|
|
279
|
+
let blockHeight;
|
|
280
|
+
let maxProcessedHeight = startHeight - 1;
|
|
281
|
+
|
|
282
|
+
const txIndexDB = await db.getDatabase('txIndex');
|
|
283
|
+
const tallyMapInstance = TallyMap.getInstance();
|
|
284
|
+
const lastConsensusHeight = await this.loadMaxProcessedHeight();
|
|
285
|
+
|
|
286
|
+
// Fetch all transaction data
|
|
287
|
+
const allTxData = await txIndexDB.findAsync({});
|
|
288
|
+
const txDataSet = allTxData.filter(txData => txData._id.startsWith('tx-'));
|
|
289
|
+
|
|
290
|
+
// Group transactions by block height
|
|
291
|
+
const txByBlockHeight = txDataSet.reduce((acc, txData) => {
|
|
292
|
+
const txBlockHeight = parseInt(txData._id.split('-')[1]);
|
|
293
|
+
if(!acc[txBlockHeight]){
|
|
294
|
+
acc[txBlockHeight] = {
|
|
295
|
+
fundingTx: [], // Bucket for funding transactions
|
|
296
|
+
tradeTx: [] // Bucket for regular trade transactions
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
//console.log('troubleshooting 1 '+txData.value)
|
|
300
|
+
// Determine if the transaction is a funding transaction (type starting with 4 or 20)
|
|
301
|
+
let counter1 = 0
|
|
302
|
+
let counter2 = 0
|
|
303
|
+
const valueData=txData.value
|
|
304
|
+
if (!valueData || !valueData.payload) return acc;
|
|
305
|
+
const payload = valueData.payload;
|
|
306
|
+
const type = parseInt(payload.slice(0, 1).toString(36), 36);
|
|
307
|
+
// Assuming types 4 and 20 are the funding types
|
|
308
|
+
if (type === 4 || type === 20) {
|
|
309
|
+
counter1++
|
|
310
|
+
//console.log('logging funding '+counter1+' '+JSON.stringify(txData))
|
|
311
|
+
acc[txBlockHeight].fundingTx.push(txData);
|
|
312
|
+
} else {
|
|
313
|
+
counter2++
|
|
314
|
+
//console.log('logging other '+counter2+' '+JSON.stringify(txData))
|
|
315
|
+
acc[txBlockHeight].tradeTx.push(txData);
|
|
316
|
+
}
|
|
317
|
+
return acc;
|
|
318
|
+
}, {});
|
|
319
|
+
|
|
320
|
+
//console.log(JSON.stringify(txByBlockHeight))
|
|
321
|
+
|
|
322
|
+
// Determine the last block height with transactions
|
|
323
|
+
const blockHeights = Object.keys(txByBlockHeight).map(Number);
|
|
324
|
+
if (blockHeights.length > 0) {
|
|
325
|
+
lastIndexBlock = Math.max(...blockHeights);
|
|
326
|
+
} else {
|
|
327
|
+
lastIndexBlock = null;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (!lastIndexBlock) {
|
|
331
|
+
console.log('No transactions to process.');
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
blockHeight = startHeight;
|
|
336
|
+
console.log('construct Consensus from Index max indexed block ' + lastIndexBlock, 'start height ' + startHeight);
|
|
337
|
+
|
|
338
|
+
for (; blockHeight <= lastIndexBlock; blockHeight++) {
|
|
339
|
+
this.parseBlock = blockHeight
|
|
340
|
+
if(blockHeight%10000==1){console.log('parsing towards real-time mode '+blockHeight)}
|
|
341
|
+
const blockData = txByBlockHeight[blockHeight];
|
|
342
|
+
let skip = true
|
|
343
|
+
//if(blockHeight%1000){console.log('block consensus processing '+blockHeight)}
|
|
344
|
+
if (blockData) {
|
|
345
|
+
// First process funding transactions
|
|
346
|
+
const skips = await this.processTxSet(blockData.fundingTx, blockHeight);
|
|
347
|
+
console.log('skips? '+skips)
|
|
348
|
+
// Then process trade transactions
|
|
349
|
+
const skips2 = await this.processTxSet(blockData.tradeTx, blockHeight);
|
|
350
|
+
console.log('skips 2? '+skips)
|
|
351
|
+
if((skips+skips2)<(blockData.fundingTx.length+blockData.tradeTx.length)){
|
|
352
|
+
console.log('skip to my lou my darlin '+skips +' '+skips2+' '+blockData.fundingTx.length+' '+blockData.tradeTx.length)
|
|
353
|
+
skip=false
|
|
354
|
+
if((skips+skips2)>0){
|
|
355
|
+
throw error("somehow there are already processed transactions in a partially processed block")
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
if(blockHeight%10000==0){skip=false}
|
|
360
|
+
if(skip==false){ //we don't do any post-processing on state for this block if it's already done, no replay of vesting, clearing
|
|
361
|
+
await Orderbook.processQueuedOnChainOrdersForBlock(blockHeight);
|
|
362
|
+
await Orderbook.processQueuedChannelTradesForBlock(blockHeight);
|
|
363
|
+
const cumulativeVolumes = await VolumeIndex.getCumulativeVolumes(blockHeight);
|
|
364
|
+
const thisBlockVolumes = await VolumeIndex.getBlockVolumes(blockHeight);
|
|
365
|
+
if (thisBlockVolumes.global > 0){
|
|
366
|
+
console.log('This is a block volume! ' + thisBlockVolumes);
|
|
367
|
+
const updateVesting = await TradeLayerManager.updateVesting(
|
|
368
|
+
cumulativeVolumes.ltcPairTotalVolume,
|
|
369
|
+
thisBlockVolumes.ltcPairs,
|
|
370
|
+
cumulativeVolumes.globalCumulativeVolume,
|
|
371
|
+
thisBlockVolumes.global
|
|
372
|
+
);
|
|
373
|
+
if (updateVesting != null && updateVesting != undefined && thisBlockVolumes != 0) {
|
|
374
|
+
console.log('Update Vesting in block ' + blockHeight + ' ' + JSON.stringify(updateVesting));
|
|
375
|
+
await TallyMap.applyVesting(2, updateVesting.two, blockHeight);
|
|
376
|
+
await TallyMap.applyVesting(3, updateVesting.three, blockHeight);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Additional processing steps like withdrawal and clearing
|
|
381
|
+
await Channels.processWithdrawals(blockHeight);
|
|
382
|
+
await Clearing.clearingFunction(blockHeight,false);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
maxProcessedHeight = blockHeight;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
await this.saveMaxProcessedHeight(maxProcessedHeight,false,null);
|
|
389
|
+
return this.syncIfNecessary();
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
async checkSync(){
|
|
393
|
+
const consensusParse = this.parseBlock
|
|
394
|
+
const txIndex = await TxIndex.getInstance();
|
|
395
|
+
const txIndexParse = txIndex.parseBlock
|
|
396
|
+
return {consensus: consensusParse, txIndex: txIndexParse}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Helper function to process a set of transactions
|
|
400
|
+
async processTxSet(txSet, blockHeight) {
|
|
401
|
+
let skips = 0
|
|
402
|
+
for (const txData of txSet) {
|
|
403
|
+
let flag = false;
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
const valueData = txData.value
|
|
407
|
+
const txId = valueData.txId;
|
|
408
|
+
|
|
409
|
+
if (await Consensus.checkIfTxProcessed(txId)) {
|
|
410
|
+
//console.log('scanning blockHeight '+blockHeight+' '+txId)
|
|
411
|
+
skips++
|
|
412
|
+
continue;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
var payload = valueData.payload;
|
|
416
|
+
const marker = valueData.marker;
|
|
417
|
+
const type = parseInt(payload.slice(0, 1).toString(36), 36);
|
|
418
|
+
console.log('type is '+type + ' height is '+blockHeight)
|
|
419
|
+
payload = payload.slice(1, payload.length).toString(36);
|
|
420
|
+
const senderAddress = valueData.sender.senderAddress;
|
|
421
|
+
const referenceAddress = valueData.reference;
|
|
422
|
+
const senderUTXO = valueData.sender.amount;
|
|
423
|
+
const referenceUTXO = valueData.reference.amount / COIN;
|
|
424
|
+
|
|
425
|
+
if (flag) {
|
|
426
|
+
console.log('missing tx params ' + txId,
|
|
427
|
+
type,
|
|
428
|
+
marker,
|
|
429
|
+
payload,
|
|
430
|
+
senderAddress,
|
|
431
|
+
referenceAddress,
|
|
432
|
+
senderUTXO,
|
|
433
|
+
referenceUTXO,
|
|
434
|
+
blockHeight);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
const decodedParams = await Types.decodePayload(
|
|
438
|
+
txId,
|
|
439
|
+
type,
|
|
440
|
+
marker,
|
|
441
|
+
payload,
|
|
442
|
+
senderAddress,
|
|
443
|
+
referenceAddress,
|
|
444
|
+
senderUTXO,
|
|
445
|
+
referenceUTXO,
|
|
446
|
+
blockHeight
|
|
447
|
+
);
|
|
448
|
+
|
|
449
|
+
if (flag) {
|
|
450
|
+
console.log('missing tx decode ' + decodedParams);
|
|
451
|
+
}
|
|
452
|
+
console.log('decoded params ' +JSON.stringify(decodedParams))
|
|
453
|
+
decodedParams.block = blockHeight;
|
|
454
|
+
|
|
455
|
+
if (decodedParams.valid === true) {
|
|
456
|
+
console.log('consensus marking valid tx '+decodedParams)
|
|
457
|
+
await Consensus.markTxAsProcessed(txId, decodedParams);
|
|
458
|
+
await Logic.typeSwitch(type, decodedParams);
|
|
459
|
+
//await TxIndex.upsertTxValidityAndReason(txId, type, decodedParams.valid, decodedParams.reason);
|
|
460
|
+
} else {
|
|
461
|
+
console.log('consensus marking invalid tx '+decodedParams)
|
|
462
|
+
await Consensus.markTxAsProcessed(txId, decodedParams);
|
|
463
|
+
await TxIndex.upsertTxValidityAndReason(txId, type, decodedParams.valid, decodedParams.reason);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
return skips
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
async processTx(txSet, blockHeight) {
|
|
470
|
+
let processedAny = false;
|
|
471
|
+
|
|
472
|
+
for (const txData of txSet) {
|
|
473
|
+
console.log('tx data in real-time' + JSON.stringify(txData));
|
|
474
|
+
const txId = txData.txId;
|
|
475
|
+
|
|
476
|
+
if (await Consensus.checkIfTxProcessed(txId)) {
|
|
477
|
+
// We *skip* this tx, but keep going in case there are new ones
|
|
478
|
+
continue;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
let payload = txData.payload;
|
|
482
|
+
const marker = 'tl';
|
|
483
|
+
|
|
484
|
+
const type = parseInt(payload.slice(0, 1).toString(36), 36);
|
|
485
|
+
payload = payload.slice(1, payload.length).toString(36);
|
|
486
|
+
|
|
487
|
+
const senderAddress = txData.sender.senderAddress;
|
|
488
|
+
const referenceAddress = txData.reference;
|
|
489
|
+
const senderUTXO = txData.sender.amount;
|
|
490
|
+
const referenceUTXO = txData.reference.amount / COIN;
|
|
491
|
+
|
|
492
|
+
console.log('params to go in during consensus builder ' + type + ' ' + payload + ' ' + senderAddress + blockHeight);
|
|
493
|
+
|
|
494
|
+
const decodedParams = await Types.decodePayload(
|
|
495
|
+
txId, type, marker, payload,
|
|
496
|
+
senderAddress, referenceAddress,
|
|
497
|
+
senderUTXO, referenceUTXO,
|
|
498
|
+
blockHeight
|
|
499
|
+
);
|
|
500
|
+
decodedParams.block = blockHeight;
|
|
501
|
+
|
|
502
|
+
// activation checks as you already have...
|
|
503
|
+
if (decodedParams.type > 0) {
|
|
504
|
+
const activationBlock = activationInstance.getActivationBlock(decodedParams.type);
|
|
505
|
+
if (blockHeight < activationBlock) {
|
|
506
|
+
decodedParams.valid = false;
|
|
507
|
+
decodedParams.reason += 'Tx not yet activated ';
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
await Consensus.markTxAsProcessed(txId, decodedParams);
|
|
512
|
+
await TxIndex.upsertTxValidityAndReason(txId, type, decodedParams.valid, decodedParams.reason);
|
|
513
|
+
|
|
514
|
+
if (decodedParams.valid === true) {
|
|
515
|
+
processedAny = true;
|
|
516
|
+
console.log('valid tx going in for processing ' + type + JSON.stringify(decodedParams) + ' ' + txId + 'blockHeight ' + blockHeight);
|
|
517
|
+
await Logic.typeSwitch(type, decodedParams);
|
|
518
|
+
} else {
|
|
519
|
+
console.log('invalid tx ' + decodedParams.reason);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
return processedAny;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
/*originally was an if-logic based switch function but refactoring real-time mode
|
|
528
|
+
it simply is a part of a flow, could be refactored into one function
|
|
529
|
+
*/
|
|
530
|
+
async syncIfNecessary(restore) {
|
|
531
|
+
const blockLag = await this.checkBlockLag();
|
|
532
|
+
if(restore){
|
|
533
|
+
blockLag.maxTrack=restore
|
|
534
|
+
console.log('set max track to restore '+blockLag.maxTrack)
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
if(pause){
|
|
538
|
+
while(pause){
|
|
539
|
+
await delay(1000)
|
|
540
|
+
}
|
|
541
|
+
return syncIfNecessary()
|
|
542
|
+
}else{
|
|
543
|
+
this.processIncomingBlocks(blockLag.lag, blockLag.maxTrack, blockLag.chainTip); // Start processing new blocks as they come
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
setPause(){
|
|
548
|
+
if(!pause){
|
|
549
|
+
pause=true
|
|
550
|
+
}else if(pause){
|
|
551
|
+
pause=false
|
|
552
|
+
}
|
|
553
|
+
return pause
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
//updates max consensus block in real-time mode
|
|
557
|
+
async checkBlockLag(){
|
|
558
|
+
const chaintip = await this.getBlockCountAsync()
|
|
559
|
+
let track = await this.loadTrackHeight()
|
|
560
|
+
if(track==null){
|
|
561
|
+
track = await this.loadMaxProcessedHeight()
|
|
562
|
+
}
|
|
563
|
+
//console.log(maxConsensusBlock)
|
|
564
|
+
var lag = chaintip - track
|
|
565
|
+
return {'lag':lag, 'chainTip':chaintip, 'maxTrack':track}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/*main function of real-time mode*/
|
|
569
|
+
async processIncomingBlocks(lag, maxTrack, chainTip) {
|
|
570
|
+
// Continuously loop through incoming blocks and process them
|
|
571
|
+
let latestProcessedBlock = maxTrack
|
|
572
|
+
console.log('entering real-time mode '+latestProcessedBlock)
|
|
573
|
+
let lagObj
|
|
574
|
+
while (true) {
|
|
575
|
+
/*if (shutdownRequested) {
|
|
576
|
+
break; // Break the loop if shutdown is requested
|
|
577
|
+
}*/
|
|
578
|
+
chainTip = await this.getBlockCountAsync()
|
|
579
|
+
//console.log('latest block '+chainTip+' max track'+latestProcessedBlock)
|
|
580
|
+
let checkTrack = await this.loadTrackHeight()
|
|
581
|
+
if(checkTrack>latestProcessedBlock){latestProcessedBlock=checkTrack}
|
|
582
|
+
for (let blockNumber = latestProcessedBlock + 1; blockNumber <= chainTip; blockNumber++) {
|
|
583
|
+
|
|
584
|
+
const networkIsUp = await this.checkNetworkStatus();
|
|
585
|
+
if (!networkIsUp) {
|
|
586
|
+
console.log('Network down, entering recovery mode.');
|
|
587
|
+
blockNumber = await this.enterRecoveryMode(latestProcessedBlock, blockNumber);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
//const blockData = await TxIndex.fetchBlockData(blockNumber);
|
|
591
|
+
const blockData = await this.fetchWithRetry(blockNumber);
|
|
592
|
+
const block= await this.processBlock(blockData, blockNumber);
|
|
593
|
+
|
|
594
|
+
if(block.reOrg==true){
|
|
595
|
+
console.log('returned block to restore '+JSON.stringify(block))
|
|
596
|
+
return this.syncIfNecessary(block.restore)
|
|
597
|
+
}
|
|
598
|
+
let trackHeight = blockNumber;
|
|
599
|
+
//console.log('updating trackHeight'+trackHeight)
|
|
600
|
+
await this.saveTrackHeight(trackHeight)
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
if(pause==true){
|
|
604
|
+
console.log('exiting real-time mode '+latestProcessedBlock)
|
|
605
|
+
break
|
|
606
|
+
};
|
|
607
|
+
// Wait for a short period before checking for new blocks
|
|
608
|
+
await new Promise(resolve => setTimeout(resolve, 10000)); // 10 seconds
|
|
609
|
+
//console.log('checking block lag '+maxConsensusBlock+' '+chainTip)
|
|
610
|
+
await this.saveTrackHeight(chainTip)
|
|
611
|
+
}
|
|
612
|
+
return syncIfNecessary()
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
async checkNetworkStatus() {
|
|
616
|
+
try {
|
|
617
|
+
// Fetch network info using the promisified getnetworkinfo RPC call
|
|
618
|
+
//console.log('about to ping')
|
|
619
|
+
const networkInfo = await this.getNetworkInfoAsync();
|
|
620
|
+
|
|
621
|
+
// Check if the network is active
|
|
622
|
+
const networkActive = networkInfo.networkactive;
|
|
623
|
+
const connections = networkInfo.connections;
|
|
624
|
+
|
|
625
|
+
//console.log('Network Status:', networkInfo);
|
|
626
|
+
|
|
627
|
+
// Determine if there is a potential network outage or issue
|
|
628
|
+
if (!networkActive) {
|
|
629
|
+
console.error('Network is inactive! The node is not connected to the Bitcoin network.');
|
|
630
|
+
return { status: false, reason: 'Network inactive' };
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
if (connections === 0) {
|
|
634
|
+
console.warn('Node has 0 connections. It may be isolated from the network.');
|
|
635
|
+
return { status: false, reason: 'No connections' };
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// If everything seems fine
|
|
639
|
+
//console.log('Network is active with', connections, 'connections.');
|
|
640
|
+
return { status: true, connections: connections };
|
|
641
|
+
|
|
642
|
+
} catch (error) {
|
|
643
|
+
// Handle errors such as ECONNREFUSED (cannot connect to the node)
|
|
644
|
+
if (error.code === 'ECONNREFUSED' || error.code === 'ETIMEDOUT') {
|
|
645
|
+
console.error(`Network error: ${error.message}. Could not reach the Bitcoin node.`);
|
|
646
|
+
return { status: 'down', reason: 'Connection refused or timeout' };
|
|
647
|
+
} else {
|
|
648
|
+
console.error('An unexpected error occurred:', error);
|
|
649
|
+
throw error; // Rethrow if it's an unexpected error
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
async enterRecoveryMode(latestProcessedBlock, trackHeight) {
|
|
655
|
+
console.log('Entering recovery mode, last processed block:', latestProcessedBlock);
|
|
656
|
+
|
|
657
|
+
while (true) {
|
|
658
|
+
const networkIsUp = await this.checkNetworkStatus();
|
|
659
|
+
if (networkIsUp.status) {
|
|
660
|
+
console.log('Network restored, resuming block processing.');
|
|
661
|
+
|
|
662
|
+
// Reload state from the database to ensure we're starting from the correct point
|
|
663
|
+
const savedTrackHeight = await this.loadTrackHeight();
|
|
664
|
+
const maxConsensusBlock = await this.loadMaxProcessedHeight();
|
|
665
|
+
|
|
666
|
+
console.log(`Resuming from block: ${latestProcessedBlock}, track height: ${trackHeight}`);
|
|
667
|
+
return savedTrackHeight; // Exit recovery mode and resume normal processing
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
// Retry after a short delay before checking the network again
|
|
671
|
+
await new Promise(resolve => setTimeout(resolve, 10000));
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
|
|
676
|
+
/*sub-function of real-time mode, breaks things into 3 steps*/
|
|
677
|
+
async processBlock(blockData, blockNumber) {
|
|
678
|
+
// Process the beginning of the block
|
|
679
|
+
const tx= await this.blockHandlerBegin(blockData, blockNumber);
|
|
680
|
+
if(typeof tx=='number'){return {reOrg: true, restore: tx}}//re-org recovery at block
|
|
681
|
+
// Process each transaction in the block
|
|
682
|
+
blockNumber = await this.blockHandlerMid(tx, blockNumber);
|
|
683
|
+
|
|
684
|
+
// Process the end of the block
|
|
685
|
+
await this.blockHandlerEnd(blockData.hash, blockNumber);
|
|
686
|
+
return {reOrg:false, restore: blockNumber}
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
async shutdown() {
|
|
690
|
+
console.log('Saving state to database...');
|
|
691
|
+
// Code to save state to database
|
|
692
|
+
console.log('Shutdown completed.');
|
|
693
|
+
process.exit(0); // or use another method to exit gracefully
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
async fetchWithRetry(blockNumber, retries = 3, delayMs = 1000) {
|
|
697
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
698
|
+
try {
|
|
699
|
+
return await TxIndex.fetchBlockData(blockNumber); // Directly call the function
|
|
700
|
+
} catch (error) {
|
|
701
|
+
if (error.code === 'ETIMEDOUT') {
|
|
702
|
+
console.warn(`Attempt ${attempt + 1} failed with ETIMEDOUT.`);
|
|
703
|
+
if (attempt === retries) throw error; // Re-throw if max retries reached
|
|
704
|
+
await this.delay(delayMs * Math.pow(2, attempt)); // Exponential backoff
|
|
705
|
+
} else {
|
|
706
|
+
throw error; // Rethrow non-timeout errors
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
async blockHandlerBegin(blockData, blockHeight) {
|
|
713
|
+
const persistence = await Persistence.getInstance();
|
|
714
|
+
|
|
715
|
+
//try {
|
|
716
|
+
if (persistence) {
|
|
717
|
+
// 1) Live reorg detection
|
|
718
|
+
const reorg = await persistence.checkForReorgForNewBlock(
|
|
719
|
+
blockHeight,
|
|
720
|
+
blockData.previousblockhash
|
|
721
|
+
);
|
|
722
|
+
if (reorg) {
|
|
723
|
+
// Handle deep reorg (offline or multiple blocks)
|
|
724
|
+
const info = await persistence.detectAndHandleReorg(blockHeight);
|
|
725
|
+
if (info && typeof info.restoredFrom === 'number') {
|
|
726
|
+
console.log(
|
|
727
|
+
`Replaying consensus from snapshot at ${info.restoredFrom + 1} ` +
|
|
728
|
+
`after reorg (common ancestor ${info.commonAncestor}).`
|
|
729
|
+
);
|
|
730
|
+
|
|
731
|
+
//await this.constructConsensusFromIndex(info.restoredFrom + 1, true);
|
|
732
|
+
return info.restoredFrom
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
// 2) Record this block header for future comparisons
|
|
737
|
+
await persistence.recordBlockHeader(
|
|
738
|
+
blockHeight,
|
|
739
|
+
blockData.hash,
|
|
740
|
+
blockData.previousblockhash
|
|
741
|
+
);
|
|
742
|
+
|
|
743
|
+
// 3) Maybe write a snapshot + consensus checkpoint
|
|
744
|
+
await persistence.maybeCheckpoint(blockHeight);
|
|
745
|
+
}
|
|
746
|
+
//const blockData = await TxIndex.fetchBlockData(blockHeight);
|
|
747
|
+
const txDetails = await TxIndex.processBlockData(blockData, blockHeight);
|
|
748
|
+
|
|
749
|
+
if(txDetails.length>=1){
|
|
750
|
+
console.log('processing new tx '+JSON.stringify(txDetails))
|
|
751
|
+
}
|
|
752
|
+
// Separate out Commit/Transfer transactions from others
|
|
753
|
+
const fundingTxs = [];
|
|
754
|
+
const otherTxs = [];
|
|
755
|
+
|
|
756
|
+
for (const tx of txDetails) {
|
|
757
|
+
if (tx.payload.startsWith('4') || tx.payload.startsWith('m')) {
|
|
758
|
+
fundingTxs.push(tx);
|
|
759
|
+
} else {
|
|
760
|
+
otherTxs.push(tx);
|
|
761
|
+
}
|
|
762
|
+
console.log('funding tx '+JSON.stringify(fundingTxs))
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// Process Commit/Transfer transactions first
|
|
766
|
+
if (fundingTxs.length > 0) {
|
|
767
|
+
await this.processTx(fundingTxs, blockHeight);
|
|
768
|
+
console.log(`Processed funding txs for block ${blockHeight}`);
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
// Pass other transactions to `blockHandlerMid` for processing later
|
|
772
|
+
return otherTxs; // Store remaining txs for mid-processing
|
|
773
|
+
/*} catch (error) {
|
|
774
|
+
console.error(`Error in blockHandlerBegin at block ${blockHeight}:`, error);
|
|
775
|
+
|
|
776
|
+
// Normalize error message into a string safely
|
|
777
|
+
const msg = (error?.message || error?.toString?.() || "");
|
|
778
|
+
|
|
779
|
+
if (msg.includes("ETIMEDOUT")) {
|
|
780
|
+
// retry
|
|
781
|
+
return this.blockHandlerBegin('', blockHeight);
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
return [];
|
|
785
|
+
}*/
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
|
|
789
|
+
/*middle part of real-time mode processed new tx */
|
|
790
|
+
async blockHandlerMid(txData, blockHeight) {
|
|
791
|
+
let didWork = false;
|
|
792
|
+
|
|
793
|
+
try {
|
|
794
|
+
if (txData && txData.length > 0) {
|
|
795
|
+
console.log(`tx Data for block ${blockHeight} txData ${JSON.stringify(txData)}`);
|
|
796
|
+
|
|
797
|
+
// processTx now returns true if any tx was newly processed
|
|
798
|
+
didWork = await this.processTx(txData, blockHeight);
|
|
799
|
+
|
|
800
|
+
// Save max height regardless, to persist progress
|
|
801
|
+
this.saveMaxProcessedHeight(blockHeight);
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
console.log(`Processed block ${blockHeight} successfully...`);
|
|
805
|
+
} catch (error) {
|
|
806
|
+
console.error(`Blockhandler Mid Error processing block ${blockHeight}:`, error);
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
// Run clearing only if there was actual new TL work done
|
|
810
|
+
if (didWork) {
|
|
811
|
+
await Orderbook.processQueuedOnChainOrdersForBlock(blockHeight);
|
|
812
|
+
await Orderbook.processQueuedChannelTradesForBlock(blockHeight);
|
|
813
|
+
console.log(`[CLEARING] Running for block ${blockHeight} (new TL work detected)`);
|
|
814
|
+
await Clearing.clearingFunction(blockHeight, /*skip=*/false);
|
|
815
|
+
} else {
|
|
816
|
+
//console.log(`[CLEARING] Skipped for block ${blockHeight} (no new TL work)`);
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
// Always return block number for chain continuity
|
|
820
|
+
return blockHeight;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
/*here's where we finish a block processing in real-time mode, handling anything that is done after
|
|
824
|
+
the main tx processing. But since I've stuck the clearing function, channel removal and others in the constructConsensus function
|
|
825
|
+
this is currently also redundant */
|
|
826
|
+
async blockHandlerEnd(blockHash, blockHeight) {
|
|
827
|
+
const cumVolumes = await VolumeIndex.getCumulativeVolumes()
|
|
828
|
+
const thisBlockVolumes = await VolumeIndex.getBlockVolumes(blockHeight)
|
|
829
|
+
if(thisBlockVolumes>0){
|
|
830
|
+
console.log('this is a block volume! '+thisBlockVolumes)
|
|
831
|
+
const updateVesting = await TradeLayerManager.updateVesting(cumVolumes.ltcPairTotalVolume,thisBlockVolumes.ltcPairs,cumVolumes.globalCumulativeVolume,thisBlockVolumes.global)
|
|
832
|
+
if(updateVesting!=null&&updateVesting!=undefined&&thisBlockVolumes!=0){
|
|
833
|
+
console.log('update Vesting in block' +blockHeight+ ' '+JSON.stringify(updateVesting))
|
|
834
|
+
await TallyMap.applyVesting(2,updateVesting.two,blockHeight)
|
|
835
|
+
await TallyMap.applyVesting(3,updateVesting.three,blockHeight)
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
return //console.log('block finish '+blockHeight)
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
async handleReorg(blockHeight) {
|
|
843
|
+
//console.log(`Handling reorganization at block ${blockHeight}`);
|
|
844
|
+
// Add logic to handle a blockchain reorganization
|
|
845
|
+
await this.blockchainPersistence.handleReorg();
|
|
846
|
+
// This could involve reverting to a previous state, re-processing blocks, etc.
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
async saveMaxProcessedHeight(maxProcessedHeight){
|
|
850
|
+
try {
|
|
851
|
+
const base = await db.getDatabase('consensus')
|
|
852
|
+
await base.updateAsync(
|
|
853
|
+
{ _id: 'MaxProcessedHeight' },
|
|
854
|
+
{ $set: { value: maxProcessedHeight } },
|
|
855
|
+
{ upsert: true }
|
|
856
|
+
);
|
|
857
|
+
//console.log('realtime mode update '+maxProcessedHeight)
|
|
858
|
+
} catch (error) {
|
|
859
|
+
console.error('Error updating MaxProcessedHeight:', error);
|
|
860
|
+
throw error; // or handle the error as needed
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
async saveTrackHeight(saveHeight){
|
|
865
|
+
const base = await db.getDatabase('consensus')
|
|
866
|
+
await base.updateAsync(
|
|
867
|
+
{ _id: 'TrackHeight' },
|
|
868
|
+
{ $set: { value: saveHeight } },
|
|
869
|
+
{ upsert: true }
|
|
870
|
+
)
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
async loadMaxProcessedHeight() {
|
|
874
|
+
const consensusDB = await db.getDatabase('consensus'); // Access the consensus sub-database
|
|
875
|
+
|
|
876
|
+
try {
|
|
877
|
+
const maxProcessedHeightDoc = await consensusDB.findOneAsync({ _id: 'MaxProcessedHeight' });
|
|
878
|
+
if (maxProcessedHeightDoc) {
|
|
879
|
+
const maxProcessedHeight = maxProcessedHeightDoc.value;
|
|
880
|
+
//console.log('MaxProcessedHeight retrieved:', maxProcessedHeight);
|
|
881
|
+
return maxProcessedHeight; // Return the retrieved value
|
|
882
|
+
} else {
|
|
883
|
+
console.log('MaxProcessedHeight not found in the database.');
|
|
884
|
+
return null; // Return null or an appropriate default value if not found
|
|
885
|
+
}
|
|
886
|
+
} catch (error) {
|
|
887
|
+
console.error('Error retrieving MaxProcessedHeight:', error);
|
|
888
|
+
throw error; // Rethrow the error or handle it as needed
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
async loadTrackHeight() {
|
|
893
|
+
const consensusDB = await db.getDatabase('consensus'); // Access the consensus sub-database
|
|
894
|
+
|
|
895
|
+
try {
|
|
896
|
+
let track = await consensusDB.findOneAsync({ _id: 'TrackHeight' });
|
|
897
|
+
if (track) {
|
|
898
|
+
track = track.value;
|
|
899
|
+
//console.log('MaxProcessedHeight retrieved:', maxProcessedHeight);
|
|
900
|
+
return track; // Return the retrieved value
|
|
901
|
+
} else {
|
|
902
|
+
console.log('MaxTrackHeight not found in the database.');
|
|
903
|
+
return null; // Return null or an appropriate default value if not found
|
|
904
|
+
}
|
|
905
|
+
} catch (error) {
|
|
906
|
+
console.error('Error retrieving MaxProcessedHeight:', error);
|
|
907
|
+
throw error; // Rethrow the error or handle it as needed
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
static async getGenesisBlock() {
|
|
912
|
+
const mainInstance = await Main.getInstance();
|
|
913
|
+
return mainInstance.genesisBlock;
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
static async getLastBlock() {
|
|
917
|
+
try {
|
|
918
|
+
return await TxUtils.getLatestBlockHeight(); // Calls the blockchain for last block height
|
|
919
|
+
} catch (error) {
|
|
920
|
+
console.error("Error getting latest block height:", error);
|
|
921
|
+
throw error;
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
// ... other methods ...
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
module.exports = Main
|