Skip to content

Commit

Permalink
Sign transactions with SK in Util app (wavesplatform#3941)
Browse files Browse the repository at this point in the history
  • Loading branch information
phearnot authored Mar 11, 2024
1 parent e318113 commit 979d2ea
Show file tree
Hide file tree
Showing 12 changed files with 61 additions and 21 deletions.
2 changes: 1 addition & 1 deletion node/src/main/scala/com/wavesplatform/Application.scala
Original file line number Diff line number Diff line change
Expand Up @@ -637,7 +637,7 @@ object Application extends ScorexLogging {
case "explore" => Explorer.main(args.tail)
case "util" => UtilApp.main(args.tail)
case "help" | "--help" | "-h" => println("Usage: waves <config> | export | import | explore | util")
case _ => startNode(args.headOption) // TODO: Consider adding option to specify network-name
case _ => startNode(args.headOption)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ case class BurnRequest(
timestamp: Option[Long],
signature: Option[ByteStr],
proofs: Option[Proofs]
) extends TxBroadcastRequest {
) extends TxBroadcastRequest[BurnTransaction] {
def toTxFrom(sender: PublicKey): Either[ValidationError, BurnTransaction] =
for {
validProofs <- toProofs(signature, proofs)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ case class CreateAliasRequest(
timestamp: Option[TxTimestamp] = None,
signature: Option[ByteStr] = None,
proofs: Option[Proofs] = None
) extends TxBroadcastRequest {
) extends TxBroadcastRequest[CreateAliasTransaction] {
def toTxFrom(sender: PublicKey): Either[ValidationError, CreateAliasTransaction] =
for {
validProofs <- toProofs(signature, proofs)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ case class ExchangeRequest(
timestamp: Option[TxTimestamp] = None,
signature: Option[ByteStr] = None,
proofs: Option[Proofs] = None
) extends TxBroadcastRequest {
) extends TxBroadcastRequest[ExchangeTransaction] {
def toTxFrom(sender: PublicKey): Either[ValidationError, ExchangeTransaction] =
for {
validProofs <- toProofs(signature, proofs)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ case class IssueRequest(
timestamp: Option[Long],
signature: Option[ByteStr],
proofs: Option[Proofs]
) extends TxBroadcastRequest {
) extends TxBroadcastRequest[IssueTransaction] {
def toTxFrom(sender: PublicKey): Either[ValidationError, IssueTransaction] = {
val actualVersion = version.getOrElse(TxVersion.V3)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ case class LeaseCancelRequest(
timestamp: Option[Long],
signature: Option[ByteStr],
proofs: Option[Proofs]
) extends TxBroadcastRequest {
) extends TxBroadcastRequest[LeaseCancelTransaction] {
def toTxFrom(sender: PublicKey): Either[ValidationError, LeaseCancelTransaction] =
for {
validProofs <- toProofs(signature, proofs)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ case class LeaseRequest(
timestamp: Option[Long],
signature: Option[ByteStr],
proofs: Option[Proofs]
) extends TxBroadcastRequest {
) extends TxBroadcastRequest[LeaseTransaction] {
def toTxFrom(sender: PublicKey): Either[ValidationError, LeaseTransaction] =
for {
validRecipient <- AddressOrAlias.fromString(recipient)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ case class ReissueRequest(
timestamp: Option[Long],
signature: Option[ByteStr],
proofs: Option[Proofs]
) extends TxBroadcastRequest {
) extends TxBroadcastRequest[ReissueTransaction] {
def toTxFrom(sender: PublicKey): Either[ValidationError, ReissueTransaction] =
for {
validProofs <- toProofs(signature, proofs)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ case class TransferRequest(
timestamp: Option[Long] = None,
signature: Option[ByteStr] = None,
proofs: Option[Proofs] = None
) extends TxBroadcastRequest {
) extends TxBroadcastRequest[TransferTransaction] {
def toTxFrom(sender: PublicKey): Either[ValidationError, TransferTransaction] =
for {
validRecipient <- AddressOrAlias.fromString(recipient)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import com.wavesplatform.lang.ValidationError
import com.wavesplatform.transaction.Transaction
import com.wavesplatform.transaction.TxValidationError.GenericError

trait TxBroadcastRequest {
trait TxBroadcastRequest[A <: Transaction] {
def sender: Option[String]
def senderPublicKey: Option[String]

def toTxFrom(sender: PublicKey): Either[ValidationError, Transaction]
def toTxFrom(sender: PublicKey): Either[ValidationError, A]

def toTx: Either[ValidationError, Transaction] =
def toTx: Either[ValidationError, A] =
for {
sender <- senderPublicKey match {
case Some(key) => PublicKey.fromBase58String(key)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ case class UpdateAssetInfoRequest(
fee: Long,
feeAssetId: Option[String],
proofs: Option[Proofs]
) extends TxBroadcastRequest {
) extends TxBroadcastRequest[UpdateAssetInfoTransaction] {
override def toTxFrom(sender: PublicKey): Either[ValidationError, UpdateAssetInfoTransaction] =
for {
_assetId <- parseBase58(assetId, "invalid.assetId", AssetIdStringLength)
Expand Down
56 changes: 48 additions & 8 deletions node/src/main/scala/com/wavesplatform/utils/UtilApp.scala
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
package com.wavesplatform.utils

import java.io.{ByteArrayInputStream, File, FileInputStream, FileOutputStream}
import java.nio.charset.StandardCharsets
import java.nio.file.{Files, Paths}

import com.google.common.io.ByteStreams
import com.wavesplatform.account.{KeyPair, PrivateKey, PublicKey}
import com.wavesplatform.api.http.requests.*
import com.wavesplatform.common.state.ByteStr
import com.wavesplatform.common.utils.{Base58, Base64, FastBase58}
import com.wavesplatform.features.EstimatorProvider.*
import com.wavesplatform.lang.script.{Script, ScriptReader}
import com.wavesplatform.settings.WavesSettings
import com.wavesplatform.transaction.TransactionFactory
import com.wavesplatform.transaction.TxValidationError.GenericError
import com.wavesplatform.transaction.smart.script.ScriptCompiler
import com.wavesplatform.transaction.{Transaction, TransactionFactory, TransactionType}
import com.wavesplatform.wallet.Wallet
import com.wavesplatform.{Application, Version}
import play.api.libs.json.{JsObject, Json}
import scopt.OParser

import java.io.{ByteArrayInputStream, File, FileInputStream, FileOutputStream}
import java.nio.charset.StandardCharsets
import java.nio.file.{Files, Paths}

//noinspection ScalaStyle
// TODO: Consider remove implemented methods from REST API
object UtilApp {
Expand All @@ -31,6 +33,7 @@ object UtilApp {
case object Hash extends Mode
case object SerializeTx extends Mode
case object SignTx extends Mode
case object SignTxWithSk extends Mode
}

case class CompileOptions(assetScript: Boolean = false)
Expand Down Expand Up @@ -63,18 +66,19 @@ object UtilApp {
def main(args: Array[String]): Unit = {
OParser.parse(commandParser, args, Command()) match {
case Some(cmd) =>
lazy val nodeState = new NodeState(cmd)
val settings = Application.loadApplicationConfig(cmd.configFile.map(new File(_)))
val inBytes = IO.readInput(cmd)
val result = {
val doAction = cmd.mode match {
case Command.CompileScript => Actions.doCompile(nodeState.settings) _
case Command.CompileScript => Actions.doCompile(settings) _
case Command.DecompileScript => Actions.doDecompile _
case Command.SignBytes => Actions.doSign _
case Command.VerifySignature => Actions.doVerify _
case Command.CreateKeyPair => Actions.doCreateKeyPair _
case Command.Hash => Actions.doHash _
case Command.SerializeTx => Actions.doSerializeTx _
case Command.SignTx => Actions.doSignTx(nodeState) _
case Command.SignTx => Actions.doSignTx(new NodeState(cmd)) _
case Command.SignTxWithSk => Actions.doSignTxWithSK _
}
doAction(cmd, inBytes)
}
Expand Down Expand Up @@ -191,6 +195,15 @@ object UtilApp {
.abbr("sa")
.text("Signer address (requires corresponding key in wallet.dat)")
.action((a, c) => c.copy(signTxOptions = c.signTxOptions.copy(signerAddress = a)))
),
cmd("sign-with-sk")
.text("Sign JSON transaction with private key")
.action((_, c) => c.copy(mode = Command.SignTxWithSk))
.children(
opt[String]("private-key")
.abbr("sk")
.text("Private key")
.action((a, c) => c.copy(signOptions = c.signOptions.copy(privateKey = PrivateKey(Base58.decode(a)))))
)
),
help("help").hidden(),
Expand Down Expand Up @@ -265,6 +278,33 @@ object UtilApp {
.left
.map(_.toString)
.map(tx => Json.toBytes(tx.json()))

def doSignTxWithSK(c: Command, data: Array[Byte]): ActionResult = {
import cats.syntax.either.*
import com.wavesplatform.api.http.requests.InvokeScriptRequest.signedInvokeScriptRequestReads
import com.wavesplatform.api.http.requests.SponsorFeeRequest.signedSponsorRequestFormat
import com.wavesplatform.transaction.TransactionType.*

val json = Json.parse(data)
(TransactionType((json \ "type").as[Int]) match {
case Issue => json.as[IssueRequest].toTx.map(_.signWith(c.signOptions.privateKey))
case Transfer => json.as[TransferRequest].toTx.map(_.signWith(c.signOptions.privateKey))
case Reissue => json.as[ReissueRequest].toTx.map(_.signWith(c.signOptions.privateKey))
case Burn => json.as[BurnRequest].toTx.map(_.signWith(c.signOptions.privateKey))
case Exchange => json.as[ExchangeRequest].toTx.map(_.signWith(c.signOptions.privateKey))
case Lease => json.as[LeaseRequest].toTx.map(_.signWith(c.signOptions.privateKey))
case LeaseCancel => json.as[LeaseCancelRequest].toTx.map(_.signWith(c.signOptions.privateKey))
case CreateAlias => json.as[CreateAliasRequest].toTx.map(_.signWith(c.signOptions.privateKey))
case MassTransfer => json.as[SignedMassTransferRequest].toTx.map(_.signWith(c.signOptions.privateKey))
case Data => json.as[SignedDataRequest].toTx.map(_.signWith(c.signOptions.privateKey))
case SetScript => json.as[SignedSetScriptRequest].toTx.map(_.signWith(c.signOptions.privateKey))
case SponsorFee => json.as[SignedSponsorFeeRequest].toTx.map(_.signWith(c.signOptions.privateKey))
case SetAssetScript => json.as[SignedSetAssetScriptRequest].toTx.map(_.signWith(c.signOptions.privateKey))
case InvokeScript => json.as[SignedInvokeScriptRequest].toTx.map(_.signWith(c.signOptions.privateKey))
case UpdateAssetInfo => json.as[SignedUpdateAssetInfoRequest].toTx.map(_.signWith(c.signOptions.privateKey))
case other => GenericError(s"Signing $other is not supported").asLeft[Transaction]
}).leftMap(_.toString).map(_.json().toString().getBytes())
}
}

private[this] object IO {
Expand Down

0 comments on commit 979d2ea

Please sign in to comment.