diff --git a/.gitignore b/.gitignore index 93846b4..f471896 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ results build node_modules +package-lock.json # extras *.swp @@ -47,3 +48,5 @@ public/css/main.css README.html po/* !po/*.po + +mynode/ diff --git a/app/package.json b/app/package.json index d59fbd2..99495df 100644 --- a/app/package.json +++ b/app/package.json @@ -37,11 +37,12 @@ "devDependencies": { "@angular/cli": "1.1.2", "@ionic/app-scripts": "1.3.7", - "@ionic/cli-plugin-cordova": "1.4.0", - "@ionic/cli-plugin-ionic-angular": "1.3.1", + "@ionic/cli-plugin-cordova": "1.5.0", + "@ionic/cli-plugin-ionic-angular": "1.4.0", "@types/jasmine": "2.5.41", "@types/node": "7.0.4", "codecov": "2.2.0", + "ionic": "3.6.0", "jasmine-core": "2.5.2", "jasmine-spec-reporter": "3.2.0", "karma": "1.4.1", diff --git a/app/src/components/transactions/transactions.html b/app/src/components/transactions/transactions.html new file mode 100644 index 0000000..4263992 --- /dev/null +++ b/app/src/components/transactions/transactions.html @@ -0,0 +1,48 @@ +
+
+

{{ tx.txid }}

+
+ first seen at + +
+
+ mined + +
+ +
+
+
+ No Inputs (Newly Generated Coins) +
+
+ +
+ > +
+ +
+
+

{{ getAddress(vout) }} {{ vout.value + ' BTC' }} (S)(U)

+
+
+
+ +
+
+
{{ vin.addr }}
+

{{ vin.value + ' BTC' }}

+
+ +
+ > +
+ +
+
+

{{ getAddress(vout) }} {{ vout.value + ' BTC' }} (S)(U)

+
+
+
+
+
diff --git a/app/src/components/transactions/transactions.module.ts b/app/src/components/transactions/transactions.module.ts new file mode 100644 index 0000000..d60c200 --- /dev/null +++ b/app/src/components/transactions/transactions.module.ts @@ -0,0 +1,16 @@ +import { NgModule } from '@angular/core'; +import { IonicModule } from 'ionic-angular'; +import { TransactionsComponent } from './transactions'; + +@NgModule({ + declarations: [ + TransactionsComponent + ], + imports: [ + IonicModule + ], + exports: [ + TransactionsComponent + ] +}) +export class TransactionsComponentModule {} diff --git a/app/src/components/transactions/transactions.scss b/app/src/components/transactions/transactions.scss new file mode 100644 index 0000000..0912214 --- /dev/null +++ b/app/src/components/transactions/transactions.scss @@ -0,0 +1,3 @@ +transactions { + +} diff --git a/app/src/components/transactions/transactions.ts b/app/src/components/transactions/transactions.ts new file mode 100644 index 0000000..4c594c2 --- /dev/null +++ b/app/src/components/transactions/transactions.ts @@ -0,0 +1,50 @@ +import { Component } from '@angular/core'; +import { Input } from '@angular/core'; +import { Http } from '@angular/http'; + +/** + * Generated class for the TransactionsComponent component. + * + * See https://angular.io/docs/ts/latest/api/core/index/ComponentMetadata-class.html + * for more info on Angular Components. + */ +@Component({ + selector: 'transactions', + templateUrl: 'transactions.html' +}) +export class TransactionsComponent { + + public loading: boolean = true; + @Input() public blockHash: string; + public transactions: any = []; + + constructor(private http: Http) { + } + + private ngOnInit(): void { + let apiPrefix: string = 'http://localhost:3001/insight-api/'; + + this.http.get(apiPrefix + 'txs?block=' + this.blockHash).subscribe( + (data) => { + this.transactions = JSON.parse(data['_body']); + this.loading = false; + + this.transactions.txs.forEach((tx) => { + console.log('tx is', tx); + }); + }, + (err) => { + console.log('err is', err); + this.loading = false; + } + ); + } + + public getAddress(vout: any): string { + if (vout.scriptPubKey && vout.scriptPubKey.addresses) { + return vout.scriptPubKey.addresses[0]; + } else { + return 'Unparsed address'; + } + } +} diff --git a/app/src/pages/block-detail/block-detail.html b/app/src/pages/block-detail/block-detail.html new file mode 100644 index 0000000..428a119 --- /dev/null +++ b/app/src/pages/block-detail/block-detail.html @@ -0,0 +1,41 @@ + + + + + Block Detail + + + + + + +

