mirror of
https://github.com/minibits-cash/minibits_wallet.git
synced 2026-10-05 19:28:26 +00:00
Improved validation of keysets provided by the mints
This commit is contained in:
Vendored
+1
-1
@@ -1 +1 @@
|
|||||||
0198388d-d8dc-72f2-b3e1-89f896656f07
|
0198586c-6212-732d-a2cb-cf9537d9aab3
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "minibits_wallet",
|
"name": "minibits_wallet",
|
||||||
"version": "0.2.2-beta.29",
|
"version": "0.2.2-beta.30",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"android:clean": "cd android && ./gradlew clean",
|
"android:clean": "cd android && ./gradlew clean",
|
||||||
|
|||||||
@@ -39,21 +39,24 @@ export const ErrorModal: FC<ErrorModalProps> = function ({ error }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const backgroundColor = useThemeColor('error')
|
const bg = useThemeColor('error')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BottomModal
|
<BottomModal
|
||||||
isVisible={isErrorVisible}
|
isVisible={isErrorVisible}
|
||||||
onBackdropPress={onClose}
|
onBackdropPress={onClose}
|
||||||
onBackButtonPress={onClose}
|
onBackButtonPress={onClose}
|
||||||
style={{ backgroundColor }}
|
|
||||||
ContentComponent={
|
ContentComponent={
|
||||||
<>
|
<>
|
||||||
<View style={{ flexDirection: 'row', alignItems: 'center', marginBottom: spacing.small }}>
|
<View style={{ flexDirection: 'row', alignItems: 'center', marginBottom: spacing.small }}>
|
||||||
<Icon icon="faInfoCircle" size={spacing.large} color="white" />
|
<Icon icon="faTriangleExclamation" size={spacing.large} color={bg} />
|
||||||
<Text style={{ color: 'white', marginLeft: spacing.small }}>{error.name}</Text>
|
<Text style={{ color: 'white', marginLeft: spacing.small }}>{error.name}</Text>
|
||||||
</View>
|
</View>
|
||||||
<ScrollView>
|
<ScrollView
|
||||||
|
style={{
|
||||||
|
//height: spacing.screenHeight * 0.1,
|
||||||
|
maxHeight: spacing.screenHeight * 0.07,
|
||||||
|
}}>
|
||||||
<Text style={{ color: 'white', marginBottom: spacing.small }}>{error.message}</Text>
|
<Text style={{ color: 'white', marginBottom: spacing.small }}>{error.message}</Text>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
{error.params && isObj(error.params) && (
|
{error.params && isObj(error.params) && (
|
||||||
|
|||||||
+41
-37
@@ -222,51 +222,55 @@ export const MintModel = types
|
|||||||
}))
|
}))
|
||||||
.actions(self => ({
|
.actions(self => ({
|
||||||
initKeyset(keyset: CashuMintKeyset, allKeysetIds: string[]) {
|
initKeyset(keyset: CashuMintKeyset, allKeysetIds: string[]) {
|
||||||
|
// ATTN: const mintsStore = getRootStore(self).mintsStore does not work here (may be because it is being called from within loop)
|
||||||
|
|
||||||
// Do not add unit the wallet does not have configured
|
// Do not add unit the wallet does not have configured
|
||||||
try {
|
|
||||||
if(!self.isUnitSupported(keyset.unit as MintUnit)) {
|
if(!self.isUnitSupported(keyset.unit as MintUnit)) {
|
||||||
throw new AppError(Err.VALIDATION_ERROR, `Unsupported unit provided by the mint: ${keyset.unit}`)
|
throw new AppError(
|
||||||
}
|
Err.VALIDATION_ERROR,
|
||||||
|
`Unsupported unit provided by the mint`,
|
||||||
const existing = self.keysets.find(k => k.id === keyset.id)
|
{caller: 'initKeyset', unit: keyset.unit}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const existing = self.keysets.find(k => k.id === keyset.id)
|
||||||
|
|
||||||
if(existing) {
|
if(existing) {
|
||||||
if (existing.unit !== keyset.unit) {
|
if (existing.unit !== keyset.unit) {
|
||||||
throw new AppError(
|
|
||||||
Err.VALIDATION_ERROR,
|
|
||||||
`Keyset unit mismatch, got ${keyset.unit}, expected ${existing.unit}`,
|
|
||||||
{caller: 'initKeyset'}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if(keyset.input_fee_ppk && existing.input_fee_ppk !== keyset.input_fee_ppk) {
|
|
||||||
self.setInputFeePpk(existing.id, keyset.input_fee_ppk)
|
|
||||||
}
|
|
||||||
|
|
||||||
return existing
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prevent keysetId collision with other mints
|
|
||||||
if(CashuUtils.isCollidingKeysetId(keyset.id, allKeysetIds)) {
|
|
||||||
throw new AppError(
|
throw new AppError(
|
||||||
Err.VALIDATION_ERROR,
|
Err.VALIDATION_ERROR,
|
||||||
`KeysetId validation failed, collision detected for ${keyset.id}`,
|
`Keyset unit mismatch.`,
|
||||||
{caller: 'initKeyset'}
|
{caller: 'initKeyset', existingUnit: existing.unit, keysetUnit: keyset.unit}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!keyset.input_fee_ppk) {
|
if(keyset.input_fee_ppk && existing.input_fee_ppk !== keyset.input_fee_ppk) {
|
||||||
keyset.input_fee_ppk = 0
|
self.setInputFeePpk(existing.id, keyset.input_fee_ppk)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.addKeyset(keyset)
|
return existing
|
||||||
self.addUnit(keyset.unit as MintUnit)
|
}
|
||||||
self.createProofsCounter(keyset)
|
|
||||||
|
|
||||||
log.trace('[initKeyset]', {newKeyset: keyset})
|
// Prevent keysetId collision with other mints
|
||||||
} catch (e: any) {
|
if(CashuUtils.isCollidingKeysetId(keyset.id, allKeysetIds)) {
|
||||||
throw new AppError(Err.WALLET_ERROR, '[initKeyset] ' + e.message)
|
throw new AppError(
|
||||||
}
|
Err.VALIDATION_ERROR,
|
||||||
|
`KeysetId validation failed, collision detected.`,
|
||||||
|
{caller: 'initKeyset', keysetId: keyset.id}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!keyset.input_fee_ppk) {
|
||||||
|
keyset.input_fee_ppk = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
self.addKeyset(keyset)
|
||||||
|
self.addUnit(keyset.unit as MintUnit)
|
||||||
|
self.createProofsCounter(keyset)
|
||||||
|
|
||||||
|
log.trace('[initKeyset]', {newKeyset: keyset})
|
||||||
|
|
||||||
},
|
},
|
||||||
initKeys(key: CashuMintKeys) {
|
initKeys(key: CashuMintKeys) {
|
||||||
// Do not add unit the wallet does not have configured
|
// Do not add unit the wallet does not have configured
|
||||||
@@ -432,7 +436,7 @@ export const MintModel = types
|
|||||||
const counters = self.proofsCounters.filter(c => c.inFlightRequests && c.inFlightRequests.length > 0)
|
const counters = self.proofsCounters.filter(c => c.inFlightRequests && c.inFlightRequests.length > 0)
|
||||||
return counters as MintProofsCounter[]
|
return counters as MintProofsCounter[]
|
||||||
},
|
},
|
||||||
get allInFLightRequests(): InFlightRequest[] {
|
get allInFlightRequests(): InFlightRequest[] {
|
||||||
const requests = self.proofsCounters
|
const requests = self.proofsCounters
|
||||||
.flatMap((counter) => counter.inFlightRequests) // Combine all `inFlightRequests` arrays
|
.flatMap((counter) => counter.inFlightRequests) // Combine all `inFlightRequests` arrays
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import type {
|
|||||||
PaymentRequest as CashuPaymentRequest,
|
PaymentRequest as CashuPaymentRequest,
|
||||||
PaymentRequestPayload,
|
PaymentRequestPayload,
|
||||||
} from '@cashu/cashu-ts'
|
} from '@cashu/cashu-ts'
|
||||||
|
import { bytesToHex } from '@noble/hashes/utils'
|
||||||
import AppError, {Err} from '../../utils/AppError'
|
import AppError, {Err} from '../../utils/AppError'
|
||||||
import { getDecodedToken } from '@cashu/cashu-ts'
|
import { getDecodedToken } from '@cashu/cashu-ts'
|
||||||
import {Proof} from '../../models/Proof'
|
import {Proof} from '../../models/Proof'
|
||||||
@@ -13,6 +14,7 @@ import { log } from '../logService'
|
|||||||
import { decodePaymentRequest, sumProofs } from '@cashu/cashu-ts/src/utils'
|
import { decodePaymentRequest, sumProofs } from '@cashu/cashu-ts/src/utils'
|
||||||
import { NostrClient } from '../nostrService'
|
import { NostrClient } from '../nostrService'
|
||||||
import { getUnixTime } from 'date-fns/getUnixTime'
|
import { getUnixTime } from 'date-fns/getUnixTime'
|
||||||
|
import { Text } from '../../components'
|
||||||
|
|
||||||
export {CashuProof}
|
export {CashuProof}
|
||||||
|
|
||||||
@@ -276,35 +278,42 @@ const validateMintKeys = function (keys: object): boolean {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getKeysetIdInt(keysetIdHex: string) {
|
function getKeysetIdInt(keysetId: string): bigint {
|
||||||
return parseInt(`0x${keysetIdHex}`, 16) % (2 ** 31 - 1)
|
if (/^[0-9a-fA-F]+$/.test(keysetId)) {
|
||||||
|
return BigInt(`0x${keysetId}`) % BigInt(2 ** 31 - 1)
|
||||||
|
} else {
|
||||||
|
const bin = atob(keysetId)
|
||||||
|
const hex = bytesToHex(new TextEncoder().encode(bin))
|
||||||
|
return BigInt(`0x${hex}`) % BigInt(2 ** 31 - 1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function isCollidingKeysetId(
|
function isCollidingKeysetId(
|
||||||
newKeysetIdHex: string,
|
newKeysetId: string,
|
||||||
storedKeysetIds: string[],
|
storedKeysetIds: string[],
|
||||||
) {
|
) {
|
||||||
const newKeysetIdInt = getKeysetIdInt(newKeysetIdHex)
|
const newKeysetIdInt = getKeysetIdInt(newKeysetId)
|
||||||
return storedKeysetIds.some((storedId) => {
|
return storedKeysetIds.some((storedId) => {
|
||||||
const storedKeysetIdInt = getKeysetIdInt(storedId)
|
|
||||||
if (storedId === newKeysetIdHex) {
|
if (storedId === newKeysetId) {
|
||||||
// Colliding keyset ID!
|
// Colliding keyset ID!
|
||||||
log.error('[isCollidingKeysetId] Colliding keyset ID', {
|
log.error('[isCollidingKeysetId] Colliding keyset ID', {
|
||||||
newKeysetIdHex,
|
newKeysetId,
|
||||||
storedId,
|
storedId,
|
||||||
newKeysetIdInt,
|
|
||||||
storedKeysetIdInt,
|
|
||||||
})
|
})
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const storedKeysetIdInt = getKeysetIdInt(storedId)
|
||||||
|
|
||||||
if (storedKeysetIdInt === newKeysetIdInt) {
|
if (storedKeysetIdInt === newKeysetIdInt) {
|
||||||
// Colliding keyset ID integer!
|
// Colliding keyset ID integer!
|
||||||
log.error('[isCollidingKeysetId] Colliding keyset ID integer', {
|
log.error('[isCollidingKeysetId] Colliding keyset ID integer', {
|
||||||
newKeysetIdHex,
|
newKeysetId,
|
||||||
storedId,
|
storedId,
|
||||||
newKeysetIdInt,
|
newKeysetIdInt: newKeysetIdInt.toString(),
|
||||||
storedKeysetIdInt,
|
storedKeysetIdInt: storedKeysetIdInt.toString(),
|
||||||
})
|
})
|
||||||
|
|
||||||
return true
|
return true
|
||||||
@@ -397,3 +406,5 @@ export const CashuUtils = {
|
|||||||
isTokenP2PKLocked,
|
isTokenP2PKLocked,
|
||||||
isCollidingKeysetId
|
isCollidingKeysetId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1164,7 +1164,7 @@ const handleInFlightByMintTask = async function (mint: Mint): Promise<WalletTask
|
|||||||
|
|
||||||
log.trace('[handleInFlightByMintTask]', {mintUrl: mint.mintUrl, inFlightCounters})
|
log.trace('[handleInFlightByMintTask]', {mintUrl: mint.mintUrl, inFlightCounters})
|
||||||
|
|
||||||
const allInFlightRequestLength = mint.allInFLightRequests?.length
|
const allInFlightRequestLength = mint.allInFlightRequests?.length
|
||||||
const errors: string[] = []
|
const errors: string[] = []
|
||||||
|
|
||||||
if(inFlightCounters && inFlightCounters.length > 0) {
|
if(inFlightCounters && inFlightCounters.length > 0) {
|
||||||
|
|||||||
Reference in New Issue
Block a user