Merge branch 'master' into nymkappa/duplicate-block
This commit is contained in:
commit
35a05a420d
@ -171,52 +171,58 @@ Helpful link: https://gist.github.com/System-Glitch/cb4e87bf1ae3fec9925725bb3ebe
|
|||||||
|
|
||||||
Run bitcoind on regtest:
|
Run bitcoind on regtest:
|
||||||
```
|
```
|
||||||
bitcoind -regtest -rpcport=8332
|
bitcoind -regtest
|
||||||
```
|
```
|
||||||
|
|
||||||
Create a new wallet, if needed:
|
Create a new wallet, if needed:
|
||||||
```
|
```
|
||||||
bitcoin-cli -regtest -rpcport=8332 createwallet test
|
bitcoin-cli -regtest createwallet test
|
||||||
```
|
```
|
||||||
|
|
||||||
Load wallet (this command may take a while if you have lot of UTXOs):
|
Load wallet (this command may take a while if you have lot of UTXOs):
|
||||||
```
|
```
|
||||||
bitcoin-cli -regtest -rpcport=8332 loadwallet test
|
bitcoin-cli -regtest loadwallet test
|
||||||
```
|
```
|
||||||
|
|
||||||
Get a new address:
|
Get a new address:
|
||||||
```
|
```
|
||||||
address=$(./src/bitcoin-cli -regtest -rpcport=8332 getnewaddress)
|
address=$(bitcoin-cli -regtest getnewaddress)
|
||||||
```
|
```
|
||||||
|
|
||||||
Mine blocks to the previously generated address. You need at least 101 blocks before you can spend. This will take some time to execute (~1 min):
|
Mine blocks to the previously generated address. You need at least 101 blocks before you can spend. This will take some time to execute (~1 min):
|
||||||
```
|
```
|
||||||
bitcoin-cli -regtest -rpcport=8332 generatetoaddress 101 $address
|
bitcoin-cli -regtest generatetoaddress 101 $address
|
||||||
```
|
```
|
||||||
|
|
||||||
Send 0.1 BTC at 5 sat/vB to another address:
|
Send 0.1 BTC at 5 sat/vB to another address:
|
||||||
```
|
```
|
||||||
./src/bitcoin-cli -named -regtest -rpcport=8332 sendtoaddress address=$(./src/bitcoin-cli -regtest -rpcport=8332 getnewaddress) amount=0.1 fee_rate=5
|
bitcoin-cli -named -regtest sendtoaddress address=$(bitcoin-cli -regtest getnewaddress) amount=0.1 fee_rate=5
|
||||||
```
|
```
|
||||||
|
|
||||||
See more example of `sendtoaddress`:
|
See more example of `sendtoaddress`:
|
||||||
```
|
```
|
||||||
./src/bitcoin-cli sendtoaddress # will print the help
|
bitcoin-cli sendtoaddress # will print the help
|
||||||
```
|
```
|
||||||
|
|
||||||
Mini script to generate transactions with random TX fee-rate (between 1 to 100 sat/vB). It's slow so don't expect to use this to test mempool spam, except if you let it run for a long time, or maybe with multiple regtest nodes connected to each other.
|
Mini script to generate random network activity (random TX count with random tx fee-rate). It's slow so don't expect to use this to test mempool spam, except if you let it run for a long time, or maybe with multiple regtest nodes connected to each other.
|
||||||
```
|
```
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
address=$(./src/bitcoin-cli -regtest -rpcport=8332 getnewaddress)
|
address=$(bitcoin-cli -regtest getnewaddress)
|
||||||
|
bitcoin-cli -regtest generatetoaddress 101 $address
|
||||||
for i in {1..1000000}
|
for i in {1..1000000}
|
||||||
do
|
do
|
||||||
./src/bitcoin-cli -regtest -rpcport=8332 -named sendtoaddress address=$address amount=0.01 fee_rate=$(jot -r 1 1 100)
|
for y in $(seq 1 "$(jot -r 1 1 1000)")
|
||||||
|
do
|
||||||
|
bitcoin-cli -regtest -named sendtoaddress address=$address amount=0.01 fee_rate=$(jot -r 1 1 100)
|
||||||
|
done
|
||||||
|
bitcoin-cli -regtest generatetoaddress 1 $address
|
||||||
|
sleep 5
|
||||||
done
|
done
|
||||||
```
|
```
|
||||||
|
|
||||||
Generate block at regular interval (every 10 seconds in this example):
|
Generate block at regular interval (every 10 seconds in this example):
|
||||||
```
|
```
|
||||||
watch -n 10 "./src/bitcoin-cli -regtest -rpcport=8332 generatetoaddress 1 $address"
|
watch -n 10 "bitcoin-cli -regtest generatetoaddress 1 $address"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Mining pools update
|
### Mining pools update
|
||||||
|
@ -11,19 +11,33 @@ import { Common } from './common';
|
|||||||
class DiskCache {
|
class DiskCache {
|
||||||
private cacheSchemaVersion = 3;
|
private cacheSchemaVersion = 3;
|
||||||
|
|
||||||
|
private static TMP_FILE_NAME = config.MEMPOOL.CACHE_DIR + '/tmp-cache.json';
|
||||||
|
private static TMP_FILE_NAMES = config.MEMPOOL.CACHE_DIR + '/tmp-cache{number}.json';
|
||||||
private static FILE_NAME = config.MEMPOOL.CACHE_DIR + '/cache.json';
|
private static FILE_NAME = config.MEMPOOL.CACHE_DIR + '/cache.json';
|
||||||
private static FILE_NAMES = config.MEMPOOL.CACHE_DIR + '/cache{number}.json';
|
private static FILE_NAMES = config.MEMPOOL.CACHE_DIR + '/cache{number}.json';
|
||||||
private static CHUNK_FILES = 25;
|
private static CHUNK_FILES = 25;
|
||||||
private isWritingCache = false;
|
private isWritingCache = false;
|
||||||
|
|
||||||
constructor() { }
|
constructor() {
|
||||||
|
if (!cluster.isMaster) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
process.on('SIGINT', (e) => {
|
||||||
|
this.saveCacheToDiskSync();
|
||||||
|
process.exit(2);
|
||||||
|
});
|
||||||
|
process.on('SIGTERM', (e) => {
|
||||||
|
this.saveCacheToDiskSync();
|
||||||
|
process.exit(2);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async $saveCacheToDisk(): Promise<void> {
|
async $saveCacheToDisk(): Promise<void> {
|
||||||
if (!cluster.isPrimary) {
|
if (!cluster.isPrimary) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this.isWritingCache) {
|
if (this.isWritingCache) {
|
||||||
logger.debug('Saving cache already in progress. Skipping.')
|
logger.debug('Saving cache already in progress. Skipping.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@ -61,7 +75,56 @@ class DiskCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wipeCache() {
|
saveCacheToDiskSync(): void {
|
||||||
|
if (!cluster.isPrimary) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.isWritingCache) {
|
||||||
|
logger.debug('Saving cache already in progress. Skipping.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
logger.debug('Writing mempool and blocks data to disk cache (sync)...');
|
||||||
|
this.isWritingCache = true;
|
||||||
|
|
||||||
|
const mempool = memPool.getMempool();
|
||||||
|
const mempoolArray: TransactionExtended[] = [];
|
||||||
|
for (const tx in mempool) {
|
||||||
|
mempoolArray.push(mempool[tx]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Common.shuffleArray(mempoolArray);
|
||||||
|
|
||||||
|
const chunkSize = Math.floor(mempoolArray.length / DiskCache.CHUNK_FILES);
|
||||||
|
|
||||||
|
fs.writeFileSync(DiskCache.TMP_FILE_NAME, JSON.stringify({
|
||||||
|
cacheSchemaVersion: this.cacheSchemaVersion,
|
||||||
|
blocks: blocks.getBlocks(),
|
||||||
|
blockSummaries: blocks.getBlockSummaries(),
|
||||||
|
mempool: {},
|
||||||
|
mempoolArray: mempoolArray.splice(0, chunkSize),
|
||||||
|
}), { flag: 'w' });
|
||||||
|
for (let i = 1; i < DiskCache.CHUNK_FILES; i++) {
|
||||||
|
fs.writeFileSync(DiskCache.TMP_FILE_NAMES.replace('{number}', i.toString()), JSON.stringify({
|
||||||
|
mempool: {},
|
||||||
|
mempoolArray: mempoolArray.splice(0, chunkSize),
|
||||||
|
}), { flag: 'w' });
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.renameSync(DiskCache.TMP_FILE_NAME, DiskCache.FILE_NAME);
|
||||||
|
for (let i = 1; i < DiskCache.CHUNK_FILES; i++) {
|
||||||
|
fs.renameSync(DiskCache.TMP_FILE_NAMES.replace('{number}', i.toString()), DiskCache.FILE_NAMES.replace('{number}', i.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug('Mempool and blocks data saved to disk cache');
|
||||||
|
this.isWritingCache = false;
|
||||||
|
} catch (e) {
|
||||||
|
logger.warn('Error writing to cache file: ' + (e instanceof Error ? e.message : e));
|
||||||
|
this.isWritingCache = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wipeCache(): void {
|
||||||
logger.notice(`Wiping nodejs backend cache/cache*.json files`);
|
logger.notice(`Wiping nodejs backend cache/cache*.json files`);
|
||||||
try {
|
try {
|
||||||
fs.unlinkSync(DiskCache.FILE_NAME);
|
fs.unlinkSync(DiskCache.FILE_NAME);
|
||||||
@ -83,7 +146,7 @@ class DiskCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loadMempoolCache() {
|
loadMempoolCache(): void {
|
||||||
if (!fs.existsSync(DiskCache.FILE_NAME)) {
|
if (!fs.existsSync(DiskCache.FILE_NAME)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -107,7 +107,7 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
this.blocks.unshift(block);
|
this.blocks.unshift(block);
|
||||||
this.blocks = this.blocks.slice(0, this.dynamicBlocksAmount);
|
this.blocks = this.blocks.slice(0, this.dynamicBlocksAmount);
|
||||||
|
|
||||||
if (txConfirmed) {
|
if (txConfirmed && this.height === block.height) {
|
||||||
this.markHeight = block.height;
|
this.markHeight = block.height;
|
||||||
this.moveArrowToPosition(true, true);
|
this.moveArrowToPosition(true, true);
|
||||||
} else {
|
} else {
|
||||||
|
@ -53,3 +53,8 @@ form {
|
|||||||
margin-top: 1px;
|
margin-top: 1px;
|
||||||
margin-right: 2px;
|
margin-right: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input:focus {
|
||||||
|
box-shadow: none;
|
||||||
|
border-color: #1b1f2c;
|
||||||
|
}
|
||||||
|
@ -488,7 +488,7 @@
|
|||||||
<div class="effective-fee-container">
|
<div class="effective-fee-container">
|
||||||
{{ tx.effectiveFeePerVsize | feeRounding }} <span class="symbol" i18n="shared.sat-vbyte|sat/vB">sat/vB</span>
|
{{ tx.effectiveFeePerVsize | feeRounding }} <span class="symbol" i18n="shared.sat-vbyte|sat/vB">sat/vB</span>
|
||||||
<ng-template [ngIf]="tx.status.confirmed">
|
<ng-template [ngIf]="tx.status.confirmed">
|
||||||
<app-tx-fee-rating class="d-none d-lg-inline ml-2" *ngIf="tx.fee" [tx]="tx"></app-tx-fee-rating>
|
<app-tx-fee-rating class="ml-2 mr-2" *ngIf="tx.fee || tx.effectiveFeePerVsize" [tx]="tx"></app-tx-fee-rating>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-outline-info btn-sm btn-small-height float-right" (click)="showCpfpDetails = !showCpfpDetails">CPFP <fa-icon [icon]="['fas', 'info-circle']" [fixedWidth]="true"></fa-icon></button>
|
<button type="button" class="btn btn-outline-info btn-sm btn-small-height float-right" (click)="showCpfpDetails = !showCpfpDetails">CPFP <fa-icon [icon]="['fas', 'info-circle']" [fixedWidth]="true"></fa-icon></button>
|
||||||
|
@ -347,7 +347,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
this.blocksSubscription = this.stateService.blocks$.subscribe(([block, txConfirmed]) => {
|
this.blocksSubscription = this.stateService.blocks$.subscribe(([block, txConfirmed]) => {
|
||||||
this.latestBlock = block;
|
this.latestBlock = block;
|
||||||
|
|
||||||
if (txConfirmed && this.tx) {
|
if (txConfirmed && this.tx && !this.tx.status.confirmed) {
|
||||||
this.tx.status = {
|
this.tx.status = {
|
||||||
confirmed: true,
|
confirmed: true,
|
||||||
block_height: block.height,
|
block_height: block.height,
|
||||||
@ -496,7 +496,9 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
@HostListener('window:resize', ['$event'])
|
@HostListener('window:resize', ['$event'])
|
||||||
setGraphSize(): void {
|
setGraphSize(): void {
|
||||||
if (this.graphContainer) {
|
if (this.graphContainer) {
|
||||||
this.graphWidth = this.graphContainer.nativeElement.clientWidth;
|
setTimeout(() => {
|
||||||
|
this.graphWidth = this.graphContainer.nativeElement.clientWidth;
|
||||||
|
}, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,8 +208,8 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
|
|||||||
this.outputs = this.initLines('out', voutWithFee, totalValue, this.maxStrands);
|
this.outputs = this.initLines('out', voutWithFee, totalValue, this.maxStrands);
|
||||||
|
|
||||||
this.middle = {
|
this.middle = {
|
||||||
path: `M ${(this.width / 2) - this.midWidth} ${(this.height / 2) + 0.5} L ${(this.width / 2) + this.midWidth} ${(this.height / 2) + 0.5}`,
|
path: `M ${(this.width / 2) - this.midWidth} ${(this.height / 2) + 0.25} L ${(this.width / 2) + this.midWidth} ${(this.height / 2) + 0.25}`,
|
||||||
style: `stroke-width: ${this.combinedWeight + 1}; stroke: ${this.gradient[1]}`
|
style: `stroke-width: ${this.combinedWeight + 0.5}; stroke: ${this.gradient[1]}`
|
||||||
};
|
};
|
||||||
|
|
||||||
this.hasLine = this.inputs.reduce((line, put) => line || !put.zeroValue, false)
|
this.hasLine = this.inputs.reduce((line, put) => line || !put.zeroValue, false)
|
||||||
@ -266,7 +266,7 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
|
|||||||
const lineParams = weights.map((w, i) => {
|
const lineParams = weights.map((w, i) => {
|
||||||
return {
|
return {
|
||||||
weight: w,
|
weight: w,
|
||||||
thickness: xputs[i].value === 0 ? this.zeroValueThickness : Math.max(this.minWeight - 1, w) + 1,
|
thickness: xputs[i].value === 0 ? this.zeroValueThickness : Math.min(this.combinedWeight + 0.5, Math.max(this.minWeight - 1, w) + 1),
|
||||||
offset: 0,
|
offset: 0,
|
||||||
innerY: 0,
|
innerY: 0,
|
||||||
outerY: 0,
|
outerY: 0,
|
||||||
@ -278,7 +278,7 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
|
|||||||
|
|
||||||
// bounds of the middle segment
|
// bounds of the middle segment
|
||||||
const innerTop = (this.height / 2) - (this.combinedWeight / 2);
|
const innerTop = (this.height / 2) - (this.combinedWeight / 2);
|
||||||
const innerBottom = innerTop + this.combinedWeight;
|
const innerBottom = innerTop + this.combinedWeight + 0.5;
|
||||||
// tracks the visual bottom of the endpoints of the previous line
|
// tracks the visual bottom of the endpoints of the previous line
|
||||||
let lastOuter = 0;
|
let lastOuter = 0;
|
||||||
let lastInner = innerTop;
|
let lastInner = innerTop;
|
||||||
@ -303,7 +303,7 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
|
|||||||
|
|
||||||
// set the vertical position of the (center of the) outer side of the line
|
// set the vertical position of the (center of the) outer side of the line
|
||||||
line.outerY = lastOuter + (line.thickness / 2);
|
line.outerY = lastOuter + (line.thickness / 2);
|
||||||
line.innerY = Math.min(innerBottom + (line.thickness / 2), Math.max(innerTop + (line.thickness / 2), lastInner + (line.weight / 2)));
|
line.innerY = Math.min(innerBottom - (line.thickness / 2), Math.max(innerTop + (line.thickness / 2), lastInner + (line.weight / 2)));
|
||||||
|
|
||||||
// special case to center single input/outputs
|
// special case to center single input/outputs
|
||||||
if (xputs.length === 1) {
|
if (xputs.length === 1) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user