Skip to content

Commit

Permalink
display parsed script
Browse files Browse the repository at this point in the history
  • Loading branch information
h0ngcha0 committed Nov 3, 2018
1 parent e4faf09 commit 5465ecf
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 32 deletions.
24 changes: 1 addition & 23 deletions client/src/modules/interpreter/InterpreterComponent.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import _ from 'lodash';
import React from 'react';
import {findElementType} from './ScriptElements';
import ScriptOpCodeList from '../transaction/ScriptOpCodeList';

class InterpreterComponent extends React.Component {

Expand Down Expand Up @@ -33,25 +32,4 @@ class InterpreterComponent extends React.Component {
}
};

const ScriptOpCodeList = ({opCodes}) => {
return (
<div className='ScriptOpCodeList'>
{
_(opCodes)
.filter((opCode) => opCode.type !== 'OP_PUSHDATA')
.map((scriptElement, index) => {
const elementType = findElementType(scriptElement.type);
const className = `OpCode ${elementType}`

return (
<div className={ className } key={index}>
{ _.includes(['ScriptConstant', 'ScriptNum'], scriptElement.type) ? scriptElement.value : scriptElement.type }
</div>
);
}).value()
}
</div>
)
};

export default InterpreterComponent;
27 changes: 27 additions & 0 deletions client/src/modules/transaction/ScriptOpCodeList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import _ from 'lodash';
import React from 'react';
import {findElementType} from './ScriptElements';


const ScriptOpCodeList = ({opCodes}) => {
return (
<div className='ScriptOpCodeList'>
{
_(opCodes)
.filter((opCode) => opCode.type !== 'OP_PUSHDATA')
.map((scriptElement, index) => {
const elementType = findElementType(scriptElement.type);
const className = `OpCode ${elementType}`

return (
<div className={ className } key={index}>
{ _.includes(['ScriptConstant', 'ScriptNum'], scriptElement.type) ? scriptElement.value : scriptElement.type }
</div>
);
}).value()
}
</div>
)
};

export default ScriptOpCodeList;
21 changes: 15 additions & 6 deletions client/src/modules/transaction/TransactionDetailsComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import TableCell from '@material-ui/core/TableCell';
import TableHead from '@material-ui/core/TableHead';
import TableRow from '@material-ui/core/TableRow';
import Grid from "@material-ui/core/Grid/Grid";
import {Link} from 'react-router-dom';
import ScriptOpCodeList from "./ScriptOpCodeList";

