REF: cleanup

This commit is contained in:
Overtorment 2026-02-08 12:49:58 +00:00
parent 0b1086e73b
commit 3a69c61635
7 changed files with 4 additions and 352 deletions

View File

@ -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

View File

@ -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'

View File

@ -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

View File

@ -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)

View File

@ -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<boolean> {
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<string> {
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<string[]> {
const response = await fetch(this.defaultBaseUrl + '/namespacekeys/' + this.namespace);
const text = await response.text();
return text.split(',');
}
async getAllKeys(): Promise<string[]> {
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');
}
}
}

186
package-lock.json generated
View File

@ -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",

View File

@ -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",