Merge branch 'master' into flow-diagram-spent-connectors
This commit is contained in:
		
						commit
						4abd77fe31
					
				
							
								
								
									
										8
									
								
								.github/workflows/on-tag.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.github/workflows/on-tag.yml
									
									
									
									
										vendored
									
									
								
							@ -68,24 +68,24 @@ jobs:
 | 
			
		||||
        run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin
 | 
			
		||||
 | 
			
		||||
      - name: Checkout project
 | 
			
		||||
        uses: actions/checkout@629c2de402a417ea7690ca6ce3f33229e27606a5 # v2
 | 
			
		||||
        uses: actions/checkout@e2f20e631ae6d7dd3b768f56a5d2af784dd54791 # v2.5.0
 | 
			
		||||
 | 
			
		||||
      - name: Init repo for Dockerization
 | 
			
		||||
        run: docker/init.sh "$TAG"
 | 
			
		||||
 | 
			
		||||
      - name: Set up QEMU
 | 
			
		||||
        uses: docker/setup-qemu-action@27d0a4f181a40b142cce983c5393082c365d1480 # v1
 | 
			
		||||
        uses: docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18 # v2.1.0
 | 
			
		||||
        id: qemu
 | 
			
		||||
 | 
			
		||||
      - name: Setup Docker buildx action
 | 
			
		||||
        uses: docker/setup-buildx-action@94ab11c41e45d028884a99163086648e898eed25 # v1
 | 
			
		||||
        uses: docker/setup-buildx-action@8c0edbc76e98fa90f69d9a2c020dcb50019dc325 # v2.2.1
 | 
			
		||||
        id: buildx
 | 
			
		||||
 | 
			
		||||
      - name: Available platforms
 | 
			
		||||
        run: echo ${{ steps.buildx.outputs.platforms }}
 | 
			
		||||
 | 
			
		||||
      - name: Cache Docker layers
 | 
			
		||||
        uses: actions/cache@661fd3eb7f2f20d8c7c84bc2b0509efd7a826628 # v2
 | 
			
		||||
        uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 # v3.0.11
 | 
			
		||||
        id: cache
 | 
			
		||||
        with:
 | 
			
		||||
          path: /tmp/.buildx-cache
 | 
			
		||||
 | 
			
		||||