class TransactionDetailsComponent extends React.Component {
render() {
Expand All @@ -19,8 +19,8 @@ class TransactionDetailsComponent extends React.Component {
{
inputsLength < 0 ?
null :
<Grid container>
<Grid item sm={6} xs={12}>
<Grid container style={ {maxWidth: '550px', textAlign: 'center', margin: '0 auto'} }>
<Grid item sm={12} xs={12} >
<Table>
<TableHead>
<TableRow>
Expand All @@ -33,13 +33,19 @@ class TransactionDetailsComponent extends React.Component {
_.map(transaction.inputs, (input, index) => {
return (
<TableRow key={index}>
<TableCell>
<TableCell style={ { whiteSpace: "normal", wordWrap: "break-word" }}>
<div className='btc-address'>
{_.head(input.addresses)}
</div>
<div>
<span className='btc spent'>{input.output_value / 100000000} BTC</span> - <a href={ `/transaction/${input.prev_hash}` }> transaction </a>
</div>
<div style={ {marginTop: "5px"}}>
<ScriptOpCodeList opCodes={input.parsed_script} />
</div>
<div>
<a href={ `/transaction/${transaction.hash}/input/${index}/interpret`}> Interpret </a>
</div>
</TableCell>
</TableRow>
)
Expand All @@ -48,7 +54,7 @@ class TransactionDetailsComponent extends React.Component {
</TableBody>
</Table>
</Grid>
<Grid item sm={6} xs={12}>
<Grid item sm={12} xs={12}>
<Table>
<TableHead>
<TableRow>
Expand All @@ -60,7 +66,7 @@ class TransactionDetailsComponent extends React.Component {
_.map(transaction.outputs, (output, index) => {
return (
<TableRow key={index}>
<TableCell>
<TableCell style={ { whiteSpace: "normal", wordWrap: "break-word" }}>
<div className='btc-address'>
{_.head(output.addresses)}
</div>
Expand All @@ -69,6 +75,9 @@ class TransactionDetailsComponent extends React.Component {
output.spent_by ? <a href={ `/transaction/${output.spent_by}` }> transaction </a> : 'not spent yet'
}
</div>
<div style={ {marginTop: "5px"}}>
<ScriptOpCodeList opCodes={output.parsed_script} />
</div>
</TableCell>

</TableRow>
Expand Down
16 changes: 15 additions & 1 deletion src/main/scala/it/softfork/bitcoin4s/ApiModels.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package it.softfork.bitcoin4s

import it.softfork.bitcoin4s.script._
import play.api.libs.json._
import julienrf.json.derived.flat.owrites
import it.softfork.bitcoin4s.Utils._
import play.api.libs.json._

object ApiModels {

Expand Down Expand Up @@ -67,6 +67,20 @@ object ApiModels {
Json.obj("type" -> scriptOpcode.name, "value" -> scriptOpcode.value)
}


implicit val scriptElementReader: Reads[ScriptElement] = Reads[ScriptElement] { in =>
(in \ "type").validate[String].flatMap {
case "ScriptNum" =>
(in \ "value").validate[String].map { x =>
ScriptNum.apply(x.toLong)
}
case _ =>
throw new NotImplementedError("Script element reader not implemented")
}
}

implicit val scriptElementFormat: Format[ScriptElement] = Format(scriptElementReader, scriptElementWriter)

implicit val scriptExecutionStageWriter: OWrites[ScriptExecutionStage] = {
owrites[ScriptExecutionStage]((JsPath \ "type").format[String])
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ import akka.stream.Materializer
import it.softfork.bitcoin4s.crypto.Hash
import it.softfork.bitcoin4s.external.HttpSender
import it.softfork.bitcoin4s.external.blockcypher.Api._
import it.softfork.bitcoin4s.script.Parser
import it.softfork.bitcoin4s.script.{Parser, ScriptElement}
import it.softfork.bitcoin4s.transaction.structure.{OutPoint, Hash => ScodecHash}
import it.softfork.bitcoin4s.transaction.{Tx, TxId, TxIn, TxOut}
import play.api.libs.json.{JsError, JsSuccess, Json}
import scodec.bits.ByteVector
import tech.minna.playjson.macros.json
import it.softfork.bitcoin4s.ApiModels.scriptElementFormat

import scala.concurrent.{ExecutionContext, Future}

Expand Down Expand Up @@ -72,6 +73,7 @@ object Api {
prev_hash: String,
output_index: Int,
script: Option[String],
parsed_script: Option[Seq[ScriptElement]],
output_value: Long,
sequence: Long,
script_type: String,
Expand All @@ -90,11 +92,20 @@ object Api {
.getOrElse(ByteVector.empty),
sequence = sequence
)

def withParsedScript() = {
val parsedScript = script.map { scriptStr =>
Parser.parse("0x" + scriptStr)
}

copy(parsed_script = parsedScript)
}
}

@json case class TransactionOutput(
value: Long,
script: String,
parsed_script: Option[Seq[ScriptElement]],
spent_by: Option[String],
addresses: List[String],
script_type: String
Expand All @@ -104,6 +115,11 @@ object Api {
value = value,
pk_script = ByteVector(Parser.parse(Hash.fromHex(script)).flatMap(_.bytes))
)

def withParsedScript() = {
val parsedScript = Parser.parse("0x" + script)
copy(parsed_script = Some(parsedScript))
}
}

@json case class Transaction(
Expand Down Expand Up @@ -134,6 +150,13 @@ object Api {
tx_out = outputs.map(_.toTxOut).toList,
lock_time = lock_time
)

def withParsedScript() = {
val inputsWithParsedScript = inputs.map(_.withParsedScript())
val outputsWithParsedScript = outputs.map(_.withParsedScript())

copy(inputs = inputsWithParsedScript, outputs = outputsWithParsedScript)
}
}

protected def rawTxUrl(txId: TxId) = Uri(s"https://api.blockcypher.com/v1/btc/main/txs/${txId.value}?limit=1000")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class Service(api: ApiInterface)(

def getTransaction(txId: TxId): Future[Option[Transaction]] = {
logger.info(s"Fetching transaction $txId")
api.getTransaction(txId)
api.getTransaction(txId).map(_.map(_.withParsedScript()))
}

def getTransactionInput(txId: TxId, inputIndex: Int): Future[Option[TransactionInput]] = {
Expand Down

0 comments on commit 5465ecf

Please sign in to comment.