rpc/client: provide scripts in AddNetworkFee

To calculate network fee properly we must know type of every
signer (simple, multisig, contract). Providing scripts is the most
simple and flexible way to know this.
This commit is contained in:
Evgenii Stratonikov 2020-08-17 11:12:21 +03:00
parent 58af143f25
commit 8699a4c1a9
2 changed files with 89 additions and 18 deletions

View file

@ -9,7 +9,6 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/block" "github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/rpc/request" "github.com/nspcc-dev/neo-go/pkg/rpc/request"
@ -513,27 +512,25 @@ func (c *Client) CalculateValidUntilBlock() (uint32, error) {
} }
// AddNetworkFee adds network fee for each witness script and optional extra // AddNetworkFee adds network fee for each witness script and optional extra
// network fee to transaction. // network fee to transaction. `accs` is an array signer's accounts.
func (c *Client) AddNetworkFee(tx *transaction.Transaction, extraFee int64, acc *wallet.Account) error { func (c *Client) AddNetworkFee(tx *transaction.Transaction, extraFee int64, accs ...*wallet.Account) error {
if len(tx.Signers) != len(accs) {
return errors.New("number of signers must match number of scripts")
}
size := io.GetVarSize(tx) size := io.GetVarSize(tx)
if acc.Contract != nil { for i, cosigner := range tx.Signers {
netFee, sizeDelta := core.CalculateNetworkFee(acc.Contract.Script) if accs[i].Contract.Script == nil {
tx.NetworkFee += netFee
size += sizeDelta
}
for _, cosigner := range tx.Signers {
script := acc.Contract.Script
if !cosigner.Account.Equals(hash.Hash160(acc.Contract.Script)) {
contract, err := c.GetContractState(cosigner.Account) contract, err := c.GetContractState(cosigner.Account)
if err != nil { if err == nil {
return err
}
if contract == nil { if contract == nil {
continue continue
} }
script = contract.Script netFee, sizeDelta := core.CalculateNetworkFee(contract.Script)
tx.NetworkFee += netFee
size += sizeDelta
} }
netFee, sizeDelta := core.CalculateNetworkFee(script) }
netFee, sizeDelta := core.CalculateNetworkFee(accs[i].Contract.Script)
tx.NetworkFee += netFee tx.NetworkFee += netFee
size += sizeDelta size += sizeDelta
} }

View file

@ -5,9 +5,16 @@ import (
"testing" "testing"
"github.com/nspcc-dev/neo-go/pkg/config/netmode" "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/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/internal/testchain" "github.com/nspcc-dev/neo-go/pkg/internal/testchain"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/rpc/client" "github.com/nspcc-dev/neo-go/pkg/rpc/client"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -57,3 +64,70 @@ func TestClient_NEP5(t *testing.T) {
require.EqualValues(t, 877, b) require.EqualValues(t, 877, b)
}) })
} }
func TestAddNetworkFee(t *testing.T) {
chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t)
defer chain.Close()
defer rpcSrv.Shutdown()
c, err := client.New(context.Background(), httpSrv.URL, client.Options{Network: testchain.Network()})
require.NoError(t, err)
getAccounts := func(t *testing.T, n int) []*wallet.Account {
accs := make([]*wallet.Account, n)
var err error
for i := range accs {
accs[i], err = wallet.NewAccount()
require.NoError(t, err)
}
return accs
}
feePerByte := chain.FeePerByte()
t.Run("Invalid", func(t *testing.T) {
tx := transaction.New(testchain.Network(), []byte{byte(opcode.PUSH1)}, 0)
accs := getAccounts(t, 2)
tx.Signers = []transaction.Signer{{
Account: accs[0].PrivateKey().GetScriptHash(),
Scopes: transaction.CalledByEntry,
}}
require.Error(t, c.AddNetworkFee(tx, 10, accs[0], accs[1]))
})
t.Run("Simple", func(t *testing.T) {
tx := transaction.New(testchain.Network(), []byte{byte(opcode.PUSH1)}, 0)
accs := getAccounts(t, 1)
tx.Signers = []transaction.Signer{{
Account: accs[0].PrivateKey().GetScriptHash(),
Scopes: transaction.CalledByEntry,
}}
require.NoError(t, c.AddNetworkFee(tx, 10, accs[0]))
require.NoError(t, accs[0].SignTx(tx))
cFee, _ := core.CalculateNetworkFee(accs[0].Contract.Script)
require.Equal(t, int64(io.GetVarSize(tx))*feePerByte+cFee+10, tx.NetworkFee)
})
t.Run("Multi", func(t *testing.T) {
tx := transaction.New(testchain.Network(), []byte{byte(opcode.PUSH1)}, 0)
accs := getAccounts(t, 3)
pubs := keys.PublicKeys{accs[1].PrivateKey().PublicKey(), accs[2].PrivateKey().PublicKey()}
require.NoError(t, accs[1].ConvertMultisig(1, pubs))
require.NoError(t, accs[2].ConvertMultisig(1, pubs))
tx.Signers = []transaction.Signer{
{
Account: accs[0].PrivateKey().GetScriptHash(),
Scopes: transaction.CalledByEntry,
},
{
Account: hash.Hash160(accs[1].Contract.Script),
Scopes: transaction.Global,
},
}
require.NoError(t, c.AddNetworkFee(tx, 10, accs[0], accs[1]))
require.NoError(t, accs[0].SignTx(tx))
require.NoError(t, accs[1].SignTx(tx))
cFee, _ := core.CalculateNetworkFee(accs[0].Contract.Script)
cFeeM, _ := core.CalculateNetworkFee(accs[1].Contract.Script)
require.Equal(t, int64(io.GetVarSize(tx))*feePerByte+cFee+cFeeM+10, tx.NetworkFee)
})
}