Import cardano-submit-api from cardano-rest repo
Publish submit-api artifacts Tidy up imports
Publish submit-api artifacts Tidy up imports
- name: Run tests
run: TMPDIR="${{ runner.temp }}" TMP="${{ runner.temp }}" KEEP_WORKSPACE=1 retry 2 cabal test --builddir="$CABAL_BUILDDIR" cardano-node-chairman
- name: Build & Test
run: |
mkdir -p artifacts
for exe in $(cat dist-newstyle/cache/plan.json | jq -r '."install-plan"[] | select(.style == "local" and (."component-name" | startswith("exe:"))) | ."bin-file"'); do
( cd artifacts
tar -C "$(dirname $exe)" -czf "$(basename $exe).tar.gz" "$(basename $exe)"
)
done
- name: Save Artifact
if: matrix.ghc == '8.10.3'
uses: actions/[email protected]
with:
name: ${{ matrix.os }}_cardano-rest
path: ./artifacts
- name: Delete socket files in preparation for upload artifacts
if: ${{ always() }}
run: |
with:
name: chairman-test-artifacts-${{ matrix.os }}-${{ matrix.ghc }}
path: ${{ runner.temp }}/chairman/
release:
needs: [build]
if: ${{ startsWith(github.ref, 'refs/tags') }}
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/[email protected]
- name: Create Release Tag
id: create_release_tag
run: echo ::set-output name=TAG::${GITHUB_REF/refs\/tags\//}
- name: Create Release
id: create_release
uses: actions/[email protected]
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Release ${{ github.ref }}
draft: true
prerelease: false
- name: Download Artifact (linux)
uses: actions/[email protected]
with:
name: ubuntu-latest_cardano-rest
- name: Download Artifact (macOS)
uses: actions/[email protected]
with:
name: macOS-latest_cardano-rest
- name: Upload Release Asset (cardano-submit-api, linux)
uses: actions/[email protected]
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./ubuntu-latest_cardano-rest/cardano-submit-api.tar.gz
asset_name: cardano-submit-api_${{ steps.create_release_tag.outputs.TAG }}-linux.tar.gz
asset_content_type: application/gzip
- name: Upload Release Asset (cardano-submit-api, macOS)
uses: actions/[email protected]
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./macOS-latest_cardano-rest/cardano-submit-api.tar.gz
asset_name: cardano-submit-api_${{ steps.create_release_tag.outputs.TAG }}-macOS.tar.gz
asset_content_type: application/gzip
cardano-config
cardano-node
cardano-node-chairman
cardano-submit-api
hedgehog-extras
package cardano-api
Cardano.Api.Protocol.Shelley
Cardano.Api.Protocol.Types
-- TODO Review this. Made public to compile cardano-rest
Cardano.Api.TxBody
other-modules: Cardano.Api.TxSubmit.ErrorRender
Cardano.Api.TxSubmit.Types
Cardano.Api.SpecialByron
Cardano.Api.StakePoolMetadata
Cardano.Api.Tx
Cardano.Api.TxBody
Cardano.Api.TxInMode
Cardano.Api.TxMetadata
Cardano.Api.Utils
# Changelog
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
module Main where
import Cardano.TxSubmit (opts, runTxSubmitWebapi)
import qualified Options.Applicative as Opt
main :: IO ()
main = runTxSubmitWebapi =<< Opt.execParser opts
cabal-version: 2.2
name: cardano-submit-api
version: 3.1.2
synopsis: A web server that allows transactions to be POSTed to the cardano chain
description:
homepage: https://github.com/input-output-hk/cardano-explorer
bug-reports: https://github.com/input-output-hk/cardano-explorer/issues
license: Apache-2.0
license-file: LICENSE
author: IOHK Engineering Team
maintainer: [email protected]
copyright: (c) 2019-2021 IOHK
category: Language
build-type: Simple
extra-source-files: CHANGELOG.md
common base { build-depends: base >= 4.12 && < 4.15 }
common cardano-api { build-depends: cardano-api }
common cardano-submit-api { build-depends: cardano-submit-api }
common aeson { build-depends: aeson >= 1.5.5.1 }
common async { build-depends: async >= 2.2.2 }
common base16-bytestring { build-depends: base16-bytestring >= 1.0.1.0 }
common bytestring { build-depends: bytestring >= 0.10.8.2 }
common cardano-binary { build-depends: cardano-binary >= 1.5.0 }
common cardano-crypto-class { build-depends: cardano-crypto-class >= 2.0.0 }
common cardano-ledger { build-depends: cardano-ledger >= 0.1.0.0 }
common cardano-ledger-shelley-ma { build-depends: cardano-ledger-shelley-ma >= 0.1.0.0 }
common cardano-prelude { build-depends: cardano-prelude >= 0.1.0.0 }
common containers { build-depends: containers >= 0.6.2.1 }
common contra-tracer { build-depends: contra-tracer >= 0.1.0.0 }
common formatting { build-depends: formatting >= 6.3.7 }
common http-media { build-depends: http-media >= 0.8.0.0 }
common iohk-monitoring { build-depends: iohk-monitoring >= 0.1.10.1 }
common monad-logger { build-depends: monad-logger >= 0.3.36 }
common mtl { build-depends: mtl >= 2.2.2 }
common network { build-depends: network >= 3.1.2.1 }
common optparse-applicative { build-depends: optparse-applicative >= 0.16.1.0 }
common ouroboros-consensus { build-depends: ouroboros-consensus >= 0.1.0.0 }
common ouroboros-consensus-byron { build-depends: ouroboros-consensus-byron >= 0.1.0.0 }
common ouroboros-consensus-cardano { build-depends: ouroboros-consensus-cardano >= 0.1.0.0 }
common ouroboros-consensus-shelley { build-depends: ouroboros-consensus-shelley >= 0.1.0.0 }
common ouroboros-network { build-depends: ouroboros-network >= 0.1.0.0 }
common ouroboros-network-framework { build-depends: ouroboros-network-framework >= 0.1.0.0 }
common prometheus { build-depends: prometheus >= 2.2.2 }
common servant { build-depends: servant >= 0.18.2 }
common servant-server { build-depends: servant-server >= 0.18.2 }
common shelley-spec-ledger { build-depends: shelley-spec-ledger >= 0.1.0.0 }
common streaming-commons { build-depends: streaming-commons >= 0.2.2.1 }
common text { build-depends: text >= 1.2.3.2 }
common transformers { build-depends: transformers >= 0.5.6.2 }
common warp { build-depends: warp >= 3.3.13 }
common yaml { build-depends: yaml >= 0.11.5.0 }
common project-config
default-language: Haskell2010
ghc-options: -Wall
-fwarn-incomplete-patterns
-fwarn-redundant-constraints
-Wcompat
-Werror
-Wincomplete-record-updates
-Wincomplete-uni-patterns
-Wno-all-missed-specialisations
-Wno-implicit-prelude
-Wno-missing-import-lists
-Wno-safe
-Wno-unsafe
library
import: base, project-config
, aeson
, async
, base16-bytestring
, bytestring
, cardano-api
, cardano-binary
, cardano-crypto-class
, cardano-ledger
, cardano-ledger-shelley-ma
, cardano-prelude
, containers
, contra-tracer
, formatting
, http-media
, iohk-monitoring
, monad-logger
, mtl
, network
, optparse-applicative
, ouroboros-consensus
, ouroboros-consensus-byron
, ouroboros-consensus-cardano
, ouroboros-consensus-shelley
, ouroboros-network
, ouroboros-network-framework
, prometheus
, servant
, servant-server
, shelley-spec-ledger
, streaming-commons
, text
, text
, transformers
, warp
, yaml
hs-source-dirs: src
exposed-modules: Cardano.TxSubmit
other-modules: Cardano.TxSubmit.CLI.Parsers
, Cardano.TxSubmit.CLI.Types
, Cardano.TxSubmit.Config
, Cardano.TxSubmit.ErrorRender
, Cardano.TxSubmit.JsonOrphans
, Cardano.TxSubmit.Metrics
, Cardano.TxSubmit.Rest.Parsers
, Cardano.TxSubmit.Rest.Types
, Cardano.TxSubmit.Rest.Web
, Cardano.TxSubmit.Tracing.ToObjectOrphans
, Cardano.TxSubmit.Tx
, Cardano.TxSubmit.Types
, Cardano.TxSubmit.Util
, Cardano.TxSubmit.Web
executable cardano-submit-api
import: base, project-config
, cardano-submit-api
, optparse-applicative
main-is: Main.hs
hs-source-dirs: app
ghc-options: -threaded -rtsopts "-with-rtsopts=-T -I0"
test-suite unit
import: base, project-config
, cardano-submit-api
type: exitcode-stdio-1.0
main-is: test.hs
hs-source-dirs: test
# Tx Submission Server Configuration
EnableLogMetrics: False
EnableLogging: True
# ------------------------------------------------------------------------------
# Logging configuration follows.
# global filter; messages must have at least this severity to pass:
minSeverity: Info
# global file rotation settings:
rotation:
rpLogLimitBytes: 5000000
rpKeepFilesNum: 10
rpMaxAgeHours: 24
# these backends are initialized:
setupBackends:
- AggregationBK
- KatipBK
# - EditorBK
# - EKGViewBK
# if not indicated otherwise, then messages are passed to these backends:
defaultBackends:
- KatipBK
# if wanted, the GUI is listening on this port:
# hasGUI: 12787
# if wanted, the EKG interface is listening on this port:
# hasEKG: 12788
# here we set up outputs of logging in 'katip':
setupScribes:
- scKind: StdoutSK
scName: stdout
scFormat: ScText
scRotation: null
# if not indicated otherwise, then log output is directed to this:
defaultScribes:
- - StdoutSK
- stdout
# more options which can be passed as key-value pairs:
options:
cfokey:
value: "Release-1.0.0"
mapSubtrace:
benchmark:
contents:
- GhcRtsStats
- MonotonicClock
subtrace: ObservableTrace
'#ekgview':
contents:
- - tag: Contains
contents: 'cardano.epoch-validation.benchmark'
- - tag: Contains
contents: .monoclock.basic.
- - tag: Contains
contents: 'cardano.epoch-validation.benchmark'
- - tag: Contains
contents: diff.RTS.cpuNs.timed.
- - tag: StartsWith
contents: '#ekgview.#aggregation.cardano.epoch-validation.benchmark'
- - tag: Contains
contents: diff.RTS.gcNum.timed.
subtrace: FilterTrace
'cardano.epoch-validation.utxo-stats':
# Change the `subtrace` value to `Neutral` in order to log
# `UTxO`-related messages during epoch validation.
subtrace: NoTrace
'#messagecounters.aggregation':
subtrace: NoTrace
'#messagecounters.ekgview':
subtrace: NoTrace
'#messagecounters.switchboard':
subtrace: NoTrace
'#messagecounters.katip':
subtrace: NoTrace
'#messagecounters.monitoring':
subtrace: NoTrace
'cardano.#messagecounters.aggregation':
subtrace: NoTrace
'cardano.#messagecounters.ekgview':
subtrace: NoTrace
'cardano.#messagecounters.switchboard':
subtrace: NoTrace
'cardano.#messagecounters.katip':
subtrace: NoTrace
'cardano.#messagecounters.monitoring':
subtrace: NoTrace
mapBackends:
cardano.epoch-validation.benchmark:
- AggregationBK
'#aggregation.cardano.epoch-validation.benchmark':
- EKGViewBK
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
module Cardano.TxSubmit
( module X
, runTxSubmitWebapi
) where
import Cardano.BM.Trace (Trace, logInfo)
import Cardano.Prelude
import Cardano.TxSubmit.CLI.Parsers as X
import Cardano.TxSubmit.CLI.Types as X
import Cardano.TxSubmit.Config as X
import Cardano.TxSubmit.Metrics (registerMetricsServer)
import Cardano.TxSubmit.Tx as X
import Cardano.TxSubmit.Types as X
import Cardano.TxSubmit.Util as X
import Cardano.TxSubmit.Web as X
import qualified Cardano.BM.Setup as Logging
import qualified Cardano.BM.Trace as Logging
import qualified Control.Concurrent.Async as Async
runTxSubmitWebapi :: TxSubmitNodeParams -> IO ()
runTxSubmitWebapi tsnp = do
tsnc <- readTxSubmitNodeConfig (unConfigFile $ tspConfigFile tsnp)
trce <- mkTracer tsnc
(metrics, metricsServer) <- registerMetricsServer
txSubmitServer <- Async.async $
runTxSubmitServer trce metrics tspWebserverConfig tspProtocol tspNetworkId tspSocketPath
void $ Async.waitAnyCancel
[ txSubmitServer
, metricsServer
]
logInfo trce "runTxSubmitWebapi: Async.waitAnyCancel returned"
where
TxSubmitNodeParams
{ tspProtocol
, tspNetworkId
, tspSocketPath
, tspWebserverConfig
} = tsnp
mkTracer :: TxSubmitNodeConfig -> IO (Trace IO Text)
mkTracer enc =
if not (tscEnableLogging enc)
then pure Logging.nullTracer
else liftIO $ Logging.setupTrace (Right $ tscLoggingConfig enc) "cardano-tx-submit"
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
module Cardano.TxSubmit.CLI.Parsers
( opts
, pTxSubmitNodeParams
, pConfigFile
, pNetworkId
, pProtocol
, pSocketPath
) where
import Cardano.Prelude
import Cardano.Api.Protocol
( Protocol (..) )
import Cardano.Api
( NetworkId (..), NetworkMagic (..) )
import Cardano.Chain.Slotting
( EpochSlots (..) )
import Cardano.TxSubmit.CLI.Types
( ConfigFile (..), SocketPath (..), TxSubmitNodeParams (..) )
import Cardano.TxSubmit.Rest.Parsers
( pWebserverConfig )
import Options.Applicative
( Parser, ParserInfo )
import qualified Options.Applicative as Opt
opts :: ParserInfo TxSubmitNodeParams
opts =
Opt.info (pTxSubmitNodeParams <**> Opt.helper)
( Opt.fullDesc
<> Opt.progDesc "Cardano transaction submission web API."
)
pTxSubmitNodeParams :: Parser TxSubmitNodeParams
pTxSubmitNodeParams =
TxSubmitNodeParams
<$> pConfigFile
<*> pProtocol
<*> pNetworkId
<*> pSocketPath
<*> pWebserverConfig 8090
pConfigFile :: Parser ConfigFile
pConfigFile =
ConfigFile <$> Opt.strOption
( Opt.long "config"
<> Opt.help "Path to the tx-submit web API configuration file"
<> Opt.completer (Opt.bashCompleter "file")
<> Opt.metavar "FILEPATH"
)
-- TODO: This was ripped from `cardano-cli` because, unfortunately, it's not
-- exported. Once we export this parser from the appropriate module and update
-- our `cardano-cli` dependency, we should remove this and import the parser
-- from there.
pNetworkId :: Parser NetworkId
pNetworkId =
pMainnet <|> fmap Testnet pTestnetMagic
where
pMainnet :: Parser NetworkId
pMainnet =
Opt.flag' Mainnet
( Opt.long "mainnet"
<> Opt.help "Use the mainnet magic id."
)
pTestnetMagic :: Parser NetworkMagic
pTestnetMagic =
NetworkMagic <$>
Opt.option Opt.auto
( Opt.long "testnet-magic"
<> Opt.metavar "NATURAL"
<> Opt.help "Specify a testnet magic id."
)
-- TODO: This was ripped from `cardano-cli` because, unfortunately, it's not
-- exported. Once we export this parser from the appropriate module and update
-- our `cardano-cli` dependency, we should remove this and import the parser
-- from there.
pProtocol :: Parser Protocol
pProtocol =
( Opt.flag' ()
( Opt.long "shelley-mode"
<> Opt.help "For talking to a node running in Shelley-only mode."
)
*> pShelley
)
<|>
( Opt.flag' ()
( Opt.long "byron-mode"
<> Opt.help "For talking to a node running in Byron-only mode."
)
*> pByron
)
<|>
( Opt.flag' ()
( Opt.long "cardano-mode"
<> Opt.help "For talking to a node running in full Cardano mode (default)."
)
*> pCardano
)
<|>
-- Default to the Cardano protocol.
pure (CardanoProtocol (EpochSlots defaultByronEpochSlots))
where
pByron :: Parser Protocol
pByron = ByronProtocol <$> pEpochSlots
pShelley :: Parser Protocol
pShelley = pure ShelleyProtocol
pCardano :: Parser Protocol
pCardano = CardanoProtocol <$> pEpochSlots
pEpochSlots :: Parser EpochSlots
pEpochSlots =
EpochSlots <$>
Opt.option Opt.auto
( Opt.long "epoch-slots"
<> Opt.metavar "NATURAL"
<> Opt.help "The number of slots per epoch for the Byron era."
<> Opt.value defaultByronEpochSlots -- Default to the mainnet value.
<> Opt.showDefault
)
defaultByronEpochSlots :: Word64
defaultByronEpochSlots = 21600
pSocketPath :: Parser SocketPath
pSocketPath =
SocketPath <$> Opt.strOption
( Opt.long "socket-path"
<> Opt.help "Path to a cardano-node socket"
<> Opt.completer (Opt.bashCompleter "file")
<> Opt.metavar "FILEPATH"
)
module Cardano.TxSubmit.CLI.Types
( ConfigFile (..)
, GenesisFile (..)
, SocketPath (..)
, TxSubmitNodeParams (..)
) where
import Cardano.Api.Protocol
( Protocol (..) )
import Cardano.Api
( NetworkId (..) )
import Cardano.TxSubmit.Rest.Types
( WebserverConfig )
-- | The product type of all command line arguments
data TxSubmitNodeParams = TxSubmitNodeParams
{ tspConfigFile :: !ConfigFile
, tspProtocol :: !Protocol
, tspNetworkId :: !NetworkId
, tspSocketPath :: !SocketPath
, tspWebserverConfig :: !WebserverConfig
}
newtype ConfigFile = ConfigFile
{ unConfigFile :: FilePath
}
newtype GenesisFile = GenesisFile
{ unGenesisFile :: FilePath
}
newtype SocketPath = SocketPath
{ unSocketPath :: FilePath
}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Cardano.TxSubmit.Config
( TxSubmitNodeConfig
, GenTxSubmitNodeConfig (..)
, readTxSubmitNodeConfig
) where
import Cardano.Prelude
import Cardano.TxSubmit.Util
import Data.Aeson (FromJSON (..), Object, Value (..), (.:))
import Data.Aeson.Types (Parser)
import qualified Cardano.BM.Configuration as Logging
import qualified Cardano.BM.Configuration.Model as Logging
import qualified Cardano.BM.Data.Configuration as Logging
import qualified Data.Aeson as Aeson
import qualified Data.ByteString.Char8 as B8
import qualified Data.Text as T
import qualified Data.Yaml as Yaml
type TxSubmitNodeConfig = GenTxSubmitNodeConfig Logging.Configuration
data GenTxSubmitNodeConfig a = GenTxSubmitNodeConfig
{ tscLoggingConfig :: !a
, tscEnableLogging :: !Bool
, tscEnableMetrics :: !Bool
}
readTxSubmitNodeConfig :: FilePath -> IO TxSubmitNodeConfig
readTxSubmitNodeConfig fp = do
res <- Yaml.decodeEither' <$> readLoggingConfig
case res of
Left err -> panic $ "readTxSubmitNodeConfig: Error parsing config: " <> textShow err
Right icr -> convertLogging icr
where
readLoggingConfig :: IO ByteString
readLoggingConfig =
catch (B8.readFile fp) $ \(_ :: IOException) ->
panic $ "Cannot find the logging configuration file at : " <> T.pack fp
convertLogging :: GenTxSubmitNodeConfig Logging.Representation -> IO TxSubmitNodeConfig
convertLogging tsc = do
lc <- Logging.setupFromRepresentation $ tscLoggingConfig tsc
pure $ tsc { tscLoggingConfig = lc }
-- -------------------------------------------------------------------------------------------------
instance FromJSON (GenTxSubmitNodeConfig Logging.Representation) where
parseJSON o =
Aeson.withObject "top-level" parseGenTxSubmitNodeConfig o
parseGenTxSubmitNodeConfig :: Object -> Parser (GenTxSubmitNodeConfig Logging.Representation)
parseGenTxSubmitNodeConfig o =
GenTxSubmitNodeConfig
<$> parseJSON (Object o)
<*> o .: "EnableLogging"
<*> o .: "EnableLogMetrics"
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
module Cardano.TxSubmit.ErrorRender
( renderApplyMempoolPayloadErr
, renderEraMismatch
) where
-- This file contains error renders. The should hve defined at a lower level, with the error
-- type definitions, but for some reason have not been.
-- They will be defined here for now and then moved where they are supposed to be once they
-- are working.
import Cardano.Chain.Byron.API
( ApplyMempoolPayloadErr (..) )
import Cardano.Chain.UTxO.UTxO
( UTxOError (..) )
import Cardano.Chain.UTxO.Validation
( TxValidationError (..), UTxOValidationError (..) )
import Data.Text
( Text )
import Formatting
( build, sformat, stext, (%) )
import Ouroboros.Consensus.Cardano.Block
( EraMismatch (..) )
import qualified Data.Text as T
renderApplyMempoolPayloadErr :: ApplyMempoolPayloadErr -> Text
renderApplyMempoolPayloadErr err =
case err of
MempoolTxErr ve -> renderValidationError ve
MempoolDlgErr {} -> "Delegation error"
MempoolUpdateProposalErr {} -> "Update proposal error"
MempoolUpdateVoteErr {} -> "Update vote error"
renderValidationError :: UTxOValidationError -> Text
renderValidationError ve =
case ve of
UTxOValidationTxValidationError tve -> renderTxValidationError tve
UTxOValidationUTxOError ue -> renderUTxOError ue
renderTxValidationError :: TxValidationError -> Text
renderTxValidationError tve =
"Tx Validation: " <>
case tve of
TxValidationLovelaceError txt e ->
sformat ("Lovelace error "% stext %": "% build) txt e
TxValidationFeeTooSmall tx expected actual ->
sformat ("Tx "% build %" fee "% build %"too low, expected "% build) tx actual expected
TxValidationWitnessWrongSignature wit pmid sig ->
sformat ("Bad witness "% build %" for signature "% stext %" protocol magic id "% stext) wit (textShow sig) (textShow pmid)
TxValidationWitnessWrongKey wit addr ->
sformat ("Bad witness "% build %" for address "% build) wit addr
TxValidationMissingInput tx ->
sformat ("Validation cannot find input tx "% build) tx
-- Fields are <expected> <actual>
TxValidationNetworkMagicMismatch expected actual ->
mconcat [ "Bad network magic ", textShow actual, ", expected ", textShow expected ]
TxValidationTxTooLarge expected actual ->
mconcat [ "Tx is ", textShow actual, " bytes, but expected < ", textShow expected, " bytes" ]
TxValidationUnknownAddressAttributes ->
"Unknown address attributes"
TxValidationUnknownAttributes ->
"Unknown attributes"
renderUTxOError :: UTxOError -> Text
renderUTxOError ue =
"UTxOError: " <>
case ue of
UTxOMissingInput tx -> sformat ("Lookup of tx "% build %" failed") tx
UTxOOverlappingUnion -> "Union or two overlapping UTxO sets"
renderEraMismatch :: EraMismatch -> Text
renderEraMismatch EraMismatch{ledgerEraName, otherEraName} =
"The era of the node and the tx do not match. " <>
"The node is running in the " <> ledgerEraName <>
" era, but the transaction is for the " <> otherEraName <> " era."
textShow :: Show a => a -> Text
textShow = T.pack . show
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# OPTIONS_GHC -Wno-orphans #-}
module Cardano.TxSubmit.JsonOrphans () where
import Cardano.Api.TxBody ( TxId (..) )
import Cardano.Crypto.Hash.Class as Cryptos
import Data.Aeson
import qualified Shelley.Spec.Ledger.TxBody as Shelley
deriving newtype instance ToJSON TxId
deriving newtype instance ToJSON (Shelley.TxId c)
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
module Cardano.TxSubmit.Metrics
( TxSubmitMetrics(..)
, makeMetrics
, registerMetricsServer
) where
import Cardano.Prelude
import System.Metrics.Prometheus.Concurrent.RegistryT (RegistryT (..), registerGauge,
runRegistryT, unRegistryT)
import System.Metrics.Prometheus.Metric.Gauge (Gauge)
import System.Metrics.Prometheus.Http.Scrape (serveMetricsT)
newtype TxSubmitMetrics = TxSubmitMetrics
{ tsmCount :: Gauge
}
registerMetricsServer :: IO (TxSubmitMetrics, Async ())
registerMetricsServer =
runRegistryT $ do
metrics <- makeMetrics
registry <- RegistryT ask
server <- liftIO . async $ runReaderT (unRegistryT $ serveMetricsT 8081 []) registry
pure (metrics, server)
makeMetrics :: RegistryT IO TxSubmitMetrics
makeMetrics =
TxSubmitMetrics
<$> registerGauge "tx_submit_count" mempty
{-# LANGUAGE ApplicativeDo #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
module Cardano.TxSubmit.Rest.Parsers
( pWebserverConfig
) where
import Cardano.TxSubmit.Rest.Types
( WebserverConfig (..) )
import Data.String
( fromString )
import Network.Wai.Handler.Warp
( HostPreference, Port )
import Options.Applicative
( Parser
, auto
, help
, long
, metavar
, option
, showDefault
, strOption
, switch
, value
)
pWebserverConfig :: Port -> Parser WebserverConfig
pWebserverConfig defaultPort = do
wcHost <- pHostPreferenceOption
isRandom <- pRandomPortOption
wcPort <- pPortOption defaultPort
pure
WebserverConfig
{ wcHost
, wcPort =
if isRandom
then 0
else wcPort
}
pHostPreferenceOption :: Parser HostPreference
pHostPreferenceOption =
fromString <$>
strOption
(long "listen-address" <>
metavar "HOST" <>
help
("Specification of which host to the bind API server to. " <>
"Can be an IPv[46] address, hostname, or '*'.") <>
value "127.0.0.1" <> showDefault)
pPortOption :: Port -> Parser Port
pPortOption defaultPort =
option auto $
long "port" <>
metavar "INT" <>
help "Port used for the API server." <> value defaultPort <> showDefault
pRandomPortOption :: Parser Bool
pRandomPortOption =
switch $
long "random-port" <>
help "Serve API on any available port (overrides --port)"
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE StrictData #-}
module Cardano.TxSubmit.Rest.Types
( WebserverConfig(..)
, toWarpSettings
) where
import Data.Function ( (&) )
import qualified Network.Wai.Handler.Warp as Warp
------------------------------------------------------------
data WebserverConfig =
WebserverConfig
{ wcHost :: Warp.HostPreference
, wcPort :: Warp.Port
}
instance Show WebserverConfig where
show WebserverConfig {wcHost, wcPort} = show wcHost <> ":" <> show wcPort
toWarpSettings :: WebserverConfig -> Warp.Settings
toWarpSettings WebserverConfig {wcHost, wcPort} =
Warp.defaultSettings & Warp.setHost wcHost & Warp.setPort wcPort
{-# LANGUAGE OverloadedStrings #-}
module Cardano.TxSubmit.Rest.Web
( runSettings
) where
import Control.Exception
( bracket )
import Control.Monad.IO.Class
( liftIO )
import Control.Monad.Logger
( logInfoN, runStdoutLoggingT )
import Data.Streaming.Network
( bindPortTCP )
import Data.Text
( pack )
import Network.Socket
( close, getSocketName, withSocketsDo )
import Network.Wai.Handler.Warp
( Settings, getHost, getPort, runSettingsSocket )
import Servant
( Application )
-- | Like 'Network.Wai.Handler.Warp.runSettings', except with better logging.
runSettings :: Settings -> Application -> IO ()
runSettings settings app =
withSocketsDo $ do
bracket
(bindPortTCP (getPort settings) (getHost settings))
close
(\socket ->
runStdoutLoggingT $ do
addr <- liftIO $ getSocketName socket
logInfoN $ "Running server on " <> pack (show addr)
liftIO $ runSettingsSocket settings socket app)
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Cardano.TxSubmit.Tracing.ToObjectOrphans () where
import Cardano.BM.Data.Severity
import Cardano.BM.Data.Tracer
import Data.Aeson ( (.=) )
import Data.Text
import Ouroboros.Network.NodeToClient ( ErrorPolicyTrace (..), WithAddr (..) )
import qualified Network.Socket as Socket
instance HasPrivacyAnnotation (WithAddr Socket.SockAddr ErrorPolicyTrace)
instance HasSeverityAnnotation (WithAddr Socket.SockAddr ErrorPolicyTrace) where
getSeverityAnnotation (WithAddr _ ev) = case ev of
ErrorPolicySuspendPeer {} -> Warning -- peer misbehaved
ErrorPolicySuspendConsumer {} -> Notice -- peer temporarily not useful
ErrorPolicyLocalNodeError {} -> Error
ErrorPolicyResumePeer {} -> Debug
ErrorPolicyKeepSuspended {} -> Debug
ErrorPolicyResumeConsumer {} -> Debug
ErrorPolicyResumeProducer {} -> Debug
ErrorPolicyUnhandledApplicationException {} -> Error
ErrorPolicyUnhandledConnectionException {} -> Error
ErrorPolicyAcceptException {} -> Error
instance HasTextFormatter (WithAddr Socket.SockAddr ErrorPolicyTrace) where
-- transform @[email protected]
instance Transformable Text IO (WithAddr Socket.SockAddr ErrorPolicyTrace) where
trTransformer verb tr = trStructured verb tr
instance ToObject (WithAddr Socket.SockAddr ErrorPolicyTrace) where
toObject _verb (WithAddr addr ev) =
mkObject [ "kind" .= ("ErrorPolicyTrace" :: String)
, "address" .= show addr
, "event" .= show ev ]
{-# LANGUAGE GADTs #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
module Cardano.TxSubmit.Tx
( TxSubmitError (..)
, renderTxSubmitError
, submitTx
) where
import Cardano.Prelude
import Cardano.Api.TxSubmit hiding
( submitTx )
import Cardano.Api
( InAnyCardanoEra(InAnyCardanoEra),
CardanoEra(ShelleyEra, ByronEra),
ShelleyEra,
ByronEra,
TxId,
getTxId,
Tx,
getTxBody,
LocalNodeConnectInfo(localNodeConsensusMode) )
import Cardano.Api.Typed
( NodeConsensusMode (..)
)
import Cardano.TxSubmit.ErrorRender
( renderApplyMempoolPayloadErr, renderEraMismatch )
import Ouroboros.Consensus.Byron.Ledger
( ByronBlock )
import Ouroboros.Consensus.Cardano.Block
( EraMismatch (..), HardForkApplyTxErr (..) )
import Ouroboros.Consensus.Ledger.SupportsMempool
( ApplyTxErr )
import Ouroboros.Consensus.Shelley.Ledger
( ShelleyBlock )
import Ouroboros.Consensus.Shelley.Protocol.Crypto
( StandardCrypto )
import qualified Cardano.Api.TxSubmit as Api
import qualified Cardano.Ledger.Allegra as LedgerA
import qualified Cardano.Ledger.Mary as LedgerM
import qualified Cardano.Ledger.Shelley as LedgerS
-- | An error that can occur while submitting a transaction to a local node.
data TxSubmitError
= TxSubmitByronError !(ApplyTxErr ByronBlock)
| TxSubmitShelleyError !(ApplyTxErr (ShelleyBlock (LedgerS.ShelleyEra StandardCrypto)))
| TxSubmitAllegraError !(ApplyTxErr (ShelleyBlock (LedgerA.AllegraEra StandardCrypto)))
| TxSubmitMaryError !(ApplyTxErr (ShelleyBlock (LedgerM.MaryEra StandardCrypto)))
| TxSubmitEraMismatchError !EraMismatch
deriving (Eq, Show)
renderTxSubmitError :: TxSubmitError -> Text
renderTxSubmitError tse =
case tse of
TxSubmitByronError err -> renderApplyMempoolPayloadErr err
TxSubmitShelleyError err -> show err -- TODO: Better rendering for Shelley errors
TxSubmitAllegraError err -> show err -- TODO: Better rendering for Allegra errors
TxSubmitMaryError err -> show err -- TODO: Better rendering for Mary errors
TxSubmitEraMismatchError err -> renderEraMismatch err
-- | Submit a transaction to a local node.
submitTx
:: forall mode block.
LocalNodeConnectInfo mode block
-> Either (Tx ByronEra) (Tx ShelleyEra)
-> IO (Either TxSubmitError TxId)
submitTx connectInfo byronOrShelleyTx =
case (localNodeConsensusMode connectInfo, byronOrShelleyTx) of
(ByronMode{}, Left tx) -> do
result <- liftIO $ Api.submitTx connectInfo (TxForByronMode tx)
pure $ case result of
TxSubmitSuccess -> Right (getTxIdForTx tx)
TxSubmitFailureByronMode err -> Left (TxSubmitByronError err)
(ByronMode{}, Right{}) ->
pure $ Left $ TxSubmitEraMismatchError EraMismatch {
ledgerEraName = "Byron",
otherEraName = "Shelley"
}
(ShelleyMode{}, Right tx) -> do
result <- liftIO $ Api.submitTx connectInfo (TxForShelleyMode tx)
case result of
TxSubmitSuccess -> pure $ Right (getTxIdForTx tx)
TxSubmitFailureShelleyMode err ->
pure $ Left (TxSubmitShelleyError err)
(ShelleyMode{}, Left{}) ->
pure $ Left $ TxSubmitEraMismatchError EraMismatch {
ledgerEraName = "Shelley",
otherEraName = "Byron"
}
(CardanoMode{}, tx) -> do
let txInEra = either (InAnyCardanoEra ByronEra) (InAnyCardanoEra ShelleyEra) tx
result <- Api.submitTx connectInfo (TxForCardanoMode txInEra)
pure $ case result of
TxSubmitSuccess -> Right (either getTxIdForTx getTxIdForTx tx)
TxSubmitFailureCardanoMode (ApplyTxErrByron err) ->
Left (TxSubmitByronError err)
TxSubmitFailureCardanoMode (ApplyTxErrShelley err) ->
Left (TxSubmitShelleyError err)
TxSubmitFailureCardanoMode (ApplyTxErrAllegra err) ->
Left (TxSubmitAllegraError err)
TxSubmitFailureCardanoMode (ApplyTxErrMary err) ->
Left (TxSubmitMaryError err)
TxSubmitFailureCardanoMode (ApplyTxErrWrongEra mismatch) ->
Left (TxSubmitEraMismatchError mismatch)
-- TODO: This function should really be implemented in `Cardano.Api.Typed`.
-- The function, 'Cardano.Api.Typed.getTxId', accepts a 'TxBody' parameter.
getTxIdForTx :: Tx era -> TxId
getTxIdForTx = getTxId . getTxBody
Transaction rewrite
We will only use the tracing infrastructure in the run function, and for messages that can't cause the node to exit
Add tests for native tokens with empty asset names
Do not try to run `pkgs.nixos` when on darwin
We only want to log errors in the env initialization