REF: cleanup
This commit is contained in:
parent
0b1086e73b
commit
3a69c61635
2
.github/workflows/build-release-apk.yml
vendored
2
.github/workflows/build-release-apk.yml
vendored
@ -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
|
||||
|
||||
4
.github/workflows/e2e.yml
vendored
4
.github/workflows/e2e.yml
vendored
@ -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'
|
||||
|
||||
2
LICENSE
2
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
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
# BlueWallet - A Bitcoin & Lightning Wallet
|
||||
|
||||
[](https://github.com/BlueWallet/BlueWallet)
|
||||
[](https://circleci.com/gh/BlueWallet/BlueWallet)
|
||||
[](https://github.com/prettier/prettier)
|
||||

|
||||
|
||||
|
||||
@ -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
186
package-lock.json
generated
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user