Block # {{ block.height }}

+

BlockHash {{ block.hash }}

+

Summary

+
+

Number of Transactions {{ block.tx.length }}

+

Height {{ block.height }} (Mainchain)

+

Block Reward {{ block.reward + ' BTC' }}

+

Timestamp {{ block.time * 1000 | date:'medium' }}

+

Mined by {{ block.poolInfo.poolName }}

+

Merkle Root {{ block.merkleroot }}

+

Previous Block {{ block.height - 1 }}

+

Difficulty {{ block.difficulty }}

+

Bits {{ block.bits }}

+

Size (bytes) {{ block.size }}

+

Version {{ block.version }}

+

Nonce {{ block.nonce }}

+

Next Block {{ block.height + 1 }}

+ +

Transactions

+ +
+
+

Loading...

+
+
diff --git a/app/src/pages/block-detail/block-detail.module.ts b/app/src/pages/block-detail/block-detail.module.ts new file mode 100644 index 0000000..1a14b2f --- /dev/null +++ b/app/src/pages/block-detail/block-detail.module.ts @@ -0,0 +1,18 @@ +import { NgModule } from '@angular/core'; +import { IonicPageModule } from 'ionic-angular'; +import { BlockDetailPage } from './block-detail'; +import { TransactionsComponent } from '../../components/transactions/transactions'; + +@NgModule({ + declarations: [ + BlockDetailPage, + TransactionsComponent + ], + imports: [ + IonicPageModule.forChild(BlockDetailPage) + ], + exports: [ + BlockDetailPage + ] +}) +export class BlockDetailPageModule {} diff --git a/app/src/pages/block-detail/block-detail.scss b/app/src/pages/block-detail/block-detail.scss new file mode 100644 index 0000000..95a60c5 --- /dev/null +++ b/app/src/pages/block-detail/block-detail.scss @@ -0,0 +1,3 @@ +page-block-detail { + +} diff --git a/app/src/pages/block-detail/block-detail.ts b/app/src/pages/block-detail/block-detail.ts new file mode 100644 index 0000000..bcd5983 --- /dev/null +++ b/app/src/pages/block-detail/block-detail.ts @@ -0,0 +1,60 @@ +import { Component } from '@angular/core'; +import { IonicPage, NavController, NavParams } from 'ionic-angular'; +import { Http } from '@angular/http'; + +/** + * Generated class for the BlockDetailPage page. + * + * See http://ionicframework.com/docs/components/#navigation for more info + * on Ionic pages and navigation. + */ +@IonicPage({ + name: 'block-detail', + segment: 'block/:blockHash' +}) +@Component({ + selector: 'page-block-detail', + templateUrl: 'block-detail.html' +}) +export class BlockDetailPage { + + public loading: boolean = true; + private blockHash: string; + public block: any = { + tx: [] + }; + + constructor(public navCtrl: NavController, private http: Http, public navParams: NavParams) { + this.blockHash = navParams.get('blockHash'); + + let apiPrefix: string = 'http://localhost:3001/insight-api/'; + + this.http.get(apiPrefix + 'block/' + this.blockHash).subscribe( + (data) => { + this.block = JSON.parse(data['_body']); + this.loading = false; + }, + (err) => { + console.log('err is', err); + this.loading = false; + } + ); + } + + public ionViewWillLeave(): void { + this.loading = true; + } + + public goToPreviousBlock(): void { + this.navCtrl.push('block-detail', { + 'blockHash': this.block.previousblockhash + }); + } + + public goToNextBlock(): void { + this.navCtrl.push('block-detail', { + 'blockHash': this.block.nextblockhash + }); + } + +} diff --git a/app/src/pages/blocksPage/blocksPage.html b/app/src/pages/blocksPage/blocksPage.html index 1449175..718c3bb 100644 --- a/app/src/pages/blocksPage/blocksPage.html +++ b/app/src/pages/blocksPage/blocksPage.html @@ -14,6 +14,9 @@ + + + Height diff --git a/app/src/pages/blocksPage/blocksPage.spec.ts b/app/src/pages/blocksPage/blocksPage.spec.ts index 8cc55b0..27921e3 100644 --- a/app/src/pages/blocksPage/blocksPage.spec.ts +++ b/app/src/pages/blocksPage/blocksPage.spec.ts @@ -20,4 +20,10 @@ describe('Blocks', () => { it('initializes', () => { expect(instance).toBeTruthy(); }); + + it('has a search method', () => { + spyOn(instance, 'search'); + instance.search(); + expect(instance.search).toHaveBeenCalled(); + }); }); diff --git a/app/src/pages/blocksPage/blocksPage.ts b/app/src/pages/blocksPage/blocksPage.ts index c7e83c6..cd06daf 100644 --- a/app/src/pages/blocksPage/blocksPage.ts +++ b/app/src/pages/blocksPage/blocksPage.ts @@ -3,6 +3,7 @@ import { NavController } from 'ionic-angular'; import { Observable } from 'rxjs'; import { Block } from '../../models'; import { BlocksService } from '../../services'; +import { Http } from '@angular/http'; @Component({ templateUrl: './blocksPage.html' @@ -10,11 +11,13 @@ import { BlocksService } from '../../services'; export class BlocksPage { + public loading: boolean; public title: string; public blocks: Observable; + public q: string; + public badQuery: boolean = false; - constructor(private nav: NavController, private blocksService: BlocksService) { - this.nav = nav; + constructor(private navCtrl: NavController, private http: Http, private blocksService: BlocksService) { this.title = 'Blocks'; this.blocks = blocksService.latestBlocks; // this.blocks.subscribe((blocks) => { @@ -22,4 +25,67 @@ export class BlocksPage { // }); blocksService.getLatestBlocks(); } + + public search(): void { + console.log('q is', this.q); + let apiPrefix: string = 'http://localhost:3001/insight-api/'; + this.http.get(apiPrefix + 'block/' + this.q).subscribe( + (data) => { + this.resetSearch(); + console.log('block', data); + }, + () => { + this.http.get(apiPrefix + 'tx/' + this.q).subscribe( + (data: any) => { + this.resetSearch(); + console.log('tx', data); + }, + () => { + this.http.get(apiPrefix + 'addr/' + this.q).subscribe( + (data: any) => { + this.resetSearch(); + console.log('addr', data); + }, + () => { + this.http.get(apiPrefix + 'block-index/' + this.q).subscribe( + function (data: any): void { + this.resetSearch(); + let parsedData: any = JSON.parse(data._body); + this.navCtrl.push('block-detail', { + 'blockHash': parsedData.blockHash + }); + }.bind(this), + function (): void { + this.loading = false; + this.reportBadQuery(); + }.bind(this) + ); + } + ); + } + ); + } + ); + } + + private resetSearch(): void { + this.q = ''; + this.loading = false; + } + + /* tslint:disable:no-unused-variable */ + private reportBadQuery(): void { + this.badQuery = true; + console.log('badQuery', this.badQuery); + + setTimeout( + function (): void { + this.badQuery = false; + console.log('badQuery', this.badQuery); + }.bind(this), + 2000 + ); + }; + /* tslint:enable:no-unused-variable */ + } diff --git a/app/src/pages/broadcastTxPage/broadcastTxPage.html b/app/src/pages/broadcastTxPage/broadcastTxPage.html index 600cbbe..0a3f19d 100644 --- a/app/src/pages/broadcastTxPage/broadcastTxPage.html +++ b/app/src/pages/broadcastTxPage/broadcastTxPage.html @@ -8,18 +8,24 @@ - - -

This form can be used to broadcast a raw transaction in hex format over the Bitcoin network.

-
+
+ + +

This form can be used to broadcast a raw transaction in hex format over the Bitcoin network.

+
- - Raw transaction data - - + + Raw transaction data + + - - - -
+ +

Raw transaction data must be a valid hexadecimal string.

+
+ + + + + +
diff --git a/app/src/pages/broadcastTxPage/broadcastTxPage.spec.ts b/app/src/pages/broadcastTxPage/broadcastTxPage.spec.ts index 7c771a3..cd15055 100644 --- a/app/src/pages/broadcastTxPage/broadcastTxPage.spec.ts +++ b/app/src/pages/broadcastTxPage/broadcastTxPage.spec.ts @@ -20,4 +20,10 @@ describe('BroadcastTxPage', () => { it('initializes', () => { expect(instance).toBeTruthy(); }); + + it('has a send method', () => { + spyOn(instance, 'send'); + instance.send(); + expect(instance.send).toHaveBeenCalled(); + }); }); diff --git a/app/src/pages/broadcastTxPage/broadcastTxPage.ts b/app/src/pages/broadcastTxPage/broadcastTxPage.ts index 63e28cd..40563ef 100644 --- a/app/src/pages/broadcastTxPage/broadcastTxPage.ts +++ b/app/src/pages/broadcastTxPage/broadcastTxPage.ts @@ -1,5 +1,6 @@ import { Component } from '@angular/core'; import { NavController } from 'ionic-angular'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; @Component({ templateUrl: './broadcastTxPage.html' @@ -9,9 +10,22 @@ export class BroadcastTxPage { public title: string; private nav: NavController; + public transaction: string; + public txForm: FormGroup; - constructor(nav: NavController) { + constructor(nav: NavController, public formBuilder: FormBuilder) { this.nav = nav; this.title = 'Broadcast Transaction'; + this.txForm = formBuilder.group({ + rawData: ['', Validators.pattern(/^[0-9A-Fa-f]+$/)] + }); + } + + public send(): void { + let postData: any = { + rawtx: this.transaction + }; + + console.log('the postData is', postData); } } diff --git a/app/src/services/blocksService.spec.ts b/app/src/services/blocksService.spec.ts index cf99bfb..f104a48 100644 --- a/app/src/services/blocksService.spec.ts +++ b/app/src/services/blocksService.spec.ts @@ -1,8 +1,8 @@ -import { BlocksService } from './blocksService'; -import { Block } from '../models'; -import { TestUtils } from '../test'; +// import { BlocksService } from './blocksService'; +// import { Block } from '../models'; +// import { TestUtils } from '../test'; -let blocks: BlocksService = null; +// let blocks: BlocksService = null; // describe('BlocksService', () => { diff --git a/app/src/services/blocksService.ts b/app/src/services/blocksService.ts index 747f49d..724b593 100644 --- a/app/src/services/blocksService.ts +++ b/app/src/services/blocksService.ts @@ -1,6 +1,6 @@ import { Http, Response } from '@angular/http'; import { Injectable } from '@angular/core'; -import { Observable, Subject } from 'rxjs'; +import { Subject } from 'rxjs'; import { Block, InsightBlockObject } from '../models'; @Injectable() diff --git a/app/src/test.ts b/app/src/test.ts index ee17df7..8ba1c44 100644 --- a/app/src/test.ts +++ b/app/src/test.ts @@ -11,7 +11,7 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { getTestBed, TestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; import { HttpModule } from '@angular/http'; -import { MockBackend } from '@angular/http/testing'; +// import { MockBackend } from '@angular/http/testing'; import { App, Config, Form, IonicModule, Keyboard, DomController, MenuController, NavController, Platform, GestureController } from 'ionic-angular'; import { ConfigMock, PlatformMock } from './mocks'; import { BlocksServiceMock } from './services/mocks';