Skip to content
Snippets Groups Projects
Unverified Commit 0a6095d0 authored by jkrvivian's avatar jkrvivian Committed by GitHub
Browse files

feat: Add payload layouts to dashboard (#425)

* fix: Rename SPA to dashboard and disable portcheck of peer_replica

* feat: Add data & unknown type of payload to dashboard

* refactor: Implement paylaod interface that allows different json structs

* feat: Implement Drng paylaod

* refactor: Use Listgroup for layout consistency

* doc: Add description

* feat: Implement Value payload handler

* feat: Add Value payload layout

* refactor: Refactor payload layouts

* refactor: Remove debug print out

* fix: Remove redundant assignment

* refactor: Fix :dog:

* refactor: Fix :dog:
parent 21bbf9e6
No related branches found
No related tags found
No related merge requests found
Showing
with 10745 additions and 20 deletions
...@@ -22,6 +22,10 @@ type ExplorerMessage struct { ...@@ -22,6 +22,10 @@ type ExplorerMessage struct {
BranchMessageID string `json:"branch_message_id"` BranchMessageID string `json:"branch_message_id"`
// Solid defines the solid status of the message. // Solid defines the solid status of the message.
Solid bool `json:"solid"` Solid bool `json:"solid"`
// PayloadType defines the type of the payload.
PayloadType uint32 `json:"payload_type"`
// Payload is the content of the payload.
Payload interface{} `json:"payload"`
} }
func createExplorerMessage(msg *message.Message) (*ExplorerMessage, error) { func createExplorerMessage(msg *message.Message) (*ExplorerMessage, error) {
...@@ -33,6 +37,8 @@ func createExplorerMessage(msg *message.Message) (*ExplorerMessage, error) { ...@@ -33,6 +37,8 @@ func createExplorerMessage(msg *message.Message) (*ExplorerMessage, error) {
TrunkMessageID: msg.TrunkId().String(), TrunkMessageID: msg.TrunkId().String(),
BranchMessageID: msg.BranchId().String(), BranchMessageID: msg.BranchId().String(),
Solid: messageMetadata.Unwrap().IsSolid(), Solid: messageMetadata.Unwrap().IsSolid(),
PayloadType: msg.Payload().Type(),
Payload: ProcessPayload(msg.Payload()),
} }
return t, nil return t, nil
......
This diff is collapsed.
...@@ -75,6 +75,7 @@ ...@@ -75,6 +75,7 @@
"react-bootstrap": "^1.0.0-beta.16", "react-bootstrap": "^1.0.0-beta.16",
"react-chartjs-2": "^2.8.0", "react-chartjs-2": "^2.8.0",
"react-dom": "^16.7.0", "react-dom": "^16.7.0",
"react-icons": "^3.10.0",
"react-router": "^4.3.1", "react-router": "^4.3.1",
"react-router-bootstrap": "^0.25.0", "react-router-bootstrap": "^0.25.0",
"react-router-dom": "^5.1.2", "react-router-dom": "^5.1.2",
......
import * as React from 'react';
import Row from "react-bootstrap/Row";
import Col from "react-bootstrap/Col";
import {inject, observer} from "mobx-react";
import ExplorerStore from "app/stores/ExplorerStore";
interface Props {
explorerStore?: ExplorerStore;
}
@inject("explorerStore")
@observer
export class BasicPayload extends React.Component<Props, any> {
render() {
let {payload} = this.props.explorerStore;
return (
payload &&
<React.Fragment>
<Row className={"mb-3"}>
<Col>
{payload.content_title}: {' '}
{payload.bytes}
</Col>
</Row>
</React.Fragment>
);
}
}
import * as React from 'react';
import Row from "react-bootstrap/Row";
import Col from "react-bootstrap/Col";
import {inject, observer} from "mobx-react";
import {ExplorerStore} from "app/stores/ExplorerStore";
import ListGroup from "react-bootstrap/ListGroup";
import {DrngSubtype} from "app/misc/Payload"
interface Props {
explorerStore?: ExplorerStore;
}
@inject("explorerStore")
@observer
export class DrngPayload extends React.Component<Props, any> {
render() {
let {payload, subpayload} = this.props.explorerStore;
return (
payload &&
<React.Fragment>
<Row className={"mb-3"}>
<Col>
<ListGroup>
<ListGroup.Item>Drng payload subtype: {payload.subpayload_type} </ListGroup.Item>
</ListGroup>
</Col>
<Col>
<ListGroup>
<ListGroup.Item>Instance ID: {payload.instance_id} </ListGroup.Item>
</ListGroup>
</Col>
</Row>
{
payload.subpayload_type == DrngSubtype.Cb ? (
<React.Fragment>
<Row className={"mb-3"}>
<Col>
<ListGroup>
<ListGroup.Item>Round: {subpayload.round} </ListGroup.Item>
</ListGroup>
</Col>
</Row>
<Row className={"mb-3"}>
<Col>
<ListGroup>
<ListGroup.Item>Previous Signature: {subpayload.prev_sig} </ListGroup.Item>
</ListGroup>
</Col>
</Row>
<Row className={"mb-3"}>
<Col>
<ListGroup>
<ListGroup.Item>Signature: {subpayload.sig} </ListGroup.Item>
</ListGroup>
</Col>
</Row>
<Row className={"mb-3"}>
<Col>
<ListGroup>
<ListGroup.Item>Distributed Public Key: {subpayload.dpk} </ListGroup.Item>
</ListGroup>
</Col>
</Row>
</React.Fragment>
) : (
<React.Fragment>
<Row className={"mb-3"}>
<Col>
<ListGroup>
<ListGroup.Item>{subpayload.content_title}: {subpayload.bytes} </ListGroup.Item>
</ListGroup>
</Col>
</Row>
</React.Fragment>
)
}
</React.Fragment>
);
}
}
...@@ -10,6 +10,10 @@ import ListGroup from "react-bootstrap/ListGroup"; ...@@ -10,6 +10,10 @@ import ListGroup from "react-bootstrap/ListGroup";
import Badge from "react-bootstrap/Badge"; import Badge from "react-bootstrap/Badge";
import * as dateformat from 'dateformat'; import * as dateformat from 'dateformat';
import {Link} from 'react-router-dom'; import {Link} from 'react-router-dom';
import {BasicPayload} from 'app/components/BasicPayload'
import {DrngPayload} from 'app/components/DrngPayload'
import {ValuePayload} from 'app/components/ValuePayload'
import {PayloadType} from 'app/misc/Payload'
interface Props { interface Props {
nodeStore?: NodeStore; nodeStore?: NodeStore;
...@@ -38,6 +42,32 @@ export class ExplorerMessageQueryResult extends React.Component<Props, any> { ...@@ -38,6 +42,32 @@ export class ExplorerMessageQueryResult extends React.Component<Props, any> {
return null; return null;
} }
getPayloadType() {
switch(this.props.explorerStore.msg.payload_type) {
case PayloadType.Data:
return "Data"
case PayloadType.Value:
return "Value"
case PayloadType.Drng:
return "Drng"
default:
return "Unknown"
}
}
renderPayload() {
switch(this.props.explorerStore.msg.payload_type) {
case PayloadType.Drng:
return <DrngPayload/>
case PayloadType.Value:
return <ValuePayload/>
case PayloadType.Data:
default:
console.log(this.props.explorerStore.msg.payload.bytes)
return <BasicPayload/>
}
}
render() { render() {
let {id} = this.props.match.params; let {id} = this.props.match.params;
let {msg, query_loading} = this.props.explorerStore; let {msg, query_loading} = this.props.explorerStore;
...@@ -62,6 +92,11 @@ export class ExplorerMessageQueryResult extends React.Component<Props, any> { ...@@ -62,6 +92,11 @@ export class ExplorerMessageQueryResult extends React.Component<Props, any> {
msg && msg &&
<React.Fragment> <React.Fragment>
<Row className={"mb-3"}> <Row className={"mb-3"}>
<Col>
<ListGroup>
<ListGroup.Item>Payload Type: {this.getPayloadType()} </ListGroup.Item>
</ListGroup>
</Col>
<Col> <Col>
<ListGroup> <ListGroup>
<ListGroup.Item>Solid: {msg.solid ? 'Yes' : 'No'}</ListGroup.Item> <ListGroup.Item>Solid: {msg.solid ? 'Yes' : 'No'}</ListGroup.Item>
...@@ -90,6 +125,20 @@ export class ExplorerMessageQueryResult extends React.Component<Props, any> { ...@@ -90,6 +125,20 @@ export class ExplorerMessageQueryResult extends React.Component<Props, any> {
</ListGroup> </ListGroup>
</Col> </Col>
</Row> </Row>
<Row className={"mb-3"}>
<Col>
<h4>Payload</h4>
</Col>
</Row>
<Row className={"mb-3"}>
<Col>
<ListGroup>
<ListGroup.Item className="text-break">
{this.renderPayload()}
</ListGroup.Item>
</ListGroup>
</Col>
</Row>
</React.Fragment> </React.Fragment>
} }
<Row className={"mb-3"}> <Row className={"mb-3"}>
......
import * as React from 'react';
import Row from "react-bootstrap/Row";
import Col from "react-bootstrap/Col";
import Badge from "react-bootstrap/Badge"
import ListGroup from "react-bootstrap/ListGroup";
import {Link} from 'react-router-dom';
import {inject, observer} from "mobx-react";
import {ExplorerStore} from "app/stores/ExplorerStore";
import {IconContext} from "react-icons"
import {FaChevronCircleRight} from "react-icons/fa"
interface Props {
explorerStore?: ExplorerStore;
}
interface OutputProps {
address: string;
balances?: BalanceProps
}
interface BalanceProps {
value: number;
color: string;
}
@inject("explorerStore")
@observer
export class ValuePayload extends React.Component<Props, any> {
render() {
let {payload} = this.props.explorerStore;
return (
payload &&
<React.Fragment>
<Row className={"mb-3"}>
<Col>
<ListGroup>
<ListGroup.Item>Parent 0: {payload.parent_id_0} </ListGroup.Item>
</ListGroup>
</Col>
<Col>
<ListGroup>
<ListGroup.Item>Parent 1: {payload.parent_id_1} </ListGroup.Item>
</ListGroup>
</Col>
</Row>
<Row className={"mb-3"}>
<Col>
<ListGroup>
<ListGroup.Item>Transaction ID: {payload.tx_id} </ListGroup.Item>
</ListGroup>
</Col>
</Row>
<Row className={"mb-3"}>
<Col>
<div style={{
marginBottom: "20px",
paddingBottom: "10px",
borderBottom: "2px solid #eee"}}>Inputs:</div>
<React.Fragment>
{
payload.inputs.map(input => (
<Inputs address={input.address} key={input.address}/>
))}
</React.Fragment>
</Col>
<Col md="auto">
<IconContext.Provider value={{ color: "#00a0ff", size: "2em"}}>
<div style={{
marginTop: "40px"}}>
<FaChevronCircleRight />
</div>
</IconContext.Provider>
</Col>
<Col>
<div style={{
marginBottom: "20px",
paddingBottom: "10px",
borderBottom: "2px solid #eee"}}>Outputs:</div>
<React.Fragment>
{
payload.outputs.map(output => (
output.balance.map(balance => (
<Outputs key={balance.value}
address={output.address}
balances={balance} />
))
))}
</React.Fragment>
</Col>
</Row>
{
payload.data &&
<Row className={"mb-3"}>
<Col>
<ListGroup>
<ListGroup.Item>Data: {payload.data} </ListGroup.Item>
</ListGroup>
</Col>
</Row>
}
</React.Fragment>
);
}
}
class Inputs extends React.Component<OutputProps> {
render() {
return (
<Row className={"mb-3"}>
<Col>
<div>
<Link to={`/explorer/address/${this.props.address}`}>
{this.props.address}
</Link>
</div>
</Col>
</Row>
);
}
}
class Outputs extends React.Component<OutputProps> {
render() {
return (
<Row className={"mb-3"}>
<Col>
<div>
<Link to={`/explorer/address/${this.props.address}`}>
{this.props.address}
</Link>
</div>
<div>
<Badge variant="success">
{this.props.balances.value}{' '}{this.props.balances.color}
</Badge>
</div>
</Col>
</Row>
);
}
}
export enum PayloadType {
Data = 0,
Value = 1,
Drng = 111,
}
export enum DrngSubtype {
Default = 0,
Cb = 1,
}
// BasicPayload
export class BasicPayload {
content_title: string;
bytes: string;
}
// DrngPayload
export class DrngPayload {
subpayload_type: string;
instance_id: number;
drngpayload: any;
}
export class DrngCbPayload {
round: number;
prev_sig: string;
sig: string;
dpk: string;
}
// Value payload
export class ValuePayload {
payload_id: string;
parent_id_0: string;
parent_id_1: string;
tx_id: string;
inputs: Array<Inputs>;
outputs: Array<Outputs>;
data: string;
}
export class Inputs {
address: string;
}
export class Outputs {
address: string;
balance: Array<Balance>;
}
export class Balance {
value: number;
color: string;
}
import {action, computed, observable} from 'mobx'; import {action, computed, observable} from 'mobx';
import {registerHandler, WSMsgType} from "app/misc/WS"; import {registerHandler, WSMsgType} from "app/misc/WS";
import {PayloadType, DrngSubtype} from "app/misc/Payload";
import {BasicPayload, DrngPayload, DrngCbPayload, ValuePayload} from "app/misc/Payload";
import * as React from "react"; import * as React from "react";
import {Link} from 'react-router-dom'; import {Link} from 'react-router-dom';
import {RouterStore} from "mobx-react-router"; import {RouterStore} from "mobx-react-router";
...@@ -10,6 +12,8 @@ export class Message { ...@@ -10,6 +12,8 @@ export class Message {
trunk_message_id: string; trunk_message_id: string;
branch_message_id: string; branch_message_id: string;
solid: boolean; solid: boolean;
payload_type: number;
payload: any;
} }
class AddressResult { class AddressResult {
...@@ -48,6 +52,8 @@ export class ExplorerStore { ...@@ -48,6 +52,8 @@ export class ExplorerStore {
@observable search: string = ""; @observable search: string = "";
@observable search_result: SearchResult = null; @observable search_result: SearchResult = null;
@observable searching: boolean = false; @observable searching: boolean = false;
@observable payload: any;
@observable subpayload: any;
routerStore: RouterStore; routerStore: RouterStore;
...@@ -149,6 +155,22 @@ export class ExplorerStore { ...@@ -149,6 +155,22 @@ export class ExplorerStore {
this.msg = msg; this.msg = msg;
this.query_err = null; this.query_err = null;
this.query_loading = false; this.query_loading = false;
switch(msg.payload_type){
case PayloadType.Drng:
this.payload = msg.payload as DrngPayload
if (this.payload.subpayload_type == DrngSubtype.Cb) {
this.subpayload = this.payload.drngpayload as DrngCbPayload
} else {
this.subpayload = this.payload.drngpayload as BasicPayload
}
break;
case PayloadType.Value:
this.payload = msg.payload as ValuePayload
case PayloadType.Data:
default:
this.payload = msg.payload as BasicPayload
break;
}
}; };
@action @action
...@@ -192,4 +214,4 @@ export class ExplorerStore { ...@@ -192,4 +214,4 @@ export class ExplorerStore {
} }
export default ExplorerStore; export default ExplorerStore;
\ No newline at end of file
This diff is collapsed.
package dashboard
import (
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/balance"
valuepayload "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/payload"
drngpayload "github.com/iotaledger/goshimmer/packages/binary/drng/payload"
drngheader "github.com/iotaledger/goshimmer/packages/binary/drng/payload/header"
cb "github.com/iotaledger/goshimmer/packages/binary/drng/subtypes/collectiveBeacon/payload"
"github.com/iotaledger/goshimmer/packages/binary/messagelayer/payload"
"github.com/iotaledger/hive.go/marshalutil"
)
// BasicPayload contains content title and bytes
// It can be reused with different payload that only contains one field.
type BasicPayload struct {
ContentTitle string `json:"content_title"`
Bytes []byte `json:"bytes"`
}
// DrngPayload contains the subtype of drng payload, instance Id
// and the subpayload
type DrngPayload struct {
SubPayloadType byte `json:"subpayload_type"`
InstanceID uint32 `json:"instance_id"`
SubPayload interface{} `json:"drngpayload"`
}
// DrngCollectiveBeaconPayload is the subpayload of DrngPayload.
type DrngCollectiveBeaconPayload struct {
Round uint64 `json:"round"`
PrevSig []byte `json:"prev_sig"`
Sig []byte `json:"sig"`
Dpk []byte `json:"dpk"`
}
// ValuePayload contains the transaction information
type ValuePayload struct {
ID string `json:"payload_id"`
ParentID0 string `json:"parent_id_0"`
ParentID1 string `json:"parent_id_1"`
TxID string `json:"tx_id"`
Input []InputContent `json:"inputs"`
Output []OutputContent `json:"outputs"`
Data []byte `json:"data"`
}
// InputContent contains the inputs of a transaction
type InputContent struct {
Address string `json:"address"`
}
// OutputContent contains the outputs of a transaction
type OutputContent struct {
Address string `json:"address"`
Balances []Balance `json:"balance"`
}
// Balance contains the amount of specific color token
type Balance struct {
Value int64 `json:"value"`
Color string `json:"color"`
}
// ProcessPayload returns different structs regarding to the
// payload type.
func ProcessPayload(p payload.Payload) interface{} {
switch p.Type() {
case payload.DataType:
// data payload
return BasicPayload{
ContentTitle: "Data",
Bytes: p.(*payload.Data).Data(),
}
case drngpayload.Type:
// drng payload
return processDrngPayload(p)
case valuepayload.Type:
return processValuePayload(p)
default:
// unknown payload
return BasicPayload{
ContentTitle: "Bytes",
Bytes: p.Bytes(),
}
}
}
// processDrngPayload handles the subtypes of Drng payload
func processDrngPayload(p payload.Payload) (dp DrngPayload) {
var subpayload interface{}
marshalUtil := marshalutil.New(p.Bytes())
drngPayload, _ := drngpayload.Parse(marshalUtil)
switch drngPayload.Header.PayloadType {
case drngheader.TypeCollectiveBeacon:
// collective beacon
marshalUtil := marshalutil.New(p.Bytes())
cbp, _ := cb.Parse(marshalUtil)
subpayload = DrngCollectiveBeaconPayload{
Round: cbp.Round,
PrevSig: cbp.PrevSignature,
Sig: cbp.Signature,
Dpk: cbp.Dpk,
}
default:
subpayload = BasicPayload{
ContentTitle: "bytes",
Bytes: drngPayload.Bytes(),
}
}
return DrngPayload{
SubPayloadType: drngPayload.Header.PayloadType,
InstanceID: drngPayload.Header.InstanceID,
SubPayload: subpayload,
}
}
// processValuePayload handles Value payload
func processValuePayload(p payload.Payload) (vp ValuePayload) {
marshalUtil := marshalutil.New(p.Bytes())
v, _ := valuepayload.Parse(marshalUtil)
var inputs []InputContent
var outputs []OutputContent
// TODO: retrieve balance
v.Transaction().Inputs().ForEachAddress(func(currentAddress address.Address) bool {
inputs = append(inputs, InputContent{Address: currentAddress.String()})
return true
})
// Get outputs address and balance
v.Transaction().Outputs().ForEach(func(address address.Address, balances []*balance.Balance) bool {
var b []Balance
for _, balance := range balances {
b = append(b, Balance{
Value: balance.Value(),
Color: balance.Color().String(),
})
}
t := OutputContent{
Address: address.String(),
Balances: b,
}
outputs = append(outputs, t)
return true
})
return ValuePayload{
ID: v.ID().String(),
ParentID0: v.TrunkID().String(),
ParentID1: v.BranchID().String(),
TxID: v.Transaction().ID().String(),
Input: inputs,
Output: outputs,
Data: v.Transaction().GetDataPayload(),
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment