Skip to content

Commit

Permalink
NODE-2337: Ethereum transaction support (#3519)
Browse files Browse the repository at this point in the history
  • Loading branch information
Sergey Nazarov authored Oct 19, 2021
1 parent a0e4f7d commit e6e3c03
Show file tree
Hide file tree
Showing 408 changed files with 7,628 additions and 3,787 deletions.
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
*.sh text eol=lf
*.scala text eol=lf
* text=auto
node/src/main/resources/swagger-ui/* linguist-vendored
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.wavesplatform.lang.v1
import java.util.concurrent.TimeUnit

import com.wavesplatform.common.state.ByteStr
import com.wavesplatform.lang.directives.DirectiveSet
import com.wavesplatform.lang.directives.values.{Account, Expression, V6}
import com.wavesplatform.lang.utils.lazyContexts
import com.wavesplatform.lang.v1.EnvironmentFunctionsBenchmark.curve25519
import com.wavesplatform.lang.v1.compiler.Terms.EXPR
import com.wavesplatform.lang.v1.compiler.TestCompiler
import com.wavesplatform.lang.v1.evaluator.EvaluatorV2
import com.wavesplatform.utils.EthHelpers
import org.openjdk.jmh.annotations._
import org.openjdk.jmh.infra.Blackhole

@OutputTimeUnit(TimeUnit.MICROSECONDS)
@BenchmarkMode(Array(Mode.AverageTime))
@Threads(1)
@Fork(1)
@Warmup(iterations = 10, time = 1)
@Measurement(iterations = 10, time = 1)
class AddressFromPublicKeyBenchmark {
@Benchmark
def addressFromPublicKeyWaves(s: PkSt, bh: Blackhole): Unit = bh.consume(EvaluatorV2.applyCompleted(s.ctx, s.exprWaves, V6))

@Benchmark
def addressFromPublicKeyEth(s: PkSt, bh: Blackhole): Unit = bh.consume(EvaluatorV2.applyCompleted(s.ctx, s.exprEth, V6))
}

@State(Scope.Benchmark)
class PkSt extends EthHelpers {
val ds = DirectiveSet(V6, Account, Expression).fold(null, identity)
val ctx = lazyContexts(ds).value().evaluationContext(EnvironmentFunctionsBenchmark.environment)

val wavesPk = ByteStr(curve25519.generateKeypair._2)
val exprWaves = TestCompiler(V6).compileExpression(s"addressFromPublicKey(base58'$wavesPk')").expr.asInstanceOf[EXPR]
val exprEth = TestCompiler(V6).compileExpression(s"addressFromPublicKey(base58'$TestEthPublicKey')").expr.asInstanceOf[EXPR]
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import java.util.concurrent.{ThreadLocalRandom, TimeUnit}
import cats.Id
import cats.syntax.bifunctor._
import com.wavesplatform.account
import com.wavesplatform.account.PublicKey
import com.wavesplatform.common.state.ByteStr
import com.wavesplatform.common.utils.EitherExt2
import com.wavesplatform.crypto.Curve25519
Expand Down Expand Up @@ -85,9 +86,6 @@ class EnvironmentFunctionsBenchmark {
Curve25519.verify(signature, message, publicKey)
}

@Benchmark
def addressFromPublicKey_test(): ByteStr = randomAddress

@Benchmark
def addressFromString(st: AddressFromString, bh: Blackhole): Unit = {
val i = Random.nextInt(100)
Expand All @@ -102,7 +100,7 @@ object EnvironmentFunctionsBenchmark {
val DataBytesLength = 512
val SeedBytesLength = 128

val defaultEnvironment: Environment[Id] = new Environment[Id] {
val environment: Environment[Id] = new Environment[Id] {
override def height: Long = 1
override def chainId: Byte = ChainId
override def inputEntity: Environment.InputEntity = ???
Expand All @@ -128,6 +126,10 @@ object EnvironmentFunctionsBenchmark {
_.toString,
address => Address(ByteStr(address.bytes))
)

override def addressFromPublicKey(publicKey: ByteStr): Either[String, Address] =
Right(Address(ByteStr(PublicKey(publicKey).toAddress.bytes)))

override def accountScript(addressOrAlias: Recipient): Option[Script] = ???
override def callScript(
dApp: Address,
Expand All @@ -139,7 +141,7 @@ object EnvironmentFunctionsBenchmark {
): Coeval[(Either[ValidationError, EVALUATED], Int)] = ???
}

val environmentFunctions = new EnvironmentFunctions(defaultEnvironment)
val environmentFunctions = new EnvironmentFunctions(environment)

val string32Kb: String = "FEDCBA9876543210" * (32 * 1024 / 16)

Expand Down Expand Up @@ -167,7 +169,7 @@ class AddressFromString {
val ctx: EvaluationContext[Environment, Id] =
WavesContext
.build(Global, DirectiveSet(V4, Account, DApp).explicitGet())
.evaluationContext(defaultEnvironment)
.evaluationContext(environment)

val expr: Array[EXPR] =
(1 to 100).map { _ =>
Expand Down
7 changes: 3 additions & 4 deletions build-with-docker.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
USER=$(id -u)
GROUP=$(id -g)

docker run --mount type=bind,source="$DIR",target=/src -i mozilla/sbt:8u232_1.3.8 /bin/sh -c "
docker run --mount type=bind,source="$DIR",target=/src -it --rm wavesplatform/sbt:8u302-b08_1.5.5_2.13.6 /bin/sh -c "
cd /src &&
COURSIER_CACHE=\"$DIR/target/docker/coursier\" sbt \"-Dsbt.boot.directory=$DIR/target/docker/sbt_cache\" \"set ThisBuild/network := $NETWORK\" packageAll &&
chown -R $USER:$GROUP .
"
COURSIER_CACHE=/src/target/docker/coursier sbt -Dsbt.boot.directory=/src/target/docker/sbt_cache -Dsbt.server.forcestart=true --batch \"set ThisBuild/network := $NETWORK\" packageAll &&
chown -R $USER:$GROUP ."
14 changes: 9 additions & 5 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ lazy val `lang-doc` = project
.dependsOn(`lang-jvm`)
.settings(
Compile / sourceGenerators += Tasks.docSource,
libraryDependencies ++= Seq("com.github.spullara.mustache.java" % "compiler" % "0.9.5") ++ Dependencies.test
libraryDependencies ++= Seq("com.github.spullara.mustache.java" % "compiler" % "0.9.10") ++ Dependencies.test
)

lazy val node = project.dependsOn(`lang-jvm`, `lang-testkit` % "test")
Expand Down Expand Up @@ -111,7 +111,9 @@ lazy val root = (project in file("."))
node,
`node-it`,
`node-generator`,
benchmark
benchmark,
`repl-js`,
`repl-jvm`
)

inScope(Global)(
Expand All @@ -131,8 +133,6 @@ inScope(Global)(
"-language:postfixOps",
"-Ywarn-unused:-implicits",
"-Xlint",
"-opt:l:inline",
"-opt-inline-from:**",
"-Wconf:cat=deprecation&site=com.wavesplatform.api.grpc.*:s", // Ignore gRPC warnings
"-Wconf:cat=deprecation&site=com.wavesplatform.protobuf.transaction.InvokeScriptResult.*:s", // Ignore deprecated argsBytes
"-Wconf:cat=deprecation&site=com.wavesplatform.state.InvokeScriptResult.*:s"
Expand All @@ -153,7 +153,10 @@ inScope(Global)(
testOptions += Tests.Argument("-oIDOF", "-u", "target/test-reports"),
testOptions += Tests.Setup(_ => sys.props("sbt-testing") = "true"),
network := Network(sys.props.get("network")),
resolvers += Resolver.sonatypeRepo("snapshots"),
resolvers ++= Seq(
Resolver.sonatypeRepo("snapshots"),
Resolver.mavenLocal
),
Compile / doc / sources := Seq.empty,
Compile / packageDoc / publishArtifact := false
)
Expand Down Expand Up @@ -184,6 +187,7 @@ checkPRRaw := Def
(`grpc-server` / Test / test).value
(node / Test / test).value
(`repl-js` / Compile / fastOptJS).value
(`node-it` / Test / compile).value
}
)
.value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ object GRPCErrors {

def toStatusException(api: ApiError): StatusException = {
val code = api match {
case TransactionDoesNotExist | AliasDoesNotExist(_) | BlockDoesNotExist | MissingSenderPrivateKey | DataKeyDoesNotExist =>
case TransactionDoesNotExist | BlockDoesNotExist | MissingSenderPrivateKey | DataKeyDoesNotExist =>
Status.NOT_FOUND
case _ => Status.INVALID_ARGUMENT
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package com.wavesplatform.api.grpc

import java.net.InetSocketAddress

import scala.concurrent.Future

import com.wavesplatform.extensions.{Extension, Context => ExtensionContext}
import com.wavesplatform.settings.GRPCSettings
import com.wavesplatform.utils.ScorexLogging
Expand All @@ -11,15 +13,13 @@ import monix.execution.Scheduler
import net.ceedubs.ficus.Ficus._
import net.ceedubs.ficus.readers.ArbitraryTypeReader._

import scala.concurrent.Future

class GRPCServerExtension(context: ExtensionContext) extends Extension with ScorexLogging {
private implicit val apiScheduler: Scheduler = Scheduler(context.actorSystem.dispatcher)
private val settings = context.settings.config.as[GRPCSettings]("waves.grpc")
private val bindAddress = new InetSocketAddress(settings.host, settings.port)
private val server: Server = NettyServerBuilder
.forAddress(bindAddress)
.addService(TransactionsApiGrpc.bindService(new TransactionsApiGrpcImpl(context.transactionsApi), apiScheduler))
.addService(TransactionsApiGrpc.bindService(new TransactionsApiGrpcImpl(context.blockchain, context.transactionsApi), apiScheduler))
.addService(BlocksApiGrpc.bindService(new BlocksApiGrpcImpl(context.blocksApi), apiScheduler))
.addService(AccountsApiGrpc.bindService(new AccountsApiGrpcImpl(context.accountsApi), apiScheduler))
.addService(AssetsApiGrpc.bindService(new AssetsApiGrpcImpl(context.assetsApi, context.accountsApi), apiScheduler))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
package com.wavesplatform.api.grpc

import scala.concurrent.Future

import com.wavesplatform.account.AddressScheme
import com.wavesplatform.api.common.CommonTransactionsApi
import com.wavesplatform.api.common.CommonTransactionsApi.TransactionMeta
import com.wavesplatform.api.common.{CommonTransactionsApi, TransactionMeta}
import com.wavesplatform.protobuf._
import com.wavesplatform.protobuf.transaction._
import com.wavesplatform.protobuf.utils.PBImplicitConversions.PBRecipientImplicitConversionOps
import com.wavesplatform.state.{InvokeScriptResult => VISR}
import com.wavesplatform.transaction.Authorized
import io.grpc.stub.StreamObserver
import com.wavesplatform.state.{Blockchain, InvokeScriptResult => VISR}
import com.wavesplatform.transaction.{Authorized, EthereumTransaction}
import com.wavesplatform.transaction.TxValidationError.GenericError
import io.grpc.{Status, StatusRuntimeException}
import io.grpc.stub.StreamObserver
import monix.execution.Scheduler
import monix.reactive.Observable

import scala.concurrent.Future

class TransactionsApiGrpcImpl(commonApi: CommonTransactionsApi)(implicit sc: Scheduler) extends TransactionsApiGrpc.TransactionsApi {
class TransactionsApiGrpcImpl(blockchain: Blockchain, commonApi: CommonTransactionsApi)(implicit sc: Scheduler)
extends TransactionsApiGrpc.TransactionsApi {

override def getTransactions(request: TransactionsRequest, responseObserver: StreamObserver[TransactionResponse]): Unit =
responseObserver.interceptErrors {
Expand All @@ -25,6 +26,7 @@ class TransactionsApiGrpcImpl(commonApi: CommonTransactionsApi)(implicit sc: Sch
case Some(subject) =>
val recipientAddrOrAlias = subject
.toAddressOrAlias(AddressScheme.current.chainId)
.flatMap(blockchain.resolveAlias(_))
.fold(e => throw new IllegalArgumentException(e.toString), identity)

val maybeSender = Option(request.sender)
Expand Down Expand Up @@ -83,7 +85,7 @@ class TransactionsApiGrpcImpl(commonApi: CommonTransactionsApi)(implicit sc: Sch
val result = Observable(request.transactionIds: _*)
.flatMap(txId => Observable.fromIterable(commonApi.transactionById(txId.toByteStr)))
.collect {
case CommonTransactionsApi.TransactionMeta.Invoke(_, transaction, _, invokeScriptResult) =>
case TransactionMeta.Invoke(_, transaction, _, invokeScriptResult) =>
InvokeScriptResultResponse.of(Some(PBTransactions.protobuf(transaction)), invokeScriptResult.map(VISR.toPB))
}

Expand Down Expand Up @@ -113,17 +115,18 @@ class TransactionsApiGrpcImpl(commonApi: CommonTransactionsApi)(implicit sc: Sch

override def broadcast(tx: PBSignedTransaction): Future[PBSignedTransaction] =
(for {
maybeTx <- Future(tx.toVanilla)
vtx <- maybeTx.toFuture
result <- commonApi.broadcastTransaction(vtx)
_ <- result.resultE.toFuture
vtxEither <- Future(tx.toVanilla) // Intercept runtime errors
vtx <- vtxEither.toFuture
_ <- Either.cond(!vtx.isInstanceOf[EthereumTransaction], (), GenericError("ETH transactions should not be broadcasted over gRPC")).toFuture
result <- commonApi.broadcastTransaction(vtx)
_ <- result.resultE.toFuture // Check for success
} yield tx).wrapErrors
}

private object TransactionsApiGrpcImpl {
def toTransactionResponse(meta: TransactionMeta): TransactionResponse = {
val transactionId = meta.transaction.id().toByteString
val status = if (meta.succeeded) ApplicationStatus.SUCCEEDED else ApplicationStatus.SCRIPT_EXECUTION_FAILED
val transactionId = meta.transaction.id().toByteString
val status = if (meta.succeeded) ApplicationStatus.SUCCEEDED else ApplicationStatus.SCRIPT_EXECUTION_FAILED
val invokeScriptResult = meta match {
case TransactionMeta.Invoke(_, _, _, r) => r.map(VISR.toPB)
case _ => None
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ package object grpc extends ScorexLogging {
}

implicit class PBSignedTransactionConversions(val tx: PBSignedTransaction) extends AnyVal {
def toVanilla: Either[ValidationError, VanillaTransaction] = PBTransactions.vanilla(tx)
def toVanilla: Either[ValidationError, VanillaTransaction] = PBTransactions.vanilla(tx, unsafe = false)
}

implicit class VanillaHeaderConversionOps(val header: vb.BlockHeader) extends AnyVal {
Expand Down
Loading

0 comments on commit e6e3c03

Please sign in to comment.