trac-msb 0.2.4 → 0.2.6
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/.dockerignore +16 -0
- package/.github/workflows/acceptance-tests.yml +7 -0
- package/.github/workflows/publish.yml +40 -0
- package/.github/workflows/{CI.yml → unit-tests.yml} +1 -1
- package/README.md +175 -50
- package/docker-compose.yml +16 -0
- package/dockerfile +41 -0
- package/docs/fee_distribution.md +89 -0
- package/msb.mjs +12 -14
- package/package.json +8 -4
- package/rpc/constants.mjs +4 -1
- package/rpc/handlers.mjs +109 -66
- package/rpc/routes/v1.mjs +3 -1
- package/rpc/rpc_services.js +126 -0
- package/rpc/utils/confirmedParameter.mjs +17 -0
- package/rpc/utils/url.mjs +38 -0
- package/src/core/network/Network.js +27 -10
- package/src/core/network/identity/NetworkWalletFactory.js +78 -0
- package/src/core/network/services/ConnectionManager.js +2 -2
- package/src/core/network/services/ValidatorObserverService.js +7 -4
- package/src/core/state/State.js +28 -22
- package/src/index.js +197 -385
- package/src/utils/cliCommands.js +280 -0
- package/src/utils/constants.js +3 -1
- package/tests/acceptance/v1/account/account.test.mjs +123 -0
- package/tests/acceptance/v1/balance/balance.test.mjs +55 -0
- package/tests/acceptance/v1/broadcast-transaction/broadcast-transaction.test.mjs +111 -0
- package/tests/acceptance/v1/confirmed-length/confirmed-length.test.mjs +19 -0
- package/tests/acceptance/v1/fee/fee.test.mjs +11 -0
- package/tests/acceptance/v1/rpc.test.mjs +62 -291
- package/tests/acceptance/v1/tx/tx.test.mjs +98 -0
- package/tests/acceptance/v1/tx-details/tx-details.test.mjs +195 -0
- package/tests/acceptance/v1/tx-hashes/tx-hashes.test.mjs +72 -0
- package/tests/acceptance/v1/tx-payloads-bulk/tx-payloads-bulk.test.mjs +27 -0
- package/tests/acceptance/v1/txv/txv.test.mjs +11 -0
- package/tests/acceptance/v1/unconfirmed-length/unconfirmed-length.test.mjs +11 -0
- package/tests/helpers/StateNetworkFactory.js +157 -0
- package/tests/helpers/autobaseTestHelpers.js +369 -0
- package/tests/helpers/createTestSignature.js +12 -0
- package/tests/helpers/transactionPayloads.mjs +78 -0
- package/tests/unit/network/NetworkWalletFactory.test.js +156 -0
- package/tests/unit/state/apply/addAdmin/addAdminHappyPathScenario.js +38 -0
- package/tests/unit/state/apply/addAdmin/addAdminScenarioHelpers.js +273 -0
- package/tests/unit/state/apply/addAdmin/adminEntryEncodingFailureScenario.js +30 -0
- package/tests/unit/state/apply/addAdmin/adminEntryExistsScenario.js +78 -0
- package/tests/unit/state/apply/addAdmin/nodeEntryInitializationFailureScenario.js +30 -0
- package/tests/unit/state/apply/addAdmin/nonBootstrapNodeScenario.js +68 -0
- package/tests/unit/state/apply/addAdmin/state.apply.addAdmin.test.js +155 -0
- package/tests/unit/state/apply/addIndexer/addIndexerHappyPathScenario.js +39 -0
- package/tests/unit/state/apply/addIndexer/addIndexerMultipleIndexersInTheNetworkScenario.js +167 -0
- package/tests/unit/state/apply/addIndexer/addIndexerPretenderAlreadyIndexerScenario.js +21 -0
- package/tests/unit/state/apply/addIndexer/addIndexerPretenderNotWriterScenario.js +21 -0
- package/tests/unit/state/apply/addIndexer/addIndexerRemoveAndReAddScenario.js +186 -0
- package/tests/unit/state/apply/addIndexer/addIndexerScenarioHelpers.js +445 -0
- package/tests/unit/state/apply/addIndexer/addIndexerWriterKeyAlreadyRegisteredScenario.js +32 -0
- package/tests/unit/state/apply/addIndexer/state.apply.addIndexer.test.js +297 -0
- package/tests/unit/state/apply/addWriter/addWriterHappyPathScenario.js +41 -0
- package/tests/unit/state/apply/addWriter/addWriterInvalidValidatorSignatureScenario.js +32 -0
- package/tests/unit/state/apply/addWriter/addWriterNewWkScenario.js +149 -0
- package/tests/unit/state/apply/addWriter/addWriterRequesterAlreadyWriterScenario.js +21 -0
- package/tests/unit/state/apply/addWriter/addWriterRequesterBalanceInsufficientScenario.js +21 -0
- package/tests/unit/state/apply/addWriter/addWriterRequesterEntryDecodeFailureScenario.js +19 -0
- package/tests/unit/state/apply/addWriter/addWriterRequesterEntryMissingScenario.js +19 -0
- package/tests/unit/state/apply/addWriter/addWriterRequesterIndexerScenario.js +21 -0
- package/tests/unit/state/apply/addWriter/addWriterRequesterNotWhitelistedScenario.js +21 -0
- package/tests/unit/state/apply/addWriter/addWriterScenarioHelpers.js +757 -0
- package/tests/unit/state/apply/addWriter/addWriterStakeBalanceUpdateFailureScenario.js +50 -0
- package/tests/unit/state/apply/addWriter/addWriterStakeInsufficientBalanceScenario.js +29 -0
- package/tests/unit/state/apply/addWriter/addWriterStakeInvalidBalanceScenario.js +29 -0
- package/tests/unit/state/apply/addWriter/addWriterStakeInvalidEntryScenario.js +21 -0
- package/tests/unit/state/apply/addWriter/addWriterStakeStakedBalanceFailureScenario.js +37 -0
- package/tests/unit/state/apply/addWriter/addWriterStakeSubtractFailureScenario.js +42 -0
- package/tests/unit/state/apply/addWriter/addWriterValidatorRewardScenario.js +105 -0
- package/tests/unit/state/apply/addWriter/addWriterWriterKeyMismatchScenario.js +54 -0
- package/tests/unit/state/apply/addWriter/addWriterWriterKeyOwnershipScenario.js +54 -0
- package/tests/unit/state/apply/addWriter/addWriterZeroWriterKeyScenario.js +29 -0
- package/tests/unit/state/apply/addWriter/state.apply.addWriter.test.js +309 -0
- package/tests/unit/state/apply/adminRecovery/adminRecoveryHappyPathScenario.js +30 -0
- package/tests/unit/state/apply/adminRecovery/adminRecoveryScenarioHelpers.js +866 -0
- package/tests/unit/state/apply/adminRecovery/state.apply.adminRecovery.test.js +439 -0
- package/tests/unit/state/apply/appendWhitelist/appendWhitelistBanAndReapplyScenario.js +78 -0
- package/tests/unit/state/apply/appendWhitelist/appendWhitelistExistingReaderHappyPathScenario.js +98 -0
- package/tests/unit/state/apply/appendWhitelist/appendWhitelistFeeAfterDisableScenario.js +66 -0
- package/tests/unit/state/apply/appendWhitelist/appendWhitelistHappyPathScenario.js +55 -0
- package/tests/unit/state/apply/appendWhitelist/appendWhitelistInsufficientAdminBalanceScenario.js +103 -0
- package/tests/unit/state/apply/appendWhitelist/appendWhitelistNodeAlreadyWhitelistedScenario.js +60 -0
- package/tests/unit/state/apply/appendWhitelist/appendWhitelistScenarioHelpers.js +191 -0
- package/tests/unit/state/apply/appendWhitelist/state.apply.appendWhitelist.test.js +220 -0
- package/tests/unit/state/apply/balanceInitialization/balanceInitializationHappyPathScenario.js +82 -0
- package/tests/unit/state/apply/balanceInitialization/balanceInitializationScenarioHelpers.js +106 -0
- package/tests/unit/state/apply/balanceInitialization/invalidAmountScenario.js +45 -0
- package/tests/unit/state/apply/balanceInitialization/nodeEntryBalanceUpdateFailureScenario.js +81 -0
- package/tests/unit/state/apply/balanceInitialization/state.apply.balanceInitialization.test.js +189 -0
- package/tests/unit/state/apply/banValidator/banValidatorBanAndReWhitelistScenario.js +155 -0
- package/tests/unit/state/apply/banValidator/banValidatorHappyPathScenario.js +36 -0
- package/tests/unit/state/apply/banValidator/banValidatorScenarioHelpers.js +534 -0
- package/tests/unit/state/apply/banValidator/banValidatorSequentialBansScenario.js +74 -0
- package/tests/unit/state/apply/banValidator/banValidatorTargetDecodeFailureScenario.js +19 -0
- package/tests/unit/state/apply/banValidator/banValidatorTargetIndexerScenario.js +32 -0
- package/tests/unit/state/apply/banValidator/banValidatorTargetNodeEntryMissingScenario.js +19 -0
- package/tests/unit/state/apply/banValidator/banValidatorTargetRoleUpdateFailureScenario.js +19 -0
- package/tests/unit/state/apply/banValidator/banValidatorWhitelistedNonWriterScenario.js +38 -0
- package/tests/unit/state/apply/banValidator/banValidatorWhitelistedZeroBalanceScenario.js +91 -0
- package/tests/unit/state/apply/banValidator/banValidatorWithdrawFailureScenario.js +19 -0
- package/tests/unit/state/apply/banValidator/state.apply.banValidator.test.js +266 -0
- package/tests/unit/state/apply/bootstrapDeployment/bootstrapDeploymentDuplicateRegistrationScenario.js +142 -0
- package/tests/unit/state/apply/bootstrapDeployment/bootstrapDeploymentHappyPathScenario.js +26 -0
- package/tests/unit/state/apply/bootstrapDeployment/bootstrapDeploymentIncompleteOperationScenario.js +94 -0
- package/tests/unit/state/apply/bootstrapDeployment/bootstrapDeploymentInvalidDeploymentEntryScenario.js +37 -0
- package/tests/unit/state/apply/bootstrapDeployment/bootstrapDeploymentMultipleBootstrapScenario.js +86 -0
- package/tests/unit/state/apply/bootstrapDeployment/bootstrapDeploymentScenarioHelpers.js +344 -0
- package/tests/unit/state/apply/bootstrapDeployment/invalidValidatorNodeEntryScenario.js +57 -0
- package/tests/unit/state/apply/bootstrapDeployment/state.apply.bootstrapDeployment.test.js +429 -0
- package/tests/unit/state/apply/common/access-control/adminConsistencyMismatchScenario.js +119 -0
- package/tests/unit/state/apply/common/access-control/adminEntryDecodeFailureScenario.js +130 -0
- package/tests/unit/state/apply/common/access-control/adminEntryExistsScenario.js +93 -0
- package/tests/unit/state/apply/common/access-control/adminEntryMissingScenario.js +108 -0
- package/tests/unit/state/apply/common/access-control/adminOnlyGuardScenario.js +126 -0
- package/tests/unit/state/apply/common/access-control/adminPublicKeyDecodeFailureScenario.js +120 -0
- package/tests/unit/state/apply/common/access-control/roleAccessOperationValidationScenario.js +50 -0
- package/tests/unit/state/apply/common/adminControlOperationValidationScenario.js +56 -0
- package/tests/unit/state/apply/common/balances/adminEntryUpdateFailureScenario.js +52 -0
- package/tests/unit/state/apply/common/balances/base/requesterBalanceScenarioBase.js +197 -0
- package/tests/unit/state/apply/common/balances/feeDecodeFailureScenario.js +52 -0
- package/tests/unit/state/apply/common/balances/requesterBalanceDecodeFailureScenario.js +15 -0
- package/tests/unit/state/apply/common/balances/requesterBalanceFeeApplicationFailureScenario.js +11 -0
- package/tests/unit/state/apply/common/balances/requesterBalanceInsufficientScenario.js +15 -0
- package/tests/unit/state/apply/common/balances/requesterBalanceUpdateFailureScenario.js +11 -0
- package/tests/unit/state/apply/common/balances/validatorEntryRewardFailureScenario.js +11 -0
- package/tests/unit/state/apply/common/balances/validatorEntryUpdateFailureScenario.js +11 -0
- package/tests/unit/state/apply/common/balances/validatorNodeEntryDecodeFailureScenario.js +40 -0
- package/tests/unit/state/apply/common/base/OperationValidationScenarioBase.js +114 -0
- package/tests/unit/state/apply/common/commonScenarioHelper.js +103 -0
- package/tests/unit/state/apply/common/indexer/indexerNodeEntryDecodeFailureScenario.js +36 -0
- package/tests/unit/state/apply/common/indexer/indexerNodeEntryMissingScenario.js +36 -0
- package/tests/unit/state/apply/common/indexer/indexerRoleUpdateFailureScenario.js +29 -0
- package/tests/unit/state/apply/common/indexer/indexerSequenceStateInvalidScenario.js +66 -0
- package/tests/unit/state/apply/common/invalidMessageComponentValidationScenario.js +84 -0
- package/tests/unit/state/apply/common/nodeEntryInitializationFailureScenario.js +47 -0
- package/tests/unit/state/apply/common/operationAlreadyAppliedScenario.js +85 -0
- package/tests/unit/state/apply/common/payload-structure/addressWithInvalidPublicKeyScenario.js +52 -0
- package/tests/unit/state/apply/common/payload-structure/initializationDisabledScenario.js +49 -0
- package/tests/unit/state/apply/common/payload-structure/invalidAddressValidationScenario.js +73 -0
- package/tests/unit/state/apply/common/payload-structure/invalidHashValidationScenario.js +71 -0
- package/tests/unit/state/apply/common/payload-structure/invalidPayloadValidationScenario.js +31 -0
- package/tests/unit/state/apply/common/payload-structure/invalidSignatureValidationScenario.js +142 -0
- package/tests/unit/state/apply/common/payload-structure/partialOperationValidationScenario.js +87 -0
- package/tests/unit/state/apply/common/requester/requesterNodeEntryBufferMissingScenario.js +70 -0
- package/tests/unit/state/apply/common/requester/requesterNodeEntryDecodeFailureScenario.js +72 -0
- package/tests/unit/state/apply/common/requester/requesterNodeEntryMissingScenario.js +36 -0
- package/tests/unit/state/apply/common/requesterAddressValidationScenario.js +44 -0
- package/tests/unit/state/apply/common/requesterPublicKeyValidationScenario.js +25 -0
- package/tests/unit/state/apply/common/transactionValidityMismatchScenario.js +98 -0
- package/tests/unit/state/apply/common/validatorConsistency/base/validatorConsistencyScenarioBase.js +201 -0
- package/tests/unit/state/apply/common/validatorConsistency/validatorEntryDecodeFailureScenario.js +17 -0
- package/tests/unit/state/apply/common/validatorConsistency/validatorEntryMissingScenario.js +44 -0
- package/tests/unit/state/apply/common/validatorConsistency/validatorInactiveScenario.js +19 -0
- package/tests/unit/state/apply/common/validatorConsistency/validatorWriterKeyMismatchScenario.js +18 -0
- package/tests/unit/state/apply/common/validatorEntryValidation/base/validatorEntryValidationScenarioBase.js +314 -0
- package/tests/unit/state/apply/common/validatorEntryValidation/validatorEntryInvalidBalanceScenario.js +18 -0
- package/tests/unit/state/apply/common/writerKeyExistsValidationScenario.js +43 -0
- package/tests/unit/state/apply/disableInitialization/disableInitializationAlreadyDisabledScenario.js +53 -0
- package/tests/unit/state/apply/disableInitialization/disableInitializationHappyPathScenario.js +24 -0
- package/tests/unit/state/apply/disableInitialization/disableInitializationScenarioHelpers.js +197 -0
- package/tests/unit/state/apply/disableInitialization/state.apply.disableInitialization.test.js +161 -0
- package/tests/unit/state/apply/missing-tests.md +18 -0
- package/tests/unit/state/apply/removeIndexer/removeIndexerHappyPathScenario.js +58 -0
- package/tests/unit/state/apply/removeIndexer/removeIndexerReAddAndRemoveAgainScenario.js +98 -0
- package/tests/unit/state/apply/removeIndexer/removeIndexerRemoveMultipleIndexersScenario.js +167 -0
- package/tests/unit/state/apply/removeIndexer/removeIndexerScenarioHelpers.js +428 -0
- package/tests/unit/state/apply/removeIndexer/removeIndexerTargetNotIndexerScenario.js +22 -0
- package/tests/unit/state/apply/removeIndexer/removeIndexerWriterKeyMissingScenario.js +20 -0
- package/tests/unit/state/apply/removeIndexer/state.apply.removeIndexer.test.js +291 -0
- package/tests/unit/state/apply/removeWriter/removeWriterAndAddWriterAgainScenario.js +87 -0
- package/tests/unit/state/apply/removeWriter/removeWriterHappyPathScenario.js +38 -0
- package/tests/unit/state/apply/removeWriter/removeWriterInvalidValidatorSignatureScenario.js +32 -0
- package/tests/unit/state/apply/removeWriter/removeWriterRequesterBalanceInsufficientScenario.js +21 -0
- package/tests/unit/state/apply/removeWriter/removeWriterRequesterEntryDecodeFailureScenario.js +19 -0
- package/tests/unit/state/apply/removeWriter/removeWriterRequesterEntryMissingScenario.js +19 -0
- package/tests/unit/state/apply/removeWriter/removeWriterRequesterIndexerScenario.js +21 -0
- package/tests/unit/state/apply/removeWriter/removeWriterRequesterNotWriterScenario.js +21 -0
- package/tests/unit/state/apply/removeWriter/removeWriterRequesterRoleUpdateFailureScenario.js +19 -0
- package/tests/unit/state/apply/removeWriter/removeWriterScenarioHelpers.js +344 -0
- package/tests/unit/state/apply/removeWriter/removeWriterThroughWriterValidatorScenario.js +113 -0
- package/tests/unit/state/apply/removeWriter/removeWriterUnstakeFailureScenario.js +33 -0
- package/tests/unit/state/apply/removeWriter/removeWriterWriterKeyMismatchScenario.js +21 -0
- package/tests/unit/state/apply/removeWriter/removeWriterWriterKeyOwnershipScenario.js +26 -0
- package/tests/unit/state/apply/removeWriter/removeWriterWriterKeyRegistryMissingScenario.js +22 -0
- package/tests/unit/state/apply/removeWriter/state.apply.removeWriter.test.js +307 -0
- package/tests/unit/state/apply/state.apply.test.js +24 -0
- package/tests/unit/state/apply/transfer/state.apply.transfer.test.js +819 -0
- package/tests/unit/state/apply/transfer/transferContractSchemaValidationScenario.js +22 -0
- package/tests/unit/state/apply/transfer/transferDoubleSpendAcrossValidatorsScenario.js +137 -0
- package/tests/unit/state/apply/transfer/transferDoubleSpendSameBatchScenario.js +63 -0
- package/tests/unit/state/apply/transfer/transferDoubleSpendSingleValidatorScenario.js +67 -0
- package/tests/unit/state/apply/transfer/transferExistingRecipientAmountScenario.js +31 -0
- package/tests/unit/state/apply/transfer/transferExistingRecipientZeroAmountScenario.js +31 -0
- package/tests/unit/state/apply/transfer/transferHandlerGuardScenarios.js +22 -0
- package/tests/unit/state/apply/transfer/transferHappyPathScenario.js +8 -0
- package/tests/unit/state/apply/transfer/transferInvalidIncomingDataScenario.js +66 -0
- package/tests/unit/state/apply/transfer/transferNewRecipientAmountScenario.js +31 -0
- package/tests/unit/state/apply/transfer/transferNewRecipientZeroAmountScenario.js +31 -0
- package/tests/unit/state/apply/transfer/transferScenarioHelpers.js +1167 -0
- package/tests/unit/state/apply/transfer/transferSelfTransferAmountScenario.js +38 -0
- package/tests/unit/state/apply/transfer/transferSelfTransferZeroAmountScenario.js +38 -0
- package/tests/unit/state/apply/transfer/transferValidatorRecipientAmountScenario.js +38 -0
- package/tests/unit/state/apply/transfer/transferValidatorRecipientZeroAmountScenario.js +38 -0
- package/tests/unit/state/apply/txOperation/state.apply.txOperation.test.js +318 -0
- package/tests/unit/state/apply/txOperation/txOperationBootstrapNotRegisteredScenario.js +70 -0
- package/tests/unit/state/apply/txOperation/txOperationDifferentValidatorCreatorHappyPathScenario.js +23 -0
- package/tests/unit/state/apply/txOperation/txOperationInvalidDeploymentEntryScenario.js +48 -0
- package/tests/unit/state/apply/txOperation/txOperationInvalidFeeAmountScenario.js +39 -0
- package/tests/unit/state/apply/txOperation/txOperationInvalidSubnetCreatorAddressScenario.js +46 -0
- package/tests/unit/state/apply/txOperation/txOperationRequesterCreatorHappyPathScenario.js +21 -0
- package/tests/unit/state/apply/txOperation/txOperationScenarioHelpers.js +429 -0
- package/tests/unit/state/apply/txOperation/txOperationStandardHappyPathScenario.js +21 -0
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeAddCreatorBalanceFailureScenario.js +26 -0
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeAddValidatorBalanceFailureScenario.js +25 -0
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeAddValidatorBonusFailureScenario.js +27 -0
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeDecodeCreatorEntryScenario.js +18 -0
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeDecodeRequesterEntryScenario.js +17 -0
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeDecodeValidatorEntryScenario.js +31 -0
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeGuardBypassScenario.js +49 -0
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeGuardScenarioFactory.js +92 -0
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeInsufficientRequesterBalanceScenario.js +28 -0
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeInvalidCreatorBalanceScenario.js +29 -0
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeInvalidRequesterBalanceScenario.js +28 -0
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeInvalidRequesterEntryScenario.js +17 -0
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeInvalidValidatorBalanceScenario.js +33 -0
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeMissingCreatorEntryScenario.js +18 -0
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeSubtractFailureScenario.js +25 -0
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeUpdateCreatorBalanceFailureScenario.js +26 -0
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeUpdateFailureScenario.js +25 -0
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeUpdateValidatorBalanceFailureScenario.js +26 -0
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeUpdateValidatorBonusFailureScenario.js +27 -0
- package/tests/unit/state/apply/txOperation/txOperationValidatorCreatorHappyPathScenario.js +21 -0
- package/tests/unit/state/stateModule.test.js +1 -0
- package/tests/unit/state/stateTestUtils.js +5 -1
- package/.env +0 -3
package/rpc/handlers.mjs
CHANGED
|
@@ -1,47 +1,54 @@
|
|
|
1
1
|
import { decodeBase64Payload, isBase64, sanitizeBulkPayloadsRequestBody, sanitizeTransferPayload, validatePayloadStructure } from "./utils/helpers.mjs"
|
|
2
|
-
import { MAX_SIGNED_LENGTH } from "./constants.mjs";
|
|
2
|
+
import { MAX_SIGNED_LENGTH, ZERO_WK } from "./constants.mjs";
|
|
3
|
+
import { buildRequestUrl } from "./utils/url.mjs";
|
|
3
4
|
import { isHexString } from "../src/utils/helpers.js";
|
|
5
|
+
import {
|
|
6
|
+
getBalance,
|
|
7
|
+
getTxv,
|
|
8
|
+
getFee,
|
|
9
|
+
getConfirmedLength,
|
|
10
|
+
getUnconfirmedLength,
|
|
11
|
+
broadcastTransaction,
|
|
12
|
+
getTxHashes,
|
|
13
|
+
getTxDetails,
|
|
14
|
+
fetchBulkTxPayloads,
|
|
15
|
+
getExtendedTxDetails
|
|
16
|
+
} from "./rpc_services.js";
|
|
17
|
+
import { bufferToBigInt, licenseBufferToBigInt } from "../src/utils/amountSerialization.js";
|
|
18
|
+
import { isAddressValid } from "../src/core/state/utils/address.js";
|
|
19
|
+
import { getConfirmedParameter } from "./utils/confirmedParameter.mjs";
|
|
4
20
|
|
|
5
21
|
export async function handleBalance({ req, respond, msbInstance }) {
|
|
6
|
-
const
|
|
7
|
-
const parts =
|
|
22
|
+
const url = buildRequestUrl(req);
|
|
23
|
+
const parts = url.pathname.split("/").filter(Boolean);
|
|
8
24
|
const address = parts[2];
|
|
9
25
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const params = new URLSearchParams(queryString);
|
|
13
|
-
if (params.has("confirmed")) {
|
|
14
|
-
confirmed = params.get("confirmed") === "true";
|
|
15
|
-
}
|
|
16
|
-
}
|
|
26
|
+
const confirmedParam = getConfirmedParameter(url);
|
|
27
|
+
const confirmed = confirmedParam === null ? false : confirmedParam; // invalid -> fallback to unconfirmed
|
|
17
28
|
|
|
29
|
+
// TODO: VALIDATION?
|
|
18
30
|
if (!address) {
|
|
19
31
|
respond(400, { error: 'Wallet address is required' });
|
|
20
32
|
return;
|
|
21
33
|
}
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
const nodeInfo = await msbInstance.handleCommand(commandString);
|
|
34
|
+
|
|
35
|
+
const nodeInfo = await getBalance(msbInstance, address, confirmed);
|
|
25
36
|
const balance = nodeInfo?.balance || 0;
|
|
26
37
|
respond(200, { address, balance });
|
|
27
38
|
}
|
|
28
39
|
|
|
29
40
|
export async function handleTxv({ msbInstance, respond }) {
|
|
30
|
-
const
|
|
31
|
-
const txvRaw = await msbInstance.handleCommand(commandString);
|
|
32
|
-
const txv = txvRaw.toString('hex');
|
|
41
|
+
const txv = await getTxv(msbInstance);
|
|
33
42
|
respond(200, { txv });
|
|
34
43
|
}
|
|
35
44
|
|
|
36
45
|
export async function handleFee({ msbInstance, respond }) {
|
|
37
|
-
const
|
|
38
|
-
const fee = await msbInstance.handleCommand(commandString);
|
|
46
|
+
const fee = await getFee(msbInstance);
|
|
39
47
|
respond(200, { fee });
|
|
40
48
|
}
|
|
41
49
|
|
|
42
50
|
export async function handleConfirmedLength({ msbInstance, respond }) {
|
|
43
|
-
const
|
|
44
|
-
const confirmed_length = await msbInstance.handleCommand(commandString);
|
|
51
|
+
const confirmed_length = await getConfirmedLength(msbInstance);
|
|
45
52
|
respond(200, { confirmed_length });
|
|
46
53
|
}
|
|
47
54
|
|
|
@@ -65,20 +72,20 @@ export async function handleBroadcastTransaction({ msbInstance, respond, req })
|
|
|
65
72
|
const decodedPayload = decodeBase64Payload(payload);
|
|
66
73
|
validatePayloadStructure(decodedPayload);
|
|
67
74
|
const sanitizedPayload = sanitizeTransferPayload(decodedPayload);
|
|
68
|
-
const result = await msbInstance
|
|
75
|
+
const result = await broadcastTransaction(msbInstance, sanitizedPayload);
|
|
69
76
|
respond(200, { result });
|
|
70
77
|
} catch (error) {
|
|
71
78
|
let code = error instanceof SyntaxError ? 400 : 500;
|
|
72
79
|
let errorMsg = code === 400 ? 'Invalid JSON payload.' : 'An error occurred processing the transaction.'
|
|
73
80
|
|
|
74
|
-
if(error.message.includes("Failed to broadcast transaction after multiple attempts.")){
|
|
81
|
+
if (error.message.includes("Failed to broadcast transaction after multiple attempts.")) {
|
|
75
82
|
code = 429;
|
|
76
83
|
errorMsg = "Failed to broadcast transaction after multiple attempts."
|
|
77
84
|
}
|
|
78
|
-
|
|
85
|
+
|
|
79
86
|
console.error('Error in handleBroadcastTransaction:', error);
|
|
80
87
|
// Use 400 for client errors (like bad JSON), 500 for server/command errors
|
|
81
|
-
respond(code, { error:
|
|
88
|
+
respond(code, { error: errorMsg });
|
|
82
89
|
}
|
|
83
90
|
});
|
|
84
91
|
|
|
@@ -89,8 +96,10 @@ export async function handleBroadcastTransaction({ msbInstance, respond, req })
|
|
|
89
96
|
}
|
|
90
97
|
|
|
91
98
|
export async function handleTxHashes({ msbInstance, respond, req }) {
|
|
92
|
-
const
|
|
93
|
-
const
|
|
99
|
+
const url = buildRequestUrl(req);
|
|
100
|
+
const pathParts = url.pathname.split('/').filter(Boolean);
|
|
101
|
+
const startSignedLengthStr = pathParts[2];
|
|
102
|
+
const endSignedLengthStr = pathParts[3];
|
|
94
103
|
|
|
95
104
|
const startSignedLength = parseInt(startSignedLengthStr);
|
|
96
105
|
const endSignedLength = parseInt(endSignedLengthStr);
|
|
@@ -116,27 +125,24 @@ export async function handleTxHashes({ msbInstance, respond, req }) {
|
|
|
116
125
|
}
|
|
117
126
|
|
|
118
127
|
// 4. Get current confirmed length
|
|
119
|
-
const currentConfirmedLength = await msbInstance
|
|
128
|
+
const currentConfirmedLength = await getConfirmedLength(msbInstance);
|
|
120
129
|
|
|
121
130
|
// 5. Adjust the end index to not exceed the confirmed length.
|
|
122
131
|
const adjustedEndLength = Math.min(endSignedLength, currentConfirmedLength)
|
|
123
|
-
|
|
132
|
+
|
|
124
133
|
// 6. Fetch txs hashes for the adjusted range, assuming the command takes start and end index.
|
|
125
|
-
const
|
|
126
|
-
const { hashes } = await msbInstance.handleCommand(commandString);
|
|
134
|
+
const { hashes } = await getTxHashes(msbInstance, startSignedLength, adjustedEndLength);
|
|
127
135
|
respond(200, { hashes });
|
|
128
136
|
}
|
|
129
137
|
|
|
130
138
|
export async function handleUnconfirmedLength({ msbInstance, respond }) {
|
|
131
|
-
const
|
|
132
|
-
const unconfirmed_length = await msbInstance.handleCommand(commandString);
|
|
139
|
+
const unconfirmed_length = await getUnconfirmedLength(msbInstance);
|
|
133
140
|
respond(200, { unconfirmed_length });
|
|
134
141
|
}
|
|
135
142
|
|
|
136
143
|
export async function handleTransactionDetails({ msbInstance, respond, req }) {
|
|
137
144
|
const hash = req.url.split('/')[3];
|
|
138
|
-
const
|
|
139
|
-
const txDetails = await msbInstance.handleCommand(commandString);
|
|
145
|
+
const txDetails = await getTxDetails(msbInstance, hash);
|
|
140
146
|
respond(txDetails === null ? 404 : 200 , { txDetails });
|
|
141
147
|
}
|
|
142
148
|
|
|
@@ -150,9 +156,9 @@ export async function handleFetchBulkTxPayloads({ msbInstance, respond, req }) {
|
|
|
150
156
|
if (headersSent) return; // Stop processing if response has started/errored
|
|
151
157
|
|
|
152
158
|
bytesRead += chunk.length;
|
|
153
|
-
if (bytesRead > limitBytes) {
|
|
159
|
+
if (bytesRead > limitBytes) {
|
|
154
160
|
respond(413, { error: 'Request body too large.' });
|
|
155
|
-
headersSent = true;
|
|
161
|
+
headersSent = true;
|
|
156
162
|
req.destroy(); // Stop receiving data (GOOD PRACTICE)
|
|
157
163
|
return;
|
|
158
164
|
}
|
|
@@ -164,16 +170,16 @@ export async function handleFetchBulkTxPayloads({ msbInstance, respond, req }) {
|
|
|
164
170
|
|
|
165
171
|
|
|
166
172
|
try {
|
|
167
|
-
if (body === null || body === ''){
|
|
173
|
+
if (body === null || body === '') {
|
|
168
174
|
return respond(400, { error: 'Missing payload.' });
|
|
169
175
|
}
|
|
170
176
|
|
|
171
177
|
const sanitizedPayload = sanitizeBulkPayloadsRequestBody(body);
|
|
172
178
|
|
|
173
|
-
if (sanitizedPayload === null){
|
|
179
|
+
if (sanitizedPayload === null) {
|
|
174
180
|
return respond(400, { error: 'Invalid payload.' });
|
|
175
181
|
}
|
|
176
|
-
|
|
182
|
+
|
|
177
183
|
const { hashes } = sanitizedPayload;
|
|
178
184
|
|
|
179
185
|
if (!Array.isArray(hashes) || hashes.length === 0) {
|
|
@@ -186,11 +192,11 @@ export async function handleFetchBulkTxPayloads({ msbInstance, respond, req }) {
|
|
|
186
192
|
|
|
187
193
|
const uniqueHashes = [...new Set(hashes)];
|
|
188
194
|
|
|
189
|
-
const commandResult = await msbInstance
|
|
195
|
+
const commandResult = await fetchBulkTxPayloads(msbInstance, uniqueHashes);
|
|
190
196
|
|
|
191
197
|
const responseString = JSON.stringify(commandResult);
|
|
192
198
|
if (Buffer.byteLength(responseString, 'utf8') > 2_000_000) {
|
|
193
|
-
return respond(413, { error: 'Response too large. Reduce number of hashes.'});
|
|
199
|
+
return respond(413, { error: 'Response too large. Reduce number of hashes.' });
|
|
194
200
|
}
|
|
195
201
|
|
|
196
202
|
return respond(200, commandResult);
|
|
@@ -209,38 +215,26 @@ export async function handleFetchBulkTxPayloads({ msbInstance, respond, req }) {
|
|
|
209
215
|
}
|
|
210
216
|
|
|
211
217
|
export async function handleTransactionExtendedDetails({ msbInstance, respond, req }) {
|
|
212
|
-
const
|
|
213
|
-
const pathParts =
|
|
214
|
-
const hash = pathParts[
|
|
215
|
-
|
|
218
|
+
const url = buildRequestUrl(req);
|
|
219
|
+
const pathParts = url.pathname.split('/').filter(Boolean);
|
|
220
|
+
const hash = pathParts[3];
|
|
221
|
+
|
|
216
222
|
if (!hash) {
|
|
217
223
|
return respond(400, { error: "Transaction hash is required" });
|
|
218
224
|
}
|
|
219
|
-
|
|
225
|
+
|
|
220
226
|
if (isHexString(hash) === false || hash.length !== 64) {
|
|
221
227
|
return respond(400, { error: "Invalid transaction hash format" });
|
|
222
228
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
const confirmedParam = params.get("confirmed");
|
|
228
|
-
if (confirmedParam !== "true" && confirmedParam !== "false") {
|
|
229
|
-
return respond(400, { error: 'Parameter "confirmed" must be exactly "true" or "false"' });
|
|
230
|
-
}
|
|
231
|
-
confirmed = confirmedParam === "true";
|
|
232
|
-
}
|
|
229
|
+
|
|
230
|
+
const confirmed = getConfirmedParameter(url);
|
|
231
|
+
if (confirmed === null) {
|
|
232
|
+
return respond(400, { error: 'Parameter "confirmed" must be exactly "true" or "false"' });
|
|
233
233
|
}
|
|
234
|
-
|
|
234
|
+
|
|
235
235
|
try {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
txDetails = await msbInstance.handleCommand(commandString);
|
|
239
|
-
if (txDetails === null) {
|
|
240
|
-
respond(404, { error: `No payload found for tx hash: ${hash}` });
|
|
241
|
-
} else {
|
|
242
|
-
respond(200, txDetails);
|
|
243
|
-
}
|
|
236
|
+
const details = await getExtendedTxDetails(msbInstance, hash, confirmed);
|
|
237
|
+
respond(200, details);
|
|
244
238
|
} catch (error) {
|
|
245
239
|
if (error.message?.includes('No payload found for tx hash')) {
|
|
246
240
|
respond(404, { error: error.message });
|
|
@@ -249,4 +243,53 @@ export async function handleTransactionExtendedDetails({ msbInstance, respond, r
|
|
|
249
243
|
respond(500, { error: 'An error occurred processing the request.' });
|
|
250
244
|
}
|
|
251
245
|
}
|
|
252
|
-
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export async function handleAccountDetails({ msbInstance, respond, req }) {
|
|
249
|
+
const url = buildRequestUrl(req);
|
|
250
|
+
const address = url.pathname.split('/').filter(Boolean)[2];
|
|
251
|
+
|
|
252
|
+
if (!address) {
|
|
253
|
+
return respond(400, { error: "Account address is required" });
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const confirmed = getConfirmedParameter(url);
|
|
257
|
+
if (confirmed === null) {
|
|
258
|
+
return respond(400, { error: 'Parameter "confirmed" must be exactly "true" or "false"' });
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (!isAddressValid(address)) {
|
|
262
|
+
return respond(400, { error: "Invalid account address format" });
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const defaultAccountState = {
|
|
266
|
+
address,
|
|
267
|
+
writingKey: ZERO_WK.toString('hex'),
|
|
268
|
+
isWhitelisted: false,
|
|
269
|
+
isValidator: false,
|
|
270
|
+
isIndexer: false,
|
|
271
|
+
license: null,
|
|
272
|
+
balance: '0',
|
|
273
|
+
stakedBalance: '0',
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
const nodeEntry = confirmed
|
|
277
|
+
? await msbInstance.state.getNodeEntry(address)
|
|
278
|
+
: await msbInstance.state.getNodeEntryUnsigned(address);
|
|
279
|
+
if (!nodeEntry) {
|
|
280
|
+
return respond(200, defaultAccountState);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const licenseValue = licenseBufferToBigInt(nodeEntry.license);
|
|
284
|
+
|
|
285
|
+
return respond(200, {
|
|
286
|
+
...defaultAccountState,
|
|
287
|
+
writingKey: nodeEntry.wk.toString('hex'),
|
|
288
|
+
isWhitelisted: nodeEntry.isWhitelisted,
|
|
289
|
+
isValidator: nodeEntry.isWriter,
|
|
290
|
+
isIndexer: nodeEntry.isIndexer,
|
|
291
|
+
license: licenseValue === 0n ? null : licenseValue.toString(),
|
|
292
|
+
balance: bufferToBigInt(nodeEntry.balance).toString(),
|
|
293
|
+
stakedBalance: bufferToBigInt(nodeEntry.stakedBalance).toString(),
|
|
294
|
+
});
|
|
295
|
+
}
|
package/rpc/routes/v1.mjs
CHANGED
|
@@ -8,7 +8,8 @@ import {
|
|
|
8
8
|
handleUnconfirmedLength,
|
|
9
9
|
handleTransactionDetails,
|
|
10
10
|
handleFetchBulkTxPayloads,
|
|
11
|
-
handleTransactionExtendedDetails
|
|
11
|
+
handleTransactionExtendedDetails,
|
|
12
|
+
handleAccountDetails
|
|
12
13
|
} from '../handlers.mjs';
|
|
13
14
|
|
|
14
15
|
export const v1Routes = [
|
|
@@ -22,4 +23,5 @@ export const v1Routes = [
|
|
|
22
23
|
{ method: 'GET', path: '/tx', handler: handleTransactionDetails },
|
|
23
24
|
{ method: 'POST', path: '/tx-payloads-bulk', handler: handleFetchBulkTxPayloads },
|
|
24
25
|
{ method: 'GET', path: '/tx/details', handler: handleTransactionExtendedDetails },
|
|
26
|
+
{ method: 'GET', path: '/account', handler: handleAccountDetails },
|
|
25
27
|
];
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { bufferToBigInt } from "../src/utils/amountSerialization.js";
|
|
2
|
+
import { normalizeDecodedPayloadForJson } from "../src/utils/normalizers.js";
|
|
3
|
+
import { get_confirmed_tx_info, get_unconfirmed_tx_info } from "../src/utils/cli.js";
|
|
4
|
+
|
|
5
|
+
export async function getBalance(msbInstance, address, confirmed) {
|
|
6
|
+
const state = msbInstance.state;
|
|
7
|
+
const useUnconfirmed = confirmed === false;
|
|
8
|
+
|
|
9
|
+
const nodeEntry = useUnconfirmed
|
|
10
|
+
? await state.getNodeEntryUnsigned(address)
|
|
11
|
+
: await state.getNodeEntry(address);
|
|
12
|
+
|
|
13
|
+
if (!nodeEntry) return undefined;
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
address,
|
|
17
|
+
balance: bufferToBigInt(nodeEntry.balance).toString(),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function getTxv(msbInstance) {
|
|
22
|
+
const txv = await msbInstance.state.getIndexerSequenceState();
|
|
23
|
+
return txv.toString("hex");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function getFee(msbInstance) {
|
|
27
|
+
const feeBuffer = msbInstance.state.getFee();
|
|
28
|
+
return bufferToBigInt(feeBuffer).toString();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export async function getConfirmedLength(msbInstance) {
|
|
32
|
+
return msbInstance.state.getSignedLength();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export async function getUnconfirmedLength(msbInstance) {
|
|
36
|
+
return msbInstance.state.getUnsignedLength();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function broadcastTransaction(msbInstance, payload) {
|
|
40
|
+
return msbInstance.broadcastTransactionCommand(payload);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function getTxHashes(msbInstance, start, end) {
|
|
44
|
+
const hashes = await msbInstance.state.confirmedTransactionsBetween(start, end);
|
|
45
|
+
return { hashes };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function getTxDetails(msbInstance, hash) {
|
|
49
|
+
const rawPayload = await get_confirmed_tx_info(msbInstance.state, hash);
|
|
50
|
+
if (!rawPayload) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return normalizeDecodedPayloadForJson(rawPayload.decoded);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export async function fetchBulkTxPayloads(msbInstance, hashes) {
|
|
58
|
+
if (!Array.isArray(hashes) || hashes.length === 0) {
|
|
59
|
+
throw new Error("Missing hash list.");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (hashes.length > 1500) {
|
|
63
|
+
throw new Error("Length of input tx hashes exceeded.");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const res = { results: [], missing: [] };
|
|
67
|
+
|
|
68
|
+
const promises = hashes.map((hash) => get_confirmed_tx_info(msbInstance.state, hash));
|
|
69
|
+
const results = await Promise.all(promises);
|
|
70
|
+
|
|
71
|
+
results.forEach((result, index) => {
|
|
72
|
+
const hash = hashes[index];
|
|
73
|
+
if (result === null || result === undefined) {
|
|
74
|
+
res.missing.push(hash);
|
|
75
|
+
} else {
|
|
76
|
+
const decodedResult = normalizeDecodedPayloadForJson(result.decoded);
|
|
77
|
+
res.results.push({ hash, payload: decodedResult });
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
return res;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export async function getExtendedTxDetails(msbInstance, hash, confirmed) {
|
|
85
|
+
const state = msbInstance.state;
|
|
86
|
+
|
|
87
|
+
if (confirmed) {
|
|
88
|
+
const rawPayload = await get_confirmed_tx_info(state, hash);
|
|
89
|
+
if (!rawPayload) {
|
|
90
|
+
throw new Error(`No payload found for tx hash: ${hash}`);
|
|
91
|
+
}
|
|
92
|
+
const confirmedLength = await state.getTransactionConfirmedLength(hash);
|
|
93
|
+
if (confirmedLength === null) {
|
|
94
|
+
throw new Error(`No confirmed length found for tx hash: ${hash} in confirmed mode`);
|
|
95
|
+
}
|
|
96
|
+
const normalizedPayload = normalizeDecodedPayloadForJson(rawPayload.decoded, true);
|
|
97
|
+
const feeBuffer = state.getFee();
|
|
98
|
+
return {
|
|
99
|
+
txDetails: normalizedPayload,
|
|
100
|
+
confirmed_length: confirmedLength,
|
|
101
|
+
fee: bufferToBigInt(feeBuffer).toString(),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const rawPayload = await get_unconfirmed_tx_info(state, hash);
|
|
106
|
+
if (!rawPayload) {
|
|
107
|
+
throw new Error(`No payload found for tx hash: ${hash}`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const normalizedPayload = normalizeDecodedPayloadForJson(rawPayload.decoded, true);
|
|
111
|
+
const length = await state.getTransactionConfirmedLength(hash);
|
|
112
|
+
if (length === null) {
|
|
113
|
+
return {
|
|
114
|
+
txDetails: normalizedPayload,
|
|
115
|
+
confirmed_length: 0,
|
|
116
|
+
fee: "0",
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const feeBuffer = state.getFee();
|
|
121
|
+
return {
|
|
122
|
+
txDetails: normalizedPayload,
|
|
123
|
+
confirmed_length: length,
|
|
124
|
+
fee: bufferToBigInt(feeBuffer).toString(),
|
|
125
|
+
};
|
|
126
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function getConfirmedParameter(url, { defaultValue = true } = {}) {
|
|
2
|
+
const confirmedParam = url.searchParams.get("confirmed");
|
|
3
|
+
|
|
4
|
+
if (confirmedParam === null) {
|
|
5
|
+
return defaultValue;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
if (confirmedParam === "true") {
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (confirmedParam === "false") {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const HOST_HEADER_PATTERN = /^[\[\]A-Za-z0-9:.-]+(:\d{1,5})?$/;
|
|
2
|
+
|
|
3
|
+
const isValidHostHeader = (hostHeader) => {
|
|
4
|
+
if (!hostHeader) return false;
|
|
5
|
+
if (hostHeader.length > 255) return false;
|
|
6
|
+
const trimmed = hostHeader.trim();
|
|
7
|
+
|
|
8
|
+
if (/[\/\\\s]/.test(trimmed)) return false;
|
|
9
|
+
|
|
10
|
+
return HOST_HEADER_PATTERN.test(trimmed);
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const socketAddressToHost = (address, port) => {
|
|
14
|
+
if (!address) return null;
|
|
15
|
+
const needsBrackets = address.includes(':') && !address.startsWith('[');
|
|
16
|
+
const host = needsBrackets ? `[${address}]` : address;
|
|
17
|
+
return port ? `${host}:${port}` : host;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const detectProtocol = (req) => {
|
|
21
|
+
const forwardedProto = req?.headers?.['x-forwarded-proto'];
|
|
22
|
+
if (forwardedProto === 'https') return 'https';
|
|
23
|
+
if (forwardedProto === 'http') return 'http';
|
|
24
|
+
|
|
25
|
+
if (req?.socket?.encrypted) return 'https';
|
|
26
|
+
|
|
27
|
+
return 'http';
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const buildRequestUrl = (req) => {
|
|
31
|
+
const safeHost = isValidHostHeader(req?.headers?.host)
|
|
32
|
+
? req.headers.host.trim()
|
|
33
|
+
: socketAddressToHost(req?.socket?.localAddress, req?.socket?.localPort);
|
|
34
|
+
|
|
35
|
+
const protocol = detectProtocol(req);
|
|
36
|
+
const base = safeHost ? `${protocol}://${safeHost}` : `${protocol}://localhost`;
|
|
37
|
+
return new URL(req?.url || '/', base);
|
|
38
|
+
};
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
DHT_BOOTSTRAPS
|
|
18
18
|
} from '../../utils/constants.js';
|
|
19
19
|
import ConnectionManager from './services/ConnectionManager.js';
|
|
20
|
-
|
|
20
|
+
import NetworkWalletFactory from './identity/NetworkWalletFactory.js';
|
|
21
21
|
const wakeup = new w();
|
|
22
22
|
|
|
23
23
|
class Network extends ReadyResource {
|
|
@@ -29,15 +29,18 @@ class Network extends ReadyResource {
|
|
|
29
29
|
#transactionPoolService;
|
|
30
30
|
#validatorObserverService;
|
|
31
31
|
#validatorConnectionManager;
|
|
32
|
+
#options;
|
|
33
|
+
#identityProvider = null;
|
|
32
34
|
|
|
33
35
|
constructor(state, channel, address = null, options = {}) {
|
|
34
36
|
super();
|
|
37
|
+
this.#options = options;
|
|
35
38
|
this.#enable_wallet = options.enable_wallet !== false;
|
|
36
39
|
this.#channel = channel;
|
|
37
40
|
this.#transactionPoolService = new TransactionPoolService(state, address, options);
|
|
38
|
-
this.#validatorObserverService = new ValidatorObserverService(this, state, address, options)
|
|
41
|
+
this.#validatorObserverService = new ValidatorObserverService(this, state, address, options);
|
|
39
42
|
this.#networkMessages = new NetworkMessages(this, options);
|
|
40
|
-
this.#validatorConnectionManager = new ConnectionManager({ maxValidators: options.max_validators })
|
|
43
|
+
this.#validatorConnectionManager = new ConnectionManager({ maxValidators: options.max_validators });
|
|
41
44
|
this.admin_stream = null;
|
|
42
45
|
this.admin = null;
|
|
43
46
|
this.validator = null;
|
|
@@ -90,6 +93,7 @@ class Network extends ReadyResource {
|
|
|
90
93
|
) {
|
|
91
94
|
if (!this.#swarm) {
|
|
92
95
|
const keyPair = await this.initializeNetworkingKeyPair(store, wallet);
|
|
96
|
+
const wrappedWallet = this.#getNetworkWalletWrapper(wallet, keyPair);
|
|
93
97
|
this.#swarm = new Hyperswarm({
|
|
94
98
|
keyPair,
|
|
95
99
|
bootstrap: this.#dht_bootstrap,
|
|
@@ -100,7 +104,7 @@ class Network extends ReadyResource {
|
|
|
100
104
|
});
|
|
101
105
|
|
|
102
106
|
console.log(`Channel: ${b4a.toString(this.#channel)}`);
|
|
103
|
-
this.#networkMessages.initializeMessageRouter(state,
|
|
107
|
+
this.#networkMessages.initializeMessageRouter(state, wrappedWallet);
|
|
104
108
|
|
|
105
109
|
this.#swarm.on('connection', async (connection) => {
|
|
106
110
|
const { message_channel, message } = await this.#networkMessages.setupProtomuxMessages(connection);
|
|
@@ -120,7 +124,7 @@ class Network extends ReadyResource {
|
|
|
120
124
|
this.custom_stream = null;
|
|
121
125
|
this.custom_node = null;
|
|
122
126
|
}
|
|
123
|
-
try{ message_channel.close() }catch(e){}
|
|
127
|
+
try { message_channel.close() } catch (e) { }
|
|
124
128
|
|
|
125
129
|
});
|
|
126
130
|
|
|
@@ -129,7 +133,7 @@ class Network extends ReadyResource {
|
|
|
129
133
|
error && error.message && (
|
|
130
134
|
error.message.includes('connection reset by peer') ||
|
|
131
135
|
error.message.includes('Duplicate connection') ||
|
|
132
|
-
error.message.includes('connection timed out')
|
|
136
|
+
error.message.includes('connection timed out'))
|
|
133
137
|
) {
|
|
134
138
|
// TODO: decide if we want to handle this error in a specific way. It generates a lot of logs.
|
|
135
139
|
return;
|
|
@@ -168,11 +172,11 @@ class Network extends ReadyResource {
|
|
|
168
172
|
cnt += 1;
|
|
169
173
|
}
|
|
170
174
|
}
|
|
171
|
-
|
|
175
|
+
|
|
172
176
|
if (this.#swarm.peers.has(publicKey)) {
|
|
173
177
|
let stream;
|
|
174
178
|
const peerInfo = this.#swarm.peers.get(publicKey)
|
|
175
|
-
stream = this.#swarm._allConnections.get(peerInfo.publicKey)
|
|
179
|
+
stream = this.#swarm._allConnections.get(peerInfo.publicKey)
|
|
176
180
|
if (stream !== undefined && stream.messenger !== undefined) {
|
|
177
181
|
if (type === 'validator') {
|
|
178
182
|
this.#validatorConnectionManager.addValidator(b4a.from(publicKey, 'hex'), stream)
|
|
@@ -183,8 +187,8 @@ class Network extends ReadyResource {
|
|
|
183
187
|
}
|
|
184
188
|
|
|
185
189
|
async isConnected(publicKey) {
|
|
186
|
-
return this.#swarm.peers.has(publicKey) &&
|
|
187
|
-
|
|
190
|
+
return this.#swarm.peers.has(publicKey) &&
|
|
191
|
+
this.#swarm.peers.get(publicKey).connectedTime != -1
|
|
188
192
|
}
|
|
189
193
|
|
|
190
194
|
async #sendRequestByType(stream, type) {
|
|
@@ -239,6 +243,19 @@ class Network extends ReadyResource {
|
|
|
239
243
|
console.log(e)
|
|
240
244
|
}
|
|
241
245
|
}
|
|
246
|
+
|
|
247
|
+
#getNetworkWalletWrapper(wallet, keyPair) {
|
|
248
|
+
if (!this.#identityProvider) {
|
|
249
|
+
this.#identityProvider = NetworkWalletFactory.provide({
|
|
250
|
+
enableWallet: this.#enable_wallet,
|
|
251
|
+
wallet,
|
|
252
|
+
keyPair,
|
|
253
|
+
networkPrefix: this.#options?.networkPrefix
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
return this.#identityProvider;
|
|
257
|
+
}
|
|
258
|
+
|
|
242
259
|
}
|
|
243
260
|
|
|
244
261
|
export default Network;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import PeerWallet from 'trac-wallet';
|
|
2
|
+
import { TRAC_NETWORK_MSB_MAINNET_PREFIX } from 'trac-wallet/constants.js';
|
|
3
|
+
import b4a from 'b4a';
|
|
4
|
+
|
|
5
|
+
export class NetworkWalletFactory {
|
|
6
|
+
static provide(options = {}) {
|
|
7
|
+
const {
|
|
8
|
+
enableWallet = true,
|
|
9
|
+
wallet,
|
|
10
|
+
keyPair,
|
|
11
|
+
networkPrefix = TRAC_NETWORK_MSB_MAINNET_PREFIX
|
|
12
|
+
} = options;
|
|
13
|
+
|
|
14
|
+
if (enableWallet) {
|
|
15
|
+
if (!wallet) {
|
|
16
|
+
throw new Error('NetworkingWalletFactory: wallet instance is required when wallet is enabled');
|
|
17
|
+
}
|
|
18
|
+
return wallet;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (!keyPair) {
|
|
22
|
+
throw new Error('NetworkingWalletFactory: keyPair must be provided when wallet is disabled');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return new EphemeralWallet(keyPair, networkPrefix);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// TODO: Once Wallet class in trac-wallet exposes a constructor/factory that accepts an existing keyPair
|
|
30
|
+
// (e.g. Wallet.fromKeyPair({ publicKey, secretKey }, networkPrefix)), replace EphemeralWallet
|
|
31
|
+
// with a thin wrapper around that functionality instead of duplicating signing/verification logic.
|
|
32
|
+
class EphemeralWallet {
|
|
33
|
+
#publicKey;
|
|
34
|
+
#secretKey;
|
|
35
|
+
#address;
|
|
36
|
+
|
|
37
|
+
constructor(keyPair, networkPrefix = TRAC_NETWORK_MSB_MAINNET_PREFIX) {
|
|
38
|
+
|
|
39
|
+
if (!keyPair?.publicKey || !keyPair?.secretKey) {
|
|
40
|
+
throw new Error('NetworkIdentityProvider: keyPair with publicKey and secretKey is required');
|
|
41
|
+
}
|
|
42
|
+
this.#assertBuffer(keyPair.publicKey);
|
|
43
|
+
this.#assertBuffer(keyPair.secretKey);
|
|
44
|
+
|
|
45
|
+
const address = PeerWallet.encodeBech32m(networkPrefix, keyPair.publicKey);
|
|
46
|
+
if (!address) {
|
|
47
|
+
throw new Error('NetworkIdentityProvider: failed to derive address from networking key pair');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
this.#publicKey = keyPair.publicKey;
|
|
51
|
+
this.#secretKey = keyPair.secretKey;
|
|
52
|
+
this.#address = address;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
get publicKey() {
|
|
56
|
+
return this.#publicKey;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
get address() {
|
|
60
|
+
return this.#address;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
sign(message) {
|
|
64
|
+
return PeerWallet.sign(message, this.#secretKey);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
verify(signature, message, publicKey = this.#publicKey) {
|
|
68
|
+
return PeerWallet.verify(signature, message, publicKey);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
#assertBuffer(value) {
|
|
72
|
+
if (!b4a.isBuffer(value)) {
|
|
73
|
+
throw new Error(`NetworkIdentityProvider: value must be a Buffer`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export default NetworkWalletFactory;
|