@ -14,10 +14,10 @@ interface Pool {
 | 
			
		||||
class PoolsParser {
 | 
			
		||||
  miningPools: any[] = [];
 | 
			
		||||
  unknownPool: any = {
 | 
			
		||||
    'name': "Unknown",
 | 
			
		||||
    'link': "https://learnmeabitcoin.com/technical/coinbase-transaction",
 | 
			
		||||
    'regexes': "[]",
 | 
			
		||||
    'addresses': "[]",
 | 
			
		||||
    'name': 'Unknown',
 | 
			
		||||
    'link': 'https://learnmeabitcoin.com/technical/coinbase-transaction',
 | 
			
		||||
    'regexes': '[]',
 | 
			
		||||
    'addresses': '[]',
 | 
			
		||||
    'slug': 'unknown'
 | 
			
		||||
  };
 | 
			
		||||
  slugWarnFlag = false;
 | 
			
		||||
@ -25,7 +25,7 @@ class PoolsParser {
 | 
			
		||||
  /**
 | 
			
		||||
   * Parse the pools.json file, consolidate the data and dump it into the database
 | 
			
		||||
   */
 | 
			
		||||
  public async migratePoolsJson(poolsJson: object) {
 | 
			
		||||
  public async migratePoolsJson(poolsJson: object): Promise<void> {
 | 
			
		||||
    if (['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) === false) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
@ -81,6 +81,7 @@ class PoolsParser {
 | 
			
		||||
    // Finally, we generate the final consolidated pools data
 | 
			
		||||
    const finalPoolDataAdd: Pool[] = [];
 | 
			
		||||
    const finalPoolDataUpdate: Pool[] = [];
 | 
			
		||||
    const finalPoolDataRename: Pool[] = [];
 | 
			
		||||
    for (let i = 0; i < poolNames.length; ++i) {
 | 
			
		||||
      let allAddresses: string[] = [];
 | 
			
		||||
      let allRegexes: string[] = [];
 | 
			
		||||
@ -127,8 +128,26 @@ class PoolsParser {
 | 
			
		||||
          finalPoolDataUpdate.push(poolObj);
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        logger.debug(`Add '${finalPoolName}' mining pool`);
 | 
			
		||||
        finalPoolDataAdd.push(poolObj);
 | 
			
		||||
        // Double check that if we're not just renaming a pool (same address same regex)
 | 
			
		||||
        const [poolToRename]: any[] = await DB.query(`
 | 
			
		||||
          SELECT * FROM pools
 | 
			
		||||
          WHERE addresses = ? OR regexes = ?`,
 | 
			
		||||
          [JSON.stringify(poolObj.addresses), JSON.stringify(poolObj.regexes)]
 | 
			
		||||
        );
 | 
			
		||||
        if (poolToRename && poolToRename.length > 0) {
 | 
			
		||||
          // We're actually renaming an existing pool
 | 
			
		||||
          finalPoolDataRename.push({
 | 
			
		||||
            'name': poolObj.name,
 | 
			
		||||
            'link': poolObj.link,
 | 
			
		||||
            'regexes': allRegexes,
 | 
			
		||||
            'addresses': allAddresses,
 | 
			
		||||
            'slug': slug
 | 
			
		||||
          });
 | 
			
		||||
          logger.debug(`Rename '${poolToRename[0].name}' mining pool to ${poolObj.name}`);
 | 
			
		||||
        } else {
 | 
			
		||||
          logger.debug(`Add '${finalPoolName}' mining pool`);
 | 
			
		||||
          finalPoolDataAdd.push(poolObj);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      this.miningPools.push({
 | 
			
		||||
@ -145,7 +164,9 @@ class PoolsParser {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (finalPoolDataAdd.length > 0 || finalPoolDataUpdate.length > 0) {    
 | 
			
		||||
    if (finalPoolDataAdd.length > 0 || finalPoolDataUpdate.length > 0 ||
 | 
			
		||||
      finalPoolDataRename.length > 0
 | 
			
		||||
    ) {    
 | 
			
		||||
      logger.debug(`Update pools table now`);
 | 
			
		||||
 | 
			
		||||
      // Add new mining pools into the database
 | 
			
		||||
@ -169,8 +190,22 @@ class PoolsParser {
 | 
			
		||||
        ;`);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // Rename mining pools
 | 
			
		||||
      const renameQueries: string[] = [];
 | 
			
		||||
      for (let i = 0; i < finalPoolDataRename.length; ++i) {
 | 
			
		||||
        renameQueries.push(`
 | 
			
		||||
          UPDATE pools
 | 
			
		||||
          SET name='${finalPoolDataRename[i].name}', link='${finalPoolDataRename[i].link}',
 | 
			
		||||
            slug='${finalPoolDataRename[i].slug}'
 | 
			
		||||
          WHERE regexes='${JSON.stringify(finalPoolDataRename[i].regexes)}'
 | 
			
		||||
            AND addresses='${JSON.stringify(finalPoolDataRename[i].addresses)}'
 | 
			
		||||
        ;`);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await this.$deleteBlocskToReindex(finalPoolDataUpdate);
 | 
			
		||||
        if (finalPoolDataAdd.length > 0 || updateQueries.length > 0) {
 | 
			
		||||
          await this.$deleteBlocskToReindex(finalPoolDataUpdate);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (finalPoolDataAdd.length > 0) {
 | 
			
		||||
          await DB.query({ sql: queryAdd, timeout: 120000 });
 | 
			
		||||
@ -178,6 +213,9 @@ class PoolsParser {
 | 
			
		||||
        for (const query of updateQueries) {
 | 
			
		||||
          await DB.query({ sql: query, timeout: 120000 });
 | 
			
		||||
        }
 | 
			
		||||
        for (const query of renameQueries) {
 | 
			
		||||
          await DB.query({ sql: query, timeout: 120000 });
 | 
			
		||||
        }
 | 
			
		||||
        await this.insertUnknownPool();
 | 
			
		||||
        logger.info('Mining pools.json import completed');
 | 
			
		||||
      } catch (e) {
 | 
			
		||||
 | 
			
		||||
@ -79,7 +79,7 @@ export const poolsColor = {
 | 
			
		||||
   'binancepool': '#1E88E5',
 | 
			
		||||
   'viabtc': '#039BE5',
 | 
			
		||||
   'btccom': '#00897B',
 | 
			
		||||
   'slushpool': '#00ACC1',
 | 
			
		||||
   'braiinspool': '#00ACC1',
 | 
			
		||||
   'sbicrypto': '#43A047',
 | 
			
		||||
   'marapool': '#7CB342',
 | 
			
		||||
   'luxor': '#C0CA33',
 | 
			
		||||
 | 
			
		||||
@ -188,7 +188,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On
 | 
			
		||||
      this.gl.viewport(0, 0, this.displayWidth, this.displayHeight);
 | 
			
		||||
    }
 | 
			
		||||
    if (this.scene) {
 | 
			
		||||
      this.scene.resize({ width: this.displayWidth, height: this.displayHeight });
 | 
			
		||||
      this.scene.resize({ width: this.displayWidth, height: this.displayHeight, animate: false });
 | 
			
		||||
      this.start();
 | 
			
		||||
    } else {
 | 
			
		||||
      this.scene = new BlockScene({ width: this.displayWidth, height: this.displayHeight, resolution: this.resolution,
 | 
			
		||||
 | 
			
		||||
@ -29,7 +29,7 @@ export default class BlockScene {
 | 
			
		||||
    this.init({ width, height, resolution, blockLimit, orientation, flip, vertexArray });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  resize({ width = this.width, height = this.height }: { width?: number, height?: number}): void {
 | 
			
		||||
  resize({ width = this.width, height = this.height, animate = true }: { width?: number, height?: number, animate: boolean }): void {
 | 
			
		||||
    this.width = width;
 | 
			
		||||
    this.height = height;
 | 
			
		||||
    this.gridSize = this.width / this.gridWidth;
 | 
			
		||||
@ -38,7 +38,7 @@ export default class BlockScene {
 | 
			
		||||
 | 
			
		||||
    this.dirty = true;
 | 
			
		||||
    if (this.initialised && this.scene) {
 | 
			
		||||
      this.updateAll(performance.now(), 50);
 | 
			
		||||
      this.updateAll(performance.now(), 50, 'left', animate);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -212,7 +212,7 @@ export default class BlockScene {
 | 
			
		||||
    this.vbytesPerUnit = blockLimit / Math.pow(resolution / 1.02, 2);
 | 
			
		||||
    this.gridWidth = resolution;
 | 
			
		||||
    this.gridHeight = resolution;
 | 
			
		||||
    this.resize({ width, height });
 | 
			
		||||
    this.resize({ width, height, animate: true });
 | 
			
		||||
    this.layout = new BlockLayout({ width: this.gridWidth, height: this.gridHeight });
 | 
			
		||||
 | 
			
		||||
    this.txs = {};
 | 
			
		||||
@ -225,14 +225,14 @@ export default class BlockScene {
 | 
			
		||||
    this.animateUntil = Math.max(this.animateUntil, tx.update(update));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private updateTx(tx: TxView, startTime: number, delay: number, direction: string = 'left'): void {
 | 
			
		||||
  private updateTx(tx: TxView, startTime: number, delay: number, direction: string = 'left', animate: boolean = true): void {
 | 
			
		||||
    if (tx.dirty || this.dirty) {
 | 
			
		||||
      this.saveGridToScreenPosition(tx);
 | 
			
		||||
      this.setTxOnScreen(tx, startTime, delay, direction);
 | 
			
		||||
      this.setTxOnScreen(tx, startTime, delay, direction, animate);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private setTxOnScreen(tx: TxView, startTime: number, delay: number = 50, direction: string = 'left'): void {
 | 
			
		||||
  private setTxOnScreen(tx: TxView, startTime: number, delay: number = 50, direction: string = 'left', animate: boolean = true): void {
 | 
			
		||||
    if (!tx.initialised) {
 | 
			
		||||
      const txColor = tx.getColor();
 | 
			
		||||
      this.applyTxUpdate(tx, {
 | 
			
		||||
@ -252,30 +252,42 @@ export default class BlockScene {
 | 
			
		||||
          position: tx.screenPosition,
 | 
			
		||||
          color: txColor
 | 
			
		||||
        },
 | 
			
		||||
        duration: 1000,
 | 
			
		||||
        duration: animate ? 1000 : 1,
 | 
			
		||||
        start: startTime,
 | 
			
		||||
        delay,
 | 
			
		||||
        delay: animate ? delay : 0,
 | 
			
		||||
      });
 | 
			
		||||
    } else {
 | 
			
		||||
      this.applyTxUpdate(tx, {
 | 
			
		||||
        display: {
 | 
			
		||||
          position: tx.screenPosition
 | 
			
		||||
        },
 | 
			
		||||
        duration: 1000,
 | 
			
		||||
        minDuration: 500,
 | 
			
		||||
        duration: animate ? 1000 : 0,
 | 
			
		||||
        minDuration: animate ? 500 : 0,
 | 
			
		||||
        start: startTime,
 | 
			
		||||
        delay,
 | 
			
		||||
        adjust: true
 | 
			
		||||
        delay: animate ? delay : 0,
 | 
			
		||||
        adjust: animate
 | 
			
		||||
      });
 | 
			
		||||
      if (!animate) {
 | 
			
		||||
        this.applyTxUpdate(tx, {
 | 
			
		||||
          display: {
 | 
			
		||||
            position: tx.screenPosition
 | 
			
		||||
          },
 | 
			
		||||
          duration: 0,
 | 
			
		||||
          minDuration: 0,
 | 
			
		||||
          start: startTime,
 | 
			
		||||
          delay: 0,
 | 
			
		||||
          adjust: false
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private updateAll(startTime: number, delay: number = 50, direction: string = 'left'): void {
 | 
			
		||||
  private updateAll(startTime: number, delay: number = 50, direction: string = 'left', animate: boolean = true): void {
 | 
			
		||||
    this.scene.count = 0;
 | 
			
		||||
    const ids = this.getTxList();
 | 
			
		||||
    startTime = startTime || performance.now();
 | 
			
		||||
    for (const id of ids) {
 | 
			
		||||
      this.updateTx(this.txs[id], startTime, delay, direction);
 | 
			
		||||
      this.updateTx(this.txs[id], startTime, delay, direction, animate);
 | 
			
		||||
    }
 | 
			
		||||
    this.dirty = false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -117,8 +117,9 @@ export class TransactionPreviewComponent implements OnInit, OnDestroy {
 | 
			
		||||
        }),
 | 
			
		||||
        switchMap(() => {
 | 
			
		||||
          let transactionObservable$: Observable<Transaction>;
 | 
			
		||||
          if (history.state.data && history.state.data.fee !== -1) {
 | 
			
		||||
            transactionObservable$ = of(history.state.data);
 | 
			
		||||
          const cached = this.stateService.getTxFromCache(this.txId);
 | 
			
		||||
          if (cached && cached.fee !== -1) {
 | 
			
		||||
            transactionObservable$ = of(cached);
 | 
			
		||||
          } else {
 | 
			
		||||
            transactionObservable$ = this.electrsApiService
 | 
			
		||||
              .getTransaction$(this.txId)
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,7 @@
 | 
			
		||||
  <div class="title-block">
 | 
			
		||||
    <div *ngIf="rbfTransaction" class="alert alert-mempool" role="alert">
 | 
			
		||||
      <span i18n="transaction.rbf.replacement|RBF replacement">This transaction has been replaced by:</span>
 | 
			
		||||
      <a class="alert-link" [routerLink]="['/tx/' | relativeUrl, rbfTransaction.txid]" [state]="{ data: rbfTransaction.size ? rbfTransaction : null }">
 | 
			
		||||
      <a class="alert-link" [routerLink]="['/tx/' | relativeUrl, rbfTransaction.txid]">
 | 
			
		||||
        <span class="d-inline d-lg-none">{{ rbfTransaction.txid | shortenString : 24 }}</span>
 | 
			
		||||
        <span class="d-none d-lg-inline">{{ rbfTransaction.txid }}</span>
 | 
			
		||||
      </a>
 | 
			
		||||
 | 
			
		||||
@ -183,8 +183,9 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
 | 
			
		||||
        }),
 | 
			
		||||
        switchMap(() => {
 | 
			
		||||
          let transactionObservable$: Observable<Transaction>;
 | 
			
		||||
          if (history.state.data && history.state.data.fee !== -1) {
 | 
			
		||||
            transactionObservable$ = of(history.state.data);
 | 
			
		||||
          const cached = this.stateService.getTxFromCache(this.txId);
 | 
			
		||||
          if (cached && cached.fee !== -1) {
 | 
			
		||||
            transactionObservable$ = of(cached);
 | 
			
		||||
          } else {
 | 
			
		||||
            transactionObservable$ = this.electrsApiService
 | 
			
		||||
              .getTransaction$(this.txId)
 | 
			
		||||
@ -279,6 +280,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
 | 
			
		||||
        this.waitingForTransaction = false;
 | 
			
		||||
      }
 | 
			
		||||
      this.rbfTransaction = rbfTransaction;
 | 
			
		||||
      this.stateService.setTxCache([this.rbfTransaction]);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    this.queryParamsSubscription = this.route.queryParams.subscribe((params) => {
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
<ng-container *ngFor="let tx of transactions; let i = index; trackBy: trackByFn">
 | 
			
		||||
  <div *ngIf="!transactionPage" class="header-bg box tx-page-container">
 | 
			
		||||
    <a class="float-left" [routerLink]="['/tx/' | relativeUrl, tx.txid]" [state]="{ data: tx }">
 | 
			
		||||
    <a class="float-left" [routerLink]="['/tx/' | relativeUrl, tx.txid]">
 | 
			
		||||
      <span style="float: left;" class="d-block d-md-none">{{ tx.txid | shortenString : 16 }}</span>
 | 
			
		||||
      <span style="float: left;" class="d-none d-md-block">{{ tx.txid }}</span>
 | 
			
		||||
    </a>
 | 
			
		||||
 | 
			
		||||
@ -119,7 +119,7 @@ export class TransactionsListComponent implements OnInit, OnChanges {
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      this.transactionsLength = this.transactions.length;
 | 
			
		||||
 | 
			
		||||
      this.stateService.setTxCache(this.transactions);
 | 
			
		||||
 | 
			
		||||
      this.transactions.forEach((tx) => {
 | 
			
		||||
        tx['@voutLimit'] = true;
 | 
			
		||||
 | 
			
		||||
@ -9,6 +9,11 @@
 | 
			
		||||
 | 
			
		||||
      <div class="doc-content">
 | 
			
		||||
 | 
			
		||||
        <div id="disclaimer">
 | 
			
		||||
          <table><tr><td><svg viewBox="0 0 304 304" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd" style="fill:#ffc107;fill-opacity:1"><path d="M135.3 34.474c-15.62 27.306-54.206 95.63-85.21 150.534L9.075 257.583C5.382 264.08 6.76 269.217 7.908 271.7c2.326 5.028 7.29 7.537 11.155 8.215l.78.133 264.698.006-.554-.02c4.152.255 9.664-1.24 12.677-6.194 1.926-3.18 3.31-8.589-1.073-16.278L213.637 114.37l-45.351-79.205c-5.681-9.932-12.272-12.022-16.8-12.022-4.42 0-10.818 1.964-16.181 11.331h-.006zm-69.072 159.94c30.997-54.885 69.563-123.184 85.16-150.446l.186-.297c.2.303.393.582.618.981l45.363 79.22s72.377 126.47 78.569 137.283l-247.618-.007 37.719-66.734" style="fill:#ffc107;fill-opacity:1"/><path d="M152.597 247.445c8.02 0 14.518-6.728 14.518-15.025 0-8.29-6.499-15.018-14.518-15.018-8.031 0-14.529 6.728-14.529 15.018 0 8.297 6.498 15.025 14.53 15.025m-.001-147.18c11.586 0 22.23 10.958 20.977 21.7l-9.922 75.564c-.966 6.601-4.95 11.433-11.055 11.433s-10.102-4.832-11.056-11.433l-9.927-75.564c-1.26-10.742 9.39-21.7 20.983-21.7" style="fill:#ffc107;fill-opacity:1"/></g></svg></td><td><p><b>mempool.space merely provides data about the Bitcoin network.</b> It cannot help you with retrieving funds, confirming your transaction quicker, etc.</p><p>For any such requests, you need to get in touch with the entity that helped make the transaction (wallet software, exchange company, etc).</p></td></tr></table>
 | 
			
		||||
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="doc-item-container" *ngFor="let item of faq">
 | 
			
		||||
          <h3 *ngIf="item.type === 'category'">{{ item.title }}</h3>
 | 
			
		||||
          <div *ngIf="item.type !== 'category'" class="endpoint-container" id="{{ item.fragment }}">
 | 
			
		||||
 | 
			
		||||
@ -219,6 +219,22 @@ h3 {
 | 
			
		||||
  display: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#disclaimer {
 | 
			
		||||
  background-color: #1d1f31;
 | 
			
		||||
  padding: 24px;
 | 
			
		||||
  margin: 24px 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#disclaimer svg {
 | 
			
		||||
  width: 50px;
 | 
			
		||||
  height: auto;
 | 
			
		||||
  margin-right: 32px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#disclaimer p:last-child {
 | 
			
		||||
  margin-bottom: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@media (max-width: 992px) {
 | 
			
		||||
 | 
			
		||||
  h3 {
 | 
			
		||||
 | 
			
		||||
@ -30,7 +30,7 @@
 | 
			
		||||
        <pre><code [innerText]="wrapEsModule(code)"></code></pre>
 | 
			
		||||
      </ng-template>
 | 
			
		||||
    </li>
 | 
			
		||||
    <li ngbNavItem *ngIf="showCodeExample[network] && network !== 'liquid' && network !== 'liquidtestnet'" role="presentation">
 | 
			
		||||
    <li ngbNavItem *ngIf="code.codeTemplate.python && network !== 'liquid' && network !== 'liquidtestnet'" role="presentation">
 | 
			
		||||
      <a ngbNavLink (click)="adjustContainerHeight( $event )" role="tab">Python</a>
 | 
			
		||||
      <ng-template ngbNavContent>
 | 
			
		||||
        <div class="subtitle"><ng-container i18n="API Docs code example">Code Example</ng-container> <app-clipboard [text]="wrapEsModule(code)"></app-clipboard></div>
 | 
			
		||||
 | 
			
		||||
@ -52,6 +52,10 @@
 | 
			
		||||
                <span i18n="unknown">Unknown</span>
 | 
			
		||||
              </td>
 | 
			
		||||
            </tr>
 | 
			
		||||
            <tr *ngIf="(avgChannelDistance$ | async) as avgDistance;">
 | 
			
		||||
              <td i18n="lightning.avg-distance" class="text-truncate">Avg channel distance</td>
 | 
			
		||||
              <td>{{ avgDistance | number : '1.0-0' }} <span class="symbol">km</span> <span class="separator">/</span> {{ kmToMiles(avgDistance) | number : '1.0-0' }} <span class="symbol">mi</span></td>
 | 
			
		||||
            </tr>
 | 
			
		||||
          </tbody>
 | 
			
		||||
        </table>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
@ -101,3 +101,7 @@ app-fiat {
 | 
			
		||||
    font-family: "Courier New", Courier, monospace;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.separator {
 | 
			
		||||
  margin: 0 1em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -3,9 +3,11 @@ import { ActivatedRoute, ParamMap } from '@angular/router';
 | 
			
		||||
import { Observable } from 'rxjs';
 | 
			
		||||
import { catchError, map, switchMap, tap } from 'rxjs/operators';
 | 
			
		||||
import { SeoService } from '../../services/seo.service';
 | 
			
		||||
import { ApiService } from '../../services/api.service';
 | 
			
		||||
import { LightningApiService } from '../lightning-api.service';
 | 
			
		||||
import { GeolocationData } from '../../shared/components/geolocation/geolocation.component';
 | 
			
		||||
import { ILiquidityAd, parseLiquidityAdHex } from './liquidity-ad';
 | 
			
		||||
import { haversineDistance, kmToMiles } from 'src/app/shared/common.utils';
 | 
			
		||||
 | 
			
		||||
interface CustomRecord {
 | 
			
		||||
  type: string;
 | 
			
		||||
@ -34,8 +36,12 @@ export class NodeComponent implements OnInit {
 | 
			
		||||
  showDetails = false;
 | 
			
		||||
  liquidityAd: ILiquidityAd;
 | 
			
		||||
  tlvRecords: CustomRecord[];
 | 
			
		||||
  avgChannelDistance$: Observable<number | null>;
 | 
			
		||||
 | 
			
		||||
  kmToMiles = kmToMiles;
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private apiService: ApiService,
 | 
			
		||||
    private lightningApiService: LightningApiService,
 | 
			
		||||
    private activatedRoute: ActivatedRoute,
 | 
			
		||||
    private seoService: SeoService,
 | 
			
		||||
@ -119,6 +125,26 @@ export class NodeComponent implements OnInit {
 | 
			
		||||
          }];
 | 
			
		||||
        })
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
    this.avgChannelDistance$ = this.activatedRoute.paramMap
 | 
			
		||||
    .pipe(
 | 
			
		||||
      switchMap((params: ParamMap) => {
 | 
			
		||||
        return this.apiService.getChannelsGeo$(params.get('public_key'), 'nodepage');
 | 
			
		||||
      }),
 | 
			
		||||
      map((channelsGeo) => {
 | 
			
		||||
        if (channelsGeo?.length) {
 | 
			
		||||
          const totalDistance = channelsGeo.reduce((sum, chan) => {
 | 
			
		||||
            return sum + haversineDistance(chan[3], chan[2], chan[7], chan[6]);
 | 
			
		||||
          }, 0);
 | 
			
		||||
          return totalDistance / channelsGeo.length;
 | 
			
		||||
        } else {
 | 
			
		||||
          return null;
 | 
			
		||||
        }
 | 
			
		||||
      }),
 | 
			
		||||
      catchError(() => {
 | 
			
		||||
        return null;
 | 
			
		||||
      })
 | 
			
		||||
    ) as Observable<number | null>;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  toggleShowDetails(): void {
 | 
			
		||||
 | 
			
		||||
@ -112,6 +112,8 @@ export class StateService {
 | 
			
		||||
  timeLtr: BehaviorSubject<boolean>;
 | 
			
		||||
  hideFlow: BehaviorSubject<boolean>;
 | 
			
		||||
 | 
			
		||||
  txCache: { [txid: string]: Transaction } = {};
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    @Inject(PLATFORM_ID) private platformId: any,
 | 
			
		||||
    @Inject(LOCALE_ID) private locale: string,
 | 
			
		||||
@ -265,4 +267,19 @@ export class StateService {
 | 
			
		||||
  isLiquid() {
 | 
			
		||||
    return this.network === 'liquid' || this.network === 'liquidtestnet';
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setTxCache(transactions) {
 | 
			
		||||
    this.txCache = {};
 | 
			
		||||
    transactions.forEach(tx => {
 | 
			
		||||
      this.txCache[tx.txid] = tx;
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
  getTxFromCache(txid) {
 | 
			
		||||
    if (this.txCache && this.txCache[txid]) {
 | 
			
		||||
      return this.txCache[txid];
 | 
			
		||||
    } else {
 | 
			
		||||
      return null;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -118,3 +118,21 @@ export function convertRegion(input, to: 'name' | 'abbreviated'): string {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function haversineDistance(lat1: number, lon1: number, lat2: number, lon2: number): number {
 | 
			
		||||
  const rlat1 = lat1 * Math.PI / 180;
 | 
			
		||||
  const rlon1 = lon1 * Math.PI / 180;
 | 
			
		||||
  const rlat2 = lat2 * Math.PI / 180;
 | 
			
		||||
  const rlon2 = lon2 * Math.PI / 180;
 | 
			
		||||
 | 
			
		||||
  const dlat = Math.sin((rlat2 - rlat1) / 2);
 | 
			
		||||
  const dlon = Math.sin((rlon2 - rlon1) / 2);
 | 
			
		||||
  const a = Math.min(1, Math.max(0, (dlat * dlat) + (Math.cos(rlat1) * Math.cos(rlat2) * dlon * dlon)));
 | 
			
		||||
  const d = 2 * 6371 * Math.asin(Math.sqrt(a));
 | 
			
		||||
 | 
			
		||||
  return d;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function kmToMiles(km: number): number {
 | 
			
		||||
  return km * 0.62137119;
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user