rpc/client: allow to use contract accounts in AddNetworkFee

This commit is contained in:
Evgenii Stratonikov 2020-08-24 14:00:05 +03:00
parent f6319f80e8
commit 9c7168e4e8
6 changed files with 129 additions and 33 deletions

View file

@ -226,24 +226,7 @@ func TestCreateBasicChain(t *testing.T) {
require.NoError(t, err)
// Push some contract into the chain.
name := prefix + "test_contract.go"
c, err := ioutil.ReadFile(name)
require.NoError(t, err)
avm, di, err := compiler.CompileWithDebugInfo(name, bytes.NewReader(c))
require.NoError(t, err)
t.Logf("contractHash: %s", hash.Hash160(avm).StringLE())
script := io.NewBufBinWriter()
m, err := di.ConvertToManifest(smartcontract.HasStorage, nil)
require.NoError(t, err)
bs, err := m.MarshalJSON()
require.NoError(t, err)
emit.Bytes(script.BinWriter, bs)
emit.Bytes(script.BinWriter, avm)
emit.Syscall(script.BinWriter, interopnames.SystemContractCreate)
txScript := script.Bytes()
txDeploy := transaction.New(testchain.Network(), txScript, 100*native.GASFactor)
txDeploy, avm := newDeployTx(t, prefix+"test_contract.go")
txDeploy.Nonce = getNextNonce()
txDeploy.ValidUntilBlock = validUntilBlock
txDeploy.Signers = []transaction.Signer{{Account: priv0ScriptHash}}
@ -255,7 +238,7 @@ func TestCreateBasicChain(t *testing.T) {
t.Logf("Block2 hash: %s", b.Hash().StringLE())
// Now invoke this contract.
script = io.NewBufBinWriter()
script := io.NewBufBinWriter()
emit.AppCallWithOperationAndArgs(script.BinWriter, hash.Hash160(avm), "putValue", "testkey", "testvalue")
txInv := transaction.New(testchain.Network(), script.Bytes(), 1*native.GASFactor)
@ -330,6 +313,16 @@ func TestCreateBasicChain(t *testing.T) {
require.NoError(t, bc.AddBlock(b))
t.Logf("sendRublesTx: %v", transferTx.Hash().StringLE())
// Push verification contract into the chain.
txDeploy2, _ := newDeployTx(t, prefix+"verification_contract.go")
txDeploy2.Nonce = getNextNonce()
txDeploy2.ValidUntilBlock = validUntilBlock
txDeploy2.Signers = []transaction.Signer{{Account: priv0ScriptHash}}
require.NoError(t, addNetworkFee(bc, txDeploy2, acc0))
require.NoError(t, acc0.SignTx(txDeploy2))
b = bc.newBlock(txDeploy2)
require.NoError(t, bc.AddBlock(b))
if saveChain {
outStream, err := os.Create(prefix + "testblocks.acc")
require.NoError(t, err)
@ -378,6 +371,27 @@ func newNEP5Transfer(sc, from, to util.Uint160, amount int64) *transaction.Trans
return transaction.New(testchain.Network(), script, 10000000)
}
func newDeployTx(t *testing.T, name string) (*transaction.Transaction, []byte) {
c, err := ioutil.ReadFile(name)
require.NoError(t, err)
avm, di, err := compiler.CompileWithDebugInfo(name, bytes.NewReader(c))
require.NoError(t, err)
t.Logf("contractHash (%s): %s", name, hash.Hash160(avm).StringLE())
t.Logf("contractScript: %x", avm)
script := io.NewBufBinWriter()
m, err := di.ConvertToManifest(smartcontract.HasStorage, nil)
require.NoError(t, err)
bs, err := m.MarshalJSON()
require.NoError(t, err)
emit.Bytes(script.BinWriter, bs)
emit.Bytes(script.BinWriter, avm)
emit.Syscall(script.BinWriter, interopnames.SystemContractCreate)
txScript := script.Bytes()
return transaction.New(testchain.Network(), txScript, 100*native.GASFactor), avm
}
func addSigners(txs ...*transaction.Transaction) {
for _, tx := range txs {
tx.Signers = []transaction.Signer{{

View file

@ -13,6 +13,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/rpc/request"
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/wallet"
)
@ -499,16 +500,19 @@ func (c *Client) AddNetworkFee(tx *transaction.Transaction, extraFee int64, accs
}
size := io.GetVarSize(tx)
for i, cosigner := range tx.Signers {
if accs[i].Contract.Script == nil {
contract, err := c.GetContractState(cosigner.Account)
if err == nil {
if contract == nil {
continue
if accs[i].Contract.Deployed {
res, err := c.InvokeFunction(cosigner.Account, manifest.MethodVerify, []smartcontract.Parameter{}, tx.Signers)
if err == nil && res.State == "HALT" && len(res.Stack) == 1 {
r, err := topIntFromStack(res.Stack)
if err != nil || r == 0 {
return core.ErrVerificationFailed
}
netFee, sizeDelta := core.CalculateNetworkFee(contract.Script)
tx.NetworkFee += netFee
size += sizeDelta
} else {
return core.ErrVerificationFailed
}
tx.NetworkFee += res.GasConsumed
size += io.GetVarSize([]byte{}) * 2 // both scripts are empty
continue
}
netFee, sizeDelta := core.CalculateNetworkFee(accs[i].Contract.Script)
tx.NetworkFee += netFee

View file

@ -2,6 +2,7 @@ package server
import (
"context"
"encoding/hex"
"testing"
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
@ -132,6 +133,65 @@ func TestAddNetworkFee(t *testing.T) {
cFeeM, _ := core.CalculateNetworkFee(accs[1].Contract.Script)
require.Equal(t, int64(io.GetVarSize(tx))*feePerByte+cFee+cFeeM+10, tx.NetworkFee)
})
t.Run("Contract", func(t *testing.T) {
tx := transaction.New(testchain.Network(), []byte{byte(opcode.PUSH1)}, 0)
priv := testchain.PrivateKeyByID(0)
acc1, err := wallet.NewAccountFromWIF(priv.WIF())
require.NoError(t, err)
acc1.Contract.Deployed = true
acc1.Contract.Script, _ = hex.DecodeString(verifyContractAVM)
h, _ := util.Uint160DecodeStringLE(verifyContractHash)
tx.ValidUntilBlock = chain.BlockHeight() + 10
t.Run("Valid", func(t *testing.T) {
acc0, err := wallet.NewAccountFromWIF(priv.WIF())
require.NoError(t, err)
tx.Signers = []transaction.Signer{
{
Account: acc0.PrivateKey().GetScriptHash(),
Scopes: transaction.CalledByEntry,
},
{
Account: h,
Scopes: transaction.Global,
},
}
require.NoError(t, c.AddNetworkFee(tx, 10, acc0, acc1))
require.NoError(t, acc0.SignTx(tx))
tx.Scripts = append(tx.Scripts, transaction.Witness{})
require.NoError(t, chain.VerifyTx(tx))
})
t.Run("Invalid", func(t *testing.T) {
acc0, err := wallet.NewAccount()
require.NoError(t, err)
tx.Signers = []transaction.Signer{
{
Account: acc0.PrivateKey().GetScriptHash(),
Scopes: transaction.CalledByEntry,
},
{
Account: h,
Scopes: transaction.Global,
},
}
require.Error(t, c.AddNetworkFee(tx, 10, acc0, acc1))
})
t.Run("InvalidContract", func(t *testing.T) {
acc0, err := wallet.NewAccountFromWIF(priv.WIF())
require.NoError(t, err)
tx.Signers = []transaction.Signer{
{
Account: acc0.PrivateKey().GetScriptHash(),
Scopes: transaction.CalledByEntry,
},
{
Account: util.Uint160{},
Scopes: transaction.Global,
},
}
require.Error(t, c.AddNetworkFee(tx, 10, acc0, acc1))
})
})
}
func TestSignAndPushInvocationTx(t *testing.T) {

View file

@ -51,8 +51,11 @@ type rpcTestCase struct {
check func(t *testing.T, e *executor, result interface{})
}
const testContractHash = "93c4983afe01a75f74c1e56011bd630e9d8cc755"
const deploymentTxHash = "583cf0e49d69d8854869efc3e97ad741061da478292a7280580789351a39a1ac"
const testContractHash = "4546ec6fcdaa1c3ccdb048526b78624b457b60a4"
const deploymentTxHash = "17be1bbb0fdecae18cd4c6a2db19311f47bd540371e2ea479a46b349a66aa0b3"
const verifyContractHash = "47ef649f9a77cad161ddaa28b39c7e450e5429e7"
const verifyContractAVM = "560340570300412d510830db4121700c14aa8acf859d4fe402b34e673f2156821796a488ebdb30716813cedb2869db289740"
var rpcTestCases = map[string][]rpcTestCase{
"getapplicationlog": {
@ -438,7 +441,7 @@ var rpcTestCases = map[string][]rpcTestCase{
require.True(t, ok)
expected := result.UnclaimedGas{
Address: testchain.MultisigScriptHash(),
Unclaimed: *big.NewInt(36000),
Unclaimed: *big.NewInt(42000),
}
assert.Equal(t, expected, *actual)
},
@ -814,7 +817,7 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) []
require.NoErrorf(t, err, "could not parse response: %s", txOut)
assert.Equal(t, *block.Transactions[0], actual.Transaction)
assert.Equal(t, 8, actual.Confirmations)
assert.Equal(t, 9, actual.Confirmations)
assert.Equal(t, TXHash, actual.Transaction.Hash())
})
@ -999,8 +1002,8 @@ func checkNep5Balances(t *testing.T, e *executor, acc interface{}) {
},
{
Asset: e.chain.UtilityTokenHash(),
Amount: "915.61059740",
LastUpdated: 6,
Amount: "815.59478530",
LastUpdated: 7,
}},
Address: testchain.PrivateKeyByID(0).GetScriptHash().StringLE(),
}

Binary file not shown.

View file

@ -0,0 +1,15 @@
package testdata
import (
"github.com/nspcc-dev/neo-go/pkg/interop/convert"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/util"
)
// Verify is a verification contract method.
// It returns true iff it is signed by NbTiM6h8r99kpRtb428XcsUk1TzKed2gTc (id-0 private key from testchain).
func Verify() bool {
tx := runtime.GetScriptContainer()
addr := util.FromAddress("NbTiM6h8r99kpRtb428XcsUk1TzKed2gTc")
return util.Equals(convert.ToByteArray(tx.Sender), convert.ToByteArray(addr))
}