core: fix GAS distribution to primary node when notary enabled

Close #2310.
This commit is contained in:
AnnaShaleva 2021-12-13 18:16:27 +03:00
parent b43335e591
commit 8c6de35ec2
3 changed files with 113 additions and 4 deletions

View file

@ -75,7 +75,7 @@ func NewContracts(cfg config.ProtocolConfiguration) *Contracts {
cs.Ledger = ledger
cs.Contracts = append(cs.Contracts, ledger)
gas := newGAS(int64(cfg.InitialGASSupply))
gas := newGAS(int64(cfg.InitialGASSupply), cfg.P2PSigExtensions)
neo := newNEO()
neo.GAS = gas
gas.NEO = neo

View file

@ -8,6 +8,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/interop"
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
"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/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util"
@ -19,6 +20,7 @@ type GAS struct {
NEO *NEO
initialSupply int64
p2pSigExtensionsEnabled bool
}
const gasContractID = -6
@ -27,8 +29,11 @@ const gasContractID = -6
const GASFactor = NEOTotalSupply
// newGAS returns GAS native contract.
func newGAS(init int64) *GAS {
g := &GAS{initialSupply: init}
func newGAS(init int64, p2pSigExtensionsEnabled bool) *GAS {
g := &GAS{
initialSupply: init,
p2pSigExtensionsEnabled: p2pSigExtensionsEnabled,
}
defer g.UpdateHash()
nep17 := newNEP17Native(nativenames.Gas, gasContractID)
@ -105,6 +110,15 @@ func (g *GAS) OnPersist(ic *interop.Context) error {
var netFee int64
for _, tx := range ic.Block.Transactions {
netFee += tx.NetworkFee
if g.p2pSigExtensionsEnabled {
// Reward for NotaryAssisted attribute will be minted to designated notary nodes
// by Notary contract.
attrs := tx.GetAttributes(transaction.NotaryAssistedT)
if len(attrs) != 0 {
na := attrs[0].Value.(*transaction.NotaryAssisted)
netFee -= (int64(na.NKeys) + 1) * transaction.NotaryServiceFeePerKey
}
}
}
g.mint(ic, primary, big.NewInt(int64(netFee)), false)
return nil

View file

@ -4,10 +4,17 @@ import (
"math/big"
"testing"
"github.com/nspcc-dev/neo-go/internal/testchain"
"github.com/nspcc-dev/neo-go/pkg/core/interop"
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@ -52,3 +59,91 @@ func TestGAS_Roundtrip(t *testing.T) {
require.Equal(t, bc.BlockHeight(), updatedHeight)
})
}
func TestGAS_RewardWithP2PSigExtensionsEnabled(t *testing.T) {
chain := newTestChain(t)
notaryHash := chain.contracts.Notary.Hash
gasHash := chain.contracts.GAS.Hash
signer := testchain.MultisigScriptHash()
var err error
const (
nNotaries = 2
nKeys = 4
)
// set Notary nodes and check their balance
notaryNodes := make([]*keys.PrivateKey, nNotaries)
notaryNodesPublicKeys := make(keys.PublicKeys, nNotaries)
for i := range notaryNodes {
notaryNodes[i], err = keys.NewPrivateKey()
require.NoError(t, err)
notaryNodesPublicKeys[i] = notaryNodes[i].PublicKey()
}
chain.setNodesByRole(t, true, noderoles.P2PNotary, notaryNodesPublicKeys)
for _, notaryNode := range notaryNodesPublicKeys {
checkBalanceOf(t, chain, notaryNode.GetScriptHash(), 0)
}
// deposit GAS for `signer` with lock until the next block
depositAmount := 100_0000 + (2+int64(nKeys))*transaction.NotaryServiceFeePerKey // sysfee + netfee of the next transaction
transferTx := transferTokenFromMultisigAccount(t, chain, notaryHash, gasHash, depositAmount, signer, int64(chain.BlockHeight()+1))
res, err := chain.GetAppExecResults(transferTx.Hash(), trigger.Application)
require.NoError(t, err)
require.Equal(t, vm.HaltState, res[0].VMState)
require.Equal(t, 0, len(res[0].Stack))
// save initial validators balance
balances := make(map[int]int64, testchain.ValidatorsCount)
for i := 0; i < testchain.ValidatorsCount; i++ {
balances[i] = chain.GetUtilityTokenBalance(testchain.PrivateKeyByID(i).GetScriptHash()).Int64()
}
ic := interop.NewContext(trigger.Application, chain, chain.dao, nil, nil, nil, nil, chain.log)
tsInitial := chain.contracts.GAS.TotalSupply(ic, nil).Value().(*big.Int).Int64()
// send transaction with Notary contract as a sender
tx := chain.newTestTx(util.Uint160{}, []byte{byte(opcode.PUSH1)})
tx.Attributes = append(tx.Attributes, transaction.Attribute{Type: transaction.NotaryAssistedT, Value: &transaction.NotaryAssisted{NKeys: uint8(nKeys)}})
tx.NetworkFee = (2 + int64(nKeys)) * transaction.NotaryServiceFeePerKey
tx.Signers = []transaction.Signer{
{
Account: notaryHash,
Scopes: transaction.None,
},
{
Account: signer,
Scopes: transaction.None,
},
}
tx.Scripts = []transaction.Witness{
{
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, notaryNodes[0].SignHashable(uint32(testchain.Network()), tx)...),
},
{
InvocationScript: testchain.Sign(tx),
VerificationScript: testchain.MultisigVerificationScript(),
},
}
b := chain.newBlock(tx)
require.NoError(t, chain.AddBlock(b))
checkBalanceOf(t, chain, notaryHash, int(depositAmount-tx.SystemFee-tx.NetworkFee))
singleReward := transaction.NotaryServiceFeePerKey * (nKeys + 1) / nNotaries
for _, notaryNode := range notaryNodesPublicKeys {
checkBalanceOf(t, chain, notaryNode.GetScriptHash(), singleReward)
}
for i := 0; i < testchain.ValidatorsCount; i++ {
newBalance := chain.GetUtilityTokenBalance(testchain.PrivateKeyByID(i).GetScriptHash()).Int64()
expectedBalance := balances[i]
if i == int(b.Index)%testchain.CommitteeSize() {
// committee reward
expectedBalance += 5000_0000
}
if testchain.IDToOrder(i) == int(b.PrimaryIndex) {
// primary reward
expectedBalance += tx.NetworkFee - int64(singleReward*nNotaries)
}
assert.Equal(t, expectedBalance, newBalance, i)
}
tsUpdated := chain.contracts.GAS.TotalSupply(ic, nil).Value().(*big.Int).Int64()
tsExpected := tsInitial + 5000_0000 - tx.SystemFee
require.Equal(t, tsExpected, tsUpdated)
}