From 8d4516f3a150c6906ff0ffd9296b3d7469e53214 Mon Sep 17 00:00:00 2001 From: esuwu Date: Tue, 14 Dec 2021 01:46:45 +0300 Subject: [PATCH 01/18] Added an empty InvokeExpressionTransaction --- pkg/proto/invoke_expression_transaction.go | 87 ++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 pkg/proto/invoke_expression_transaction.go diff --git a/pkg/proto/invoke_expression_transaction.go b/pkg/proto/invoke_expression_transaction.go new file mode 100644 index 0000000000..953ed691ac --- /dev/null +++ b/pkg/proto/invoke_expression_transaction.go @@ -0,0 +1,87 @@ +package proto + +import ( + "github.com/wavesplatform/gowaves/pkg/crypto" + g "github.com/wavesplatform/gowaves/pkg/grpc/generated/waves" +) + +type InvokeExpressionTransaction struct { +} + +func (tx InvokeExpressionTransaction) GetTypeInfo() TransactionTypeInfo { + return TransactionTypeInfo{} +} + +func (tx InvokeExpressionTransaction) GetVersion() byte { + return 1 +} + +func (tx *InvokeExpressionTransaction) GetID(scheme Scheme) ([]byte, error) { + return nil, nil +} + +func (tx InvokeExpressionTransaction) GetSender(scheme Scheme) (Address, error) { + return WavesAddress{}, nil +} + +func (tx InvokeExpressionTransaction) GetFee() uint64 { + return 1 +} + +func (tx InvokeExpressionTransaction) GetTimestamp() uint64 { + return 1 +} + +func (tx *InvokeExpressionTransaction) Validate(scheme Scheme) (Transaction, error) { + return nil, nil +} + +func (tx *InvokeExpressionTransaction) GenerateID(scheme Scheme) error { + return nil +} + +func (tx *InvokeExpressionTransaction) Sign(scheme Scheme, sk crypto.SecretKey) error { + return nil +} + +func (tx *InvokeExpressionTransaction) MerkleBytes(scheme Scheme) ([]byte, error) { + return nil, nil +} + +func (tx *InvokeExpressionTransaction) MarshalBinary() ([]byte, error) { + return nil, nil +} + +func (tx *InvokeExpressionTransaction) UnmarshalBinary([]byte, Scheme) error { + return nil +} + +func (tx *InvokeExpressionTransaction) BodyMarshalBinary() ([]byte, error) { + return nil, nil +} + +func (tx *InvokeExpressionTransaction) BinarySize() int { + return 1 +} + +func (tx *InvokeExpressionTransaction) MarshalToProtobuf(scheme Scheme) ([]byte, error) { + return nil, nil +} + +func (tx *InvokeExpressionTransaction) UnmarshalFromProtobuf([]byte) error { + return nil +} + +func (tx *InvokeExpressionTransaction) MarshalSignedToProtobuf(scheme Scheme) ([]byte, error) { + return nil, nil +} + +func (tx *InvokeExpressionTransaction) UnmarshalSignedFromProtobuf([]byte) error { + return nil +} +func (tx *InvokeExpressionTransaction) ToProtobuf(scheme Scheme) (*g.Transaction, error) { + return nil, nil +} +func (tx *InvokeExpressionTransaction) ToProtobufSigned(scheme Scheme) (*g.SignedTransaction, error) { + return nil, nil +} From 1b844ed34923d9536827f90c2b3348c179d09018 Mon Sep 17 00:00:00 2001 From: esuwu Date: Sun, 19 Dec 2021 13:26:41 +0300 Subject: [PATCH 02/18] Added invoke expression tx, added some methods and diff --- pkg/proto/invoke_expression_transaction.go | 79 ++++++++++++++-------- pkg/proto/protobuf_converters.go | 13 ++++ pkg/proto/transactions.go | 3 +- pkg/state/appender.go | 39 +++++------ pkg/state/invoke_applier.go | 49 ++++++++++---- pkg/state/script_caller.go | 12 +++- 6 files changed, 130 insertions(+), 65 deletions(-) diff --git a/pkg/proto/invoke_expression_transaction.go b/pkg/proto/invoke_expression_transaction.go index 953ed691ac..b2f03c526a 100644 --- a/pkg/proto/invoke_expression_transaction.go +++ b/pkg/proto/invoke_expression_transaction.go @@ -5,83 +5,106 @@ import ( g "github.com/wavesplatform/gowaves/pkg/grpc/generated/waves" ) -type InvokeExpressionTransaction struct { +type InvokeExpressionTransactionWithProofs struct { + ID *crypto.Digest `json:"id,omitempty"` + Type TransactionType `json:"type"` + Version byte `json:"version,omitempty"` + ChainID byte `json:"-"` + SenderPK crypto.PublicKey `json:"senderPublicKey"` + Fee uint64 `json:"fee"` + FeeAsset OptionalAsset `json:"feeAssetId"` + Timestamp uint64 `json:"timestamp,omitempty"` + Proofs *ProofsV1 `json:"proofs,omitempty"` + Expression string `json:"expression,omitempty"` } -func (tx InvokeExpressionTransaction) GetTypeInfo() TransactionTypeInfo { - return TransactionTypeInfo{} +func (tx InvokeExpressionTransactionWithProofs) GetTypeInfo() TransactionTypeInfo { + return TransactionTypeInfo{tx.Type, Proof} } -func (tx InvokeExpressionTransaction) GetVersion() byte { - return 1 +func (tx InvokeExpressionTransactionWithProofs) GetVersion() byte { + return tx.Version } -func (tx *InvokeExpressionTransaction) GetID(scheme Scheme) ([]byte, error) { - return nil, nil +func (tx *InvokeExpressionTransactionWithProofs) GetID(scheme Scheme) ([]byte, error) { + if tx.ID == nil { + if err := tx.GenerateID(scheme); err != nil { + return nil, err + } + } + return tx.ID.Bytes(), nil } -func (tx InvokeExpressionTransaction) GetSender(scheme Scheme) (Address, error) { - return WavesAddress{}, nil +func (tx InvokeExpressionTransactionWithProofs) GetSender(scheme Scheme) (Address, error) { + return NewAddressFromPublicKey(scheme, tx.SenderPK) } -func (tx InvokeExpressionTransaction) GetFee() uint64 { - return 1 +func (tx InvokeExpressionTransactionWithProofs) GetFee() uint64 { + return tx.Fee } -func (tx InvokeExpressionTransaction) GetTimestamp() uint64 { - return 1 +func (tx InvokeExpressionTransactionWithProofs) GetTimestamp() uint64 { + return tx.Timestamp } -func (tx *InvokeExpressionTransaction) Validate(scheme Scheme) (Transaction, error) { +func (tx *InvokeExpressionTransactionWithProofs) Validate(scheme Scheme) (Transaction, error) { return nil, nil } -func (tx *InvokeExpressionTransaction) GenerateID(scheme Scheme) error { +func (tx *InvokeExpressionTransactionWithProofs) GenerateID(scheme Scheme) error { + if tx.ID == nil { + body, err := MarshalTxBody(scheme, tx) + if err != nil { + return err + } + id := crypto.MustFastHash(body) + tx.ID = &id + } return nil } -func (tx *InvokeExpressionTransaction) Sign(scheme Scheme, sk crypto.SecretKey) error { +func (tx *InvokeExpressionTransactionWithProofs) Sign(scheme Scheme, sk crypto.SecretKey) error { return nil } -func (tx *InvokeExpressionTransaction) MerkleBytes(scheme Scheme) ([]byte, error) { - return nil, nil +func (tx *InvokeExpressionTransactionWithProofs) MerkleBytes(scheme Scheme) ([]byte, error) { + return tx.MarshalSignedToProtobuf(scheme) } -func (tx *InvokeExpressionTransaction) MarshalBinary() ([]byte, error) { +func (tx *InvokeExpressionTransactionWithProofs) MarshalBinary() ([]byte, error) { return nil, nil } -func (tx *InvokeExpressionTransaction) UnmarshalBinary([]byte, Scheme) error { +func (tx *InvokeExpressionTransactionWithProofs) UnmarshalBinary([]byte, Scheme) error { return nil } -func (tx *InvokeExpressionTransaction) BodyMarshalBinary() ([]byte, error) { +func (tx *InvokeExpressionTransactionWithProofs) BodyMarshalBinary() ([]byte, error) { return nil, nil } -func (tx *InvokeExpressionTransaction) BinarySize() int { +func (tx *InvokeExpressionTransactionWithProofs) BinarySize() int { return 1 } -func (tx *InvokeExpressionTransaction) MarshalToProtobuf(scheme Scheme) ([]byte, error) { +func (tx *InvokeExpressionTransactionWithProofs) MarshalToProtobuf(scheme Scheme) ([]byte, error) { return nil, nil } -func (tx *InvokeExpressionTransaction) UnmarshalFromProtobuf([]byte) error { +func (tx *InvokeExpressionTransactionWithProofs) UnmarshalFromProtobuf([]byte) error { return nil } -func (tx *InvokeExpressionTransaction) MarshalSignedToProtobuf(scheme Scheme) ([]byte, error) { +func (tx *InvokeExpressionTransactionWithProofs) MarshalSignedToProtobuf(scheme Scheme) ([]byte, error) { return nil, nil } -func (tx *InvokeExpressionTransaction) UnmarshalSignedFromProtobuf([]byte) error { +func (tx *InvokeExpressionTransactionWithProofs) UnmarshalSignedFromProtobuf([]byte) error { return nil } -func (tx *InvokeExpressionTransaction) ToProtobuf(scheme Scheme) (*g.Transaction, error) { +func (tx *InvokeExpressionTransactionWithProofs) ToProtobuf(scheme Scheme) (*g.Transaction, error) { return nil, nil } -func (tx *InvokeExpressionTransaction) ToProtobufSigned(scheme Scheme) (*g.SignedTransaction, error) { +func (tx *InvokeExpressionTransactionWithProofs) ToProtobufSigned(scheme Scheme) (*g.SignedTransaction, error) { return nil, nil } diff --git a/pkg/proto/protobuf_converters.go b/pkg/proto/protobuf_converters.go index 74a136dfb4..76ed581ee4 100644 --- a/pkg/proto/protobuf_converters.go +++ b/pkg/proto/protobuf_converters.go @@ -1016,6 +1016,19 @@ func (c *ProtobufConverter) Transaction(tx *g.Transaction) (Transaction, error) Fee: c.amount(tx.Fee), Timestamp: ts, } + case *g.Transaction_InvokeExpression: + feeAsset, feeAmount := c.convertAmount(tx.Fee) + rtx = &InvokeExpressionTransactionWithProofs{ + Type: InvokeScriptTransaction, + Version: v, + ChainID: scheme, + SenderPK: c.publicKey(tx.SenderPublicKey), + FeeAsset: feeAsset, + Fee: feeAmount, + Timestamp: ts, + //TODO + Expression: "", + } case *g.Transaction_InvokeScript: rcp, err := c.Recipient(scheme, d.InvokeScript.DApp) diff --git a/pkg/proto/transactions.go b/pkg/proto/transactions.go index 68cd1f5d16..557be4b900 100644 --- a/pkg/proto/transactions.go +++ b/pkg/proto/transactions.go @@ -37,8 +37,9 @@ const ( SetAssetScriptTransaction // 15 - SetAssetScript transaction InvokeScriptTransaction // 16 - InvokeScript transaction UpdateAssetInfoTransaction // 17 - UpdateAssetInfoTransaction - _ // 18 - reserved + InvokeExpressionTransaction // 18 - reserved EthereumMetamaskTransaction // 19 - EthereumMetamaskTransaction is a transaction which received from metamask + ) // TxFailureReason indicates Transactions failure reasons. diff --git a/pkg/state/appender.go b/pkg/state/appender.go index 77f008c62f..3ed7965407 100644 --- a/pkg/state/appender.go +++ b/pkg/state/appender.go @@ -2,6 +2,7 @@ package state import ( "fmt" + "github.com/wavesplatform/gowaves/pkg/crypto" "github.com/mr-tron/base58/base58" "github.com/pkg/errors" @@ -454,7 +455,7 @@ func (a *txAppender) appendTx(tx proto.Transaction, params *appendTxParams) erro var applicationRes *applicationResult needToValidateBalanceDiff := false switch tx.GetTypeInfo().Type { - case proto.InvokeScriptTransaction, proto.ExchangeTransaction: + case proto.InvokeScriptTransaction, proto.InvokeExpressionTransaction, proto.ExchangeTransaction: // Invoke and Exchange transactions should be handled differently. // They may fail, and will be saved to blockchain anyway. fallibleInfo := &fallibleValidationParams{appendTxParams: params, senderScripted: accountHasVerifierScript, senderAddress: senderAddr} @@ -632,27 +633,21 @@ type applicationResult struct { } func (a *txAppender) handleInvoke(tx proto.Transaction, info *fallibleValidationParams) (*applicationResult, error) { - invokeTx, ok := tx.(*proto.InvokeScriptWithProofs) - if !ok { - return nil, errors.New("failed to convert transaction to type InvokeScriptWithProofs") - } - res, err := a.ia.applyInvokeScript(invokeTx, info) - if err != nil { - zap.S().Debugf("failed to apply InvokeScript transaction %s to state: %v", invokeTx.ID.String(), err) - return nil, err - } - return res, nil -} - -func (a *txAppender) handleEthereumInvoke(tx proto.Transaction, info *fallibleValidationParams) (*applicationResult, error) { - ethTx, ok := tx.(*proto.EthereumTransaction) - if !ok { - return nil, errors.New("failed to convert transaction to type EthereumTransaction") + var ID crypto.Digest + switch t := tx.(type) { + case *proto.InvokeScriptWithProofs: + ID = *t.ID + case *proto.InvokeExpressionTransactionWithProofs: + ID = *t.ID + case *proto.EthereumTransaction: + if _, ok := t.TxKind.(*proto.EthereumInvokeScriptTxKind); !ok { + return nil, errors.New("wrong ethereum tx kind. expected invoke kind") + } + ID = *t.ID } - - res, err := a.ia.applyInvokeScript(ethTx, info) + res, err := a.ia.applyInvokeScript(tx, info) if err != nil { - zap.S().Debugf("failed to apply Ethereum invoke transaction %s to state: %v", ethTx.ID.String(), err) + zap.S().Debugf("failed to apply InvokeScript transaction %s to state: %v", ID.String(), err) return nil, err } return res, nil @@ -770,12 +765,10 @@ func (a *txAppender) handleFallible(tx proto.Transaction, info *fallibleValidati } } switch tx.GetTypeInfo().Type { - case proto.InvokeScriptTransaction: + case proto.InvokeScriptTransaction, proto.InvokeExpressionTransaction, proto.EthereumMetamaskTransaction: return a.handleInvoke(tx, info) case proto.ExchangeTransaction: return a.handleExchange(tx, info) - case proto.EthereumMetamaskTransaction: - return a.handleEthereumInvoke(tx, info) } return nil, errors.New("transaction is not fallible") } diff --git a/pkg/state/invoke_applier.go b/pkg/state/invoke_applier.go index 7e6d69306f..02cbf76874 100644 --- a/pkg/state/invoke_applier.go +++ b/pkg/state/invoke_applier.go @@ -1,6 +1,7 @@ package state import ( + "encoding/base64" "fmt" "math" "math/big" @@ -727,10 +728,14 @@ func (ia *invokeApplier) applyInvokeScript(tx proto.Transaction, info *fallibleV ia.invokeDiffStor.invokeDiffsStor.reset() }() - var paymentsLength int - var scriptAddr *proto.WavesAddress - var txID crypto.Digest - var sender proto.Address + var ( + paymentsLength int + scriptAddr *proto.WavesAddress + txID crypto.Digest + sender proto.Address + tree *ride.Tree + scriptPK crypto.PublicKey + ) switch transaction := tx.(type) { case *proto.InvokeScriptWithProofs: @@ -745,6 +750,25 @@ func (ia *invokeApplier) applyInvokeScript(tx proto.Transaction, info *fallibleV if err != nil { return nil, errors.Wrapf(err, "failed to apply script invocation") } + tree, err = ia.stor.scriptsStorage.newestScriptByAddr(*scriptAddr, !info.initialisation) + if err != nil { + return nil, errors.Wrapf(err, "failed to instantiate script on address '%s'", scriptAddr.String()) + } + scriptPK, err = ia.stor.scriptsStorage.newestScriptPKByAddr(*scriptAddr, !info.initialisation) + if err != nil { + return nil, errors.Wrapf(err, "failed to get script's public key on address '%s'", scriptAddr.String()) + } + case *proto.InvokeExpressionTransactionWithProofs: + src, err := base64.StdEncoding.DecodeString(transaction.Expression) + if err != nil { + return nil, errors.Wrap(err, "failed to decode invoke expression") + } + + tree, err = ride.Parse(src) + if err != nil { + return nil, errors.Wrap(err, "failed to parse decoded invoke expression into tree") + } + case *proto.EthereumTransaction: var err error scriptAddr, err = transaction.WavesAddressTo(ia.settings.AddressSchemeCharacter) @@ -758,6 +782,14 @@ func (ia *invokeApplier) applyInvokeScript(tx proto.Transaction, info *fallibleV if err != nil { return nil, errors.Wrapf(err, "failed to apply script invocation") } + tree, err = ia.stor.scriptsStorage.newestScriptByAddr(*scriptAddr, !info.initialisation) + if err != nil { + return nil, errors.Wrapf(err, "failed to instantiate script on address '%s'", scriptAddr.String()) + } + scriptPK, err = ia.stor.scriptsStorage.newestScriptPKByAddr(*scriptAddr, !info.initialisation) + if err != nil { + return nil, errors.Wrapf(err, "failed to get script's public key on address '%s'", scriptAddr.String()) + } default: return nil, errors.New("failed to apply an invoke script: unexpected type of transaction ") } @@ -776,14 +808,7 @@ func (ia *invokeApplier) applyInvokeScript(tx proto.Transaction, info *fallibleV if err != nil { return nil, err } - tree, err := ia.stor.scriptsStorage.newestScriptByAddr(*scriptAddr, !info.initialisation) - if err != nil { - return nil, errors.Wrapf(err, "failed to instantiate script on address '%s'", scriptAddr.String()) - } - scriptPK, err := ia.stor.scriptsStorage.newestScriptPKByAddr(*scriptAddr, !info.initialisation) - if err != nil { - return nil, errors.Wrapf(err, "failed to get script's public key on address '%s'", scriptAddr.String()) - } + // Check that the script's library supports multiple payments. // We don't have to check feature activation because we've done it before. if paymentsLength >= 2 && tree.LibVersion < 4 { diff --git a/pkg/state/script_caller.go b/pkg/state/script_caller.go index 8ee4ce7d75..b63765d2c0 100644 --- a/pkg/state/script_caller.go +++ b/pkg/state/script_caller.go @@ -237,7 +237,17 @@ func (a *scriptCaller) invokeFunction(tree *ride.Tree, tx proto.Transaction, inf functionName = transaction.FunctionCall.Name functionArguments = transaction.FunctionCall.Arguments defaultFunction = transaction.FunctionCall.Default - + case *proto.InvokeExpressionTransactionWithProofs: + //err = env.SetInvoke(transaction, tree.LibVersion) + //if err != nil { + // return nil, err + //} + //payments = transaction.Payments + sender, err = proto.NewAddressFromPublicKey(a.settings.AddressSchemeCharacter, transaction.SenderPK) + if err != nil { + return nil, err + } + functionName = "" case *proto.EthereumTransaction: abiPayments := transaction.TxKind.DecodedData().Payments scriptPayments := make([]proto.ScriptPayment, 0, len(abiPayments)) From 7d367b8fbedfc97acf1ca384f50dc8cf959e6cfb Mon Sep 17 00:00:00 2001 From: esuwu Date: Tue, 21 Dec 2021 17:21:41 +0300 Subject: [PATCH 03/18] Added test --- pkg/ride/parser.go | 28 ++++++++++++++++++++++++++++ pkg/ride/result.go | 22 ++++++++++++++++++++++ pkg/ride/tree.go | 1 + pkg/ride/tree_evaluation_test.go | 29 +++++++++++++++++++++++++++++ pkg/ride/tree_evaluator.go | 23 ++++++++++++++--------- 5 files changed, 94 insertions(+), 9 deletions(-) diff --git a/pkg/ride/parser.go b/pkg/ride/parser.go index 59bb579705..a59c8fbb98 100644 --- a/pkg/ride/parser.go +++ b/pkg/ride/parser.go @@ -82,6 +82,18 @@ func (p *parser) parse() (*Tree, error) { return p.parseDApp() case 1, 2, 3, 4, 5: return p.parseScript(v) + case 255: + _, err := p.r.ReadByte() + if err != nil { + return nil, err + } + // TODO here should be a lib version + tree, err := p.parseScript(5) + if err != nil { + return nil, err + } + tree.isExpression = true + return tree, nil default: return nil, errors.Errorf("unsupported script version %d", v) } @@ -187,6 +199,22 @@ func (p *parser) parseScript(v int) (*Tree, error) { return tree, nil } +func (p *parser) parseExpression(v int) (*Tree, error) { + tree := &Tree{ + AppVersion: scriptApplicationVersion, + LibVersion: v, + } + node, err := p.parseNext() + if err != nil { + return nil, err + } + tree.Verifier = node + tree.HasBlockV2 = p.seenBlockV2 + tree.Digest = p.id + tree.isExpression = true + return tree, nil +} + func (p *parser) parseNext() (Node, error) { t, err := p.r.ReadByte() if err != nil { diff --git a/pkg/ride/result.go b/pkg/ride/result.go index 43459c8af0..9547f5101e 100644 --- a/pkg/ride/result.go +++ b/pkg/ride/result.go @@ -52,3 +52,25 @@ func (r DAppResult) ScriptActions() []proto.ScriptAction { func (r DAppResult) Complexity() int { return r.complexity } + +type ExpressionResult struct { + actions []proto.ScriptAction + param rideType + complexity int +} + +func (r ExpressionResult) Result() bool { + return true +} + +func (r ExpressionResult) userResult() rideType { + return r.param +} + +func (r ExpressionResult) ScriptActions() []proto.ScriptAction { + return r.actions +} + +func (r ExpressionResult) Complexity() int { + return r.complexity +} diff --git a/pkg/ride/tree.go b/pkg/ride/tree.go index e34fa654b4..50ca14c5ae 100644 --- a/pkg/ride/tree.go +++ b/pkg/ride/tree.go @@ -190,6 +190,7 @@ type Tree struct { AppVersion int LibVersion int HasBlockV2 bool + isExpression bool Meta meta.DApp Declarations []Node Functions []Node diff --git a/pkg/ride/tree_evaluation_test.go b/pkg/ride/tree_evaluation_test.go index 80ebc04034..91c08f4acc 100644 --- a/pkg/ride/tree_evaluation_test.go +++ b/pkg/ride/tree_evaluation_test.go @@ -436,6 +436,35 @@ func TestOverlapping(t *testing.T) { assert.True(t, r.Result()) } +func TestInvokeExpression(t *testing.T) { + /* + {-# STDLIB_VERSION 6 #-} + {-# CONTENT_TYPE EXPRESSION #-} + {-# SCRIPT_TYPE CALL #-} + + let lease = Lease(Address(base58'3FMdfKQ3yrkrGawp4QYkf8phE6ZMup7hfR2'), 10) + [ + lease, + BooleanEntry("key", true) + ] + */ + s := "/wYEAAAABWxlYXNlCQAERAAAAAIJAQAAAAdBZGRyZXNzAAAAAQEAAAAaAUP2ZeK0oJWLGYVbOVovHApDYXsAHYcycskAAAAAAAAAAAoJAARMAAAAAgUAAAAFbGVhc2UJAARMAAAAAgkBAAAADEJvb2xlYW5FbnRyeQAAAAICAAAAA2tleQYFAAAAA25pbE0OINk=" + src, err := base64.StdEncoding.DecodeString(s) + require.NoError(t, err) + + tree, err := Parse(src) + require.NoError(t, err) + assert.NotNil(t, tree) + + env, _ := testInvokeEnv(true) + res, err := CallVerifier(env, tree) + require.NoError(t, err) + require.NotNil(t, res.ScriptActions()) + r, ok := res.(ExpressionResult) + require.True(t, ok) + assert.True(t, r.Result()) +} + func TestUserFunctionsInExpression(t *testing.T) { /* {-# STDLIB_VERSION 3 #-} diff --git a/pkg/ride/tree_evaluator.go b/pkg/ride/tree_evaluator.go index c4cb6052c4..7b1676a96d 100644 --- a/pkg/ride/tree_evaluator.go +++ b/pkg/ride/tree_evaluator.go @@ -223,11 +223,12 @@ func selectFunctionNames(v int, enableInvocation bool) ([]string, error) { } type treeEvaluator struct { - dapp bool - complexity int - f Node - s evaluationScope - env environment + dapp bool + complexity int + isExpression bool + f Node + s evaluationScope + env environment } func (e *treeEvaluator) evaluate() (Result, error) { @@ -254,6 +255,9 @@ func (e *treeEvaluator) evaluate() (Result, error) { } actions = append(actions, a) } + if e.isExpression { + return ExpressionResult{actions: actions, complexity: e.complexity}, nil + } return DAppResult{actions: actions, complexity: e.complexity}, nil case tuple2: var actions []proto.ScriptAction @@ -475,10 +479,11 @@ func treeVerifierEvaluator(env environment, tree *Tree) (*treeEvaluator, error) return nil, EvaluationFailure.New("no verifier declaration") } return &treeEvaluator{ - dapp: tree.IsDApp(), - f: tree.Verifier, // In simple script verifier is an expression itself - s: s, - env: env, + dapp: tree.IsDApp(), + f: tree.Verifier, // In simple script verifier is an expression itself + s: s, + env: env, + isExpression: tree.isExpression, }, nil } From 31be4ebc04ac9f66d936d7d1d430859e99a47cc7 Mon Sep 17 00:00:00 2001 From: esuwu Date: Thu, 23 Dec 2021 10:49:59 +0300 Subject: [PATCH 04/18] Added methods for tx, added checker and fee counter --- pkg/proto/invoke_expression_transaction.go | 106 +++++++++++++++++++-- pkg/proto/protobuf_converters.go | 2 +- pkg/ride/parser.go | 16 ---- pkg/ride/tree_evaluation_test.go | 16 ++-- pkg/state/invoke_applier.go | 2 +- pkg/state/script_caller.go | 91 +++++++++++++----- pkg/state/transaction_checker.go | 26 +++++ pkg/state/transaction_differ.go | 32 +++++++ pkg/state/transaction_fee_counter.go | 8 ++ pkg/state/transaction_handler.go | 3 + pkg/state/transaction_performer.go | 10 ++ 11 files changed, 254 insertions(+), 58 deletions(-) diff --git a/pkg/proto/invoke_expression_transaction.go b/pkg/proto/invoke_expression_transaction.go index b2f03c526a..da15a4af3a 100644 --- a/pkg/proto/invoke_expression_transaction.go +++ b/pkg/proto/invoke_expression_transaction.go @@ -1,6 +1,7 @@ package proto import ( + "github.com/pkg/errors" "github.com/wavesplatform/gowaves/pkg/crypto" g "github.com/wavesplatform/gowaves/pkg/grpc/generated/waves" ) @@ -15,7 +16,7 @@ type InvokeExpressionTransactionWithProofs struct { FeeAsset OptionalAsset `json:"feeAssetId"` Timestamp uint64 `json:"timestamp,omitempty"` Proofs *ProofsV1 `json:"proofs,omitempty"` - Expression string `json:"expression,omitempty"` + Expression []byte `json:"expression,omitempty"` } func (tx InvokeExpressionTransactionWithProofs) GetTypeInfo() TransactionTypeInfo { @@ -48,7 +49,20 @@ func (tx InvokeExpressionTransactionWithProofs) GetTimestamp() uint64 { } func (tx *InvokeExpressionTransactionWithProofs) Validate(scheme Scheme) (Transaction, error) { - return nil, nil + if tx.Version < 1 || tx.Version > MaxInvokeScriptTransactionVersion { + return tx, errors.Errorf("unexpected version %d for InvokeExpression", tx.Version) + } + if tx.Fee == 0 { + return tx, errors.New("fee should be positive") + } + if !validJVMLong(tx.Fee) { + return tx, errors.New("fee is too big") + } + + if tx.ChainID != scheme { + return tx, errors.New("the chain id of InvokeExpression is not equal to network byte") + } + return tx, nil } func (tx *InvokeExpressionTransactionWithProofs) GenerateID(scheme Scheme) error { @@ -64,6 +78,22 @@ func (tx *InvokeExpressionTransactionWithProofs) GenerateID(scheme Scheme) error } func (tx *InvokeExpressionTransactionWithProofs) Sign(scheme Scheme, sk crypto.SecretKey) error { + b, err := MarshalTxBody(scheme, tx) + if err != nil { + return errors.Wrap(err, "failed to sign InvokeExpression transaction") + } + if tx.Proofs == nil { + tx.Proofs = &ProofsV1{proofsVersion, make([]B58Bytes, 0)} + } + err = tx.Proofs.Sign(0, sk, b) + if err != nil { + return errors.Wrap(err, "failed to sign InvokeExpression transaction") + } + d, err := crypto.FastHash(b) + if err != nil { + return errors.Wrap(err, "failed to sign InvokeExpression transaction") + } + tx.ID = &d return nil } @@ -80,31 +110,89 @@ func (tx *InvokeExpressionTransactionWithProofs) UnmarshalBinary([]byte, Scheme) } func (tx *InvokeExpressionTransactionWithProofs) BodyMarshalBinary() ([]byte, error) { + //p := 0 + //buf := make([]byte, 1+1+1+crypto.PublicKeySize+tx.FeeAsset.BinarySize()+len(tx.Expression)) + //buf[p] = byte(tx.Type) + //p++ + //buf[p] = tx.Version + //p++ + //buf[p] = tx.ChainID + //p++ + //copy(buf[p:], tx.SenderPK[:]) + //p += crypto.PublicKeySize + //binary.BigEndian.PutUint64(buf[p:], tx.Fee) + //p += 8 + //fab, err := tx.FeeAsset.MarshalBinary() + //if err != nil { + // return nil, err + //} + //copy(buf[p:], fab) + //p += len(fab) + //binary.BigEndian.PutUint64(buf[p:], tx.Timestamp) + //copy(buf[p:], tx.Expression) + //return buf, nil return nil, nil } +// TODO check on correctness func (tx *InvokeExpressionTransactionWithProofs) BinarySize() int { - return 1 + return 4 + tx.Proofs.BinarySize() + crypto.PublicKeySize + tx.FeeAsset.BinarySize() + 16 + len(tx.Expression) } func (tx *InvokeExpressionTransactionWithProofs) MarshalToProtobuf(scheme Scheme) ([]byte, error) { - return nil, nil + return MarshalTxDeterministic(tx, scheme) } -func (tx *InvokeExpressionTransactionWithProofs) UnmarshalFromProtobuf([]byte) error { +func (tx *InvokeExpressionTransactionWithProofs) UnmarshalFromProtobuf(data []byte) error { + t, err := TxFromProtobuf(data) + if err != nil { + return err + } + invokeExpressionTx, ok := t.(*InvokeExpressionTransactionWithProofs) + if !ok { + return errors.New("failed to convert result to InvokeScripV1") + } + *tx = *invokeExpressionTx return nil } func (tx *InvokeExpressionTransactionWithProofs) MarshalSignedToProtobuf(scheme Scheme) ([]byte, error) { - return nil, nil + return MarshalSignedTxDeterministic(tx, scheme) } -func (tx *InvokeExpressionTransactionWithProofs) UnmarshalSignedFromProtobuf([]byte) error { +func (tx *InvokeExpressionTransactionWithProofs) UnmarshalSignedFromProtobuf(data []byte) error { + t, err := SignedTxFromProtobuf(data) + if err != nil { + return err + } + invokeExpressionTx, ok := t.(*InvokeExpressionTransactionWithProofs) + if !ok { + return errors.New("failed to convert result to InvokeScriptWithProofs") + } + *tx = *invokeExpressionTx return nil } func (tx *InvokeExpressionTransactionWithProofs) ToProtobuf(scheme Scheme) (*g.Transaction, error) { - return nil, nil + + txData := &g.Transaction_InvokeExpression{InvokeExpression: &g.InvokeExpressionTransactionData{ + Expression: tx.Expression, + }} + fee := &g.Amount{AssetId: tx.FeeAsset.ToID(), Amount: int64(tx.Fee)} + res := TransactionToProtobufCommon(scheme, tx.SenderPK.Bytes(), tx) + res.Fee = fee + res.Data = txData + return res, nil } func (tx *InvokeExpressionTransactionWithProofs) ToProtobufSigned(scheme Scheme) (*g.SignedTransaction, error) { - return nil, nil + unsigned, err := tx.ToProtobuf(scheme) + if err != nil { + return nil, err + } + if tx.Proofs == nil { + return nil, errors.New("no proofs provided") + } + return &g.SignedTransaction{ + Transaction: &g.SignedTransaction_WavesTransaction{WavesTransaction: unsigned}, + Proofs: tx.Proofs.Bytes(), + }, nil } diff --git a/pkg/proto/protobuf_converters.go b/pkg/proto/protobuf_converters.go index 76ed581ee4..b8b73e5550 100644 --- a/pkg/proto/protobuf_converters.go +++ b/pkg/proto/protobuf_converters.go @@ -1027,7 +1027,7 @@ func (c *ProtobufConverter) Transaction(tx *g.Transaction) (Transaction, error) Fee: feeAmount, Timestamp: ts, //TODO - Expression: "", + Expression: []byte{}, } case *g.Transaction_InvokeScript: diff --git a/pkg/ride/parser.go b/pkg/ride/parser.go index a59c8fbb98..060e9c6470 100644 --- a/pkg/ride/parser.go +++ b/pkg/ride/parser.go @@ -199,22 +199,6 @@ func (p *parser) parseScript(v int) (*Tree, error) { return tree, nil } -func (p *parser) parseExpression(v int) (*Tree, error) { - tree := &Tree{ - AppVersion: scriptApplicationVersion, - LibVersion: v, - } - node, err := p.parseNext() - if err != nil { - return nil, err - } - tree.Verifier = node - tree.HasBlockV2 = p.seenBlockV2 - tree.Digest = p.id - tree.isExpression = true - return tree, nil -} - func (p *parser) parseNext() (Node, error) { t, err := p.r.ReadByte() if err != nil { diff --git a/pkg/ride/tree_evaluation_test.go b/pkg/ride/tree_evaluation_test.go index 91c08f4acc..fa8b07494e 100644 --- a/pkg/ride/tree_evaluation_test.go +++ b/pkg/ride/tree_evaluation_test.go @@ -438,15 +438,15 @@ func TestOverlapping(t *testing.T) { func TestInvokeExpression(t *testing.T) { /* - {-# STDLIB_VERSION 6 #-} - {-# CONTENT_TYPE EXPRESSION #-} - {-# SCRIPT_TYPE CALL #-} + {-# STDLIB_VERSION 6 #-} + {-# CONTENT_TYPE EXPRESSION #-} + {-# SCRIPT_TYPE CALL #-} - let lease = Lease(Address(base58'3FMdfKQ3yrkrGawp4QYkf8phE6ZMup7hfR2'), 10) - [ - lease, - BooleanEntry("key", true) - ] + let lease = Lease(Address(base58'3FMdfKQ3yrkrGawp4QYkf8phE6ZMup7hfR2'), 10) + [ + lease, + BooleanEntry("key", true) + ] */ s := "/wYEAAAABWxlYXNlCQAERAAAAAIJAQAAAAdBZGRyZXNzAAAAAQEAAAAaAUP2ZeK0oJWLGYVbOVovHApDYXsAHYcycskAAAAAAAAAAAoJAARMAAAAAgUAAAAFbGVhc2UJAARMAAAAAgkBAAAADEJvb2xlYW5FbnRyeQAAAAICAAAAA2tleQYFAAAAA25pbE0OINk=" src, err := base64.StdEncoding.DecodeString(s) diff --git a/pkg/state/invoke_applier.go b/pkg/state/invoke_applier.go index 02cbf76874..b8cd5cf17e 100644 --- a/pkg/state/invoke_applier.go +++ b/pkg/state/invoke_applier.go @@ -759,7 +759,7 @@ func (ia *invokeApplier) applyInvokeScript(tx proto.Transaction, info *fallibleV return nil, errors.Wrapf(err, "failed to get script's public key on address '%s'", scriptAddr.String()) } case *proto.InvokeExpressionTransactionWithProofs: - src, err := base64.StdEncoding.DecodeString(transaction.Expression) + src, err := base64.StdEncoding.DecodeString(string(transaction.Expression)) if err != nil { return nil, errors.Wrap(err, "failed to decode invoke expression") } diff --git a/pkg/state/script_caller.go b/pkg/state/script_caller.go index b63765d2c0..3caa73a91e 100644 --- a/pkg/state/script_caller.go +++ b/pkg/state/script_caller.go @@ -218,11 +218,15 @@ func (a *scriptCaller) invokeFunction(tree *ride.Tree, tx proto.Transaction, inf return nil, err } - var functionName string - var functionArguments proto.Arguments - var defaultFunction bool - var payments proto.ScriptPayments - var sender proto.WavesAddress + var ( + functionName string + functionArguments proto.Arguments + defaultFunction bool + payments proto.ScriptPayments + sender proto.WavesAddress + r ride.Result + ) + switch transaction := tx.(type) { case *proto.InvokeScriptWithProofs: err = env.SetInvoke(transaction, tree.LibVersion) @@ -237,17 +241,57 @@ func (a *scriptCaller) invokeFunction(tree *ride.Tree, tx proto.Transaction, inf functionName = transaction.FunctionCall.Name functionArguments = transaction.FunctionCall.Arguments defaultFunction = transaction.FunctionCall.Default + + env.ChooseSizeCheck(tree.LibVersion) + env.ChooseTakeString(info.rideV5Activated) + env.ChooseMaxDataEntriesSize(info.rideV5Activated) + + // Since V5 we have to create environment with wrapped state to which we put attached payments + if tree.LibVersion >= 5 { + env, err = ride.NewEnvironmentWithWrappedState(env, payments, sender, info.rideV6Activated) + if err != nil { + return nil, errors.Wrapf(err, "failed to create RIDE environment with wrapped state") + } + } + + r, err = ride.CallFunction(env, tree, functionName, functionArguments) + if err != nil { + if appendErr := a.appendFunctionComplexity(ride.EvaluationErrorSpentComplexity(err), scriptAddress, functionName, defaultFunction, info); appendErr != nil { + return nil, appendErr + } + return nil, err + } case *proto.InvokeExpressionTransactionWithProofs: + // TODO set invoke //err = env.SetInvoke(transaction, tree.LibVersion) //if err != nil { // return nil, err //} - //payments = transaction.Payments sender, err = proto.NewAddressFromPublicKey(a.settings.AddressSchemeCharacter, transaction.SenderPK) if err != nil { return nil, err } functionName = "" + env.ChooseSizeCheck(tree.LibVersion) + env.ChooseTakeString(info.rideV5Activated) + env.ChooseMaxDataEntriesSize(info.rideV5Activated) + + // Since V5 we have to create environment with wrapped state to which we put attached payments + if tree.LibVersion >= 5 { + env, err = ride.NewEnvironmentWithWrappedState(env, payments, sender, info.rideV6Activated) + if err != nil { + return nil, errors.Wrapf(err, "failed to create RIDE environment with wrapped state") + } + } + + r, err = ride.CallVerifier(env, tree) + if err != nil { + if appendErr := a.appendFunctionComplexity(ride.EvaluationErrorSpentComplexity(err), scriptAddress, functionName, defaultFunction, info); appendErr != nil { + return nil, appendErr + } + return nil, err + } + case *proto.EthereumTransaction: abiPayments := transaction.TxKind.DecodedData().Payments scriptPayments := make([]proto.ScriptPayment, 0, len(abiPayments)) @@ -280,29 +324,30 @@ func (a *scriptCaller) invokeFunction(tree *ride.Tree, tx proto.Transaction, inf functionArguments = arguments defaultFunction = true - default: - return nil, errors.New("failed to invoke function: unexpected type of transaction ") - } + env.ChooseSizeCheck(tree.LibVersion) + env.ChooseTakeString(info.rideV5Activated) + env.ChooseMaxDataEntriesSize(info.rideV5Activated) - env.ChooseSizeCheck(tree.LibVersion) - env.ChooseTakeString(info.rideV5Activated) - env.ChooseMaxDataEntriesSize(info.rideV5Activated) + // Since V5 we have to create environment with wrapped state to which we put attached payments + if tree.LibVersion >= 5 { + env, err = ride.NewEnvironmentWithWrappedState(env, payments, sender, info.rideV6Activated) + if err != nil { + return nil, errors.Wrapf(err, "failed to create RIDE environment with wrapped state") + } + } - // Since V5 we have to create environment with wrapped state to which we put attached payments - if tree.LibVersion >= 5 { - env, err = ride.NewEnvironmentWithWrappedState(env, payments, sender, info.rideV6Activated) + r, err = ride.CallFunction(env, tree, functionName, functionArguments) if err != nil { - return nil, errors.Wrapf(err, "failed to create RIDE environment with wrapped state") + if appendErr := a.appendFunctionComplexity(ride.EvaluationErrorSpentComplexity(err), scriptAddress, functionName, defaultFunction, info); appendErr != nil { + return nil, appendErr + } + return nil, err } - } - r, err := ride.CallFunction(env, tree, functionName, functionArguments) - if err != nil { - if appendErr := a.appendFunctionComplexity(ride.EvaluationErrorSpentComplexity(err), scriptAddress, functionName, defaultFunction, info); appendErr != nil { - return nil, appendErr - } - return nil, err + default: + return nil, errors.New("failed to invoke function: unexpected type of transaction ") } + if err := a.appendFunctionComplexity(r.Complexity(), scriptAddress, functionName, defaultFunction, info); err != nil { return nil, err } diff --git a/pkg/state/transaction_checker.go b/pkg/state/transaction_checker.go index 338f2115cf..40640e10d5 100644 --- a/pkg/state/transaction_checker.go +++ b/pkg/state/transaction_checker.go @@ -1277,6 +1277,32 @@ func (tc *transactionChecker) checkInvokeScriptWithProofs(transaction proto.Tran return smartAssets, nil } +func (tc *transactionChecker) checkInvokeExpressionWithProofs(transaction proto.Transaction, info *checkerInfo) ([]crypto.Digest, error) { + tx, ok := transaction.(*proto.InvokeExpressionTransactionWithProofs) + if !ok { + return nil, errors.New("failed to convert interface to InvokeExpressionWithProofs transaction") + } + if err := tc.checkTimestamps(tx.Timestamp, info.currentTimestamp, info.parentTimestamp); err != nil { + return nil, errs.Extend(err, "invalid timestamp") + } + rideV6, err := tc.stor.features.newestIsActivated(int16(settings.RideV6)) + if err != nil { + return nil, err + } + if !rideV6 { + return nil, errors.New("can not use InvokeExpression before RideV6 activation") + } + if err := tc.checkFeeAsset(&tx.FeeAsset, info.initialisation); err != nil { + return nil, err + } + + assets := &txAssets{feeAsset: tx.FeeAsset, smartAssets: nil} + if err := tc.checkFee(transaction, assets, info); err != nil { + return nil, err + } + return nil, nil +} + func (tc *transactionChecker) checkUpdateAssetInfoWithProofs(transaction proto.Transaction, info *checkerInfo) ([]crypto.Digest, error) { tx, ok := transaction.(*proto.UpdateAssetInfoWithProofs) if !ok { diff --git a/pkg/state/transaction_differ.go b/pkg/state/transaction_differ.go index 7e09e9a161..e0515ba239 100644 --- a/pkg/state/transaction_differ.go +++ b/pkg/state/transaction_differ.go @@ -1425,6 +1425,38 @@ func (td *transactionDiffer) createDiffInvokeScriptWithProofs(transaction proto. return changes, nil } +func (td *transactionDiffer) createDiffInvokeExpressionWithProofs(transaction proto.Transaction, info *differInfo) (txBalanceChanges, error) { + tx, ok := transaction.(*proto.InvokeExpressionTransactionWithProofs) + if !ok { + return txBalanceChanges{}, errors.New("failed to convert interface to InvokeExpessionWithProofs transaction") + } + diff := newTxDiff() + // Append sender diff. + senderAddr, err := proto.NewAddressFromPublicKey(td.settings.AddressSchemeCharacter, tx.SenderPK) + if err != nil { + return txBalanceChanges{}, err + } + senderAddrID := senderAddr.ID() + + senderFeeKey := byteKey(senderAddrID, tx.FeeAsset) + senderFeeBalanceDiff := -int64(tx.Fee) + if err := diff.appendBalanceDiff(senderFeeKey, newBalanceDiff(senderFeeBalanceDiff, 0, 0, false)); err != nil { + return txBalanceChanges{}, err + } + scriptAddr, err := recipientToAddress(proto.NewRecipientFromAddress(senderAddr), td.stor.aliases, !info.initialisation) + if err != nil { + return txBalanceChanges{}, err + } + + addresses := []proto.WavesAddress{senderAddr, *scriptAddr} + changes := newTxBalanceChanges(addresses, diff) + if err := td.handleSponsorship(&changes, tx.Fee, tx.FeeAsset, info); err != nil { + return txBalanceChanges{}, err + } + + return changes, nil +} + func (td *transactionDiffer) createDiffEthereumInvokeScript(tx *proto.EthereumTransaction, info *differInfo) (txBalanceChanges, error) { updateMinIntermediateBalance := false diff --git a/pkg/state/transaction_fee_counter.go b/pkg/state/transaction_fee_counter.go index a6d2fd3258..d19353c8bd 100644 --- a/pkg/state/transaction_fee_counter.go +++ b/pkg/state/transaction_fee_counter.go @@ -244,6 +244,14 @@ func (tf *transactionFeeCounter) minerFeeInvokeScriptWithProofs(transaction prot return tf.minerFee(distr, tx.Fee, tx.FeeAsset) } +func (tf *transactionFeeCounter) minerFeeInvokeExpressionWithProofs(transaction proto.Transaction, distr *feeDistribution) error { + tx, ok := transaction.(*proto.InvokeExpressionTransactionWithProofs) + if !ok { + return errors.New("failed to convert interface to InvokeExpressionWithProofs tx") + } + return tf.minerFee(distr, tx.Fee, tx.FeeAsset) +} + func (tf *transactionFeeCounter) minerFeeUpdateAssetInfoWithProofs(transaction proto.Transaction, distr *feeDistribution) error { tx, ok := transaction.(*proto.UpdateAssetInfoWithProofs) if !ok { diff --git a/pkg/state/transaction_handler.go b/pkg/state/transaction_handler.go index 0f891d0ab5..33a3c686e6 100644 --- a/pkg/state/transaction_handler.go +++ b/pkg/state/transaction_handler.go @@ -105,6 +105,9 @@ func buildHandles(tc *transactionChecker, tp *transactionPerformer, td *transact proto.TransactionTypeInfo{Type: proto.InvokeScriptTransaction, ProofVersion: proto.Proof}: txHandleFuncs{ tc.checkInvokeScriptWithProofs, tp.performInvokeScriptWithProofs, td.createDiffInvokeScriptWithProofs, tf.minerFeeInvokeScriptWithProofs, }, + proto.TransactionTypeInfo{Type: proto.InvokeExpressionTransaction, ProofVersion: proto.Proof}: txHandleFuncs{ + tc.checkInvokeExpressionWithProofs, tp.performInvokeExpressionWithProofs, td.createDiffInvokeExpressionWithProofs, tf.minerFeeInvokeExpressionWithProofs, + }, proto.TransactionTypeInfo{Type: proto.UpdateAssetInfoTransaction, ProofVersion: proto.Proof}: txHandleFuncs{ tc.checkUpdateAssetInfoWithProofs, tp.performUpdateAssetInfoWithProofs, td.createDiffUpdateAssetInfoWithProofs, tf.minerFeeUpdateAssetInfoWithProofs, }, diff --git a/pkg/state/transaction_performer.go b/pkg/state/transaction_performer.go index 2ecda605f8..701234666b 100644 --- a/pkg/state/transaction_performer.go +++ b/pkg/state/transaction_performer.go @@ -344,6 +344,16 @@ func (tp *transactionPerformer) performInvokeScriptWithProofs(transaction proto. return nil } +func (tp *transactionPerformer) performInvokeExpressionWithProofs(transaction proto.Transaction, info *performerInfo) error { + if _, ok := transaction.(*proto.InvokeExpressionTransactionWithProofs); !ok { + return errors.New("failed to convert interface to InvokeExpressionWithProofs transaction") + } + if err := tp.stor.commitUncertain(info.blockID); err != nil { + return errors.Wrap(err, "failed to commit invoke changes") + } + return nil +} + func (tp *transactionPerformer) performEthereumTransactionWithProofs(transaction proto.Transaction, info *performerInfo) error { ethTx, ok := transaction.(*proto.EthereumTransaction) if !ok { From 2e2fdc5f6d932e8155aec6391fed97818dea9553 Mon Sep 17 00:00:00 2001 From: esuwu Date: Thu, 23 Dec 2021 11:00:44 +0300 Subject: [PATCH 05/18] Fixd transaction type in protobuf converters --- pkg/proto/protobuf_converters.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/proto/protobuf_converters.go b/pkg/proto/protobuf_converters.go index b8b73e5550..3320f9b31b 100644 --- a/pkg/proto/protobuf_converters.go +++ b/pkg/proto/protobuf_converters.go @@ -1019,7 +1019,7 @@ func (c *ProtobufConverter) Transaction(tx *g.Transaction) (Transaction, error) case *g.Transaction_InvokeExpression: feeAsset, feeAmount := c.convertAmount(tx.Fee) rtx = &InvokeExpressionTransactionWithProofs{ - Type: InvokeScriptTransaction, + Type: InvokeExpressionTransaction, Version: v, ChainID: scheme, SenderPK: c.publicKey(tx.SenderPublicKey), From 2fd94b37811c7672370de7188a96fed8f39b2ffe Mon Sep 17 00:00:00 2001 From: esuwu Date: Fri, 24 Dec 2021 09:23:44 +0300 Subject: [PATCH 06/18] Added invocation set --- pkg/proto/invoke_expression_transaction.go | 27 ++-------- pkg/proto/protobuf_converters.go | 17 +++--- pkg/ride/converters.go | 63 ++++++++++++++-------- pkg/ride/environment.go | 2 +- pkg/state/script_caller.go | 11 ++-- 5 files changed, 58 insertions(+), 62 deletions(-) diff --git a/pkg/proto/invoke_expression_transaction.go b/pkg/proto/invoke_expression_transaction.go index da15a4af3a..1c72d5a443 100644 --- a/pkg/proto/invoke_expression_transaction.go +++ b/pkg/proto/invoke_expression_transaction.go @@ -102,36 +102,15 @@ func (tx *InvokeExpressionTransactionWithProofs) MerkleBytes(scheme Scheme) ([]b } func (tx *InvokeExpressionTransactionWithProofs) MarshalBinary() ([]byte, error) { - return nil, nil + return nil, errors.New("MarshalBinary is not implemented") } func (tx *InvokeExpressionTransactionWithProofs) UnmarshalBinary([]byte, Scheme) error { - return nil + return errors.New("UnmarshalBinary is not implemented") } func (tx *InvokeExpressionTransactionWithProofs) BodyMarshalBinary() ([]byte, error) { - //p := 0 - //buf := make([]byte, 1+1+1+crypto.PublicKeySize+tx.FeeAsset.BinarySize()+len(tx.Expression)) - //buf[p] = byte(tx.Type) - //p++ - //buf[p] = tx.Version - //p++ - //buf[p] = tx.ChainID - //p++ - //copy(buf[p:], tx.SenderPK[:]) - //p += crypto.PublicKeySize - //binary.BigEndian.PutUint64(buf[p:], tx.Fee) - //p += 8 - //fab, err := tx.FeeAsset.MarshalBinary() - //if err != nil { - // return nil, err - //} - //copy(buf[p:], fab) - //p += len(fab) - //binary.BigEndian.PutUint64(buf[p:], tx.Timestamp) - //copy(buf[p:], tx.Expression) - //return buf, nil - return nil, nil + return nil, errors.New("BodyMarshalBinary is not implemented") } // TODO check on correctness diff --git a/pkg/proto/protobuf_converters.go b/pkg/proto/protobuf_converters.go index 3320f9b31b..058ccdd755 100644 --- a/pkg/proto/protobuf_converters.go +++ b/pkg/proto/protobuf_converters.go @@ -1019,15 +1019,14 @@ func (c *ProtobufConverter) Transaction(tx *g.Transaction) (Transaction, error) case *g.Transaction_InvokeExpression: feeAsset, feeAmount := c.convertAmount(tx.Fee) rtx = &InvokeExpressionTransactionWithProofs{ - Type: InvokeExpressionTransaction, - Version: v, - ChainID: scheme, - SenderPK: c.publicKey(tx.SenderPublicKey), - FeeAsset: feeAsset, - Fee: feeAmount, - Timestamp: ts, - //TODO - Expression: []byte{}, + Type: InvokeExpressionTransaction, + Version: v, + ChainID: scheme, + SenderPK: c.publicKey(tx.SenderPublicKey), + FeeAsset: feeAsset, + Fee: feeAmount, + Timestamp: ts, + Expression: d.InvokeExpression.Expression, } case *g.Transaction_InvokeScript: diff --git a/pkg/ride/converters.go b/pkg/ride/converters.go index 841fc01054..ca23491640 100644 --- a/pkg/ride/converters.go +++ b/pkg/ride/converters.go @@ -1142,36 +1142,57 @@ func convertArgument(arg proto.Argument) (rideType, error) { } } -func invocationToObject(v int, scheme byte, tx *proto.InvokeScriptWithProofs) (rideObject, error) { - sender, err := proto.NewAddressFromPublicKey(scheme, tx.SenderPK) +func invocationToObject(v int, scheme byte, tx proto.Transaction) (rideObject, error) { + var ( + senderPK crypto.PublicKey + ID crypto.Digest + FeeAsset proto.OptionalAsset + Fee uint64 + ) + r := make(rideObject) + r[instanceFieldName] = rideString("Invocation") + + switch transaction := tx.(type) { + case *proto.InvokeScriptWithProofs: + senderPK = transaction.SenderPK + ID = *transaction.ID + FeeAsset = transaction.FeeAsset + Fee = transaction.Fee + switch v { + case 4, 5: + payments := make(rideList, len(transaction.Payments)) + for i, p := range transaction.Payments { + payments[i] = attachedPaymentToObject(p) + } + r["payments"] = payments + default: + r["payment"] = rideUnit{} + if len(transaction.Payments) > 0 { + r["payment"] = attachedPaymentToObject(transaction.Payments[0]) + } + } + case *proto.InvokeExpressionTransactionWithProofs: + senderPK = transaction.SenderPK + ID = *transaction.ID + FeeAsset = transaction.FeeAsset + Fee = transaction.Fee + r["payments"] = nil + } + sender, err := proto.NewAddressFromPublicKey(scheme, senderPK) if err != nil { return nil, err } - r := make(rideObject) - r[instanceFieldName] = rideString("Invocation") - r["transactionId"] = rideBytes(tx.ID.Bytes()) + r["transactionId"] = rideBytes(ID.Bytes()) r["caller"] = rideAddress(sender) - callerPK := rideBytes(common.Dup(tx.SenderPK.Bytes())) + callerPK := rideBytes(common.Dup(senderPK.Bytes())) r["callerPublicKey"] = callerPK if v >= 5 { r["originCaller"] = rideAddress(sender) r["originCallerPublicKey"] = callerPK } - switch v { - case 4, 5: - payments := make(rideList, len(tx.Payments)) - for i, p := range tx.Payments { - payments[i] = attachedPaymentToObject(p) - } - r["payments"] = payments - default: - r["payment"] = rideUnit{} - if len(tx.Payments) > 0 { - r["payment"] = attachedPaymentToObject(tx.Payments[0]) - } - } - r["feeAssetId"] = optionalAsset(tx.FeeAsset) - r["fee"] = rideInt(tx.Fee) + + r["feeAssetId"] = optionalAsset(FeeAsset) + r["fee"] = rideInt(Fee) return r, nil } diff --git a/pkg/ride/environment.go b/pkg/ride/environment.go index a64b486fda..9e6c0825a1 100644 --- a/pkg/ride/environment.go +++ b/pkg/ride/environment.go @@ -1340,7 +1340,7 @@ func (e *EvaluationEnvironment) SetTransactionFromOrder(order proto.Order) error return nil } -func (e *EvaluationEnvironment) SetInvoke(tx *proto.InvokeScriptWithProofs, v int) error { +func (e *EvaluationEnvironment) SetInvoke(tx proto.Transaction, v int) error { obj, err := invocationToObject(v, e.sch, tx) if err != nil { return err diff --git a/pkg/state/script_caller.go b/pkg/state/script_caller.go index 3caa73a91e..1318b26138 100644 --- a/pkg/state/script_caller.go +++ b/pkg/state/script_caller.go @@ -226,10 +226,9 @@ func (a *scriptCaller) invokeFunction(tree *ride.Tree, tx proto.Transaction, inf sender proto.WavesAddress r ride.Result ) - + err = env.SetInvoke(tx, tree.LibVersion) switch transaction := tx.(type) { case *proto.InvokeScriptWithProofs: - err = env.SetInvoke(transaction, tree.LibVersion) if err != nil { return nil, err } @@ -262,11 +261,9 @@ func (a *scriptCaller) invokeFunction(tree *ride.Tree, tx proto.Transaction, inf return nil, err } case *proto.InvokeExpressionTransactionWithProofs: - // TODO set invoke - //err = env.SetInvoke(transaction, tree.LibVersion) - //if err != nil { - // return nil, err - //} + if err != nil { + return nil, err + } sender, err = proto.NewAddressFromPublicKey(a.settings.AddressSchemeCharacter, transaction.SenderPK) if err != nil { return nil, err From 5efbde15513d74c91dae95e5f4f4656587779a02 Mon Sep 17 00:00:00 2001 From: esuwu Date: Fri, 24 Dec 2021 12:11:13 +0300 Subject: [PATCH 07/18] Added verify function for tx --- pkg/proto/invoke_expression_transaction.go | 8 ++++++++ pkg/state/verifier.go | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/pkg/proto/invoke_expression_transaction.go b/pkg/proto/invoke_expression_transaction.go index 1c72d5a443..093952770c 100644 --- a/pkg/proto/invoke_expression_transaction.go +++ b/pkg/proto/invoke_expression_transaction.go @@ -19,6 +19,14 @@ type InvokeExpressionTransactionWithProofs struct { Expression []byte `json:"expression,omitempty"` } +func (tx *InvokeExpressionTransactionWithProofs) Verify(scheme Scheme, publicKey crypto.PublicKey) (bool, error) { + b, err := MarshalTxBody(scheme, tx) + if err != nil { + return false, errors.Wrap(err, "failed to verify signature of InvokeScriptWithProofs transaction") + } + return tx.Proofs.Verify(0, publicKey, b) +} + func (tx InvokeExpressionTransactionWithProofs) GetTypeInfo() TransactionTypeInfo { return TransactionTypeInfo{tx.Type, Proof} } diff --git a/pkg/state/verifier.go b/pkg/state/verifier.go index 01e034898d..4f948ee1a6 100644 --- a/pkg/state/verifier.go +++ b/pkg/state/verifier.go @@ -162,6 +162,10 @@ func checkTx(tx proto.Transaction, checkTxSig, checkOrder1, checkOrder2 bool, sc if ok, _ := t.Verify(scheme, t.SenderPK); !ok { return errors.New("invokescript tx signature verification failed") } + case *proto.InvokeExpressionTransactionWithProofs: + if ok, _ := t.Verify(scheme, t.SenderPK); !ok { + return errors.New("InvokeExpression tx signature verification failed") + } case *proto.UpdateAssetInfoWithProofs: if ok, _ := t.Verify(scheme, t.SenderPK); !ok { return errors.New("updateassetinfo tx signature verification failed") From c2b2234751074c69662fede591671d10ad82acff Mon Sep 17 00:00:00 2001 From: esuwu Date: Sun, 26 Dec 2021 15:54:51 +0300 Subject: [PATCH 08/18] Added base64 type --- pkg/proto/invoke_expression_transaction.go | 3 +- pkg/proto/types.go | 36 ++++++++++++++++++++++ pkg/util/common/util.go | 11 +++++++ 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/pkg/proto/invoke_expression_transaction.go b/pkg/proto/invoke_expression_transaction.go index 093952770c..3ffce53655 100644 --- a/pkg/proto/invoke_expression_transaction.go +++ b/pkg/proto/invoke_expression_transaction.go @@ -16,7 +16,7 @@ type InvokeExpressionTransactionWithProofs struct { FeeAsset OptionalAsset `json:"feeAssetId"` Timestamp uint64 `json:"timestamp,omitempty"` Proofs *ProofsV1 `json:"proofs,omitempty"` - Expression []byte `json:"expression,omitempty"` + Expression B64Bytes `json:"expression,omitempty"` } func (tx *InvokeExpressionTransactionWithProofs) Verify(scheme Scheme, publicKey crypto.PublicKey) (bool, error) { @@ -160,7 +160,6 @@ func (tx *InvokeExpressionTransactionWithProofs) UnmarshalSignedFromProtobuf(dat return nil } func (tx *InvokeExpressionTransactionWithProofs) ToProtobuf(scheme Scheme) (*g.Transaction, error) { - txData := &g.Transaction_InvokeExpression{InvokeExpression: &g.InvokeExpressionTransactionData{ Expression: tx.Expression, }} diff --git a/pkg/proto/types.go b/pkg/proto/types.go index 24273bdba1..19341c9136 100644 --- a/pkg/proto/types.go +++ b/pkg/proto/types.go @@ -124,6 +124,42 @@ func (b B58Bytes) Bytes() []byte { return b } +type B64Bytes []byte + +func (b B64Bytes) String() string { + return base64.StdEncoding.EncodeToString(b) +} + +func (b B64Bytes) MarshalJSON() ([]byte, error) { + return common.ToBase64JSON(b), nil +} + +func (b *B64Bytes) UnmarshalJSON(value []byte) error { + str := string(value) + if str == jsonNull { + return nil + } + s, err := strconv.Unquote(str) + if err != nil { + *b = nil + return errors.Wrap(err, "failed to unmarshal B64Bytes from JSON") + } + if s == "" { + *b = []byte{} + return nil + } + v, err := base64.StdEncoding.DecodeString(s) + if err != nil { + return errors.Wrap(err, "failed to decode B64Bytes") + } + *b = v + return nil +} + +func (b B64Bytes) Bytes() []byte { + return b +} + type HexBytes []byte // String represents underlying bytes as Hex string with 0x prefix diff --git a/pkg/util/common/util.go b/pkg/util/common/util.go index 5ae7de8575..7123ac175e 100644 --- a/pkg/util/common/util.go +++ b/pkg/util/common/util.go @@ -2,6 +2,7 @@ package common import ( "bytes" + "encoding/base64" "encoding/hex" "fmt" "os" @@ -131,6 +132,16 @@ func ParseDuration(str string) (uint64, error) { return total, nil } +func ToBase64JSON(b []byte) []byte { + s := base64.StdEncoding.EncodeToString(b) + var sb bytes.Buffer + sb.Grow(2 + len(s)) + sb.WriteRune('"') + sb.WriteString(s) + sb.WriteRune('"') + return sb.Bytes() +} + func ToBase58JSON(b []byte) []byte { s := base58.Encode(b) var sb bytes.Buffer From 25d9c0ccdaf7f8f7470a856a5665aa23b0e2259d Mon Sep 17 00:00:00 2001 From: esuwu Date: Mon, 27 Dec 2021 23:14:38 +0300 Subject: [PATCH 09/18] Added fee validation and protobuf version for tx --- pkg/proto/invoke_expression_transaction.go | 10 +++--- pkg/proto/protobuf_converters.go | 5 ++- pkg/proto/transactions.go | 35 +++++++++++---------- pkg/proto/types.go | 36 ---------------------- pkg/ride/converters.go | 31 +++++++++++++++++++ pkg/settings/features.go | 2 +- pkg/state/appender.go | 6 ++++ pkg/state/block_differ.go | 5 +++ pkg/state/fee_validation.go | 35 +++++++++++---------- pkg/state/invoke_applier.go | 29 +++++++++-------- pkg/state/state.go | 2 +- pkg/state/transaction_differ.go | 26 ++++++++++++++++ pkg/util/common/util.go | 11 ------- 13 files changed, 131 insertions(+), 102 deletions(-) diff --git a/pkg/proto/invoke_expression_transaction.go b/pkg/proto/invoke_expression_transaction.go index 3ffce53655..59d9f47afb 100644 --- a/pkg/proto/invoke_expression_transaction.go +++ b/pkg/proto/invoke_expression_transaction.go @@ -16,7 +16,7 @@ type InvokeExpressionTransactionWithProofs struct { FeeAsset OptionalAsset `json:"feeAssetId"` Timestamp uint64 `json:"timestamp,omitempty"` Proofs *ProofsV1 `json:"proofs,omitempty"` - Expression B64Bytes `json:"expression,omitempty"` + Expression string `json:"expression,omitempty"` } func (tx *InvokeExpressionTransactionWithProofs) Verify(scheme Scheme, publicKey crypto.PublicKey) (bool, error) { @@ -110,15 +110,15 @@ func (tx *InvokeExpressionTransactionWithProofs) MerkleBytes(scheme Scheme) ([]b } func (tx *InvokeExpressionTransactionWithProofs) MarshalBinary() ([]byte, error) { - return nil, errors.New("MarshalBinary is not implemented") + panic("MarshalBinary is not implemented") } func (tx *InvokeExpressionTransactionWithProofs) UnmarshalBinary([]byte, Scheme) error { - return errors.New("UnmarshalBinary is not implemented") + panic("UnmarshalBinary is not implemented") } func (tx *InvokeExpressionTransactionWithProofs) BodyMarshalBinary() ([]byte, error) { - return nil, errors.New("BodyMarshalBinary is not implemented") + panic("BodyMarshalBinary is not implemented") } // TODO check on correctness @@ -161,7 +161,7 @@ func (tx *InvokeExpressionTransactionWithProofs) UnmarshalSignedFromProtobuf(dat } func (tx *InvokeExpressionTransactionWithProofs) ToProtobuf(scheme Scheme) (*g.Transaction, error) { txData := &g.Transaction_InvokeExpression{InvokeExpression: &g.InvokeExpressionTransactionData{ - Expression: tx.Expression, + Expression: []byte(tx.Expression), }} fee := &g.Amount{AssetId: tx.FeeAsset.ToID(), Amount: int64(tx.Fee)} res := TransactionToProtobufCommon(scheme, tx.SenderPK.Bytes(), tx) diff --git a/pkg/proto/protobuf_converters.go b/pkg/proto/protobuf_converters.go index 058ccdd755..5580a9bcb0 100644 --- a/pkg/proto/protobuf_converters.go +++ b/pkg/proto/protobuf_converters.go @@ -1026,7 +1026,7 @@ func (c *ProtobufConverter) Transaction(tx *g.Transaction) (Transaction, error) FeeAsset: feeAsset, Fee: feeAmount, Timestamp: ts, - Expression: d.InvokeExpression.Expression, + Expression: string(d.InvokeExpression.Expression), } case *g.Transaction_InvokeScript: @@ -1207,6 +1207,9 @@ func (c *ProtobufConverter) signedTransaction(stx *g.SignedTransaction) (Transac case *InvokeScriptWithProofs: t.Proofs = proofs return t, nil + case *InvokeExpressionTransactionWithProofs: + t.Proofs = proofs + return t, nil case *UpdateAssetInfoWithProofs: t.Proofs = proofs return t, nil diff --git a/pkg/proto/transactions.go b/pkg/proto/transactions.go index 557be4b900..8371e906fb 100644 --- a/pkg/proto/transactions.go +++ b/pkg/proto/transactions.go @@ -126,23 +126,24 @@ var ( } ProtobufTransactionsVersions = map[TransactionType]byte{ - GenesisTransaction: 2, - PaymentTransaction: 2, - TransferTransaction: 3, - IssueTransaction: 3, - ReissueTransaction: 3, - BurnTransaction: 3, - ExchangeTransaction: 3, - LeaseTransaction: 3, - LeaseCancelTransaction: 3, - CreateAliasTransaction: 3, - MassTransferTransaction: 2, - DataTransaction: 2, - SetScriptTransaction: 2, - SponsorshipTransaction: 2, - SetAssetScriptTransaction: 2, - InvokeScriptTransaction: 2, - UpdateAssetInfoTransaction: 1, + GenesisTransaction: 2, + PaymentTransaction: 2, + TransferTransaction: 3, + IssueTransaction: 3, + ReissueTransaction: 3, + BurnTransaction: 3, + ExchangeTransaction: 3, + LeaseTransaction: 3, + LeaseCancelTransaction: 3, + CreateAliasTransaction: 3, + MassTransferTransaction: 2, + DataTransaction: 2, + SetScriptTransaction: 2, + SponsorshipTransaction: 2, + SetAssetScriptTransaction: 2, + InvokeScriptTransaction: 2, + InvokeExpressionTransaction: 1, + UpdateAssetInfoTransaction: 1, // EthereumMetamaskTransaction should not be added because it doesn't exist as protobuf transaction } ) diff --git a/pkg/proto/types.go b/pkg/proto/types.go index 19341c9136..24273bdba1 100644 --- a/pkg/proto/types.go +++ b/pkg/proto/types.go @@ -124,42 +124,6 @@ func (b B58Bytes) Bytes() []byte { return b } -type B64Bytes []byte - -func (b B64Bytes) String() string { - return base64.StdEncoding.EncodeToString(b) -} - -func (b B64Bytes) MarshalJSON() ([]byte, error) { - return common.ToBase64JSON(b), nil -} - -func (b *B64Bytes) UnmarshalJSON(value []byte) error { - str := string(value) - if str == jsonNull { - return nil - } - s, err := strconv.Unquote(str) - if err != nil { - *b = nil - return errors.Wrap(err, "failed to unmarshal B64Bytes from JSON") - } - if s == "" { - *b = []byte{} - return nil - } - v, err := base64.StdEncoding.DecodeString(s) - if err != nil { - return errors.Wrap(err, "failed to decode B64Bytes") - } - *b = v - return nil -} - -func (b B64Bytes) Bytes() []byte { - return b -} - type HexBytes []byte // String represents underlying bytes as Hex string with 0x prefix diff --git a/pkg/ride/converters.go b/pkg/ride/converters.go index ca23491640..c65cf99096 100644 --- a/pkg/ride/converters.go +++ b/pkg/ride/converters.go @@ -64,6 +64,8 @@ func transactionToObject(scheme byte, tx proto.Transaction) (rideObject, error) return updateAssetInfoWithProofsToObject(scheme, transaction) case *proto.EthereumTransaction: return ethereumTransactionToObject(scheme, transaction) + case *proto.InvokeExpressionTransactionWithProofs: + return invokeExpressionWithProofsToObject(scheme, transaction) default: return nil, EvaluationFailure.Errorf("conversion to RIDE object is not implemented for %T", transaction) } @@ -884,6 +886,35 @@ func invokeScriptWithProofsToObject(scheme byte, tx *proto.InvokeScriptWithProof return r, nil } +// TODO think of reusing "InvokeScripTToObject" function. Also should we fill "payments" and "function name" fields"? +func invokeExpressionWithProofsToObject(scheme byte, tx *proto.InvokeExpressionTransactionWithProofs) (rideObject, error) { + sender, err := proto.NewAddressFromPublicKey(scheme, tx.SenderPK) + if err != nil { + return nil, EvaluationFailure.Wrap(err, "invokeScriptWithProofsToObject") + } + body, err := proto.MarshalTxBody(scheme, tx) + if err != nil { + return nil, EvaluationFailure.Wrap(err, "invokeScriptWithProofsToObject") + } + r := make(rideObject) + r[instanceFieldName] = rideString("InvokeExpressionTransaction") + r["version"] = rideInt(tx.Version) + r["id"] = rideBytes(tx.ID.Bytes()) + r["sender"] = rideAddress(sender) + r["senderPublicKey"] = rideBytes(common.Dup(tx.SenderPK.Bytes())) + r["dApp"] = rideRecipient(proto.NewRecipientFromAddress(sender)) + r["payment"] = rideUnit{} + r["payments"] = make(rideList, 0) + r["feeAssetId"] = optionalAsset(tx.FeeAsset) + r["function"] = rideString("default") + r["args"] = rideList{} + r["fee"] = rideInt(tx.Fee) + r["timestamp"] = rideInt(tx.Timestamp) + r["bodyBytes"] = rideBytes(body) + r["proofs"] = proofs(tx.Proofs) + return r, nil +} + func ConvertEthereumRideArgumentsToSpecificArgument(decodedArg rideType) (proto.Argument, error) { var arg proto.Argument switch m := decodedArg.(type) { diff --git a/pkg/settings/features.go b/pkg/settings/features.go index c675c0279d..97b1082742 100644 --- a/pkg/settings/features.go +++ b/pkg/settings/features.go @@ -44,5 +44,5 @@ var FeaturesInfo = map[Feature]FeatureInfo{ BlockReward: {true, "Block Reward and Community Driven Monetary Policy"}, BlockV5: {true, "Ride V4, VRF, Protobuf, Failed transactions"}, RideV5: {true, "Ride V5, dApp-to-dApp invocations"}, - RideV6: {false, "Ride V6, MetaMask support, Invoke Expression"}, + RideV6: {true, "Ride V6, MetaMask support, Invoke Expression"}, } diff --git a/pkg/state/appender.go b/pkg/state/appender.go index 3ed7965407..ebf0199c14 100644 --- a/pkg/state/appender.go +++ b/pkg/state/appender.go @@ -207,7 +207,13 @@ func (a *txAppender) checkTxFees(tx proto.Transaction, info *fallibleValidationP if err != nil { return err } + case proto.InvokeExpressionTransaction: + feeChanges, err = a.txHandler.td.createFeeDiffInvokeExpressionWithProofs(tx, differInfo) + if err != nil { + return err + } } + return a.diffApplier.validateTxDiff(feeChanges.diff, a.diffStor, !info.initialisation) } diff --git a/pkg/state/block_differ.go b/pkg/state/block_differ.go index 8b6f263cac..1c01ce2f4c 100644 --- a/pkg/state/block_differ.go +++ b/pkg/state/block_differ.go @@ -129,6 +129,11 @@ func (d *blockDiffer) createFailedTransactionDiff(tx proto.Transaction, block *p if err != nil { return txBalanceChanges{}, err } + case proto.InvokeExpressionTransaction: + txChanges, err = d.handler.td.createFeeDiffInvokeExpressionWithProofs(tx, differInfo) + if err != nil { + return txBalanceChanges{}, err + } default: return txBalanceChanges{}, errors.New("only Exchange and Invoke transactions may fail") } diff --git a/pkg/state/fee_validation.go b/pkg/state/fee_validation.go index ea67cabf26..7ad840b043 100644 --- a/pkg/state/fee_validation.go +++ b/pkg/state/fee_validation.go @@ -16,23 +16,24 @@ const ( ) var feeConstants = map[proto.TransactionType]uint64{ - proto.GenesisTransaction: 0, - proto.PaymentTransaction: 1, - proto.TransferTransaction: 1, - proto.IssueTransaction: 1000, - proto.ReissueTransaction: 1000, - proto.BurnTransaction: 1, - proto.ExchangeTransaction: 3, - proto.MassTransferTransaction: 1, - proto.LeaseTransaction: 1, - proto.LeaseCancelTransaction: 1, - proto.CreateAliasTransaction: 1, - proto.DataTransaction: 1, - proto.SetScriptTransaction: 10, - proto.SponsorshipTransaction: 1000, - proto.SetAssetScriptTransaction: 1000 - 4, - proto.InvokeScriptTransaction: 5, - proto.UpdateAssetInfoTransaction: 1, + proto.GenesisTransaction: 0, + proto.PaymentTransaction: 1, + proto.TransferTransaction: 1, + proto.IssueTransaction: 1000, + proto.ReissueTransaction: 1000, + proto.BurnTransaction: 1, + proto.ExchangeTransaction: 3, + proto.MassTransferTransaction: 1, + proto.LeaseTransaction: 1, + proto.LeaseCancelTransaction: 1, + proto.CreateAliasTransaction: 1, + proto.DataTransaction: 1, + proto.SetScriptTransaction: 10, + proto.SponsorshipTransaction: 1000, + proto.SetAssetScriptTransaction: 1000 - 4, + proto.InvokeScriptTransaction: 5, + proto.UpdateAssetInfoTransaction: 1, + proto.InvokeExpressionTransaction: 5, } type feeValidationParams struct { diff --git a/pkg/state/invoke_applier.go b/pkg/state/invoke_applier.go index b8cd5cf17e..f459e4d692 100644 --- a/pkg/state/invoke_applier.go +++ b/pkg/state/invoke_applier.go @@ -1,7 +1,6 @@ package state import ( - "encoding/base64" "fmt" "math" "math/big" @@ -729,12 +728,13 @@ func (ia *invokeApplier) applyInvokeScript(tx proto.Transaction, info *fallibleV }() var ( - paymentsLength int - scriptAddr *proto.WavesAddress - txID crypto.Digest - sender proto.Address - tree *ride.Tree - scriptPK crypto.PublicKey + paymentsLength int + scriptAddr *proto.WavesAddress + txID crypto.Digest + sender proto.Address + tree *ride.Tree + scriptPK crypto.PublicKey + isInvokeExpression bool ) switch transaction := tx.(type) { case *proto.InvokeScriptWithProofs: @@ -759,16 +759,19 @@ func (ia *invokeApplier) applyInvokeScript(tx proto.Transaction, info *fallibleV return nil, errors.Wrapf(err, "failed to get script's public key on address '%s'", scriptAddr.String()) } case *proto.InvokeExpressionTransactionWithProofs: - src, err := base64.StdEncoding.DecodeString(string(transaction.Expression)) + addr, err := proto.NewAddressFromPublicKey(ia.settings.AddressSchemeCharacter, transaction.SenderPK) if err != nil { - return nil, errors.Wrap(err, "failed to decode invoke expression") + return nil, errors.Wrap(err, "recipientToAddress() failed") } - - tree, err = ride.Parse(src) + sender = addr + scriptAddr = &addr + tree, err = ride.Parse([]byte(transaction.Expression)) if err != nil { return nil, errors.Wrap(err, "failed to parse decoded invoke expression into tree") } - + isInvokeExpression = true + txID = *transaction.ID + scriptPK = transaction.SenderPK case *proto.EthereumTransaction: var err error scriptAddr, err = transaction.WavesAddressTo(ia.settings.AddressSchemeCharacter) @@ -817,7 +820,7 @@ func (ia *invokeApplier) applyInvokeScript(tx proto.Transaction, info *fallibleV // Refuse payments to DApp itself since activation of BlockV5 (acceptFailed) and for DApps with StdLib V4. disableSelfTransfers := info.acceptFailed && tree.LibVersion >= 4 if disableSelfTransfers && paymentsLength > 0 { - if sender == *scriptAddr { + if sender == *scriptAddr && !isInvokeExpression { return nil, errors.New("paying to DApp itself is forbidden since RIDE V4") } } diff --git a/pkg/state/state.go b/pkg/state/state.go index b6ed1ed9b1..5ad407a39b 100644 --- a/pkg/state/state.go +++ b/pkg/state/state.go @@ -984,7 +984,7 @@ func (s *stateManager) addNewBlock(block, parent *proto.Block, initialisation bo height: height, initialisation: initialisation, } - // Check and perform block's transactions, create balance diffs, write transactions to storage. + // Check and perform block's transactions, create balance diffs, write transactions to storage.\ if err := s.appender.appendBlock(params); err != nil { return err } diff --git a/pkg/state/transaction_differ.go b/pkg/state/transaction_differ.go index e0515ba239..1b7236a569 100644 --- a/pkg/state/transaction_differ.go +++ b/pkg/state/transaction_differ.go @@ -1511,6 +1511,32 @@ func (td *transactionDiffer) createDiffEthereumInvokeScript(tx *proto.EthereumTr return changes, nil } +// TODO make one function for 3 tx types +func (td *transactionDiffer) createFeeDiffInvokeExpressionWithProofs(transaction proto.Transaction, info *differInfo) (txBalanceChanges, error) { + tx, ok := transaction.(*proto.InvokeExpressionTransactionWithProofs) + if !ok { + return txBalanceChanges{}, errors.New("failed to convert interface to InvokeScriptWithProofs transaction") + } + diff := newTxDiff() + // Append sender diff. + senderAddr, err := proto.NewAddressFromPublicKey(td.settings.AddressSchemeCharacter, tx.SenderPK) + if err != nil { + return txBalanceChanges{}, err + } + senderFeeKey := byteKey(senderAddr.ID(), tx.FeeAsset) + senderFeeBalanceDiff := -int64(tx.Fee) + if err := diff.appendBalanceDiff(senderFeeKey, newBalanceDiff(senderFeeBalanceDiff, 0, 0, true)); err != nil { + return txBalanceChanges{}, err + } + + addresses := []proto.WavesAddress{senderAddr} + changes := newTxBalanceChanges(addresses, diff) + if err := td.handleSponsorship(&changes, tx.Fee, tx.FeeAsset, info); err != nil { + return txBalanceChanges{}, err + } + return changes, nil +} + func (td *transactionDiffer) createFeeDiffInvokeScriptWithProofs(transaction proto.Transaction, info *differInfo) (txBalanceChanges, error) { tx, ok := transaction.(*proto.InvokeScriptWithProofs) if !ok { diff --git a/pkg/util/common/util.go b/pkg/util/common/util.go index 7123ac175e..5ae7de8575 100644 --- a/pkg/util/common/util.go +++ b/pkg/util/common/util.go @@ -2,7 +2,6 @@ package common import ( "bytes" - "encoding/base64" "encoding/hex" "fmt" "os" @@ -132,16 +131,6 @@ func ParseDuration(str string) (uint64, error) { return total, nil } -func ToBase64JSON(b []byte) []byte { - s := base64.StdEncoding.EncodeToString(b) - var sb bytes.Buffer - sb.Grow(2 + len(s)) - sb.WriteRune('"') - sb.WriteString(s) - sb.WriteRune('"') - return sb.Bytes() -} - func ToBase58JSON(b []byte) []byte { s := base58.Encode(b) var sb bytes.Buffer From fc221468b60f4be5e088ecd60f9a27bd221a9ecd Mon Sep 17 00:00:00 2001 From: esuwu Date: Wed, 29 Dec 2021 13:32:08 +0300 Subject: [PATCH 10/18] Added sponsorship handling --- pkg/state/transaction_differ.go | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/pkg/state/transaction_differ.go b/pkg/state/transaction_differ.go index 1b7236a569..ebf0de5936 100644 --- a/pkg/state/transaction_differ.go +++ b/pkg/state/transaction_differ.go @@ -506,7 +506,9 @@ func (td *transactionDiffer) createDiffEthereumTransferWaves(tx *proto.EthereumT } addrs := []proto.WavesAddress{senderAddress, recipientAddress} changes := newTxBalanceChanges(addrs, diff) - // sponsorship might be handled here + if err := td.handleSponsorship(&changes, tx.GetFee(), proto.NewOptionalAssetWaves(), info); err != nil { + return txBalanceChanges{}, err + } return changes, nil } @@ -571,7 +573,9 @@ func (td *transactionDiffer) createDiffEthereumErc20(tx *proto.EthereumTransacti } addrs := []proto.WavesAddress{senderAddress, etc20TransferRecipient} changes := newTxBalanceChanges(addrs, diff) - // sponsorship might be handled here + if err := td.handleSponsorship(&changes, tx.GetFee(), proto.NewOptionalAssetWaves(), info); err != nil { + return txBalanceChanges{}, err + } return changes, nil } @@ -1453,7 +1457,6 @@ func (td *transactionDiffer) createDiffInvokeExpressionWithProofs(transaction pr if err := td.handleSponsorship(&changes, tx.Fee, tx.FeeAsset, info); err != nil { return txBalanceChanges{}, err } - return changes, nil } @@ -1508,6 +1511,9 @@ func (td *transactionDiffer) createDiffEthereumInvokeScript(tx *proto.EthereumTr return txBalanceChanges{}, err } } + if err := td.handleSponsorship(&changes, tx.GetFee(), proto.NewOptionalAssetWaves(), info); err != nil { + return txBalanceChanges{}, err + } return changes, nil } @@ -1594,7 +1600,9 @@ func (td *transactionDiffer) createFeeDiffEthereumInvokeScriptWithProofs(transac addresses := []proto.WavesAddress{senderAddress, scriptAddress} changes := newTxBalanceChanges(addresses, diff) - // sponsorship might be handled here + if err := td.handleSponsorship(&changes, tx.GetFee(), proto.NewOptionalAssetWaves(), info); err != nil { + return txBalanceChanges{}, err + } return changes, nil } From b13594c3ae6fbf66adb2ba3998521f898c884ede Mon Sep 17 00:00:00 2001 From: esuwu Date: Wed, 29 Dec 2021 14:28:37 +0300 Subject: [PATCH 11/18] Moved invocation set --- pkg/state/script_caller.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/state/script_caller.go b/pkg/state/script_caller.go index 1318b26138..c560edbe77 100644 --- a/pkg/state/script_caller.go +++ b/pkg/state/script_caller.go @@ -226,9 +226,9 @@ func (a *scriptCaller) invokeFunction(tree *ride.Tree, tx proto.Transaction, inf sender proto.WavesAddress r ride.Result ) - err = env.SetInvoke(tx, tree.LibVersion) switch transaction := tx.(type) { case *proto.InvokeScriptWithProofs: + err = env.SetInvoke(tx, tree.LibVersion) if err != nil { return nil, err } @@ -261,6 +261,7 @@ func (a *scriptCaller) invokeFunction(tree *ride.Tree, tx proto.Transaction, inf return nil, err } case *proto.InvokeExpressionTransactionWithProofs: + err = env.SetInvoke(tx, tree.LibVersion) if err != nil { return nil, err } From cb182545d3ad6f9e8a5c2e393bd403341e833506 Mon Sep 17 00:00:00 2001 From: esuwu Date: Fri, 14 Jan 2022 16:38:32 +0300 Subject: [PATCH 12/18] Moved invocation set --- pkg/proto/eth_signer.go | 10 ++++++++++ pkg/state/ethereum_tx_test.go | 23 ++++++++++++----------- pkg/state/script_caller.go | 4 +++- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/pkg/proto/eth_signer.go b/pkg/proto/eth_signer.go index 46de87a4ea..beff5a5d42 100644 --- a/pkg/proto/eth_signer.go +++ b/pkg/proto/eth_signer.go @@ -2,6 +2,7 @@ package proto import ( "crypto/ecdsa" + "github.com/mr-tron/base58/base58" "math/big" "github.com/btcsuite/btcd/btcec" @@ -55,6 +56,15 @@ func NewEthereumPublicKeyFromHexString(s string) (EthereumPublicKey, error) { return NewEthereumPublicKeyFromBytes(b) } +// NewEthereumAddressFromBase58String creates an EthereumPublicKey from its string representation. +func NewEthereumPublicKeyFromBase58String(s string) (EthereumPublicKey, error) { + b, err := base58.Decode(s) + if err != nil { + return EthereumPublicKey{}, errors.Wrap(err, "invalid Base58 string") + } + return NewEthereumPublicKeyFromBytes(b) +} + func NewEthereumPublicKeyFromBytes(b []byte) (EthereumPublicKey, error) { var pubKey EthereumPublicKey if err := pubKey.UnmarshalBinary(b); err != nil { diff --git a/pkg/state/ethereum_tx_test.go b/pkg/state/ethereum_tx_test.go index d8cc36c219..6038f63f6b 100644 --- a/pkg/state/ethereum_tx_test.go +++ b/pkg/state/ethereum_tx_test.go @@ -66,8 +66,8 @@ func defaultTxAppender(t *testing.T, storage scriptStorageState, state types.Sma } return txAppender } -func defaultEthereumLegacyTxData(value int64, to *proto.EthereumAddress, data []byte, gas uint64) *proto.EthereumLegacyTx { - v := big.NewInt(87) // MainNet byte +func defaultEthereumLegacyTxData(value int64, to *proto.EthereumAddress, data []byte, gas uint64, scheme proto.Scheme) *proto.EthereumLegacyTx { + v := big.NewInt(int64(scheme)) // MainNet byte v.Mul(v, big.NewInt(2)) v.Add(v, big.NewInt(35)) @@ -91,7 +91,7 @@ func TestEthereumTransferWaves(t *testing.T) { assert.NoError(t, err) recipientEth := proto.BytesToEthereumAddress(recipientBytes) - txData := defaultEthereumLegacyTxData(1000000000000000, &recipientEth, nil, 100000) + txData := defaultEthereumLegacyTxData(1000000000000000, &recipientEth, nil, 100000, proto.MainNetScheme) tx := proto.NewEthereumTransaction(txData, nil, nil, &senderPK, 0) tx.TxKind, err = txAppender.ethInfo.ethereumTransactionKind(&tx, nil) @@ -152,7 +152,7 @@ func TestEthereumTransferAssets(t *testing.T) { require.NoError(t, err) //0x989393922c92e07c209f67636731bf1f04871d8b - txData := defaultEthereumLegacyTxData(1000000000000000, &recipientEth, data, 100000) + txData := defaultEthereumLegacyTxData(1000000000000000, &recipientEth, data, 100000, proto.MainNetScheme) tx := proto.NewEthereumTransaction(txData, nil, nil, &senderPK, 0) db := ethabi.NewErc20MethodsMap() @@ -262,7 +262,7 @@ func TestEthereumInvoke(t *testing.T) { } txAppender := defaultTxAppender(t, storage, state, assetsUncertain, proto.MainNetScheme) - txData := defaultEthereumLegacyTxData(1000000000000000, &recipientEth, nil, 500000) + txData := defaultEthereumLegacyTxData(1000000000000000, &recipientEth, nil, 500000, proto.MainNetScheme) decodedData := defaultDecodedData("call", []ethabi.DecodedArg{{Value: ethabi.Int(10)}}, []ethabi.Payment{{Amount: 5, AssetID: proto.NewOptionalAssetWaves().ID}}) txKind := proto.NewEthereumInvokeScriptTxKind(decodedData) tx := proto.NewEthereumTransaction(txData, txKind, &crypto.Digest{}, &senderPK, 0) @@ -282,7 +282,7 @@ func TestEthereumInvoke(t *testing.T) { assert.Equal(t, expectedDataEntryWrites[0], res.ScriptActions()[0]) // fee test - txDataForFeeCheck := defaultEthereumLegacyTxData(1000000000000000, &recipientEth, nil, 499999) + txDataForFeeCheck := defaultEthereumLegacyTxData(1000000000000000, &recipientEth, nil, 499999, proto.MainNetScheme) tx = proto.NewEthereumTransaction(txDataForFeeCheck, txKind, &crypto.Digest{}, &senderPK, 0) _, err = txAppender.ia.txHandler.checkTx(&tx, fallibleInfo.checkerInfo) @@ -298,7 +298,7 @@ func TestTransferZeroAmount(t *testing.T) { assert.NoError(t, err) recipientEth := proto.BytesToEthereumAddress(recipientBytes) - txData := defaultEthereumLegacyTxData(0, &recipientEth, nil, 100000) + txData := defaultEthereumLegacyTxData(0, &recipientEth, nil, 100000, proto.MainNetScheme) tx := proto.NewEthereumTransaction(txData, nil, nil, &senderPK, 0) tx.TxKind, err = txAppender.ethInfo.ethereumTransactionKind(&tx, nil) assert.NoError(t, err) @@ -316,7 +316,7 @@ func TestTransferMainNetTestnet(t *testing.T) { assert.NoError(t, err) recipientEth := proto.BytesToEthereumAddress(recipientBytes) - txData := defaultEthereumLegacyTxData(100, &recipientEth, nil, 100000) + txData := defaultEthereumLegacyTxData(100, &recipientEth, nil, 100000, proto.MainNetScheme) tx := proto.NewEthereumTransaction(txData, nil, nil, &senderPK, 0) tx.TxKind, err = txAppender.ethInfo.ethereumTransactionKind(&tx, nil) assert.NoError(t, err) @@ -334,7 +334,7 @@ func TestTransferCheckFee(t *testing.T) { assert.NoError(t, err) recipientEth := proto.BytesToEthereumAddress(recipientBytes) - txData := defaultEthereumLegacyTxData(100, &recipientEth, nil, 100) + txData := defaultEthereumLegacyTxData(100, &recipientEth, nil, 100, proto.MainNetScheme) tx := proto.NewEthereumTransaction(txData, nil, nil, &senderPK, 0) tx.TxKind, err = txAppender.ethInfo.ethereumTransactionKind(&tx, nil) assert.NoError(t, err) @@ -391,7 +391,7 @@ func TestEthereumInvokeWithoutPaymentsAndArguments(t *testing.T) { assert.NoError(t, err) recipientEth := proto.BytesToEthereumAddress(recipientBytes) - txData := defaultEthereumLegacyTxData(1000000000000000, &recipientEth, nil, 500000) + txData := defaultEthereumLegacyTxData(1000000000000000, &recipientEth, nil, 500000, proto.MainNetScheme) decodedData := defaultDecodedData("call", nil, nil) tx := proto.NewEthereumTransaction(txData, proto.NewEthereumInvokeScriptTxKind(decodedData), &crypto.Digest{}, &senderPK, 0) @@ -459,7 +459,7 @@ func TestEthereumInvokeAllArguments(t *testing.T) { assert.NoError(t, err) recipientEth := proto.BytesToEthereumAddress(recipientBytes) - txData := defaultEthereumLegacyTxData(1000000000000000, &recipientEth, nil, 500000) + txData := defaultEthereumLegacyTxData(1000000000000000, &recipientEth, nil, 500000, proto.MainNetScheme) decodedData := defaultDecodedData("call", []ethabi.DecodedArg{ {Value: ethabi.Int(1)}, {Value: ethabi.Bool(true)}, @@ -483,3 +483,4 @@ func TestEthereumInvokeAllArguments(t *testing.T) { } assert.Equal(t, expectedDataEntryWrites[0], res.ScriptActions()[0]) } + diff --git a/pkg/state/script_caller.go b/pkg/state/script_caller.go index 1318b26138..1393cdb12c 100644 --- a/pkg/state/script_caller.go +++ b/pkg/state/script_caller.go @@ -226,9 +226,9 @@ func (a *scriptCaller) invokeFunction(tree *ride.Tree, tx proto.Transaction, inf sender proto.WavesAddress r ride.Result ) - err = env.SetInvoke(tx, tree.LibVersion) switch transaction := tx.(type) { case *proto.InvokeScriptWithProofs: + err = env.SetInvoke(tx, tree.LibVersion) if err != nil { return nil, err } @@ -261,6 +261,7 @@ func (a *scriptCaller) invokeFunction(tree *ride.Tree, tx proto.Transaction, inf return nil, err } case *proto.InvokeExpressionTransactionWithProofs: + err = env.SetInvoke(tx, tree.LibVersion) if err != nil { return nil, err } @@ -345,6 +346,7 @@ func (a *scriptCaller) invokeFunction(tree *ride.Tree, tx proto.Transaction, inf return nil, errors.New("failed to invoke function: unexpected type of transaction ") } + if err := a.appendFunctionComplexity(r.Complexity(), scriptAddress, functionName, defaultFunction, info); err != nil { return nil, err } From 9a380cb9cb4037bd18f4b8066d73bf3b9a6197c0 Mon Sep 17 00:00:00 2001 From: Alexey Kiselev Date: Fri, 14 Jan 2022 17:36:36 +0300 Subject: [PATCH 13/18] Fix formatting issues --- pkg/state/ethereum_tx_test.go | 11 +++++------ pkg/state/script_caller.go | 1 - 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/pkg/state/ethereum_tx_test.go b/pkg/state/ethereum_tx_test.go index 6038f63f6b..2bef71ce3d 100644 --- a/pkg/state/ethereum_tx_test.go +++ b/pkg/state/ethereum_tx_test.go @@ -282,7 +282,7 @@ func TestEthereumInvoke(t *testing.T) { assert.Equal(t, expectedDataEntryWrites[0], res.ScriptActions()[0]) // fee test - txDataForFeeCheck := defaultEthereumLegacyTxData(1000000000000000, &recipientEth, nil, 499999, proto.MainNetScheme) + txDataForFeeCheck := defaultEthereumLegacyTxData(1000000000000000, &recipientEth, nil, 499999, proto.MainNetScheme) tx = proto.NewEthereumTransaction(txDataForFeeCheck, txKind, &crypto.Digest{}, &senderPK, 0) _, err = txAppender.ia.txHandler.checkTx(&tx, fallibleInfo.checkerInfo) @@ -298,7 +298,7 @@ func TestTransferZeroAmount(t *testing.T) { assert.NoError(t, err) recipientEth := proto.BytesToEthereumAddress(recipientBytes) - txData := defaultEthereumLegacyTxData(0, &recipientEth, nil, 100000, proto.MainNetScheme) + txData := defaultEthereumLegacyTxData(0, &recipientEth, nil, 100000, proto.MainNetScheme) tx := proto.NewEthereumTransaction(txData, nil, nil, &senderPK, 0) tx.TxKind, err = txAppender.ethInfo.ethereumTransactionKind(&tx, nil) assert.NoError(t, err) @@ -334,7 +334,7 @@ func TestTransferCheckFee(t *testing.T) { assert.NoError(t, err) recipientEth := proto.BytesToEthereumAddress(recipientBytes) - txData := defaultEthereumLegacyTxData(100, &recipientEth, nil, 100, proto.MainNetScheme) + txData := defaultEthereumLegacyTxData(100, &recipientEth, nil, 100, proto.MainNetScheme) tx := proto.NewEthereumTransaction(txData, nil, nil, &senderPK, 0) tx.TxKind, err = txAppender.ethInfo.ethereumTransactionKind(&tx, nil) assert.NoError(t, err) @@ -391,7 +391,7 @@ func TestEthereumInvokeWithoutPaymentsAndArguments(t *testing.T) { assert.NoError(t, err) recipientEth := proto.BytesToEthereumAddress(recipientBytes) - txData := defaultEthereumLegacyTxData(1000000000000000, &recipientEth, nil, 500000, proto.MainNetScheme) + txData := defaultEthereumLegacyTxData(1000000000000000, &recipientEth, nil, 500000, proto.MainNetScheme) decodedData := defaultDecodedData("call", nil, nil) tx := proto.NewEthereumTransaction(txData, proto.NewEthereumInvokeScriptTxKind(decodedData), &crypto.Digest{}, &senderPK, 0) @@ -459,7 +459,7 @@ func TestEthereumInvokeAllArguments(t *testing.T) { assert.NoError(t, err) recipientEth := proto.BytesToEthereumAddress(recipientBytes) - txData := defaultEthereumLegacyTxData(1000000000000000, &recipientEth, nil, 500000, proto.MainNetScheme) + txData := defaultEthereumLegacyTxData(1000000000000000, &recipientEth, nil, 500000, proto.MainNetScheme) decodedData := defaultDecodedData("call", []ethabi.DecodedArg{ {Value: ethabi.Int(1)}, {Value: ethabi.Bool(true)}, @@ -483,4 +483,3 @@ func TestEthereumInvokeAllArguments(t *testing.T) { } assert.Equal(t, expectedDataEntryWrites[0], res.ScriptActions()[0]) } - diff --git a/pkg/state/script_caller.go b/pkg/state/script_caller.go index 1393cdb12c..c560edbe77 100644 --- a/pkg/state/script_caller.go +++ b/pkg/state/script_caller.go @@ -346,7 +346,6 @@ func (a *scriptCaller) invokeFunction(tree *ride.Tree, tx proto.Transaction, inf return nil, errors.New("failed to invoke function: unexpected type of transaction ") } - if err := a.appendFunctionComplexity(r.Complexity(), scriptAddress, functionName, defaultFunction, info); err != nil { return nil, err } From 5ff93441ed252712118b30d38ab96a64a5f61bdc Mon Sep 17 00:00:00 2001 From: Alexey Kiselev Date: Wed, 19 Jan 2022 11:34:51 +0300 Subject: [PATCH 14/18] Better height logging. Better formatting. --- pkg/state/appender.go | 7 ++++--- pkg/state/invoke_applier.go | 4 +++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/pkg/state/appender.go b/pkg/state/appender.go index 2984e5edda..9cde9e68c8 100644 --- a/pkg/state/appender.go +++ b/pkg/state/appender.go @@ -2,6 +2,7 @@ package state import ( "fmt" + "github.com/wavesplatform/gowaves/pkg/crypto" "github.com/mr-tron/base58/base58" @@ -486,7 +487,7 @@ func (a *txAppender) appendTx(tx proto.Transaction, params *appendTxParams) erro applicationRes, err = a.handleDefaultTransaction(tx, params, accountHasVerifierScript) if err != nil { return errors.Errorf("failed to handle ethereum transaction (type %s) with id %s, on height %d: %v", - ethTx.TxKind.String(), ethTx.ID.String(), params.block.Height, err, + ethTx.TxKind.String(), ethTx.ID.String(), params.checkerInfo.height+1, err, ) } // In UTX balances are always validated. @@ -501,7 +502,7 @@ func (a *txAppender) appendTx(tx proto.Transaction, params *appendTxParams) erro if err != nil { return errors.Errorf( "failed to handle ethereum invoke script transaction (type %s) with id %s, on height %d: %v", - ethTx.TxKind.String(), ethTx.ID.String(), params.block.Height, err, + ethTx.TxKind.String(), ethTx.ID.String(), params.checkerInfo.height+1, err, ) } } @@ -646,7 +647,7 @@ func (a *txAppender) handleInvoke(tx proto.Transaction, info *fallibleValidation ID = *t.ID case *proto.EthereumTransaction: if _, ok := t.TxKind.(*proto.EthereumInvokeScriptTxKind); !ok { - return nil, errors.New("wrong ethereum tx kind. expected invoke kind") + return nil, errors.Errorf("unexpected ethereum tx kind '%T'", tx) } ID = *t.ID } diff --git a/pkg/state/invoke_applier.go b/pkg/state/invoke_applier.go index f459e4d692..366a9b64db 100644 --- a/pkg/state/invoke_applier.go +++ b/pkg/state/invoke_applier.go @@ -738,7 +738,6 @@ func (ia *invokeApplier) applyInvokeScript(tx proto.Transaction, info *fallibleV ) switch transaction := tx.(type) { case *proto.InvokeScriptWithProofs: - var err error scriptAddr, err = recipientToAddress(transaction.ScriptRecipient, ia.stor.aliases, !info.initialisation) if err != nil { @@ -758,6 +757,7 @@ func (ia *invokeApplier) applyInvokeScript(tx proto.Transaction, info *fallibleV if err != nil { return nil, errors.Wrapf(err, "failed to get script's public key on address '%s'", scriptAddr.String()) } + case *proto.InvokeExpressionTransactionWithProofs: addr, err := proto.NewAddressFromPublicKey(ia.settings.AddressSchemeCharacter, transaction.SenderPK) if err != nil { @@ -772,6 +772,7 @@ func (ia *invokeApplier) applyInvokeScript(tx proto.Transaction, info *fallibleV isInvokeExpression = true txID = *transaction.ID scriptPK = transaction.SenderPK + case *proto.EthereumTransaction: var err error scriptAddr, err = transaction.WavesAddressTo(ia.settings.AddressSchemeCharacter) @@ -793,6 +794,7 @@ func (ia *invokeApplier) applyInvokeScript(tx proto.Transaction, info *fallibleV if err != nil { return nil, errors.Wrapf(err, "failed to get script's public key on address '%s'", scriptAddr.String()) } + default: return nil, errors.New("failed to apply an invoke script: unexpected type of transaction ") } From 07d5ad2a69b3218395e25d24526f9e711bd71509 Mon Sep 17 00:00:00 2001 From: esuwu Date: Tue, 25 Jan 2022 01:20:50 +0300 Subject: [PATCH 15/18] Added ethereum invoke expression kind --- pkg/metamask/service.go | 6 +- pkg/proto/eth_transaction.go | 16 ++++ pkg/state/appender.go | 10 +-- pkg/state/eth_info.go | 13 +++- pkg/state/invoke_applier.go | 59 ++++++++++----- pkg/state/script_caller.go | 115 +++++++++++++++++++---------- pkg/state/transaction_checker.go | 9 +++ pkg/state/transaction_performer.go | 12 ++- 8 files changed, 161 insertions(+), 79 deletions(-) diff --git a/pkg/metamask/service.go b/pkg/metamask/service.go index 53bdb8367c..b7c0ad9205 100644 --- a/pkg/metamask/service.go +++ b/pkg/metamask/service.go @@ -143,10 +143,6 @@ func (s RPCService) Eth_EstimateGas(req estimateGasRequest) (string, error) { value = new(big.Int) data []byte ) - if req.To == nil { - zap.S().Debug("Eth_EstimateGas: trying estimate gas for set dApp transaction") - return "", errors.New("gas estimation for set dApp transaction is not permitted") - } if req.Value != nil { var _, ok = value.SetString(strings.TrimPrefix(*req.Value, "0x"), 16) if !ok { @@ -163,7 +159,7 @@ func (s RPCService) Eth_EstimateGas(req estimateGasRequest) (string, error) { } } - txKind, err := state.GuessEthereumTransactionKind(data) + txKind, err := state.GuessEthereumTransactionKind(data, req.To) if err != nil { return "", errors.Errorf("failed to guess ethereum tx kind, %v", err) } diff --git a/pkg/proto/eth_transaction.go b/pkg/proto/eth_transaction.go index 068b3cb22f..c02330c370 100644 --- a/pkg/proto/eth_transaction.go +++ b/pkg/proto/eth_transaction.go @@ -143,6 +143,22 @@ func (tx *EthereumInvokeScriptTxKind) String() string { return "EthereumInvokeScriptTxKind" } +type EthereumInvokeExpressionTxKind struct { + Expression string +} + +func NewEthereumInvokeExpressionTxKind(expression string) *EthereumInvokeExpressionTxKind { + return &EthereumInvokeExpressionTxKind{Expression: expression} +} + +func (tx *EthereumInvokeExpressionTxKind) DecodedData() *ethabi.DecodedCallData { + return nil +} + +func (tx *EthereumInvokeExpressionTxKind) String() string { + return "EthereumInvokeExpressionTxKind" +} + type EthereumTransaction struct { inner EthereumTxData innerBinarySize int diff --git a/pkg/state/appender.go b/pkg/state/appender.go index 9cde9e68c8..066fc9dd60 100644 --- a/pkg/state/appender.go +++ b/pkg/state/appender.go @@ -490,9 +490,7 @@ func (a *txAppender) appendTx(tx proto.Transaction, params *appendTxParams) erro ethTx.TxKind.String(), ethTx.ID.String(), params.checkerInfo.height+1, err, ) } - // In UTX balances are always validated. - needToValidateBalanceDiff = params.validatingUtx - case *proto.EthereumInvokeScriptTxKind: + case *proto.EthereumInvokeScriptTxKind, *proto.EthereumInvokeExpressionTxKind: fallibleInfo := &fallibleValidationParams{ appendTxParams: params, senderScripted: accountHasVerifierScript, @@ -505,7 +503,10 @@ func (a *txAppender) appendTx(tx proto.Transaction, params *appendTxParams) erro ethTx.TxKind.String(), ethTx.ID.String(), params.checkerInfo.height+1, err, ) } + } + // In UTX balances are always validated. + needToValidateBalanceDiff = params.validatingUtx default: applicationRes, err = a.handleDefaultTransaction(tx, params, accountHasVerifierScript) if err != nil { @@ -646,9 +647,6 @@ func (a *txAppender) handleInvoke(tx proto.Transaction, info *fallibleValidation case *proto.InvokeExpressionTransactionWithProofs: ID = *t.ID case *proto.EthereumTransaction: - if _, ok := t.TxKind.(*proto.EthereumInvokeScriptTxKind); !ok { - return nil, errors.Errorf("unexpected ethereum tx kind '%T'", tx) - } ID = *t.ID } res, err := a.ia.applyInvokeScript(tx, info) diff --git a/pkg/state/eth_info.go b/pkg/state/eth_info.go index d8d4bbf78b..01c0d81ebb 100644 --- a/pkg/state/eth_info.go +++ b/pkg/state/eth_info.go @@ -11,6 +11,7 @@ const ( EthereumTransferWavesKind = iota + 1 EthereumTransferAssetsKind EthereumInvokeKind + EthereumInvokeExpressionKind ) type ethInfo struct { @@ -22,11 +23,15 @@ func newEthInfo(stor *blockchainEntitiesStorage, settings *settings.BlockchainSe return ðInfo{stor: stor, settings: settings} } -func GuessEthereumTransactionKind(data []byte) (int64, error) { +func GuessEthereumTransactionKind(data []byte, receiver *proto.EthereumAddress) (int64, error) { if len(data) == 0 { return EthereumTransferWavesKind, nil } + if receiver == nil { + return EthereumInvokeExpressionKind, nil + } + selectorBytes := data if len(data) < ethabi.SelectorSize { return 0, errors.Errorf("length of data from ethereum transaction is less than %d", ethabi.SelectorSize) @@ -44,7 +49,7 @@ func GuessEthereumTransactionKind(data []byte) (int64, error) { } func (e *ethInfo) ethereumTransactionKind(ethTx *proto.EthereumTransaction, params *appendTxParams) (proto.EthereumTransactionKind, error) { - txKind, err := GuessEthereumTransactionKind(ethTx.Data()) + txKind, err := GuessEthereumTransactionKind(ethTx.Data(), ethTx.To()) if err != nil { return nil, errors.Wrap(err, "failed to guess ethereum tx kind") } @@ -93,7 +98,9 @@ func (e *ethInfo) ethereumTransactionKind(ethTx *proto.EthereumTransaction, para } return proto.NewEthereumInvokeScriptTxKind(*decodedData), nil - + case EthereumInvokeExpressionKind: + expression := ethTx.Data() + return proto.NewEthereumInvokeExpressionTxKind(string(expression)), nil default: return nil, errors.New("unexpected ethereum tx kind") } diff --git a/pkg/state/invoke_applier.go b/pkg/state/invoke_applier.go index 366a9b64db..56c2318601 100644 --- a/pkg/state/invoke_applier.go +++ b/pkg/state/invoke_applier.go @@ -731,11 +731,12 @@ func (ia *invokeApplier) applyInvokeScript(tx proto.Transaction, info *fallibleV paymentsLength int scriptAddr *proto.WavesAddress txID crypto.Digest - sender proto.Address + sender proto.Address // TODO change to WavesAddress tree *ride.Tree scriptPK crypto.PublicKey isInvokeExpression bool ) + // TODO refactor this unreadable switch case in the next PR. Create entities for InvokeTx and InvokeExpressionTx switch transaction := tx.(type) { case *proto.InvokeScriptWithProofs: var err error @@ -774,25 +775,43 @@ func (ia *invokeApplier) applyInvokeScript(tx proto.Transaction, info *fallibleV scriptPK = transaction.SenderPK case *proto.EthereumTransaction: - var err error - scriptAddr, err = transaction.WavesAddressTo(ia.settings.AddressSchemeCharacter) - if err != nil { - return nil, err - } - decodedData := transaction.TxKind.DecodedData() - paymentsLength = len(decodedData.Payments) - txID = *transaction.ID - sender, err = transaction.WavesAddressFrom(ia.settings.AddressSchemeCharacter) - if err != nil { - return nil, errors.Wrapf(err, "failed to apply script invocation") - } - tree, err = ia.stor.scriptsStorage.newestScriptByAddr(*scriptAddr, !info.initialisation) - if err != nil { - return nil, errors.Wrapf(err, "failed to instantiate script on address '%s'", scriptAddr.String()) - } - scriptPK, err = ia.stor.scriptsStorage.newestScriptPKByAddr(*scriptAddr, !info.initialisation) - if err != nil { - return nil, errors.Wrapf(err, "failed to get script's public key on address '%s'", scriptAddr.String()) + switch kind := transaction.TxKind.(type) { + case *proto.EthereumInvokeScriptTxKind: + var err error + scriptAddr, err = transaction.WavesAddressTo(ia.settings.AddressSchemeCharacter) + if err != nil { + return nil, err + } + decodedData := transaction.TxKind.DecodedData() + paymentsLength = len(decodedData.Payments) + txID = *transaction.ID + sender, err = transaction.WavesAddressFrom(ia.settings.AddressSchemeCharacter) + if err != nil { + return nil, errors.Wrapf(err, "failed to apply script invocation") + } + tree, err = ia.stor.scriptsStorage.newestScriptByAddr(*scriptAddr, !info.initialisation) + if err != nil { + return nil, errors.Wrapf(err, "failed to instantiate script on address '%s'", scriptAddr.String()) + } + scriptPK, err = ia.stor.scriptsStorage.newestScriptPKByAddr(*scriptAddr, !info.initialisation) + if err != nil { + return nil, errors.Wrapf(err, "failed to get script's public key on address '%s'", scriptAddr.String()) + } + case *proto.EthereumInvokeExpressionTxKind: + var err error + address, err := transaction.WavesAddressFrom(ia.settings.AddressSchemeCharacter) + if err != nil { + return nil, err + } + sender = address + scriptAddr = &address + tree, err = ride.Parse([]byte(kind.Expression)) + if err != nil { + return nil, errors.Wrap(err, "failed to parse decoded invoke expression into tree") + } + isInvokeExpression = true + txID = *transaction.ID + scriptPK, err = ia.stor.scriptsStorage.newestScriptPKByAddr(*scriptAddr, !info.initialisation) } default: diff --git a/pkg/state/script_caller.go b/pkg/state/script_caller.go index c560edbe77..656e0cc8d5 100644 --- a/pkg/state/script_caller.go +++ b/pkg/state/script_caller.go @@ -226,6 +226,7 @@ func (a *scriptCaller) invokeFunction(tree *ride.Tree, tx proto.Transaction, inf sender proto.WavesAddress r ride.Result ) + // TODO refactor this switch case in the next PR switch transaction := tx.(type) { case *proto.InvokeScriptWithProofs: err = env.SetInvoke(tx, tree.LibVersion) @@ -291,55 +292,87 @@ func (a *scriptCaller) invokeFunction(tree *ride.Tree, tx proto.Transaction, inf } case *proto.EthereumTransaction: - abiPayments := transaction.TxKind.DecodedData().Payments - scriptPayments := make([]proto.ScriptPayment, 0, len(abiPayments)) - for _, p := range abiPayments { - var optAsset proto.OptionalAsset - if p.PresentAssetID { - optAsset = *proto.NewOptionalAssetFromDigest(p.AssetID) - } else { - optAsset = proto.NewOptionalAssetWaves() + switch transaction.TxKind.(type) { + case *proto.EthereumInvokeScriptTxKind: + abiPayments := transaction.TxKind.DecodedData().Payments + scriptPayments := make([]proto.ScriptPayment, 0, len(abiPayments)) + for _, p := range abiPayments { + var optAsset proto.OptionalAsset + if p.PresentAssetID { + optAsset = *proto.NewOptionalAssetFromDigest(p.AssetID) + } else { + optAsset = proto.NewOptionalAssetWaves() + } + scriptPayment := proto.ScriptPayment{Amount: uint64(p.Amount), Asset: optAsset} + scriptPayments = append(scriptPayments, scriptPayment) } - scriptPayment := proto.ScriptPayment{Amount: uint64(p.Amount), Asset: optAsset} - scriptPayments = append(scriptPayments, scriptPayment) - } - payments = scriptPayments + payments = scriptPayments - err = env.SetEthereumInvoke(transaction, tree.LibVersion, scriptPayments) - if err != nil { - return nil, err - } - sender, err = transaction.WavesAddressFrom(a.settings.AddressSchemeCharacter) - if err != nil { - return nil, errors.Errorf("failed to get waves address from ethereum transaction %v", err) - } - decodedData := transaction.TxKind.DecodedData() - functionName = decodedData.Name - arguments, err := ride.ConvertDecodedEthereumArgumentsToProtoArguments(decodedData.Inputs) - if err != nil { - return nil, errors.Errorf("failed to convert ethereum arguments, %v", err) - } - functionArguments = arguments - defaultFunction = true + err = env.SetEthereumInvoke(transaction, tree.LibVersion, scriptPayments) + if err != nil { + return nil, err + } + sender, err = transaction.WavesAddressFrom(a.settings.AddressSchemeCharacter) + if err != nil { + return nil, errors.Errorf("failed to get waves address from ethereum transaction %v", err) + } + decodedData := transaction.TxKind.DecodedData() + functionName = decodedData.Name + arguments, err := ride.ConvertDecodedEthereumArgumentsToProtoArguments(decodedData.Inputs) + if err != nil { + return nil, errors.Errorf("failed to convert ethereum arguments, %v", err) + } + functionArguments = arguments + defaultFunction = true - env.ChooseSizeCheck(tree.LibVersion) - env.ChooseTakeString(info.rideV5Activated) - env.ChooseMaxDataEntriesSize(info.rideV5Activated) + env.ChooseSizeCheck(tree.LibVersion) + env.ChooseTakeString(info.rideV5Activated) + env.ChooseMaxDataEntriesSize(info.rideV5Activated) - // Since V5 we have to create environment with wrapped state to which we put attached payments - if tree.LibVersion >= 5 { - env, err = ride.NewEnvironmentWithWrappedState(env, payments, sender, info.rideV6Activated) + // Since V5 we have to create environment with wrapped state to which we put attached payments + if tree.LibVersion >= 5 { + env, err = ride.NewEnvironmentWithWrappedState(env, payments, sender, info.rideV6Activated) + if err != nil { + return nil, errors.Wrapf(err, "failed to create RIDE environment with wrapped state") + } + } + + r, err = ride.CallFunction(env, tree, functionName, functionArguments) if err != nil { - return nil, errors.Wrapf(err, "failed to create RIDE environment with wrapped state") + if appendErr := a.appendFunctionComplexity(ride.EvaluationErrorSpentComplexity(err), scriptAddress, functionName, defaultFunction, info); appendErr != nil { + return nil, appendErr + } + return nil, err } - } + case *proto.EthereumInvokeExpressionTxKind: + err = env.SetInvoke(tx, tree.LibVersion) + if err != nil { + return nil, err + } + sender, err = transaction.WavesAddressFrom(a.settings.AddressSchemeCharacter) + if err != nil { + return nil, err + } + functionName = "" + env.ChooseSizeCheck(tree.LibVersion) + env.ChooseTakeString(info.rideV5Activated) + env.ChooseMaxDataEntriesSize(info.rideV5Activated) - r, err = ride.CallFunction(env, tree, functionName, functionArguments) - if err != nil { - if appendErr := a.appendFunctionComplexity(ride.EvaluationErrorSpentComplexity(err), scriptAddress, functionName, defaultFunction, info); appendErr != nil { - return nil, appendErr + // Since V5 we have to create environment with wrapped state to which we put attached payments + if tree.LibVersion >= 5 { + env, err = ride.NewEnvironmentWithWrappedState(env, payments, sender, info.rideV6Activated) + if err != nil { + return nil, errors.Wrapf(err, "failed to create RIDE environment with wrapped state") + } + } + + r, err = ride.CallVerifier(env, tree) + if err != nil { + if appendErr := a.appendFunctionComplexity(ride.EvaluationErrorSpentComplexity(err), scriptAddress, functionName, defaultFunction, info); appendErr != nil { + return nil, appendErr + } + return nil, err } - return nil, err } default: diff --git a/pkg/state/transaction_checker.go b/pkg/state/transaction_checker.go index e0dccfad4f..b2763988f3 100644 --- a/pkg/state/transaction_checker.go +++ b/pkg/state/transaction_checker.go @@ -489,7 +489,16 @@ func (tc *transactionChecker) checkEthereumTransactionWithProofs(transaction pro } return smartAssets, nil + case *proto.EthereumInvokeExpressionTxKind: + minFee := proto.EthereumInvokeMinFee + if err := tc.checkTimestamps(tx.GetTimestamp(), info.currentTimestamp, info.parentTimestamp); err != nil { + return nil, errs.Extend(err, "invalid timestamp") + } + if tx.GetFee() < minFee { + return nil, errors.Errorf("the fee for ethereum invoke tx is not enough, min fee is %d, got %d", proto.EthereumInvokeMinFee, tx.GetFee()) + } + return nil, nil default: return nil, errors.New("failed to check ethereum transaction, wrong kind of tx") } diff --git a/pkg/state/transaction_performer.go b/pkg/state/transaction_performer.go index 701234666b..48629caa8a 100644 --- a/pkg/state/transaction_performer.go +++ b/pkg/state/transaction_performer.go @@ -359,10 +359,14 @@ func (tp *transactionPerformer) performEthereumTransactionWithProofs(transaction if !ok { return errors.New("failed to convert interface to EthereumTransaction transaction") } - if _, ok := ethTx.TxKind.(*proto.EthereumInvokeScriptTxKind); ok { - if err := tp.stor.commitUncertain(info.blockID); err != nil { - return errors.Wrap(err, "failed to commit invoke changes") - } + _, isEthInvokeScript := ethTx.TxKind.(*proto.EthereumInvokeScriptTxKind) + _, isEthInvokeExpression := ethTx.TxKind.(*proto.EthereumInvokeExpressionTxKind) + if !(isEthInvokeScript || isEthInvokeExpression) { + return errors.New("performing transaction was called, but the transaction is not InvokeScript or InvokeExpression") + } + err := tp.stor.commitUncertain(info.blockID) + if err != nil { + return errors.Wrap(err, "failed to commit invoke changes") } // nothing to do for proto.EthereumTransferWavesTxKind and proto.EthereumTransferAssetsErc20TxKind return nil From 175c9475ab04f93c8eac16cae6754a633896af1e Mon Sep 17 00:00:00 2001 From: esuwu Date: Tue, 25 Jan 2022 21:46:53 +0300 Subject: [PATCH 16/18] Added transaction object set --- pkg/ride/converters.go | 16 +++++++++++++++- pkg/ride/environment.go | 8 ++++++-- pkg/state/invoke_applier.go | 4 +++- pkg/state/script_caller.go | 2 +- 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/pkg/ride/converters.go b/pkg/ride/converters.go index 3637882b36..e834da99d2 100644 --- a/pkg/ride/converters.go +++ b/pkg/ride/converters.go @@ -1076,7 +1076,21 @@ func ethereumTransactionToObject(scheme proto.Scheme, tx *proto.EthereumTransact r["args"] = args r["fee"] = rideInt(tx.GetFee()) r["timestamp"] = rideInt(tx.GetTimestamp()) - + case *proto.EthereumInvokeExpressionTxKind: + r := make(rideObject) + r[instanceFieldName] = rideString("InvokeExpressionTransaction") + r["version"] = rideInt(tx.GetVersion()) + r["id"] = rideBytes(tx.ID.Bytes()) + r["sender"] = rideAddress(sender) + r["senderPublicKey"] = rideBytes(callerPK) + r["dApp"] = rideRecipient(proto.NewRecipientFromAddress(sender)) + r["payment"] = rideUnit{} + r["payments"] = make(rideList, 0) + r["feeAssetId"] = optionalAsset(proto.NewOptionalAssetWaves()) + r["function"] = rideString("default") + r["args"] = rideList{} + r["fee"] = rideInt(tx.GetFee()) + r["timestamp"] = rideInt(tx.GetTimestamp()) default: return nil, errors.New("unknown ethereum transaction kind") } diff --git a/pkg/ride/environment.go b/pkg/ride/environment.go index 9e6c0825a1..a2429d4bdb 100644 --- a/pkg/ride/environment.go +++ b/pkg/ride/environment.go @@ -1350,8 +1350,12 @@ func (e *EvaluationEnvironment) SetInvoke(tx proto.Transaction, v int) error { return nil } -func (e *EvaluationEnvironment) SetEthereumInvoke(tx *proto.EthereumTransaction, v int, payments []proto.ScriptPayment) error { - obj, err := ethereumInvocationToObject(v, e.sch, tx, payments) +func (e *EvaluationEnvironment) SetEthereumInvoke(tx proto.Transaction, v int, payments []proto.ScriptPayment) error { + ethTx, ok := tx.(*proto.EthereumTransaction) + if !ok { + return errors.New("failed to transform interface to ethereum transaction") + } + obj, err := ethereumInvocationToObject(v, e.sch, ethTx, payments) if err != nil { return err } diff --git a/pkg/state/invoke_applier.go b/pkg/state/invoke_applier.go index 56c2318601..bc3c2dcd53 100644 --- a/pkg/state/invoke_applier.go +++ b/pkg/state/invoke_applier.go @@ -798,7 +798,6 @@ func (ia *invokeApplier) applyInvokeScript(tx proto.Transaction, info *fallibleV return nil, errors.Wrapf(err, "failed to get script's public key on address '%s'", scriptAddr.String()) } case *proto.EthereumInvokeExpressionTxKind: - var err error address, err := transaction.WavesAddressFrom(ia.settings.AddressSchemeCharacter) if err != nil { return nil, err @@ -812,6 +811,9 @@ func (ia *invokeApplier) applyInvokeScript(tx proto.Transaction, info *fallibleV isInvokeExpression = true txID = *transaction.ID scriptPK, err = ia.stor.scriptsStorage.newestScriptPKByAddr(*scriptAddr, !info.initialisation) + if err != nil { + return nil, err + } } default: diff --git a/pkg/state/script_caller.go b/pkg/state/script_caller.go index 656e0cc8d5..2657d225e1 100644 --- a/pkg/state/script_caller.go +++ b/pkg/state/script_caller.go @@ -345,7 +345,7 @@ func (a *scriptCaller) invokeFunction(tree *ride.Tree, tx proto.Transaction, inf return nil, err } case *proto.EthereumInvokeExpressionTxKind: - err = env.SetInvoke(tx, tree.LibVersion) + err = env.SetEthereumInvoke(tx, tree.LibVersion, nil) if err != nil { return nil, err } From df14c5e2fdd4f6519cd64de7772f8476e9a08df0 Mon Sep 17 00:00:00 2001 From: esuwu Date: Fri, 28 Jan 2022 15:19:31 +0300 Subject: [PATCH 17/18] Added diff creation --- pkg/state/appender.go | 8 ++- pkg/state/block_differ.go | 2 +- pkg/state/transaction_checker.go | 4 ++ pkg/state/transaction_differ.go | 89 +++++++++++++++++++------------- 4 files changed, 60 insertions(+), 43 deletions(-) diff --git a/pkg/state/appender.go b/pkg/state/appender.go index 9b37857b7a..ddd3d15f5a 100644 --- a/pkg/state/appender.go +++ b/pkg/state/appender.go @@ -212,9 +212,9 @@ func (a *txAppender) checkTxFees(tx proto.Transaction, info *fallibleValidationP if err != nil { return err } - // TODO handle ethereum invoke expression tx + // ethereum InvokeScript and InvokeExpression case proto.EthereumMetamaskTransaction: - feeChanges, err = a.txHandler.td.createFeeDiffEthereumInvokeScriptWithProofs(tx, differInfo) + feeChanges, err = a.txHandler.td.createFeeDiffEthereumInvokeWithProofs(tx, differInfo) if err != nil { return err } @@ -654,14 +654,12 @@ func (a *txAppender) handleInvoke(tx proto.Transaction, info *fallibleValidation case *proto.InvokeExpressionTransactionWithProofs: ID = *t.ID case *proto.EthereumTransaction: - // TODO: handle ethereum invoke expression tx switch t.TxKind.(type) { - case *proto.EthereumInvokeScriptTxKind: + case *proto.EthereumInvokeScriptTxKind, *proto.EthereumInvokeExpressionTxKind: ID = *t.ID default: return nil, errors.Errorf("unexpected ethereum tx kind (%T)", tx) } - ID = *t.ID default: return nil, errors.Errorf("failed to handle invoke: wrong type of transaction (%T)", tx) } diff --git a/pkg/state/block_differ.go b/pkg/state/block_differ.go index 1c01ce2f4c..4be7867f20 100644 --- a/pkg/state/block_differ.go +++ b/pkg/state/block_differ.go @@ -125,7 +125,7 @@ func (d *blockDiffer) createFailedTransactionDiff(tx proto.Transaction, block *p return txBalanceChanges{}, err } case proto.EthereumMetamaskTransaction: - txChanges, err = d.handler.td.createFeeDiffEthereumInvokeScriptWithProofs(tx, differInfo) + txChanges, err = d.handler.td.createFeeDiffEthereumInvokeWithProofs(tx, differInfo) if err != nil { return txBalanceChanges{}, err } diff --git a/pkg/state/transaction_checker.go b/pkg/state/transaction_checker.go index b8f2961cbd..b5a595fb8d 100644 --- a/pkg/state/transaction_checker.go +++ b/pkg/state/transaction_checker.go @@ -486,6 +486,10 @@ func (tc *transactionChecker) checkEthereumTransactionWithProofs(transaction pro case *proto.EthereumInvokeExpressionTxKind: minFee := proto.EthereumInvokeMinFee + if l := len(kind.Expression); l > proto.MaxContractScriptSize { + return nil, errors.Errorf("size of the expression %d is exceeded limit %d", l, proto.MaxContractScriptSize) + } + if err := tc.checkTimestamps(tx.GetTimestamp(), info.currentTimestamp, info.parentTimestamp); err != nil { return nil, errs.Extend(err, "invalid timestamp") } diff --git a/pkg/state/transaction_differ.go b/pkg/state/transaction_differ.go index 19e232c2d9..770383ec01 100644 --- a/pkg/state/transaction_differ.go +++ b/pkg/state/transaction_differ.go @@ -599,8 +599,8 @@ func (td *transactionDiffer) createDiffEthereumTransactionWithProofs(transaction return td.createDiffEthereumTransferWaves(ethTx, info) case *proto.EthereumTransferAssetsErc20TxKind: return td.createDiffEthereumErc20(ethTx, info) - case *proto.EthereumInvokeScriptTxKind: - return td.createDiffEthereumInvokeScript(ethTx, info) + case *proto.EthereumInvokeScriptTxKind, *proto.EthereumInvokeExpressionTxKind: + return td.createDiffEthereumInvoke(ethTx, info) default: return txBalanceChanges{}, errors.New("wrong kind of ethereum transaction") @@ -1463,27 +1463,35 @@ func (td *transactionDiffer) createDiffInvokeExpressionWithProofs(transaction pr return changes, nil } -func (td *transactionDiffer) createDiffEthereumInvokeScript(tx *proto.EthereumTransaction, info *differInfo) (txBalanceChanges, error) { +func (td *transactionDiffer) createDiffEthereumInvoke(tx *proto.EthereumTransaction, info *differInfo) (txBalanceChanges, error) { updateMinIntermediateBalance := false - - txInvokeScriptKind, ok := tx.TxKind.(*proto.EthereumInvokeScriptTxKind) - if !ok { - return txBalanceChanges{}, errors.New("failed to convert ethereum tx kind to EthereumTransferAssetsErc20TxKind") - } - - decodedData := txInvokeScriptKind.DecodedData() - - noPayments := len(decodedData.Payments) == 0 - if info.blockInfo.Timestamp >= td.settings.CheckTempNegativeAfterTime && !noPayments { - updateMinIntermediateBalance = true - } + var ( + decodedData *ethabi.DecodedCallData + noPayments = true + scriptAddress *proto.WavesAddress + ) diff := newTxDiff() // Append sender diff. senderAddress, err := tx.WavesAddressFrom(td.settings.AddressSchemeCharacter) if err != nil { return txBalanceChanges{}, errors.Wrapf(err, "failed to get sender address from ethereum invoke tx") } + switch kind := tx.TxKind.(type) { + case *proto.EthereumInvokeScriptTxKind: + decodedData = kind.DecodedData() + noPayments = len(decodedData.Payments) == 0 + scriptAddress, err = tx.WavesAddressTo(td.settings.AddressSchemeCharacter) + if err != nil { + return txBalanceChanges{}, err + } + case *proto.EthereumInvokeExpressionTxKind: + scriptAddress = &senderAddress + } + + if info.blockInfo.Timestamp >= td.settings.CheckTempNegativeAfterTime && !noPayments { + updateMinIntermediateBalance = true + } senderAddrID := senderAddress.ID() assetFee := proto.NewOptionalAssetWaves() @@ -1492,28 +1500,27 @@ func (td *transactionDiffer) createDiffEthereumInvokeScript(tx *proto.EthereumTr if err := diff.appendBalanceDiff(senderFeeKey, newBalanceDiff(senderFeeBalanceDiff, 0, 0, updateMinIntermediateBalance)); err != nil { return txBalanceChanges{}, err } - scriptAddr, err := tx.WavesAddressTo(td.settings.AddressSchemeCharacter) - if err != nil { - return txBalanceChanges{}, err - } - scriptAddrID := scriptAddr.ID() + scriptAddrID := scriptAddress.ID() - addresses := []proto.WavesAddress{senderAddress, *scriptAddr} + addresses := []proto.WavesAddress{senderAddress, *scriptAddress} changes := newTxBalanceChanges(addresses, diff) - for _, payment := range decodedData.Payments { - assetID := proto.NewOptionalAssetFromDigest(payment.AssetID) - senderPaymentKey := byteKey(senderAddrID, *assetID) - senderBalanceDiff := -payment.Amount - if err := diff.appendBalanceDiff(senderPaymentKey, newBalanceDiff(senderBalanceDiff, 0, 0, updateMinIntermediateBalance)); err != nil { - return txBalanceChanges{}, err - } - receiverKey := byteKey(scriptAddrID, *assetID) - receiverBalanceDiff := payment.Amount - if err := diff.appendBalanceDiff(receiverKey, newBalanceDiff(receiverBalanceDiff, 0, 0, updateMinIntermediateBalance)); err != nil { - return txBalanceChanges{}, err + if decodedData != nil { + for _, payment := range decodedData.Payments { + assetID := proto.NewOptionalAssetFromDigest(payment.AssetID) + senderPaymentKey := byteKey(senderAddrID, *assetID) + senderBalanceDiff := -payment.Amount + if err := diff.appendBalanceDiff(senderPaymentKey, newBalanceDiff(senderBalanceDiff, 0, 0, updateMinIntermediateBalance)); err != nil { + return txBalanceChanges{}, err + } + receiverKey := byteKey(scriptAddrID, *assetID) + receiverBalanceDiff := payment.Amount + if err := diff.appendBalanceDiff(receiverKey, newBalanceDiff(receiverBalanceDiff, 0, 0, updateMinIntermediateBalance)); err != nil { + return txBalanceChanges{}, err + } } } + if err := td.payoutMinerWithSponsorshipHandling(&changes, tx.GetFee(), proto.NewOptionalAssetWaves(), info); err != nil { return txBalanceChanges{}, err } @@ -1574,7 +1581,7 @@ func (td *transactionDiffer) createFeeDiffInvokeScriptWithProofs(transaction pro return changes, nil } -func (td *transactionDiffer) createFeeDiffEthereumInvokeScriptWithProofs(transaction proto.Transaction, info *differInfo) (txBalanceChanges, error) { +func (td *transactionDiffer) createFeeDiffEthereumInvokeWithProofs(transaction proto.Transaction, info *differInfo) (txBalanceChanges, error) { tx, ok := transaction.(*proto.EthereumTransaction) if !ok { return txBalanceChanges{}, errors.New("failed to convert interface to InvokeScriptWithProofs transaction") @@ -1589,16 +1596,24 @@ func (td *transactionDiffer) createFeeDiffEthereumInvokeScriptWithProofs(transac if err != nil { return txBalanceChanges{}, err } + + var scriptAddress proto.WavesAddress + switch tx.TxKind.(type) { + case *proto.EthereumInvokeScriptTxKind: + scriptAddress, err = tx.To().ToWavesAddress(td.settings.AddressSchemeCharacter) + if err != nil { + return txBalanceChanges{}, err + } + case *proto.EthereumInvokeExpressionTxKind: + scriptAddress = senderAddress + } + wavesAsset := proto.NewOptionalAssetWaves() senderFeeKey := byteKey(senderAddress.ID(), wavesAsset) senderFeeBalanceDiff := -int64(tx.GetFee()) if err := diff.appendBalanceDiff(senderFeeKey, newBalanceDiff(senderFeeBalanceDiff, 0, 0, true)); err != nil { return txBalanceChanges{}, err } - scriptAddress, err := tx.To().ToWavesAddress(td.settings.AddressSchemeCharacter) - if err != nil { - return txBalanceChanges{}, err - } addresses := []proto.WavesAddress{senderAddress, scriptAddress} changes := newTxBalanceChanges(addresses, diff) From db9cf6f3edcc9bc188cec813f66287f7f222c743 Mon Sep 17 00:00:00 2001 From: Alexandr Dolgavin <41806871+esuwu@users.noreply.github.com> Date: Mon, 7 Feb 2022 15:26:02 +0300 Subject: [PATCH 18/18] Eth invoke and ethereum tx refactoring (#657) * Added new entities for InvokeScript and InvokeExpression * Deleted a useless comment * Fixed tests and style * Deleted copies of code * Fixed tests * Fixed a very important mistake * Fixed ethereum invoke tx tests * Changed comments --- pkg/proto/eth_transaction.go | 21 ++- pkg/proto/invoke_union.go | 41 +++++ pkg/proto/transactions.go | 1 - pkg/proto/transactions_with_proofs.go | 7 + pkg/ride/converters.go | 38 +---- pkg/state/appender.go | 24 +-- pkg/state/eth_info.go | 17 +- pkg/state/ethereum_tx_test.go | 26 ++- pkg/state/invoke_applier.go | 106 ++++++------ pkg/state/invoke_applier_test.go | 7 +- pkg/state/script_caller.go | 234 ++++++++++++-------------- 11 files changed, 284 insertions(+), 238 deletions(-) create mode 100644 pkg/proto/invoke_union.go diff --git a/pkg/proto/eth_transaction.go b/pkg/proto/eth_transaction.go index c02330c370..53163757d8 100644 --- a/pkg/proto/eth_transaction.go +++ b/pkg/proto/eth_transaction.go @@ -129,10 +129,13 @@ func (tx *EthereumTransferAssetsErc20TxKind) String() string { type EthereumInvokeScriptTxKind struct { decodedData ethabi.DecodedCallData + TxID *crypto.Digest + From WavesAddress + To WavesAddress } -func NewEthereumInvokeScriptTxKind(decodedData ethabi.DecodedCallData) *EthereumInvokeScriptTxKind { - return &EthereumInvokeScriptTxKind{decodedData: decodedData} +func NewEthereumInvokeScriptTxKind(decodedData ethabi.DecodedCallData, txID *crypto.Digest, from WavesAddress, to WavesAddress) *EthereumInvokeScriptTxKind { + return &EthereumInvokeScriptTxKind{decodedData: decodedData, TxID: txID, From: from, To: to} } func (tx *EthereumInvokeScriptTxKind) DecodedData() *ethabi.DecodedCallData { @@ -143,12 +146,18 @@ func (tx *EthereumInvokeScriptTxKind) String() string { return "EthereumInvokeScriptTxKind" } +func (tx *EthereumInvokeScriptTxKind) markerInvokeScriptTx() { + +} + type EthereumInvokeExpressionTxKind struct { Expression string + TxID *crypto.Digest + From WavesAddress } -func NewEthereumInvokeExpressionTxKind(expression string) *EthereumInvokeExpressionTxKind { - return &EthereumInvokeExpressionTxKind{Expression: expression} +func NewEthereumInvokeExpressionTxKind(expression string, txID *crypto.Digest, from WavesAddress) *EthereumInvokeExpressionTxKind { + return &EthereumInvokeExpressionTxKind{Expression: expression, TxID: txID, From: from} } func (tx *EthereumInvokeExpressionTxKind) DecodedData() *ethabi.DecodedCallData { @@ -159,6 +168,10 @@ func (tx *EthereumInvokeExpressionTxKind) String() string { return "EthereumInvokeExpressionTxKind" } +func (tx *EthereumInvokeExpressionTxKind) markerInvokeExpressionTx() { + +} + type EthereumTransaction struct { inner EthereumTxData innerBinarySize int diff --git a/pkg/proto/invoke_union.go b/pkg/proto/invoke_union.go new file mode 100644 index 0000000000..421221e4ef --- /dev/null +++ b/pkg/proto/invoke_union.go @@ -0,0 +1,41 @@ +package proto + +import "github.com/wavesplatform/gowaves/pkg/crypto" + +type InvokeTxUnion interface { + TxID() crypto.Digest +} + +type InvokeScriptSubTransaction interface { + markerInvokeScriptTx() +} + +type InvokeExpressionSubTransaction interface { + markerInvokeExpressionTx() +} + +type InvokeScriptTxUnion struct { + SubTx InvokeScriptSubTransaction + txID crypto.Digest +} + +func NewInvokeScriptTxUnion(subTransaction InvokeScriptSubTransaction, id crypto.Digest) *InvokeScriptTxUnion { + return &InvokeScriptTxUnion{SubTx: subTransaction, txID: id} +} + +func (invScript InvokeScriptTxUnion) TxID() crypto.Digest { + return invScript.txID +} + +type InvokeExpressionTxUnion struct { + SubTx InvokeExpressionSubTransaction + txID crypto.Digest +} + +func NewInvokeExpressionTxUnion(subTransaction InvokeExpressionSubTransaction, id crypto.Digest) *InvokeExpressionTxUnion { + return &InvokeExpressionTxUnion{SubTx: subTransaction, txID: id} +} + +func (invExp InvokeExpressionTxUnion) TxID() crypto.Digest { + return invExp.txID +} diff --git a/pkg/proto/transactions.go b/pkg/proto/transactions.go index 8d396bf16b..a315b23aee 100644 --- a/pkg/proto/transactions.go +++ b/pkg/proto/transactions.go @@ -39,7 +39,6 @@ const ( UpdateAssetInfoTransaction // 17 - UpdateAssetInfoTransaction InvokeExpressionTransaction // 18 - InvokeExpressionTransaction EthereumMetamaskTransaction // 19 - EthereumMetamaskTransaction is a transaction which is received from metamask - ) // TxFailureReason indicates Transactions failure reasons. diff --git a/pkg/proto/transactions_with_proofs.go b/pkg/proto/transactions_with_proofs.go index c45bb068e6..ec10a3b7f6 100644 --- a/pkg/proto/transactions_with_proofs.go +++ b/pkg/proto/transactions_with_proofs.go @@ -4758,6 +4758,9 @@ func (tx *InvokeScriptWithProofs) ToProtobufSigned(scheme Scheme) (*g.SignedTran }, nil } +func (tx *InvokeScriptWithProofs) markerInvokeScriptTx() { +} + type UpdateAssetInfoWithProofs struct { Type TransactionType `json:"type"` Version byte `json:"version,omitempty"` @@ -5136,3 +5139,7 @@ func (tx *InvokeExpressionTransactionWithProofs) ToProtobufSigned(scheme Scheme) Proofs: tx.Proofs.Bytes(), }, nil } + +func (tx *InvokeExpressionTransactionWithProofs) markerInvokeExpressionTx() { + +} diff --git a/pkg/ride/converters.go b/pkg/ride/converters.go index 658ecedd27..70472cb141 100644 --- a/pkg/ride/converters.go +++ b/pkg/ride/converters.go @@ -886,7 +886,7 @@ func invokeScriptWithProofsToObject(scheme byte, tx *proto.InvokeScriptWithProof return r, nil } -// TODO think of reusing "InvokeScripTToObject" function. Also should we fill "payments" and "function name" fields"? +// TODO think of reusing "InvokeScripTToObject" function.TestEthereumInvokeWithoutPaymentsAndArguments func invokeExpressionWithProofsToObject(scheme byte, tx *proto.InvokeExpressionTransactionWithProofs) (rideObject, error) { sender, err := proto.NewAddressFromPublicKey(scheme, tx.SenderPK) if err != nil { @@ -982,13 +982,16 @@ func ethereumTransactionToObject(scheme proto.Scheme, tx *proto.EthereumTransact r["bodyBytes"] = rideBytes(nil) r["proofs"] = proofs(proto.NewProofs()) + r["version"] = rideInt(tx.GetVersion()) + r["id"] = rideBytes(tx.ID.Bytes()) + r["sender"] = rideAddress(sender) + r["senderPublicKey"] = rideBytes(callerPK) + r["feeAssetId"] = optionalAsset(proto.NewOptionalAssetWaves()) + r["timestamp"] = rideInt(tx.GetTimestamp()) + r["fee"] = rideInt(tx.GetFee()) switch kind := tx.TxKind.(type) { case *proto.EthereumTransferWavesTxKind: r[instanceFieldName] = rideString("TransferTransaction") - r["version"] = rideInt(tx.GetVersion()) - r["id"] = rideBytes(tx.ID.Bytes()) - r["sender"] = rideAddress(sender) - r["senderPublicKey"] = rideBytes(callerPK) r["recipient"] = rideRecipient(proto.NewRecipientFromAddress(*to)) r["assetId"] = optionalAsset(proto.NewOptionalAssetWaves()) res := new(big.Int).Div(tx.Value(), big.NewInt(int64(proto.DiffEthWaves))) @@ -999,18 +1002,10 @@ func ethereumTransactionToObject(scheme proto.Scheme, tx *proto.EthereumTransact } amount := res.Int64() r["amount"] = rideInt(amount) - r["fee"] = rideInt(tx.GetFee()) - r["feeAssetId"] = optionalAsset(proto.NewOptionalAssetWaves()) r["attachment"] = rideBytes(nil) - r["timestamp"] = rideInt(tx.GetTimestamp()) case *proto.EthereumTransferAssetsErc20TxKind: r[instanceFieldName] = rideString("TransferTransaction") - r["version"] = rideInt(tx.GetVersion()) - r["id"] = rideBytes(tx.ID.Bytes()) - r["sender"] = rideAddress(sender) - r["senderPublicKey"] = rideBytes(callerPK) - recipientAddr, err := proto.EthereumAddress(kind.Arguments.Recipient).ToWavesAddress(scheme) if err != nil { return nil, errors.Wrap(err, "failed to convert ethereum ERC20 transfer recipient to WavesAddress") @@ -1018,17 +1013,10 @@ func ethereumTransactionToObject(scheme proto.Scheme, tx *proto.EthereumTransact r["recipient"] = rideRecipient(proto.NewRecipientFromAddress(recipientAddr)) r["assetId"] = optionalAsset(kind.Asset) r["amount"] = rideInt(kind.Arguments.Amount) - r["fee"] = rideInt(tx.GetFee()) - r["feeAssetId"] = optionalAsset(proto.NewOptionalAssetWaves()) r["attachment"] = rideBytes(nil) - r["timestamp"] = rideInt(tx.GetTimestamp()) case *proto.EthereumInvokeScriptTxKind: r[instanceFieldName] = rideString("InvokeScriptTransaction") - r["version"] = rideInt(tx.GetVersion()) - r["id"] = rideBytes(tx.ID.Bytes()) - r["sender"] = rideAddress(sender) - r["senderPublicKey"] = rideBytes(callerPK) r["dApp"] = rideRecipient(proto.NewRecipientFromAddress(*to)) var scriptPayments []proto.ScriptPayment @@ -1059,7 +1047,6 @@ func ethereumTransactionToObject(scheme proto.Scheme, tx *proto.EthereumTransact r["payment"] = rideUnit{} r["payments"] = make(rideList, 0) } - r["feeAssetId"] = optionalAsset(proto.NewOptionalAssetWaves()) r["function"] = rideString(tx.TxKind.DecodedData().Name) arguments, err := ConvertDecodedEthereumArgumentsToProtoArguments(tx.TxKind.DecodedData().Inputs) if err != nil { @@ -1074,23 +1061,14 @@ func ethereumTransactionToObject(scheme proto.Scheme, tx *proto.EthereumTransact args[i] = a } r["args"] = args - r["fee"] = rideInt(tx.GetFee()) - r["timestamp"] = rideInt(tx.GetTimestamp()) case *proto.EthereumInvokeExpressionTxKind: r := make(rideObject) r[instanceFieldName] = rideString("InvokeExpressionTransaction") - r["version"] = rideInt(tx.GetVersion()) - r["id"] = rideBytes(tx.ID.Bytes()) - r["sender"] = rideAddress(sender) - r["senderPublicKey"] = rideBytes(callerPK) r["dApp"] = rideRecipient(proto.NewRecipientFromAddress(sender)) r["payment"] = rideUnit{} r["payments"] = make(rideList, 0) - r["feeAssetId"] = optionalAsset(proto.NewOptionalAssetWaves()) r["function"] = rideString("default") r["args"] = rideList{} - r["fee"] = rideInt(tx.GetFee()) - r["timestamp"] = rideInt(tx.GetTimestamp()) default: return nil, errors.New("unknown ethereum transaction kind") } diff --git a/pkg/state/appender.go b/pkg/state/appender.go index ddd3d15f5a..f6e8b2909f 100644 --- a/pkg/state/appender.go +++ b/pkg/state/appender.go @@ -5,7 +5,6 @@ import ( "github.com/mr-tron/base58/base58" "github.com/pkg/errors" - "github.com/wavesplatform/gowaves/pkg/crypto" "github.com/wavesplatform/gowaves/pkg/errs" "github.com/wavesplatform/gowaves/pkg/proto" "github.com/wavesplatform/gowaves/pkg/settings" @@ -647,25 +646,28 @@ type applicationResult struct { } func (a *txAppender) handleInvoke(tx proto.Transaction, info *fallibleValidationParams) (*applicationResult, error) { - var ID crypto.Digest + var invokeUnion proto.InvokeTxUnion switch t := tx.(type) { case *proto.InvokeScriptWithProofs: - ID = *t.ID + invokeUnion = proto.NewInvokeScriptTxUnion(t, *t.ID) case *proto.InvokeExpressionTransactionWithProofs: - ID = *t.ID - case *proto.EthereumTransaction: - switch t.TxKind.(type) { - case *proto.EthereumInvokeScriptTxKind, *proto.EthereumInvokeExpressionTxKind: - ID = *t.ID + invokeUnion = proto.NewInvokeExpressionTxUnion(t, *t.ID) + case *proto.EthereumTransaction: // ethereum InvokeExpression and InvokeScript + switch kind := t.TxKind.(type) { + case *proto.EthereumInvokeScriptTxKind: + invokeUnion = proto.NewInvokeScriptTxUnion(kind, *t.ID) + case *proto.EthereumInvokeExpressionTxKind: + invokeUnion = proto.NewInvokeExpressionTxUnion(kind, *t.ID) default: - return nil, errors.Errorf("unexpected ethereum tx kind (%T)", tx) + return nil, errors.Errorf("failed to handle invoke: wrong type of ethereum transactiion (%T)", tx) } default: return nil, errors.Errorf("failed to handle invoke: wrong type of transaction (%T)", tx) } - res, err := a.ia.applyInvokeScript(tx, info) + + res, err := a.ia.applyInvokeScript(tx, invokeUnion, info) if err != nil { - zap.S().Debugf("failed to apply InvokeScript transaction %s to state: %v", ID.String(), err) + zap.S().Debugf("failed to apply InvokeScript transaction %s to state: %v", invokeUnion.TxID().String(), err) return nil, err } return res, nil diff --git a/pkg/state/eth_info.go b/pkg/state/eth_info.go index 01c0d81ebb..29952e969a 100644 --- a/pkg/state/eth_info.go +++ b/pkg/state/eth_info.go @@ -96,11 +96,22 @@ func (e *ethInfo) ethereumTransactionKind(ethTx *proto.EthereumTransaction, para if err != nil { return nil, errors.Wrap(err, "failed to parse ethereum data") } - - return proto.NewEthereumInvokeScriptTxKind(*decodedData), nil + from, err := ethTx.WavesAddressFrom(e.settings.AddressSchemeCharacter) + if err != nil { + return nil, err + } + to, err := ethTx.WavesAddressTo(e.settings.AddressSchemeCharacter) + if err != nil { + return nil, err + } + return proto.NewEthereumInvokeScriptTxKind(*decodedData, ethTx.ID, from, *to), nil case EthereumInvokeExpressionKind: expression := ethTx.Data() - return proto.NewEthereumInvokeExpressionTxKind(string(expression)), nil + from, err := ethTx.WavesAddressFrom(e.settings.AddressSchemeCharacter) + if err != nil { + return nil, err + } + return proto.NewEthereumInvokeExpressionTxKind(string(expression), ethTx.ID, from), nil default: return nil, errors.New("unexpected ethereum tx kind") } diff --git a/pkg/state/ethereum_tx_test.go b/pkg/state/ethereum_tx_test.go index 2bef71ce3d..a222bf7cdc 100644 --- a/pkg/state/ethereum_tx_test.go +++ b/pkg/state/ethereum_tx_test.go @@ -261,16 +261,18 @@ func TestEthereumInvoke(t *testing.T) { proto.AssetID(recipientEth): {}, } txAppender := defaultTxAppender(t, storage, state, assetsUncertain, proto.MainNetScheme) - + recipient, err := recipientEth.ToWavesAddress(0) + assert.NoError(t, err) txData := defaultEthereumLegacyTxData(1000000000000000, &recipientEth, nil, 500000, proto.MainNetScheme) decodedData := defaultDecodedData("call", []ethabi.DecodedArg{{Value: ethabi.Int(10)}}, []ethabi.Payment{{Amount: 5, AssetID: proto.NewOptionalAssetWaves().ID}}) - txKind := proto.NewEthereumInvokeScriptTxKind(decodedData) + txKind := proto.NewEthereumInvokeScriptTxKind(decodedData, &crypto.Digest{}, sender, recipient) tx := proto.NewEthereumTransaction(txData, txKind, &crypto.Digest{}, &senderPK, 0) fallibleInfo := &fallibleValidationParams{appendTxParams: appendTxParams, senderScripted: false, senderAddress: sender} scriptAddress, tree := applyScript(t, &tx, storage, fallibleInfo) fallibleInfo.rideV5Activated = true - res, err := txAppender.ia.sc.invokeFunction(tree, &tx, fallibleInfo, scriptAddress, *tx.ID) + invokeUnion := proto.NewInvokeScriptTxUnion(txKind, *tx.ID) + res, err := txAppender.ia.sc.invokeFunction(tree, &tx, fallibleInfo, scriptAddress, invokeUnion) assert.NoError(t, err) assert.True(t, res.Result()) @@ -393,12 +395,17 @@ func TestEthereumInvokeWithoutPaymentsAndArguments(t *testing.T) { txData := defaultEthereumLegacyTxData(1000000000000000, &recipientEth, nil, 500000, proto.MainNetScheme) decodedData := defaultDecodedData("call", nil, nil) - tx := proto.NewEthereumTransaction(txData, proto.NewEthereumInvokeScriptTxKind(decodedData), &crypto.Digest{}, &senderPK, 0) + + recipient, err := recipientEth.ToWavesAddress(0) + assert.NoError(t, err) + txKind := proto.NewEthereumInvokeScriptTxKind(decodedData, &crypto.Digest{}, sender, recipient) + tx := proto.NewEthereumTransaction(txData, txKind, &crypto.Digest{}, &senderPK, 0) fallibleInfo := &fallibleValidationParams{appendTxParams: appendTxParams, senderScripted: false, senderAddress: sender} scriptAddress, tree := applyScript(t, &tx, storage, fallibleInfo) fallibleInfo.rideV5Activated = true - res, err := txAppender.ia.sc.invokeFunction(tree, &tx, fallibleInfo, scriptAddress, *tx.ID) + invokeUnion := proto.NewInvokeScriptTxUnion(txKind, *tx.ID) + res, err := txAppender.ia.sc.invokeFunction(tree, &tx, fallibleInfo, scriptAddress, invokeUnion) assert.NoError(t, err) assert.True(t, res.Result()) @@ -467,12 +474,17 @@ func TestEthereumInvokeAllArguments(t *testing.T) { {Value: ethabi.Bool(true)}, // will leave it here {Value: ethabi.List{ethabi.Int(4)}}, }, nil) - tx := proto.NewEthereumTransaction(txData, proto.NewEthereumInvokeScriptTxKind(decodedData), &crypto.Digest{}, &senderPK, 0) + + recipient, err := recipientEth.ToWavesAddress(0) + assert.NoError(t, err) + txKind := proto.NewEthereumInvokeScriptTxKind(decodedData, &crypto.Digest{}, sender, recipient) + tx := proto.NewEthereumTransaction(txData, txKind, &crypto.Digest{}, &senderPK, 0) fallibleInfo := &fallibleValidationParams{appendTxParams: appendTxParams, senderScripted: false, senderAddress: sender} scriptAddress, tree := applyScript(t, &tx, storage, fallibleInfo) fallibleInfo.rideV5Activated = true - res, err := txAppender.ia.sc.invokeFunction(tree, &tx, fallibleInfo, scriptAddress, *tx.ID) + invokeUnion := proto.NewInvokeScriptTxUnion(txKind, *tx.ID) + res, err := txAppender.ia.sc.invokeFunction(tree, &tx, fallibleInfo, scriptAddress, invokeUnion) assert.NoError(t, err) assert.True(t, res.Result()) diff --git a/pkg/state/invoke_applier.go b/pkg/state/invoke_applier.go index fe2fb726f4..c9808fd4e2 100644 --- a/pkg/state/invoke_applier.go +++ b/pkg/state/invoke_applier.go @@ -718,7 +718,7 @@ func (ia *invokeApplier) fallibleValidation(tx proto.Transaction, info *addlInvo // If the transaction does not fail, changes are committed (moved from uncertain to normal storage) // later in performInvokeScriptWithProofs(). // If the transaction fails, performInvokeScriptWithProofs() is not called and changes are discarded later using dropUncertain(). -func (ia *invokeApplier) applyInvokeScript(tx proto.Transaction, info *fallibleValidationParams) (*applicationResult, error) { +func (ia *invokeApplier) applyInvokeScript(tx proto.Transaction, invokeUnion proto.InvokeTxUnion, info *fallibleValidationParams) (*applicationResult, error) { // In defer we should clean all the temp changes invoke does to state. defer func() { ia.invokeDiffStor.invokeDiffsStor.reset() @@ -728,23 +728,37 @@ func (ia *invokeApplier) applyInvokeScript(tx proto.Transaction, info *fallibleV paymentsLength int scriptAddr *proto.WavesAddress txID crypto.Digest - sender proto.Address + sender proto.WavesAddress tree *ride.Tree scriptPK crypto.PublicKey ) - switch transaction := tx.(type) { - case *proto.InvokeScriptWithProofs: - var err error - scriptAddr, err = recipientToAddress(transaction.ScriptRecipient, ia.stor.aliases, !info.initialisation) - if err != nil { - return nil, errors.Wrap(err, "recipientToAddress() failed") - } - paymentsLength = len(transaction.Payments) - txID = *transaction.ID - sender, err = proto.NewAddressFromPublicKey(ia.settings.AddressSchemeCharacter, transaction.SenderPK) - if err != nil { - return nil, errors.Wrapf(err, "failed to apply script invocation") + switch invokeType := invokeUnion.(type) { + case *proto.InvokeScriptTxUnion: + switch subTx := invokeType.SubTx.(type) { + case *proto.InvokeScriptWithProofs: + var err error + scriptAddr, err = recipientToAddress(subTx.ScriptRecipient, ia.stor.aliases, !info.initialisation) + if err != nil { + return nil, errors.Wrap(err, "recipientToAddress() failed") + } + paymentsLength = len(subTx.Payments) + txID = *subTx.ID + sender, err = proto.NewAddressFromPublicKey(ia.settings.AddressSchemeCharacter, subTx.SenderPK) + if err != nil { + return nil, errors.Wrapf(err, "failed to apply script invocation") + } + case *proto.EthereumInvokeScriptTxKind: + scriptAddr = &subTx.To + decodedData := subTx.DecodedData() + paymentsLength = len(decodedData.Payments) + txID = *subTx.TxID + sender = subTx.From + default: + return nil, errors.New("wrong sub transaction of invoke script transaction") + } + + var err error tree, err = ia.stor.scriptsStorage.newestScriptByAddr(*scriptAddr, !info.initialisation) if err != nil { return nil, errors.Wrapf(err, "failed to instantiate script on address '%s'", scriptAddr.String()) @@ -753,66 +767,44 @@ func (ia *invokeApplier) applyInvokeScript(tx proto.Transaction, info *fallibleV if err != nil { return nil, errors.Wrapf(err, "failed to get script's public key on address '%s'", scriptAddr.String()) } + case *proto.InvokeExpressionTxUnion: + switch subTx := invokeType.SubTx.(type) { + case *proto.InvokeExpressionTransactionWithProofs: - case *proto.InvokeExpressionTransactionWithProofs: - addr, err := proto.NewAddressFromPublicKey(ia.settings.AddressSchemeCharacter, transaction.SenderPK) - if err != nil { - return nil, errors.Wrap(err, "recipientToAddress() failed") - } - sender = addr - scriptAddr = &addr - tree, err = ride.Parse(transaction.Expression) - if err != nil { - return nil, errors.Wrap(err, "failed to parse decoded invoke expression into tree") - } - txID = *transaction.ID - scriptPK = transaction.SenderPK - - case *proto.EthereumTransaction: - switch kind := transaction.TxKind.(type) { - case *proto.EthereumInvokeScriptTxKind: var err error - scriptAddr, err = transaction.WavesAddressTo(ia.settings.AddressSchemeCharacter) + sender, err = proto.NewAddressFromPublicKey(ia.settings.AddressSchemeCharacter, subTx.SenderPK) if err != nil { - return nil, err + return nil, errors.Wrap(err, "recipientToAddress() failed") } - decodedData := transaction.TxKind.DecodedData() - paymentsLength = len(decodedData.Payments) - txID = *transaction.ID - sender, err = transaction.WavesAddressFrom(ia.settings.AddressSchemeCharacter) + scriptAddr = &sender + tree, err = ride.Parse(subTx.Expression) if err != nil { - return nil, errors.Wrapf(err, "failed to apply script invocation") - } - tree, err = ia.stor.scriptsStorage.newestScriptByAddr(*scriptAddr, !info.initialisation) - if err != nil { - return nil, errors.Wrapf(err, "failed to instantiate script on address '%s'", scriptAddr.String()) - } - scriptPK, err = ia.stor.scriptsStorage.newestScriptPKByAddr(*scriptAddr, !info.initialisation) - if err != nil { - return nil, errors.Wrapf(err, "failed to get script's public key on address '%s'", scriptAddr.String()) + return nil, errors.Wrap(err, "failed to parse decoded invoke expression into tree") } + txID = *subTx.ID + scriptPK = subTx.SenderPK case *proto.EthereumInvokeExpressionTxKind: - address, err := transaction.WavesAddressFrom(ia.settings.AddressSchemeCharacter) - if err != nil { - return nil, err - } - sender = address - scriptAddr = &address - tree, err = ride.Parse([]byte(kind.Expression)) + var err error + sender = subTx.From + scriptAddr = &sender + tree, err = ride.Parse([]byte(subTx.Expression)) + if err != nil { return nil, errors.Wrap(err, "failed to parse decoded invoke expression into tree") } - txID = *transaction.ID + + txID = *subTx.TxID scriptPK, err = ia.stor.scriptsStorage.newestScriptPKByAddr(*scriptAddr, !info.initialisation) if err != nil { return nil, err } + default: + return nil, errors.Errorf("wrong sub transaction of invoke expression transaction: unexpected type of transaction (%T)", tx) } - default: + return nil, errors.Errorf("failed to apply an invoke script: unexpected type of transaction (%T)", tx) } - // If BlockV5 feature is not activated, we never accept failed transactions. info.acceptFailed = info.blockV5Activated && info.acceptFailed // Check sender script, if any. @@ -849,7 +841,7 @@ func (ia *invokeApplier) applyInvokeScript(tx proto.Transaction, info *fallibleV } // Call script function. - r, err := ia.sc.invokeFunction(tree, tx, info, *scriptAddr, txID) + r, err := ia.sc.invokeFunction(tree, tx, info, *scriptAddr, invokeUnion) if err != nil { // Script returned error, it's OK, but we have to decide is it failed or rejected transaction. // In the following cases the transaction is rejected: diff --git a/pkg/state/invoke_applier_test.go b/pkg/state/invoke_applier_test.go index f7ec887975..3be0eba0f3 100644 --- a/pkg/state/invoke_applier_test.go +++ b/pkg/state/invoke_applier_test.go @@ -108,8 +108,8 @@ func (to *invokeApplierTestObjects) applyAndSaveInvoke(t *testing.T, tx *proto.I to.state.stor.dropUncertain() to.state.appender.ia.sc.resetComplexity() }() - - res, err := to.state.appender.ia.applyInvokeScript(tx, info) + invokeUnion := proto.NewInvokeScriptTxUnion(tx, *tx.ID) + res, err := to.state.appender.ia.applyInvokeScript(tx, invokeUnion, info) require.NoError(t, err) err = to.state.appender.diffStor.saveTxDiff(res.changes.diff) assert.NoError(t, err) @@ -175,7 +175,8 @@ func (id *invokeApplierTestData) applyTest(t *testing.T, to *invokeApplierTestOb tx := createInvokeScriptWithProofs(t, id.payments, id.fc, feeAsset, invokeFee) if id.errorRes { - _, err := to.state.appender.ia.applyInvokeScript(tx, id.info) + invokeUnion := proto.NewInvokeScriptTxUnion(tx, *tx.ID) + _, err := to.state.appender.ia.applyInvokeScript(tx, invokeUnion, id.info) assert.Error(t, err) return } diff --git a/pkg/state/script_caller.go b/pkg/state/script_caller.go index a5cb04ed9e..4614833f66 100644 --- a/pkg/state/script_caller.go +++ b/pkg/state/script_caller.go @@ -205,168 +205,158 @@ func (a *scriptCaller) callAssetScript(tx proto.Transaction, assetID crypto.Dige return a.callAssetScriptCommon(env, assetID, params) } -func (a *scriptCaller) invokeFunction(tree *ride.Tree, tx proto.Transaction, info *fallibleValidationParams, scriptAddress proto.WavesAddress, txID crypto.Digest) (ride.Result, error) { - env, err := ride.NewEnvironment(a.settings.AddressSchemeCharacter, a.state, a.settings.InternalInvokePaymentsValidationAfterHeight) - if err != nil { - return nil, errors.Wrap(err, "failed to create RIDE environment") - } - env.SetThisFromAddress(scriptAddress) - env.SetLastBlock(info.blockInfo) - env.SetTimestamp(tx.GetTimestamp()) - err = env.SetTransaction(tx) - if err != nil { - return nil, err - } - env.ChooseSizeCheck(tree.LibVersion) - env.ChooseTakeString(info.rideV5Activated) - env.ChooseMaxDataEntriesSize(info.rideV5Activated) +type invokeParameters struct { + tree *ride.Tree + info *fallibleValidationParams + functionName string + defaultFunction bool +} +func (a *scriptCaller) invokeScriptFunction(invokeScriptUnion *proto.InvokeScriptTxUnion, + env *ride.EvaluationEnvironment, tx proto.Transaction, invParams *invokeParameters) (ride.Result, error) { var ( - functionName string + err error functionArguments proto.Arguments - defaultFunction bool payments proto.ScriptPayments sender proto.WavesAddress - r ride.Result ) - switch transaction := tx.(type) { + switch subTx := invokeScriptUnion.SubTx.(type) { case *proto.InvokeScriptWithProofs: - err = env.SetInvoke(tx, tree.LibVersion) + err = env.SetInvoke(tx, invParams.tree.LibVersion) if err != nil { return nil, err } - payments = transaction.Payments - sender, err = proto.NewAddressFromPublicKey(a.settings.AddressSchemeCharacter, transaction.SenderPK) + payments = subTx.Payments + sender, err = proto.NewAddressFromPublicKey(a.settings.AddressSchemeCharacter, subTx.SenderPK) if err != nil { return nil, err } - functionName = transaction.FunctionCall.Name - functionArguments = transaction.FunctionCall.Arguments - defaultFunction = transaction.FunctionCall.Default - // Since V5 we have to create environment with wrapped state to which we put attached payments - if tree.LibVersion >= 5 { - env, err = ride.NewEnvironmentWithWrappedState(env, payments, sender, info.rideV5Activated, info.rideV6Activated) - if err != nil { - return nil, errors.Wrapf(err, "failed to create RIDE environment with wrapped state") + invParams.functionName = subTx.FunctionCall.Name + functionArguments = subTx.FunctionCall.Arguments + invParams.defaultFunction = subTx.FunctionCall.Default + case *proto.EthereumInvokeScriptTxKind: + abiPayments := subTx.DecodedData().Payments + scriptPayments := make([]proto.ScriptPayment, 0, len(abiPayments)) + for _, p := range abiPayments { + var optAsset proto.OptionalAsset + if p.PresentAssetID { + optAsset = *proto.NewOptionalAssetFromDigest(p.AssetID) + } else { + optAsset = proto.NewOptionalAssetWaves() } + scriptPayment := proto.ScriptPayment{Amount: uint64(p.Amount), Asset: optAsset} + scriptPayments = append(scriptPayments, scriptPayment) } - - r, err = ride.CallFunction(env, tree, functionName, functionArguments) + payments = scriptPayments + err = env.SetEthereumInvoke(tx, invParams.tree.LibVersion, scriptPayments) if err != nil { - if appendErr := a.appendFunctionComplexity(ride.EvaluationErrorSpentComplexity(err), scriptAddress, functionName, defaultFunction, info); appendErr != nil { - return nil, appendErr - } return nil, err } + sender = subTx.From + decodedData := subTx.DecodedData() + invParams.functionName = decodedData.Name + arguments, err := ride.ConvertDecodedEthereumArgumentsToProtoArguments(decodedData.Inputs) + if err != nil { + return nil, errors.Errorf("failed to convert ethereum arguments, %v", err) + } + functionArguments = arguments + invParams.defaultFunction = true + default: + return nil, errors.New("wrong sub transaction of invoke script transaction") + } + + // Since V5 we have to create environment with wrapped state to which we put attached payments + if invParams.tree.LibVersion >= 5 { + env, err = ride.NewEnvironmentWithWrappedState(env, payments, sender, invParams.info.rideV5Activated, invParams.info.rideV6Activated) + if err != nil { + return nil, errors.Wrapf(err, "failed to create RIDE environment with wrapped state") + } + } + + return ride.CallFunction(env, invParams.tree, invParams.functionName, functionArguments) +} + +func (a *scriptCaller) invokeExpressionFunction(invokeExpressionUnion *proto.InvokeExpressionTxUnion, + env *ride.EvaluationEnvironment, + tx proto.Transaction, invParams *invokeParameters) (ride.Result, error) { + var ( + err error + sender proto.WavesAddress + ) + switch subTx := invokeExpressionUnion.SubTx.(type) { case *proto.InvokeExpressionTransactionWithProofs: - err = env.SetInvoke(tx, tree.LibVersion) + err = env.SetInvoke(tx, invParams.tree.LibVersion) if err != nil { return nil, err } - sender, err = proto.NewAddressFromPublicKey(a.settings.AddressSchemeCharacter, transaction.SenderPK) + sender, err = proto.NewAddressFromPublicKey(a.settings.AddressSchemeCharacter, subTx.SenderPK) if err != nil { return nil, err } - functionName = "" - - // Since V5 we have to create environment with wrapped state to which we put attached payments - if tree.LibVersion >= 5 { - env, err = ride.NewEnvironmentWithWrappedState(env, payments, sender, info.rideV5Activated, info.rideV6Activated) - if err != nil { - return nil, errors.Wrapf(err, "failed to create RIDE environment with wrapped state") - } + case *proto.EthereumInvokeExpressionTxKind: + err = env.SetEthereumInvoke(tx, invParams.tree.LibVersion, nil) + if err != nil { + return nil, err } + sender = subTx.From + default: + return nil, errors.New("wrong sub transaction of invoke expression transaction") + } - r, err = ride.CallVerifier(env, tree) + // Since V5 we have to create environment with wrapped state to which we put attached payments + if invParams.tree.LibVersion >= 5 { + env, err = ride.NewEnvironmentWithWrappedState(env, nil, sender, invParams.info.rideV5Activated, invParams.info.rideV6Activated) if err != nil { - if appendErr := a.appendFunctionComplexity(ride.EvaluationErrorSpentComplexity(err), scriptAddress, functionName, defaultFunction, info); appendErr != nil { - return nil, appendErr - } - return nil, err + return nil, errors.Wrapf(err, "failed to create RIDE environment with wrapped state") } + } + return ride.CallVerifier(env, invParams.tree) - case *proto.EthereumTransaction: - switch transaction.TxKind.(type) { - case *proto.EthereumInvokeScriptTxKind: - abiPayments := transaction.TxKind.DecodedData().Payments - scriptPayments := make([]proto.ScriptPayment, 0, len(abiPayments)) - for _, p := range abiPayments { - var optAsset proto.OptionalAsset - if p.PresentAssetID { - optAsset = *proto.NewOptionalAssetFromDigest(p.AssetID) - } else { - optAsset = proto.NewOptionalAssetWaves() - } - scriptPayment := proto.ScriptPayment{Amount: uint64(p.Amount), Asset: optAsset} - scriptPayments = append(scriptPayments, scriptPayment) - } - payments = scriptPayments +} - err = env.SetEthereumInvoke(transaction, tree.LibVersion, scriptPayments) - if err != nil { - return nil, err - } - sender, err = transaction.WavesAddressFrom(a.settings.AddressSchemeCharacter) - if err != nil { - return nil, errors.Errorf("failed to get waves address from ethereum transaction %v", err) - } - decodedData := transaction.TxKind.DecodedData() - functionName = decodedData.Name - arguments, err := ride.ConvertDecodedEthereumArgumentsToProtoArguments(decodedData.Inputs) - if err != nil { - return nil, errors.Errorf("failed to convert ethereum arguments, %v", err) - } - functionArguments = arguments - defaultFunction = true +func (a *scriptCaller) invokeFunction(tree *ride.Tree, tx proto.Transaction, info *fallibleValidationParams, scriptAddress proto.WavesAddress, invokeUnion proto.InvokeTxUnion) (ride.Result, error) { + env, err := ride.NewEnvironment(a.settings.AddressSchemeCharacter, a.state, a.settings.InternalInvokePaymentsValidationAfterHeight) + if err != nil { + return nil, errors.Wrap(err, "failed to create RIDE environment") + } + env.SetThisFromAddress(scriptAddress) + env.SetLastBlock(info.blockInfo) + env.SetTimestamp(tx.GetTimestamp()) + err = env.SetTransaction(tx) + if err != nil { + return nil, err + } + env.ChooseSizeCheck(tree.LibVersion) + env.ChooseTakeString(info.rideV5Activated) + env.ChooseMaxDataEntriesSize(info.rideV5Activated) - // Since V5 we have to create environment with wrapped state to which we put attached payments - if tree.LibVersion >= 5 { - env, err = ride.NewEnvironmentWithWrappedState(env, payments, sender, info.rideV5Activated, info.rideV6Activated) - if err != nil { - return nil, errors.Wrapf(err, "failed to create RIDE environment with wrapped state") - } - } + var r ride.Result - r, err = ride.CallFunction(env, tree, functionName, functionArguments) - if err != nil { - if appendErr := a.appendFunctionComplexity(ride.EvaluationErrorSpentComplexity(err), scriptAddress, functionName, defaultFunction, info); appendErr != nil { - return nil, appendErr - } - return nil, err - } - case *proto.EthereumInvokeExpressionTxKind: - err = env.SetEthereumInvoke(tx, tree.LibVersion, nil) - if err != nil { - return nil, err - } - sender, err = transaction.WavesAddressFrom(a.settings.AddressSchemeCharacter) - if err != nil { - return nil, err - } - functionName = "" - // Since V5 we have to create environment with wrapped state to which we put attached payments - if tree.LibVersion >= 5 { - env, err = ride.NewEnvironmentWithWrappedState(env, payments, sender, info.rideV5Activated, info.rideV6Activated) - if err != nil { - return nil, errors.Wrapf(err, "failed to create RIDE environment with wrapped state") - } - } + invParams := &invokeParameters{tree: tree, info: info, functionName: "", defaultFunction: false} - r, err = ride.CallVerifier(env, tree) - if err != nil { - if appendErr := a.appendFunctionComplexity(ride.EvaluationErrorSpentComplexity(err), scriptAddress, functionName, defaultFunction, info); appendErr != nil { - return nil, appendErr - } - return nil, err + switch invokeType := invokeUnion.(type) { + case *proto.InvokeScriptTxUnion: + r, err = a.invokeScriptFunction(invokeType, env, tx, invParams) + if err != nil { + if appendErr := a.appendFunctionComplexity(ride.EvaluationErrorSpentComplexity(err), scriptAddress, invParams.functionName, invParams.defaultFunction, info); appendErr != nil { + return nil, appendErr } + return nil, err // here is forbidden to make a custom error + } + case *proto.InvokeExpressionTxUnion: + r, err = a.invokeExpressionFunction(invokeType, env, tx, invParams) + if err != nil { + if appendErr := a.appendFunctionComplexity(ride.EvaluationErrorSpentComplexity(err), scriptAddress, invParams.functionName, invParams.defaultFunction, info); appendErr != nil { + return nil, appendErr + } + return nil, err // here is forbidden to make a custom error } - default: - return nil, errors.Errorf("failed to invoke function: unexpected type of transaction (%T)", transaction) + return nil, errors.Errorf("failed to invoke function: unexpected type of transaction (%T)", tx) } - if err := a.appendFunctionComplexity(r.Complexity(), scriptAddress, functionName, defaultFunction, info); err != nil { + if err := a.appendFunctionComplexity(r.Complexity(), scriptAddress, invParams.functionName, invParams.defaultFunction, info); err != nil { return nil, err } return r, nil