diff --git a/.github/workflows/build-release-apk.yml b/.github/workflows/build-release-apk.yml index dce0ed1bb..c27011ca1 100644 --- a/.github/workflows/build-release-apk.yml +++ b/.github/workflows/build-release-apk.yml @@ -100,7 +100,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Set up Ruby uses: ruby/setup-ruby@v1 diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index d3aa27674..3cbc3e588 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -164,7 +164,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 @@ -196,7 +196,7 @@ jobs: sudo chown -R runner /mnt/artifacts - name: Specify node version - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: 24 cache: 'npm' diff --git a/LICENSE b/LICENSE index 7c6a630f9..ca4e589c8 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2024 BlueWallet developers +Copyright (c) 2026 BlueWallet developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index f950b26f2..5f1147a21 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # BlueWallet - A Bitcoin & Lightning Wallet [![GitHub tag](https://img.shields.io/badge/dynamic/json.svg?url=https://raw.githubusercontent.com/BlueWallet/BlueWallet/master/package.json&query=$.version&label=Version)](https://github.com/BlueWallet/BlueWallet) -[![CircleCI](https://circleci.com/gh/BlueWallet/BlueWallet.svg?style=svg)](https://circleci.com/gh/BlueWallet/BlueWallet) [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier) ![](https://img.shields.io/github/license/BlueWallet/BlueWallet.svg) diff --git a/class/synced-async-storage.ts b/class/synced-async-storage.ts deleted file mode 100644 index e9144e757..000000000 --- a/class/synced-async-storage.ts +++ /dev/null @@ -1,160 +0,0 @@ -import AsyncStorage from '@react-native-async-storage/async-storage'; -import AES from 'crypto-js/aes'; -import ENCHEX from 'crypto-js/enc-hex'; -import ENCUTF8 from 'crypto-js/enc-utf8'; -import SHA256 from 'crypto-js/sha256'; - -export default class SyncedAsyncStorage { - defaultBaseUrl = 'https://bytes-store.herokuapp.com'; - encryptionMarker = 'encrypted://'; - - namespace: string = ''; - encryptionKey: string = ''; - - constructor(entropy: string) { - if (!entropy) throw new Error('entropy not provided'); - - this.namespace = this.hashIt(this.hashIt('namespace' + entropy)); - this.encryptionKey = this.hashIt(this.hashIt('encryption' + entropy)); - } - - hashIt(arg: string) { - return ENCHEX.stringify(SHA256(arg)); - } - - encrypt(clearData: string): string { - return this.encryptionMarker + AES.encrypt(clearData, this.encryptionKey).toString(); - } - - decrypt(encryptedData: string | null, encryptionKey: string | null = null): string { - if (encryptedData === null) return ''; - if (!encryptedData.startsWith(this.encryptionMarker)) return encryptedData; - const bytes = AES.decrypt(encryptedData.replace(this.encryptionMarker, ''), encryptionKey || this.encryptionKey); - return bytes.toString(ENCUTF8); - } - - static assertEquals(a: any, b: any) { - if (a !== b) throw new Error('Assertion failed that ' + a + ' equals ' + b); - } - - static assertNotEquals(a: any, b: any) { - if (a === b) throw new Error('Assertion failed that ' + a + ' NOT equals ' + b); - } - - async selftest(): Promise { - const clear = 'text line to be encrypted'; - const encrypted = this.encrypt(clear); - - SyncedAsyncStorage.assertEquals(encrypted.startsWith(this.encryptionMarker), true); - SyncedAsyncStorage.assertNotEquals(clear, encrypted); - const decrypted = this.decrypt(encrypted); - SyncedAsyncStorage.assertEquals(clear, decrypted); - - SyncedAsyncStorage.assertEquals(this.decrypt(clear), clear); - - SyncedAsyncStorage.assertEquals( - this.decrypt( - 'encrypted://U2FsdGVkX19XQWgwS8q5XjQSQ19OmBsNax4k6NZOAsKFhCgw9sJFwb+qVYfqy6X5', - '3a013f391e59daf2f5074fa66652784d17511ea072d7a8329ff9bddf371932ab', - ), - 'text line to be encrypted', - ); - - return true; - } - - /** - * @param key {string} - * @param value {string} - * - * @return {string} New sequence number from remote - */ - async setItemRemote(key: string, value: string): Promise { - const that = this; - return new Promise(function (resolve, reject) { - fetch(that.defaultBaseUrl + '/namespace/' + that.namespace + '/' + key, { - method: 'POST', - headers: { - Accept: 'text/plain', - 'Content-Type': 'text/plain', - }, - body: value, - }) - .then(async response => { - const text = await response.text(); - console.log('saved, seq num:', text); - resolve(text); - }) - .catch((reason: Error) => reject(reason)); - }); - } - - async setItem(key: string, value: string) { - value = this.encrypt(value); - await AsyncStorage.setItem(this.namespace + '_' + key, value); - const newSeqNum = await this.setItemRemote(key, value); - const localSeqNum = await this.getLocalSeqNum(); - if (+localSeqNum > +newSeqNum) { - // some race condition during save happened..? - return; - } - await AsyncStorage.setItem(this.namespace + '_' + 'seqnum', newSeqNum); - } - - async getItemRemote(key: string) { - const response = await fetch(this.defaultBaseUrl + '/namespace/' + this.namespace + '/' + key); - return await response.text(); - } - - async getItem(key: string) { - return this.decrypt(await AsyncStorage.getItem(this.namespace + '_' + key)); - } - - async getAllKeysRemote(): Promise { - const response = await fetch(this.defaultBaseUrl + '/namespacekeys/' + this.namespace); - const text = await response.text(); - return text.split(','); - } - - async getAllKeys(): Promise { - return (await AsyncStorage.getAllKeys()) - .filter(key => key.startsWith(this.namespace + '_')) - .map(key => key.replace(this.namespace + '_', '')); - } - - async getLocalSeqNum() { - return (await AsyncStorage.getItem(this.namespace + '_' + 'seqnum')) || '0'; - } - - async purgeLocalStorage() { - if (!this.namespace) throw new Error('No namespace'); - const keys = (await AsyncStorage.getAllKeys()).filter(key => key.startsWith(this.namespace)); - for (const key of keys) { - await AsyncStorage.removeItem(key); - } - } - - /** - * Should be called at init. - * Checks remote sequence number, and if remote is ahead - we sync all keys with local storage. - */ - async synchronize() { - const response = await fetch(this.defaultBaseUrl + '/namespaceseq/' + this.namespace); - const remoteSeqNum = (await response.text()) || '0'; - const localSeqNum = await this.getLocalSeqNum(); - if (+remoteSeqNum > +localSeqNum) { - console.log('remote storage is ahead, need to sync;', +remoteSeqNum, '>', +localSeqNum); - - // sort to ensure channel_manager comes first - for (const key of (await this.getAllKeysRemote()).sort()) { - const value = await this.getItemRemote(key); - await AsyncStorage.setItem(this.namespace + '_' + key, value); - console.log('synced', key, 'to', value); - } - - await AsyncStorage.setItem(this.namespace + '_' + 'seqnum', remoteSeqNum); - } else { - console.log('storage is up-to-date, no need for sync'); - } - } -} diff --git a/package-lock.json b/package-lock.json index e08074855..0cecd8889 100644 --- a/package-lock.json +++ b/package-lock.json @@ -145,7 +145,6 @@ "eslint-plugin-react-native": "^4.1.0", "jest": "^29.6.3", "jest-environment-node": "^29.7.0", - "metro-react-native-babel-preset": "0.76.8", "node-fetch": "^2.6.7", "prettier": "^3.2.5", "ts-jest": "^29.1.1", @@ -394,17 +393,6 @@ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.24.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-globals": { "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", @@ -676,38 +664,6 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-proposal-async-generator-functions": { - "version": "7.20.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-remap-async-to-generator": "^7.18.9", - "@babel/plugin-syntax-async-generators": "^7.8.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-class-properties": { - "version": "7.18.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-proposal-export-default-from": { "version": "7.25.9", "license": "MIT", @@ -721,85 +677,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.18.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-numeric-separator": { - "version": "7.18.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-object-rest-spread": { - "version": "7.20.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.20.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-optional-catch-binding": { - "version": "7.18.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-optional-chaining": { - "version": "7.21.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-proposal-private-property-in-object": { "version": "7.21.0-placeholder-for-preset-env.2", "dev": true, @@ -14496,69 +14373,6 @@ "node": ">=18.18" } }, - "node_modules/metro-react-native-babel-preset": { - "version": "0.76.8", - "resolved": "https://registry.npmjs.org/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.76.8.tgz", - "integrity": "sha512-Ptza08GgqzxEdK8apYsjTx2S8WDUlS2ilBlu9DR1CUcHmg4g3kOkFylZroogVAUKtpYQNYwAvdsjmrSdDNtiAg==", - "deprecated": "Use @react-native/babel-preset instead", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.20.0", - "@babel/plugin-proposal-async-generator-functions": "^7.0.0", - "@babel/plugin-proposal-class-properties": "^7.18.0", - "@babel/plugin-proposal-export-default-from": "^7.0.0", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.0", - "@babel/plugin-proposal-numeric-separator": "^7.0.0", - "@babel/plugin-proposal-object-rest-spread": "^7.20.0", - "@babel/plugin-proposal-optional-catch-binding": "^7.0.0", - "@babel/plugin-proposal-optional-chaining": "^7.20.0", - "@babel/plugin-syntax-dynamic-import": "^7.8.0", - "@babel/plugin-syntax-export-default-from": "^7.0.0", - "@babel/plugin-syntax-flow": "^7.18.0", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.0.0", - "@babel/plugin-syntax-optional-chaining": "^7.0.0", - "@babel/plugin-transform-arrow-functions": "^7.0.0", - "@babel/plugin-transform-async-to-generator": "^7.20.0", - "@babel/plugin-transform-block-scoping": "^7.0.0", - "@babel/plugin-transform-classes": "^7.0.0", - "@babel/plugin-transform-computed-properties": "^7.0.0", - "@babel/plugin-transform-destructuring": "^7.20.0", - "@babel/plugin-transform-flow-strip-types": "^7.20.0", - "@babel/plugin-transform-function-name": "^7.0.0", - "@babel/plugin-transform-literals": "^7.0.0", - "@babel/plugin-transform-modules-commonjs": "^7.0.0", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.0.0", - "@babel/plugin-transform-parameters": "^7.0.0", - "@babel/plugin-transform-react-display-name": "^7.0.0", - "@babel/plugin-transform-react-jsx": "^7.0.0", - "@babel/plugin-transform-react-jsx-self": "^7.0.0", - "@babel/plugin-transform-react-jsx-source": "^7.0.0", - "@babel/plugin-transform-runtime": "^7.0.0", - "@babel/plugin-transform-shorthand-properties": "^7.0.0", - "@babel/plugin-transform-spread": "^7.0.0", - "@babel/plugin-transform-sticky-regex": "^7.0.0", - "@babel/plugin-transform-typescript": "^7.5.0", - "@babel/plugin-transform-unicode-regex": "^7.0.0", - "@babel/template": "^7.0.0", - "babel-plugin-transform-flow-enums": "^0.0.2", - "react-refresh": "^0.4.0" - }, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "@babel/core": "*" - } - }, - "node_modules/metro-react-native-babel-preset/node_modules/react-refresh": { - "version": "0.4.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/metro-resolver": { "version": "0.81.5", "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.81.5.tgz", diff --git a/package.json b/package.json index 36ae702a8..2b746c045 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,6 @@ "eslint-plugin-react-native": "^4.1.0", "jest": "^29.6.3", "jest-environment-node": "^29.7.0", - "metro-react-native-babel-preset": "0.76.8", "node-fetch": "^2.6.7", "prettier": "^3.2.5", "ts-jest": "^29.1.1",