Merge branch 'master' into natsoni/tx-preview-feedback

This commit is contained in:
mononaut 2025-03-29 16:06:55 +08:00 committed by GitHub
commit 5c02670281
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 131 additions and 24 deletions

View File

@ -105,7 +105,6 @@ jobs:
--cache-to "type=local,dest=/tmp/.buildx-cache,mode=max" \
--platform linux/amd64,linux/arm64 \
--tag ${{ secrets.DOCKER_HUB_USER }}/${{ matrix.service }}:$TAG \
--tag ${{ secrets.DOCKER_HUB_USER }}/${{ matrix.service }}:latest \
--build-context rustgbt=./rust \
--build-context backend=./backend \
--output "type=registry,push=true" \

View File

@ -406,8 +406,8 @@ class BitcoinRoutes {
res.setHeader('Expires', new Date(Date.now() + 1000 * cacheDuration).toUTCString());
res.json(block);
} catch (e) {
handleError(req, res, 500, 'Failed to get block');
} catch (e: any) {
handleError(req, res, e?.response?.status === 404 ? 404 : 500, 'Failed to get block');
}
}

View File

@ -30,6 +30,7 @@ const POLL_FREQUENCY = 5 * 60 * 1000; // 5 minutes
class WalletApi {
private wallets: Record<string, Wallet> = {};
private syncing = false;
private lastSync = 0;
constructor() {
this.wallets = config.WALLETS.ENABLED ? (config.WALLETS.WALLETS as string[]).reduce((acc, wallet) => {
@ -47,7 +48,38 @@ class WalletApi {
if (!config.WALLETS.ENABLED || this.syncing) {
return;
}
this.syncing = true;
if (config.WALLETS.AUTO && (Date.now() - this.lastSync) > POLL_FREQUENCY) {
try {
// update list of active wallets
this.lastSync = Date.now();
const response = await axios.get(config.MEMPOOL_SERVICES.API + `/wallets`);
const walletList: string[] = response.data;
if (walletList) {
// create a quick lookup dictionary of active wallets
const newWallets: Record<string, boolean> = Object.fromEntries(
walletList.map(wallet => [wallet, true])
);
for (const wallet of walletList) {
// don't overwrite existing wallets
if (!this.wallets[wallet]) {
this.wallets[wallet] = { name: wallet, addresses: {}, lastPoll: 0 };
}
}
// remove wallets that are no longer active
for (const wallet of Object.keys(this.wallets)) {
if (!newWallets[wallet]) {
delete this.wallets[wallet];
}
}
}
} catch (e) {
logger.err(`Error updating active wallets: ${(e instanceof Error ? e.message : e)}`);
}
}
for (const walletKey of Object.keys(this.wallets)) {
const wallet = this.wallets[walletKey];
if (wallet.lastPoll < (Date.now() - POLL_FREQUENCY)) {
@ -72,6 +104,7 @@ class WalletApi {
}
}
}
this.syncing = false;
}

View File

@ -164,6 +164,7 @@ interface IConfig {
},
WALLETS: {
ENABLED: boolean;
AUTO: boolean;
WALLETS: string[];
},
STRATUM: {
@ -334,6 +335,7 @@ const defaults: IConfig = {
},
'WALLETS': {
'ENABLED': false,
'AUTO': false,
'WALLETS': [],
},
'STRATUM': {

View File

@ -16,10 +16,10 @@
"mobileOrder": 4
},
{
"component": "balance",
"component": "walletBalance",
"mobileOrder": 1,
"props": {
"address": "32ixEdVJWo3kmvJGMTZq5jAQVZZeuwnqzo"
"wallet": "ONBTC"
}
},
{
@ -30,21 +30,22 @@
}
},
{
"component": "address",
"component": "wallet",
"mobileOrder": 2,
"props": {
"address": "32ixEdVJWo3kmvJGMTZq5jAQVZZeuwnqzo",
"period": "1m"
"wallet": "ONBTC",
"period": "1m",
"label": "bitcoin.gob.sv"
}
},
{
"component": "blocks"
},
{
"component": "addressTransactions",
"component": "walletTransactions",
"mobileOrder": 3,
"props": {
"address": "32ixEdVJWo3kmvJGMTZq5jAQVZZeuwnqzo"
"wallet": "ONBTC"
}
}
]

View File

@ -256,6 +256,11 @@ export function detectScriptTemplate(type: ScriptType, script_asm: string, witne
return ScriptTemplates.multisig(tapscriptMultisig.m, tapscriptMultisig.n);
}
const tapscriptUnanimousMultisig = parseTapscriptUnanimousMultisig(script_asm);
if (tapscriptUnanimousMultisig) {
return ScriptTemplates.multisig(tapscriptUnanimousMultisig, tapscriptUnanimousMultisig);
}
return;
}
@ -310,11 +315,13 @@ export function parseTapscriptMultisig(script: string): undefined | { m: number,
}
const ops = script.split(' ');
// At minimum, one pubkey group (3 tokens) + m push + final opcode = 5 tokens
if (ops.length < 5) return;
// At minimum, 2 pubkey group (3 tokens) + m push + final opcode = 8 tokens
if (ops.length < 8) {
return;
}
const finalOp = ops.pop();
if (finalOp !== 'OP_NUMEQUAL' && finalOp !== 'OP_GREATERTHANOREQUAL') {
if (!['OP_NUMEQUAL', 'OP_NUMEQUALVERIFY', 'OP_GREATERTHANOREQUAL', 'OP_GREATERTHAN', 'OP_EQUAL', 'OP_EQUALVERIFY'].includes(finalOp)) {
return;
}
@ -329,6 +336,10 @@ export function parseTapscriptMultisig(script: string): undefined | { m: number,
return;
}
if (finalOp === 'OP_GREATERTHAN') {
m += 1;
}
if (ops.length % 3 !== 0) {
return;
}
@ -360,6 +371,53 @@ export function parseTapscriptMultisig(script: string): undefined | { m: number,
return { m, n };
}
export function parseTapscriptUnanimousMultisig(script: string): undefined | number {
if (!script) {
return;
}
const ops = script.split(' ');
// At minimum, 2 pubkey group (3 tokens) = 6 tokens
if (ops.length < 6) {
return;
}
if (ops.length % 3 !== 0) {
return;
}
const n = ops.length / 3;
for (let i = 0; i < n; i++) {
const pushOp = ops.shift();
const pubkey = ops.shift();
const sigOp = ops.shift();
if (pushOp !== 'OP_PUSHBYTES_32') {
return;
}
if (!/^[0-9a-fA-F]{64}$/.test(pubkey)) {
return;
}
if (i < n - 1) {
if (sigOp !== 'OP_CHECKSIGVERIFY') {
return;
}
} else {
// Last opcode can be either CHECKSIG or CHECKSIGVERIFY
if (!(sigOp === 'OP_CHECKSIGVERIFY' || sigOp === 'OP_CHECKSIG')) {
return;
}
}
}
if (ops.length) {
return;
}
return n;
}
export function getVarIntLength(n: number): number {
if (n < 0xfd) {
return 1;

View File

@ -1,7 +1,16 @@
@reboot sleep 5 ; /usr/local/bin/bitcoind -testnet >/dev/null 2>&1
@reboot sleep 5 ; /usr/local/bin/bitcoind -testnet4 >/dev/null 2>&1
@reboot sleep 5 ; /usr/local/bin/bitcoind -signet >/dev/null 2>&1
@reboot sleep 10 ; screen -dmS mainnet /bitcoin/electrs/start mainnet
@reboot sleep 10 ; screen -dmS testnet /bitcoin/electrs/start testnet
@reboot sleep 10 ; screen -dmS testnet4 /bitcoin/electrs/start testnet4
@reboot sleep 10 ; screen -dmS signet /bitcoin/electrs/start signet
# start test network daemons on boot
@reboot sleep 10 ; /usr/local/bin/bitcoind -testnet >/dev/null 2>&1
@reboot sleep 20 ; /usr/local/bin/bitcoind -testnet4 >/dev/null 2>&1
@reboot sleep 30 ; /usr/local/bin/bitcoind -signet >/dev/null 2>&1
# start electrs on boot
@reboot sleep 40 ; screen -dmS mainnet /bitcoin/electrs/start mainnet
@reboot sleep 50 ; screen -dmS testnet /bitcoin/electrs/start testnet
@reboot sleep 60 ; screen -dmS testnet4 /bitcoin/electrs/start testnet4
@reboot sleep 70 ; screen -dmS signet /bitcoin/electrs/start signet
# daily update of popular-scripts
30 03 * * * $HOME/electrs/start testnet4 popular-scripts >/dev/null 2>&1
31 03 * * * $HOME/electrs/start testnet popular-scripts >/dev/null 2>&1
32 03 * * * $HOME/electrs/start signet popular-scripts >/dev/null 2>&1
33 03 * * * $HOME/electrs/start mainnet popular-scripts >/dev/null 2>&1

View File

@ -8,3 +8,7 @@
# hourly asset update and electrs restart
6 * * * * cd $HOME/asset_registry_db && git pull --quiet origin master && cd $HOME/asset_registry_testnet_db && git pull --quiet origin master && killall electrs
# daily update of popular-scripts
32 03 * * * $HOME/electrs/start liquid popular-scripts >/dev/null 2>&1
33 03 * * * $HOME/electrs/start liquidtestnet popular-scripts >/dev/null 2>&1

View File

@ -159,6 +159,7 @@
},
"WALLETS": {
"ENABLED": true,
"AUTO": true,
"WALLETS": ["BITB", "3350"]
},
"STRATUM": {

View File

@ -4,7 +4,7 @@ hostname=$(hostname)
heat()
{
echo "$1"
curl -i -s "$1" | head -1
curl -o /dev/null -s "$1"
}
heatURLs=(

View File

@ -6,19 +6,19 @@ slugs=(`curl -sSL https://${hostname}/api/v1/mining/pools/3y|jq -r -S '(.pools[]
warmSlurp()
{
echo "$1"
curl -i -s -H 'User-Agent: Googlebot' "$1" | head -1
curl -o /dev/null -s -H 'User-Agent: Googlebot' "$1"
}
warmUnfurl()
{
echo "$1"
curl -i -s -H 'User-Agent: Twitterbot' "$1" | head -1
curl -o /dev/null -s -H 'User-Agent: Twitterbot' "$1"
}
warm()
{
echo "$1"
curl -i -s "$1" | head -1
curl -o /dev/null -s "$1"
}
warmSlurpURLs=(