diff --git a/cli/smartcontract/smart_contract.go b/cli/smartcontract/smart_contract.go index a58111162..7fb687e0a 100644 --- a/cli/smartcontract/smart_contract.go +++ b/cli/smartcontract/smart_contract.go @@ -443,7 +443,7 @@ func contractCompile(ctx *cli.Context) error { } if len(confFile) != 0 { - conf, err := parseContractConfig(confFile) + conf, err := ParseContractConfig(confFile) if err != nil { return err } @@ -875,7 +875,8 @@ func contractDeploy(ctx *cli.Context) error { return nil } -func parseContractConfig(confFile string) (ProjectConfig, error) { +// ParseContractConfig reads contract configuration file (.yaml) and returns unmarshalled ProjectConfig. +func ParseContractConfig(confFile string) (ProjectConfig, error) { conf := ProjectConfig{} confBytes, err := ioutil.ReadFile(confFile) if err != nil { diff --git a/internal/testchain/transaction.go b/internal/testchain/transaction.go index d9a42b43f..13b0e9283 100644 --- a/internal/testchain/transaction.go +++ b/internal/testchain/transaction.go @@ -2,8 +2,10 @@ package testchain import ( "encoding/json" + "fmt" gio "io" + "github.com/nspcc-dev/neo-go/cli/smartcontract" "github.com/nspcc-dev/neo-go/pkg/compiler" "github.com/nspcc-dev/neo-go/pkg/config" "github.com/nspcc-dev/neo-go/pkg/config/netmode" @@ -49,7 +51,7 @@ func NewTransferFromOwner(bc blockchainer.Blockchainer, contractHash, to util.Ui } // NewDeployTx returns new deployment tx for contract with name with Go code read from r. -func NewDeployTx(bc blockchainer.Blockchainer, name string, sender util.Uint160, r gio.Reader) (*transaction.Transaction, util.Uint160, []byte, error) { +func NewDeployTx(bc blockchainer.Blockchainer, name string, sender util.Uint160, r gio.Reader, confFile *string) (*transaction.Transaction, util.Uint160, []byte, error) { // nef.NewFile() cares about version a lot. config.Version = "0.90.0-test" @@ -63,9 +65,25 @@ func NewDeployTx(bc blockchainer.Blockchainer, name string, sender util.Uint160, return nil, util.Uint160{}, nil, err } - m, err := di.ConvertToManifest(&compiler.Options{Name: name}) + o := &compiler.Options{ + Name: name, + NoStandardCheck: true, + NoEventsCheck: true, + } + if confFile != nil { + conf, err := smartcontract.ParseContractConfig(*confFile) + if err != nil { + return nil, util.Uint160{}, nil, fmt.Errorf("failed to parse configuration: %w", err) + } + o.Name = conf.Name + o.ContractEvents = conf.Events + o.ContractSupportedStandards = conf.SupportedStandards + o.SafeMethods = conf.SafeMethods + + } + m, err := compiler.CreateManifest(di, o) if err != nil { - return nil, util.Uint160{}, nil, err + return nil, util.Uint160{}, nil, fmt.Errorf("failed to create manifest: %w", err) } rawManifest, err := json.Marshal(m) diff --git a/pkg/core/blockchain_test.go b/pkg/core/blockchain_test.go index 45119d6f8..bc933a83f 100644 --- a/pkg/core/blockchain_test.go +++ b/pkg/core/blockchain_test.go @@ -1230,7 +1230,7 @@ func TestIsTxStillRelevant(t *testing.T) { currentHeight := contract.Call(addr, "currentIndex", contract.ReadStates) return currentHeight.(int) < %d }`, bc.BlockHeight()+2) // deploy + next block - txDeploy, h, _, err := testchain.NewDeployTx(bc, "TestVerify", neoOwner, strings.NewReader(src)) + txDeploy, h, _, err := testchain.NewDeployTx(bc, "TestVerify", neoOwner, strings.NewReader(src), nil) require.NoError(t, err) txDeploy.ValidUntilBlock = bc.BlockHeight() + 1 addSigners(neoOwner, txDeploy) diff --git a/pkg/core/helper_test.go b/pkg/core/helper_test.go index 1ed189a8a..751039bbc 100644 --- a/pkg/core/helper_test.go +++ b/pkg/core/helper_test.go @@ -313,6 +313,8 @@ func initBasicChain(t *testing.T, bc *Blockchain) { b := bc.newBlock(txMoveNeo, txMoveGas) require.NoError(t, bc.AddBlock(b)) + checkTxHalt(t, bc, txMoveGas.Hash()) + checkTxHalt(t, bc, txMoveNeo.Hash()) t.Logf("Block1 hash: %s", b.Hash().StringLE()) bw := io.NewBufBinWriter() b.EncodeBinary(bw.BinWriter) @@ -345,13 +347,15 @@ func initBasicChain(t *testing.T, bc *Blockchain) { acc0 := wallet.NewAccountFromPrivateKey(priv0) // Push some contract into the chain. - txDeploy, cHash := newDeployTx(t, bc, priv0ScriptHash, prefix+"test_contract.go", "Rubl") + cfgPath := prefix + "test_contract.yml" + txDeploy, cHash := newDeployTx(t, bc, priv0ScriptHash, prefix+"test_contract.go", "Rubl", &cfgPath) txDeploy.Nonce = getNextNonce() txDeploy.ValidUntilBlock = validUntilBlock require.NoError(t, addNetworkFee(bc, txDeploy, acc0)) require.NoError(t, acc0.SignTx(txDeploy)) b = bc.newBlock(txDeploy) require.NoError(t, bc.AddBlock(b)) + checkTxHalt(t, bc, txDeploy.Hash()) t.Logf("txDeploy: %s", txDeploy.Hash().StringLE()) t.Logf("Block2 hash: %s", b.Hash().StringLE()) @@ -367,6 +371,7 @@ func initBasicChain(t *testing.T, bc *Blockchain) { require.NoError(t, acc0.SignTx(txInv)) b = bc.newBlock(txInv) require.NoError(t, bc.AddBlock(b)) + checkTxHalt(t, bc, txInv.Hash()) t.Logf("txInv: %s", txInv.Hash().StringLE()) priv1 := testchain.PrivateKeyByID(1) @@ -385,6 +390,7 @@ func initBasicChain(t *testing.T, bc *Blockchain) { require.NoError(t, acc0.SignTx(txNeo0to1)) b = bc.newBlock(txNeo0to1) require.NoError(t, bc.AddBlock(b)) + checkTxHalt(t, bc, txNeo0to1.Hash()) w := io.NewBufBinWriter() emit.AppCall(w.BinWriter, cHash, "init", callflag.All) @@ -410,6 +416,8 @@ func initBasicChain(t *testing.T, bc *Blockchain) { b = bc.newBlock(initTx, transferTx) require.NoError(t, bc.AddBlock(b)) + checkTxHalt(t, bc, initTx.Hash()) + checkTxHalt(t, bc, transferTx.Hash()) t.Logf("recieveRublesTx: %v", transferTx.Hash().StringLE()) transferTx = newNEP17Transfer(cHash, priv0.GetScriptHash(), priv1.GetScriptHash(), 123) @@ -428,16 +436,18 @@ func initBasicChain(t *testing.T, bc *Blockchain) { b = bc.newBlock(transferTx) require.NoError(t, bc.AddBlock(b)) + checkTxHalt(t, bc, transferTx.Hash()) t.Logf("sendRublesTx: %v", transferTx.Hash().StringLE()) // Push verification contract into the chain. - txDeploy2, _ := newDeployTx(t, bc, priv0ScriptHash, prefix+"verification_contract.go", "Verify") + txDeploy2, _ := newDeployTx(t, bc, priv0ScriptHash, prefix+"verification_contract.go", "Verify", nil) txDeploy2.Nonce = getNextNonce() txDeploy2.ValidUntilBlock = validUntilBlock require.NoError(t, addNetworkFee(bc, txDeploy2, acc0)) require.NoError(t, acc0.SignTx(txDeploy2)) b = bc.newBlock(txDeploy2) require.NoError(t, bc.AddBlock(b)) + checkTxHalt(t, bc, txDeploy2.Hash()) // Deposit some GAS to notary contract for priv0 transferTx = newNEP17Transfer(gasHash, priv0.GetScriptHash(), notaryHash, 10_0000_0000, priv0.GetScriptHash(), int64(bc.BlockHeight()+1000)) @@ -455,6 +465,7 @@ func initBasicChain(t *testing.T, bc *Blockchain) { b = bc.newBlock(transferTx) require.NoError(t, bc.AddBlock(b)) + checkTxHalt(t, bc, transferTx.Hash()) t.Logf("notaryDepositTxPriv0: %v", transferTx.Hash().StringLE()) // Designate new Notary node @@ -465,16 +476,17 @@ func initBasicChain(t *testing.T, bc *Blockchain) { t.Logf("Designated Notary node: %s", hex.EncodeToString(ntr.Accounts[0].PrivateKey().PublicKey().Bytes())) // Push verification contract with arguments into the chain. - txDeploy3, _ := newDeployTx(t, bc, priv0ScriptHash, prefix+"verification_with_args_contract.go", "VerifyWithArgs") + txDeploy3, _ := newDeployTx(t, bc, priv0ScriptHash, prefix+"verification_with_args_contract.go", "VerifyWithArgs", nil) txDeploy3.Nonce = getNextNonce() txDeploy3.ValidUntilBlock = validUntilBlock require.NoError(t, addNetworkFee(bc, txDeploy3, acc0)) require.NoError(t, acc0.SignTx(txDeploy3)) b = bc.newBlock(txDeploy3) require.NoError(t, bc.AddBlock(b)) + checkTxHalt(t, bc, txDeploy3.Hash()) // Compile contract to test `invokescript` RPC call - _, _ = newDeployTx(t, bc, priv0ScriptHash, prefix+"invokescript_contract.go", "ContractForInvokescriptTest") + _, _ = newDeployTx(t, bc, priv0ScriptHash, prefix+"invokescript_contract.go", "ContractForInvokescriptTest", nil) } func newNEP17Transfer(sc, from, to util.Uint160, amount int64, additionalArgs ...interface{}) *transaction.Transaction { @@ -489,10 +501,10 @@ func newNEP17Transfer(sc, from, to util.Uint160, amount int64, additionalArgs .. return transaction.New(testchain.Network(), script, 11000000) } -func newDeployTx(t *testing.T, bc *Blockchain, sender util.Uint160, name, ctrName string) (*transaction.Transaction, util.Uint160) { +func newDeployTx(t *testing.T, bc *Blockchain, sender util.Uint160, name, ctrName string, cfgName *string) (*transaction.Transaction, util.Uint160) { c, err := ioutil.ReadFile(name) require.NoError(t, err) - tx, h, avm, err := testchain.NewDeployTx(bc, ctrName, sender, bytes.NewReader(c)) + tx, h, avm, err := testchain.NewDeployTx(bc, ctrName, sender, bytes.NewReader(c), cfgName) require.NoError(t, err) t.Logf("contract (%s): \n\tHash: %s\n\tAVM: %s", name, h.StringLE(), base64.StdEncoding.EncodeToString(avm)) return tx, h diff --git a/pkg/rpc/client/rpc.go b/pkg/rpc/client/rpc.go index 1308d9848..a70a10242 100644 --- a/pkg/rpc/client/rpc.go +++ b/pkg/rpc/client/rpc.go @@ -6,7 +6,6 @@ import ( "fmt" "github.com/nspcc-dev/neo-go/pkg/config/netmode" - "github.com/nspcc-dev/neo-go/pkg/core" "github.com/nspcc-dev/neo-go/pkg/core/block" "github.com/nspcc-dev/neo-go/pkg/core/fee" "github.com/nspcc-dev/neo-go/pkg/core/native" @@ -781,8 +780,11 @@ func (c *Client) AddNetworkFee(tx *transaction.Transaction, extraFee int64, accs return fmt.Errorf("result stack length should be equal to 1, got %d", l) } r, err := topIntFromStack(res.Stack) - if err != nil || r == 0 { - return core.ErrVerificationFailed + if err != nil { + return fmt.Errorf("signer #%d: failed to get `verify` result from stack: %w", i, err) + } + if r == 0 { + return fmt.Errorf("signer #%d: `verify` returned `false`", i) } tx.NetworkFee += res.GasConsumed size += io.GetVarSize([]byte{}) * 2 // both scripts are empty diff --git a/pkg/rpc/server/server_test.go b/pkg/rpc/server/server_test.go index 762511946..aa222cb32 100644 --- a/pkg/rpc/server/server_test.go +++ b/pkg/rpc/server/server_test.go @@ -60,8 +60,8 @@ type rpcTestCase struct { check func(t *testing.T, e *executor, result interface{}) } -const testContractHash = "1e1c3024bd955ff3baf7cb92e3b7608c7bb3712b" -const deploymentTxHash = "9218bba2a6e145aab5dc21c4bb16a650efdf0b9b16b0d69bd754278363a1d1c2" +const testContractHash = "c6ca2347bb84b99807221365c900ec069a265e7c" +const deploymentTxHash = "fdd4f9252cde778010d14e9710efeeb80796fd38d778e9943c1d5bb2dc656c99" const genesisBlockHash = "5b60644c6c6f58faca72c70689d7ed1f40c2e795772bd0de5a88e983ad55080c" const verifyContractHash = "5bb4bac40e961e334ba7bd36d2496010f67e246e" @@ -1650,7 +1650,7 @@ func checkNep17Balances(t *testing.T, e *executor, acc interface{}) { }, { Asset: e.chain.UtilityTokenHash(), - Amount: "68992647820", + Amount: "68992456820", LastUpdated: 10, }}, Address: testchain.PrivateKeyByID(0).GetScriptHash().StringLE(), diff --git a/pkg/rpc/server/testdata/test_contract.go b/pkg/rpc/server/testdata/test_contract.go index 60b7909a4..01e6a63f4 100644 --- a/pkg/rpc/server/testdata/test_contract.go +++ b/pkg/rpc/server/testdata/test_contract.go @@ -1,6 +1,9 @@ package testdata import ( + "github.com/nspcc-dev/neo-go/pkg/interop" + "github.com/nspcc-dev/neo-go/pkg/interop/contract" + "github.com/nspcc-dev/neo-go/pkg/interop/native/management" "github.com/nspcc-dev/neo-go/pkg/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/interop/storage" ) @@ -19,7 +22,7 @@ func Init() bool { return true } -func Transfer(from, to []byte, amount int, data interface{}) bool { +func Transfer(from, to interop.Hash160, amount int, data interface{}) bool { ctx := storage.GetContext() if len(from) != 20 { runtime.Log("invalid 'from' address") @@ -55,22 +58,22 @@ func Transfer(from, to []byte, amount int, data interface{}) bool { storage.Put(ctx, to, toBalance) runtime.Notify("Transfer", from, to, amount) - + if management.GetContract(to) != nil { + contract.Call(to, "onNEP17Payment", contract.All, from, amount, data) + } return true } -func BalanceOf(addr []byte) int { +func BalanceOf(account interop.Hash160) int { ctx := storage.GetContext() - if len(addr) != 20 { - runtime.Log("invalid address") - return 0 + if len(account) != 20 { + panic("invalid address") } var amount int - val := storage.Get(ctx, addr) + val := storage.Get(ctx, account) if val != nil { amount = val.(int) } - runtime.Notify("balanceOf", addr, amount) return amount } diff --git a/pkg/rpc/server/testdata/test_contract.yml b/pkg/rpc/server/testdata/test_contract.yml new file mode 100644 index 000000000..604bc1b0d --- /dev/null +++ b/pkg/rpc/server/testdata/test_contract.yml @@ -0,0 +1,12 @@ +name: "Rubl" +supportedstandards: ["NEP-17"] +safemethods: ["balanceOf", "decimals", "symbol", "totalSupply"] +events: + - name: Transfer + parameters: + - name: from + type: Hash160 + - name: to + type: Hash160 + - name: amount + type: Integer diff --git a/pkg/rpc/server/testdata/testblocks.acc b/pkg/rpc/server/testdata/testblocks.acc index 298b5ed6f..fd8c3bc97 100644 Binary files a/pkg/rpc/server/testdata/testblocks.acc and b/pkg/rpc/server/testdata/testblocks.acc differ diff --git a/scripts/gendump/main.go b/scripts/gendump/main.go index 40581d2ae..0c1f1df64 100644 --- a/scripts/gendump/main.go +++ b/scripts/gendump/main.go @@ -75,7 +75,7 @@ func main() { handleError("can't tranfser GAS", err) lastBlock = addBlock(bc, lastBlock, valScript, txMoveNeo, txMoveGas) - tx, contractHash, _, err := testchain.NewDeployTx(bc, "DumpContract", h, strings.NewReader(contract)) + tx, contractHash, _, err := testchain.NewDeployTx(bc, "DumpContract", h, strings.NewReader(contract), nil) handleError("can't create deploy tx", err) tx.NetworkFee = 10_000_000 tx.ValidUntilBlock = bc.BlockHeight() + 1