Skip to content

Commit f48ef72

Browse files
TinyChain --> new secure parts added.
1 parent 20c7f71 commit f48ef72

3 files changed

Lines changed: 65 additions & 33 deletions

File tree

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,6 @@
104104
},
105105
"dependencies": {
106106
"clone": "^2.1.2",
107-
"tiny-essentials": "^1.8.0"
107+
"tiny-essentials": "^1.8.1"
108108
}
109109
}

src/TinyChain/Instance.mjs

Lines changed: 60 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ class TinyChainInstance {
185185
* @returns {boolean} `true` if a valid genesis block is present, otherwise `false`.
186186
*/
187187
hasGenesisBlock() {
188-
if (this.chain.length === 0) return false;
188+
if (this.#getChain().length === 0) return false;
189189
const firstBlock = this.getFirstBlock();
190190
return (
191191
firstBlock.index === 0n &&
@@ -209,6 +209,32 @@ class TinyChainInstance {
209209
*/
210210
initialBalances = {};
211211

212+
/**
213+
* Validates and returns the internal balances object.
214+
* Throws an error if balances is not a plain object.
215+
*
216+
* @returns {Balances} A reference to the balances object if valid.
217+
* @throws {Error} If balances is not a plain object.
218+
*/
219+
#getBalances() {
220+
if (typeof this.balances !== 'object' || this.balances === null || Array.isArray(this.balances))
221+
throw new Error('balances must be a plain object');
222+
return this.balances;
223+
}
224+
225+
/**
226+
* Safely retrieves the current blockchain array.
227+
* Ensures that `this.chain` is a valid array before returning it.
228+
* If the internal `chain` property is corrupted or invalid, an error is thrown to prevent further logic failures.
229+
*
230+
* @returns {TinyChainBlock[]} The current blockchain as an array of TinyChainBlock instances.
231+
* @throws {Error} If `this.chain` is not an array.
232+
*/
233+
#getChain() {
234+
if (!Array.isArray(this.chain)) throw new Error('chain must be an array');
235+
return this.chain;
236+
}
237+
212238
/**
213239
* Constructs a new blockchain instance with optional configuration parameters.
214240
*
@@ -478,8 +504,8 @@ class TinyChainInstance {
478504
*
479505
*/
480506
#init(signer = this.#signer) {
481-
if (this.chain.length > 0)
482-
throw new Error('Blockchain already initialized with a genesis block');
507+
const chain = this.#getChain();
508+
if (chain.length > 0) throw new Error('Blockchain already initialized with a genesis block');
483509

484510
const data = {
485511
transfers: [],
@@ -502,7 +528,7 @@ class TinyChainInstance {
502528
data: [data],
503529
sigs: [sig.toString('hex')],
504530
});
505-
this.chain.push(block);
531+
chain.push(block);
506532
return block;
507533
}
508534

@@ -622,11 +648,12 @@ class TinyChainInstance {
622648
* @returns {boolean|null} Returns `true` if the chain is valid, `false` or `null` otherwise.
623649
*/
624650
isValid(startIndex = 0, endIndex = null) {
625-
const end = endIndex ?? this.chain.length - 1;
651+
const chain = this.#getChain();
652+
const end = endIndex ?? chain.length - 1;
626653

627654
for (let i = Math.max(startIndex, 1); i <= end; i++) {
628-
const current = this.chain[i];
629-
const previous = this.chain[i - 1];
655+
const current = chain[i];
656+
const previous = chain[i - 1];
630657
const result = this.#isValidBlock(current, previous);
631658
if (!result) return result;
632659
}
@@ -768,7 +795,7 @@ class TinyChainInstance {
768795
if (!isValid) throw new Error('Block mining invalid');
769796

770797
if (this.isCurrencyMode() || this.isPayloadMode()) this.updateBalance(newBlock);
771-
this.chain.push(newBlock);
798+
this.#getChain().push(newBlock);
772799
this.#emit(TinyChainEvents.NewBlock, newBlock);
773800
return newBlock;
774801
};
@@ -798,7 +825,7 @@ class TinyChainInstance {
798825
if (!isValid) throw new Error('Invalid block cannot be added to the chain.');
799826

800827
if (this.isCurrencyMode() || this.isPayloadMode()) this.updateBalance(minedBlock);
801-
this.chain.push(minedBlock);
828+
this.#getChain().push(minedBlock);
802829
this.#emit(TinyChainEvents.NewBlock, minedBlock);
803830
return minedBlock;
804831
});
@@ -823,7 +850,7 @@ class TinyChainInstance {
823850
* @returns {TinyChainBlock} The latest block in the blockchain.
824851
*/
825852
getLatestBlock() {
826-
return this.getChainBlock(this.chain.length - 1);
853+
return this.getChainBlock(this.#getChain().length - 1);
827854
}
828855

829856
/**
@@ -841,9 +868,10 @@ class TinyChainInstance {
841868
this.balances = {};
842869
this.#emit(TinyChainEvents.BalancesInitialized, this.balances);
843870
if (this.isCurrencyMode()) {
871+
const balances = this.#getBalances();
844872
for (const [address, balance] of Object.entries(this.getInitialBalances())) {
845-
this.balances[address] = BigInt(balance);
846-
this.#emit(TinyChainEvents.BalanceStarted, address, this.balances[address]);
873+
balances[address] = BigInt(balance);
874+
this.#emit(TinyChainEvents.BalanceStarted, address, balances[address]);
847875
}
848876
}
849877
}
@@ -860,7 +888,7 @@ class TinyChainInstance {
860888
* @throws {Error} If the list is not an array, if any transfer is malformed,
861889
* or if the sender is unauthorized or lacks balance.
862890
*/
863-
validateTransfers(pubKey, addressType, transfers, balances = this.balances) {
891+
validateTransfers(pubKey, addressType, transfers, balances = this.#getBalances()) {
864892
const isAdmin = this.getAdmins().has(pubKey);
865893
const address = this.#signer.getAddress(Buffer.from(pubKey, 'hex'), addressType);
866894

@@ -911,7 +939,7 @@ class TinyChainInstance {
911939
*
912940
* @returns {void} This method does not return any value.
913941
*/
914-
updateBalance(block, balances = this.balances, emitEvents = true) {
942+
updateBalance(block, balances = this.#getBalances(), emitEvents = true) {
915943
const reward = block.reward;
916944
const minerAddress = block.miner;
917945
const isMinerAddress = typeof minerAddress === 'string' && minerAddress.length > 0;
@@ -1039,7 +1067,7 @@ class TinyChainInstance {
10391067
if (!this.isCurrencyMode()) throw new Error('Currency mode must be enabled.');
10401068
/** @type {Balances} */
10411069
const result = {};
1042-
for (const [addr, amount] of Object.entries(this.balances)) result[addr] = amount;
1070+
for (const [addr, amount] of Object.entries(this.#getBalances())) result[addr] = amount;
10431071
return result;
10441072
}
10451073

@@ -1059,18 +1087,19 @@ class TinyChainInstance {
10591087
* @throws {Error} Throws if `currencyMode` is disabled or if the index is invalid.
10601088
*/
10611089
getBalancesAt(startIndex = 0, endIndex = null) {
1090+
const chainList = this.#getChain();
10621091
if (!this.isCurrencyMode() && !this.isPayloadMode())
10631092
throw new Error('Currency mode or payload mode must be enabled.');
1064-
const end = endIndex !== null ? endIndex : this.chain.length - 1;
1065-
if (startIndex < 0 || end >= this.chain.length || startIndex > end)
1093+
const end = endIndex !== null ? endIndex : chainList.length - 1;
1094+
if (startIndex < 0 || end >= chainList.length || startIndex > end)
10661095
throw new Error('Invalid startIndex or endIndex range.');
10671096

10681097
/** @type {Balances} */
10691098
const balances = {};
10701099
for (const [address, balance] of Object.entries(this.getInitialBalances()))
10711100
balances[address] = BigInt(balance);
10721101

1073-
const chain = this.chain.slice(startIndex, end + 1);
1102+
const chain = chainList.slice(startIndex, end + 1);
10741103
for (const block of chain) this.updateBalance(block, balances, false);
10751104

10761105
return balances;
@@ -1101,7 +1130,8 @@ class TinyChainInstance {
11011130
*/
11021131
getBalance(address) {
11031132
if (!this.isCurrencyMode()) throw new Error('Currency mode must be enabled.');
1104-
if (typeof this.balances[address] === 'bigint') return this.balances[address];
1133+
const balances = this.#getBalances();
1134+
if (typeof balances[address] === 'bigint') return balances[address];
11051135
return 0n;
11061136
}
11071137

@@ -1119,8 +1149,8 @@ class TinyChainInstance {
11191149
recalculateBalances() {
11201150
this.startBalances();
11211151
if (this.isCurrencyMode() || this.isPayloadMode())
1122-
for (const block of this.chain) this.updateBalance(block);
1123-
this.#emit(TinyChainEvents.BalanceRecalculated, this.balances);
1152+
for (const block of this.#getChain()) this.updateBalance(block);
1153+
this.#emit(TinyChainEvents.BalanceRecalculated, this.#getBalances());
11241154
}
11251155

11261156
/**
@@ -1141,8 +1171,9 @@ class TinyChainInstance {
11411171
* @throws {Error} If the block at the given index does not exist.
11421172
*/
11431173
getChainBlock(index) {
1144-
if (!this.chain[index]) throw new Error(`The chain data ${index} don't exist!`);
1145-
return this.chain[index];
1174+
const chain = this.#getChain();
1175+
if (!chain[index]) throw new Error(`The chain data ${index} don't exist!`);
1176+
return chain[index];
11461177
}
11471178

11481179
/**
@@ -1164,7 +1195,7 @@ class TinyChainInstance {
11641195
* @returns {GetTransactionData[]} An array containing all blocks' data.
11651196
*/
11661197
getAllChainData() {
1167-
return this.chain.map((block) => block.get());
1198+
return this.#getChain().map((block) => block.get());
11681199
}
11691200

11701201
/**
@@ -1195,11 +1226,12 @@ class TinyChainInstance {
11951226
* @throws {Error} If indices are out of bounds or invalid.
11961227
*/
11971228
exportChain(startIndex = 0, endIndex = null) {
1198-
const end = endIndex !== null ? endIndex : this.chain.length - 1;
1199-
if (startIndex < 0 || end >= this.chain.length || startIndex > end)
1229+
const chain = this.#getChain();
1230+
const end = endIndex !== null ? endIndex : chain.length - 1;
1231+
if (startIndex < 0 || end >= chain.length || startIndex > end)
12001232
throw new Error('Invalid startIndex or endIndex range.');
12011233

1202-
return this.chain.slice(startIndex, end + 1).map((b) => b.export());
1234+
return chain.slice(startIndex, end + 1).map((b) => b.export());
12031235
}
12041236

12051237
/**
@@ -1223,7 +1255,7 @@ class TinyChainInstance {
12231255
const isValid = this.isValid();
12241256
if (isValid === null) throw new Error('The data chain is null or corrupted.');
12251257
if (!isValid) throw new Error('The data chain is invalid or corrupted.');
1226-
this.#emit(TinyChainEvents.ImportChain, this.chain);
1258+
this.#emit(TinyChainEvents.ImportChain, this.#getChain());
12271259
}
12281260

12291261
/**

0 commit comments

Comments
 (0)