...
 
Commits (2)
......@@ -3,10 +3,10 @@
module Main (main) where
import Bitcoin.Script (Script)
import qualified Bitcoin.Script as BS
import qualified Data.Bitcoin.Script as BS
import Data.Aeson
import Data.ByteString.Lazy.Char8 (pack)
import Data.ByteString.Lazy.Char8 (ByteString, pack, unpack)
import qualified Data.Csv as Csv
import Data.List
import Data.Scientific (Scientific, FPFormat(Fixed), formatScientific)
import GHC.Generics
......@@ -14,18 +14,30 @@ import System.Environment
import System.IO
import System.Process
getBlackcoindCommand :: IO String
getBlackcoindCommand = do
customName <- lookupEnv "BLACKCOIND_COMMAND"
case customName of
Just name -> return name
Nothing -> return "blackcoind"
execCommand :: String -> [String] -> IO String
execCommand command args = readProcess "blackcoind" ([command] ++ args) ""
execCommand command args = do
blackcoindCommand <- getBlackcoindCommand
let fullCommand = intercalate " " ([blackcoindCommand, command] ++ args)
readCreateProcess (shell fullCommand) ""
data Block = Block {
hash :: String,
height :: Int,
time :: Int,
tx :: [String]
} deriving (Generic, Show)
instance FromJSON Block
data Tx = Tx {
txid :: String,
vout :: [VOut],
time :: Int
vout :: [VOut]
} deriving (Generic, Show)
instance FromJSON Tx
......@@ -33,7 +45,7 @@ data VOut = VOut {
n :: Int,
scriptHex :: String,
value :: Scientific,
script :: Script
script :: BS.Script
} deriving (Show)
instance FromJSON VOut where
parseJSON (Object o) = do
......@@ -60,22 +72,31 @@ getTx idx = do
case decode (pack txJSON) of
Just tx -> return tx
show_tx_vout :: (Tx, VOut) -> String
show_tx_vout (tx, vout) =
intercalate " " [txid tx,
show (n vout),
formatScientific Fixed Nothing (value vout),
scriptHex vout,
show (BS.scriptOps (script vout))]
ourEncodeOptions :: Csv.EncodeOptions
ourEncodeOptions = Csv.defaultEncodeOptions {
Csv.encQuoting = Csv.QuoteMinimal
}
blockTxVoutToCSV :: (Block, Tx, VOut) -> ByteString
blockTxVoutToCSV (block, tx, vout) =
Csv.encodeWith ourEncodeOptions [(show (height block),
hash block,
show (time block),
txid tx,
show (n vout),
formatScientific Fixed (Just 0) (100000000 * (value vout)),
scriptHex vout,
show (BS.scriptOps (script vout)))]
processTxs :: [Int] -> IO ()
processTxs [] = return ()
processTxs (height:heights) = do
block <- getBlockByNumber height
txs <- getTxsFromBlock block
let txs_vouts = concat [zip (repeat tx) (vout tx) | tx <- txs]
let interesting = filter ((elem BS.OP_RETURN) . BS.scriptOps . script . snd) txs_vouts
mapM_ (putStrLn . show_tx_vout) interesting
let txs_vouts = concat [zip3 (repeat block) (repeat tx) (vout tx) | tx <- txs]
let third (_, _, x) = x
let interesting = filter ((elem BS.OP_RETURN) . BS.scriptOps . script . third) txs_vouts
mapM_ (putStr . unpack . blockTxVoutToCSV) interesting
processTxs heights
main :: IO ()
......
......@@ -6,18 +6,20 @@ This unsophisticated application prints all [`OP_RETURN`](https://en.bitcoin.it/
burnt-explorer <start height> <stop height>
```
You must have the `blackcoind` binary (name currently hard-coded) available in `$PATH`.
It is assumed that you have `blackcoind` binary available in `$PATH`. If you want to use another command, set the `BLACKCOIND_COMMAND` environment variable.
Output format is the following:
Output format is [CSV (RFC 4180)](https://tools.ietf.org/html/rfc4180) without header, with minimal double quotes usage and with the following columns:
```
<transaction id> <vout index> <value> <script hex> <decoded script>
<block height>,<block hash>,<block time>,<transaction id>,<vout index>,<satoshis burnt>,<script hex>,<decoded script>
```
[`bitcoin-script` package](https://hackage.haskell.org/package/bitcoin-script) is used for decoding scripts.
[`bitcoin-script` package](https://hackage.haskell.org/package/bitcoin-script) is used for decoding scripts. If you don’t have a full-fledged CSV parser, you may want to omit the last field, for example by piping the output through `cut -d , -f -7`.
For prebuilt binaries, see job artifacts in [CI builds](https://gitlab.com/KrzysiekJ/burnt-explorer/pipelines).
This application uses [semantic versioning](http://semver.org/) 2.0.
## License
This software is licensed under under [the Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) (the “License”); you may not use this software except in compliance with the License. Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
......@@ -2,7 +2,7 @@
-- documentation, see http://haskell.org/cabal/users-guide/
name: burnt-explorer
version: 1.0.0
version: 2.0.0
synopsis: List OP_RETURN cryptocurrency transaction outputs.
-- description:
license: Apache-2.0
......@@ -29,8 +29,9 @@ executable burnt-explorer
build-depends: base >=4.8 && <4.12
, process >=1.6 && <1.7
, aeson >=1.4 && <1.5
, bitcoin-script >=0.9 && <0.10
, bitcoin-script >=0.11 && <0.12
, bytestring >=0.10 && <0.11
, scientific >=0.3 && <0.4
, cassava == 0.5.1.0
-- hs-source-dirs:
default-language: Haskell2010