commit
5e18a6141e
11 changed files with 100 additions and 61 deletions
|
@ -8,12 +8,12 @@ import (
|
|||
func Opcode(base int64, opcodes ...opcode.Opcode) int64 {
|
||||
var result int64
|
||||
for _, op := range opcodes {
|
||||
result += coefficients[op]
|
||||
result += int64(coefficients[op])
|
||||
}
|
||||
return result * base
|
||||
}
|
||||
|
||||
var coefficients = map[opcode.Opcode]int64{
|
||||
var coefficients = [256]uint16{
|
||||
opcode.PUSHINT8: 1 << 0,
|
||||
opcode.PUSHINT16: 1 << 0,
|
||||
opcode.PUSHINT32: 1 << 0,
|
||||
|
|
19
pkg/core/fee/opcode_test.go
Normal file
19
pkg/core/fee/opcode_test.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
package fee
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||
)
|
||||
|
||||
const feeFactor = 30
|
||||
|
||||
// The most common Opcode() use case is to get price for single opcode.
|
||||
func BenchmarkOpcode1(t *testing.B) {
|
||||
// Just so that we don't always test the same opcode.
|
||||
script := []opcode.Opcode{opcode.NOP, opcode.ADD, opcode.SYSCALL, opcode.APPEND}
|
||||
l := len(script)
|
||||
for n := 0; n < t.N; n++ {
|
||||
_ = Opcode(feeFactor, script[n%l])
|
||||
}
|
||||
}
|
|
@ -54,13 +54,17 @@ func newGAS(init int64) *GAS {
|
|||
return g
|
||||
}
|
||||
|
||||
func (g *GAS) increaseBalance(_ *interop.Context, _ util.Uint160, si *state.StorageItem, amount *big.Int) error {
|
||||
func (g *GAS) increaseBalance(_ *interop.Context, _ util.Uint160, si *state.StorageItem, amount *big.Int, checkBal *big.Int) error {
|
||||
acc, err := state.NEP17BalanceFromBytes(*si)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if sign := amount.Sign(); sign == 0 {
|
||||
return nil
|
||||
// Requested self-transfer amount can be higher than actual balance.
|
||||
if checkBal != nil && acc.Balance.Cmp(checkBal) < 0 {
|
||||
err = errors.New("insufficient funds")
|
||||
}
|
||||
return err
|
||||
} else if sign == -1 && acc.Balance.Cmp(new(big.Int).Neg(amount)) == -1 {
|
||||
return errors.New("insufficient funds")
|
||||
}
|
||||
|
@ -104,7 +108,8 @@ func (g *GAS) Initialize(ic *interop.Context) error {
|
|||
if err := g.nep17TokenNative.Initialize(ic); err != nil {
|
||||
return err
|
||||
}
|
||||
if g.nep17TokenNative.getTotalSupply(ic.DAO).Sign() != 0 {
|
||||
_, totalSupply := g.nep17TokenNative.getTotalSupply(ic.DAO)
|
||||
if totalSupply.Sign() != 0 {
|
||||
return errors.New("already initialized")
|
||||
}
|
||||
h, err := getStandbyValidatorsHash(ic)
|
||||
|
|
|
@ -189,7 +189,8 @@ func (n *NEO) Initialize(ic *interop.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if n.nep17TokenNative.getTotalSupply(ic.DAO).Sign() != 0 {
|
||||
_, totalSupply := n.nep17TokenNative.getTotalSupply(ic.DAO)
|
||||
if totalSupply.Sign() != 0 {
|
||||
return errors.New("already initialized")
|
||||
}
|
||||
|
||||
|
@ -391,12 +392,13 @@ func (n *NEO) getGASPerVote(d dao.DAO, key []byte, index ...uint32) []big.Int {
|
|||
return reward
|
||||
}
|
||||
|
||||
func (n *NEO) increaseBalance(ic *interop.Context, h util.Uint160, si *state.StorageItem, amount *big.Int) error {
|
||||
func (n *NEO) increaseBalance(ic *interop.Context, h util.Uint160, si *state.StorageItem, amount *big.Int, checkBal *big.Int) error {
|
||||
acc, err := state.NEOBalanceFromBytes(*si)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if amount.Sign() == -1 && acc.Balance.Cmp(new(big.Int).Neg(amount)) == -1 {
|
||||
if (amount.Sign() == -1 && acc.Balance.Cmp(new(big.Int).Neg(amount)) == -1) ||
|
||||
(amount.Sign() == 0 && checkBal != nil && acc.Balance.Cmp(checkBal) == -1) {
|
||||
return errors.New("insufficient funds")
|
||||
}
|
||||
if err := n.distributeGas(ic, h, acc); err != nil {
|
||||
|
@ -976,7 +978,8 @@ func (n *NEO) computeCommitteeMembers(bc blockchainer.Blockchainer, d dao.DAO) (
|
|||
votersCount := bigint.FromBytes(si)
|
||||
// votersCount / totalSupply must be >= 0.2
|
||||
votersCount.Mul(votersCount, big.NewInt(effectiveVoterTurnout))
|
||||
voterTurnout := votersCount.Div(votersCount, n.getTotalSupply(d))
|
||||
_, totalSupply := n.getTotalSupply(d)
|
||||
voterTurnout := votersCount.Div(votersCount, totalSupply)
|
||||
|
||||
sbVals := bc.GetStandByCommittee()
|
||||
count := len(sbVals)
|
||||
|
|
|
@ -33,7 +33,7 @@ type nep17TokenNative struct {
|
|||
symbol string
|
||||
decimals int64
|
||||
factor int64
|
||||
incBalance func(*interop.Context, util.Uint160, *state.StorageItem, *big.Int) error
|
||||
incBalance func(*interop.Context, util.Uint160, *state.StorageItem, *big.Int, *big.Int) error
|
||||
balFromBytes func(item *state.StorageItem) (*big.Int, error)
|
||||
}
|
||||
|
||||
|
@ -95,19 +95,21 @@ func (c *nep17TokenNative) Decimals(_ *interop.Context, _ []stackitem.Item) stac
|
|||
}
|
||||
|
||||
func (c *nep17TokenNative) TotalSupply(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
|
||||
return stackitem.NewBigInteger(c.getTotalSupply(ic.DAO))
|
||||
_, supply := c.getTotalSupply(ic.DAO)
|
||||
return stackitem.NewBigInteger(supply)
|
||||
}
|
||||
|
||||
func (c *nep17TokenNative) getTotalSupply(d dao.DAO) *big.Int {
|
||||
func (c *nep17TokenNative) getTotalSupply(d dao.DAO) (state.StorageItem, *big.Int) {
|
||||
si := d.GetStorageItem(c.ID, totalSupplyKey)
|
||||
if si == nil {
|
||||
return big.NewInt(0)
|
||||
si = []byte{}
|
||||
}
|
||||
return bigint.FromBytes(si)
|
||||
return si, bigint.FromBytes(si)
|
||||
}
|
||||
|
||||
func (c *nep17TokenNative) saveTotalSupply(d dao.DAO, supply *big.Int) error {
|
||||
return d.PutStorageItem(c.ID, totalSupplyKey, bigint.ToBytes(supply))
|
||||
func (c *nep17TokenNative) saveTotalSupply(d dao.DAO, si state.StorageItem, supply *big.Int) error {
|
||||
si = state.StorageItem(bigint.ToPreallocatedBytes(supply, si))
|
||||
return d.PutStorageItem(c.ID, totalSupplyKey, si)
|
||||
}
|
||||
|
||||
func (c *nep17TokenNative) Transfer(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||
|
@ -173,29 +175,11 @@ func (c *nep17TokenNative) updateAccBalance(ic *interop.Context, acc util.Uint16
|
|||
return errors.New("insufficient funds")
|
||||
}
|
||||
si = state.StorageItem{}
|
||||
} else if amount.Sign() == 0 && requiredBalance != nil {
|
||||
// If amount == 0 then it's either a round trip or an empty transfer. In
|
||||
// case of a round trip account's balance may still be less than actual
|
||||
// transfer's amount, so we need to check it. Other cases are handled by
|
||||
// `incBalance` method.
|
||||
balance, err := c.balFromBytes(&si)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to deserialise balance: %w", err)
|
||||
}
|
||||
if balance.Cmp(requiredBalance) < 0 {
|
||||
// Firstly, need to put it back to storage as it affects dumps.
|
||||
err = ic.DAO.PutStorageItem(c.ID, key, si)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Finally, return an error.
|
||||
return errors.New("insufficient funds")
|
||||
}
|
||||
}
|
||||
|
||||
err := c.incBalance(ic, acc, &si, amount)
|
||||
err := c.incBalance(ic, acc, &si, amount, requiredBalance)
|
||||
if err != nil {
|
||||
if si != nil && amount.Sign() < 0 {
|
||||
if si != nil && amount.Sign() <= 0 {
|
||||
_ = ic.DAO.PutStorageItem(c.ID, key, si)
|
||||
}
|
||||
return err
|
||||
|
@ -288,7 +272,7 @@ func (c *nep17TokenNative) addTokens(ic *interop.Context, h util.Uint160, amount
|
|||
if si == nil {
|
||||
si = state.StorageItem{}
|
||||
}
|
||||
if err := c.incBalance(ic, h, &si, amount); err != nil {
|
||||
if err := c.incBalance(ic, h, &si, amount, nil); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
var err error
|
||||
|
@ -301,9 +285,9 @@ func (c *nep17TokenNative) addTokens(ic *interop.Context, h util.Uint160, amount
|
|||
panic(err)
|
||||
}
|
||||
|
||||
supply := c.getTotalSupply(ic.DAO)
|
||||
buf, supply := c.getTotalSupply(ic.DAO)
|
||||
supply.Add(supply, amount)
|
||||
err = c.saveTotalSupply(ic.DAO, supply)
|
||||
err = c.saveTotalSupply(ic.DAO, buf, supply)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
|
@ -91,15 +91,18 @@ func (s *NEOBalance) Bytes() []byte {
|
|||
|
||||
// ToStackItem implements stackitem.Convertible interface. It never returns an error.
|
||||
func (s *NEOBalance) ToStackItem() (stackitem.Item, error) {
|
||||
resItem, _ := s.NEP17Balance.ToStackItem()
|
||||
result := resItem.(*stackitem.Struct)
|
||||
result.Append(stackitem.NewBigInteger(big.NewInt(int64(s.BalanceHeight))))
|
||||
var voteItem stackitem.Item
|
||||
|
||||
if s.VoteTo != nil {
|
||||
result.Append(stackitem.NewByteArray(s.VoteTo.Bytes()))
|
||||
voteItem = stackitem.NewByteArray(s.VoteTo.Bytes())
|
||||
} else {
|
||||
result.Append(stackitem.Null{})
|
||||
voteItem = stackitem.Null{}
|
||||
}
|
||||
return result, nil
|
||||
return stackitem.NewStruct([]stackitem.Item{
|
||||
stackitem.NewBigInteger(&s.Balance),
|
||||
stackitem.NewBigInteger(big.NewInt(int64(s.BalanceHeight))),
|
||||
voteItem,
|
||||
}), nil
|
||||
}
|
||||
|
||||
// FromStackItem converts stackitem.Item to NEOBalance.
|
||||
|
|
15
pkg/vm/opcode/isvalid_test.go
Normal file
15
pkg/vm/opcode/isvalid_test.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
package opcode
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// IsValid() is called for every VM instruction.
|
||||
func BenchmarkIsValid(t *testing.B) {
|
||||
// Just so that we don't always test the same opcode.
|
||||
script := []Opcode{NOP, ADD, SYSCALL, APPEND, 0xff, 0xf0}
|
||||
l := len(script)
|
||||
for n := 0; n < t.N; n++ {
|
||||
_ = IsValid(script[n%l])
|
||||
}
|
||||
}
|
|
@ -222,8 +222,16 @@ const (
|
|||
CONVERT Opcode = 0xDB
|
||||
)
|
||||
|
||||
var validCodes [256]bool
|
||||
|
||||
func init() {
|
||||
// We rely on stringer here, it has a map anyway.
|
||||
for op := range _Opcode_map {
|
||||
validCodes[int(op)] = true
|
||||
}
|
||||
}
|
||||
|
||||
// IsValid returns true if the opcode passed is valid (defined in the VM).
|
||||
func IsValid(op Opcode) bool {
|
||||
_, ok := _Opcode_map[op] // We rely on stringer here, it has a map anyway.
|
||||
return ok
|
||||
return validCodes[int(op)]
|
||||
}
|
||||
|
|
|
@ -18,6 +18,9 @@ func newRefCounter() *refCounter {
|
|||
|
||||
// Add adds an item to the reference counter.
|
||||
func (r *refCounter) Add(item stackitem.Item) {
|
||||
if r == nil {
|
||||
return
|
||||
}
|
||||
r.size++
|
||||
|
||||
switch item.(type) {
|
||||
|
@ -41,6 +44,9 @@ func (r *refCounter) Add(item stackitem.Item) {
|
|||
|
||||
// Remove removes item from the reference counter.
|
||||
func (r *refCounter) Remove(item stackitem.Item) {
|
||||
if r == nil {
|
||||
return
|
||||
}
|
||||
r.size--
|
||||
|
||||
switch item.(type) {
|
||||
|
|
|
@ -158,13 +158,16 @@ type Stack struct {
|
|||
|
||||
// NewStack returns a new stack name by the given name.
|
||||
func NewStack(n string) *Stack {
|
||||
return newStack(n, newRefCounter())
|
||||
}
|
||||
|
||||
func newStack(n string, refc *refCounter) *Stack {
|
||||
s := &Stack{
|
||||
name: n,
|
||||
refs: refc,
|
||||
}
|
||||
s.top.next = &s.top
|
||||
s.top.prev = &s.top
|
||||
s.len = 0
|
||||
s.refs = newRefCounter()
|
||||
return s
|
||||
}
|
||||
|
||||
|
|
17
pkg/vm/vm.go
17
pkg/vm/vm.go
|
@ -99,7 +99,7 @@ func New() *VM {
|
|||
func NewWithTrigger(t trigger.Type) *VM {
|
||||
vm := &VM{
|
||||
state: NoneState,
|
||||
istack: NewStack("invocation"),
|
||||
istack: newStack("invocation", nil),
|
||||
refs: newRefCounter(),
|
||||
trigger: t,
|
||||
|
||||
|
@ -107,17 +107,10 @@ func NewWithTrigger(t trigger.Type) *VM {
|
|||
Invocations: make(map[util.Uint160]int),
|
||||
}
|
||||
|
||||
vm.estack = vm.newItemStack("evaluation")
|
||||
vm.estack = newStack("evaluation", vm.refs)
|
||||
return vm
|
||||
}
|
||||
|
||||
func (v *VM) newItemStack(n string) *Stack {
|
||||
s := NewStack(n)
|
||||
s.refs = v.refs
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// SetPriceGetter registers the given PriceGetterFunc in v.
|
||||
// f accepts vm's Context, current instruction and instruction parameter.
|
||||
func (v *VM) SetPriceGetter(f func(opcode.Opcode, []byte) int64) {
|
||||
|
@ -288,9 +281,9 @@ func (v *VM) LoadScript(b []byte) {
|
|||
func (v *VM) LoadScriptWithFlags(b []byte, f callflag.CallFlag) {
|
||||
v.checkInvocationStackSize()
|
||||
ctx := NewContextWithParams(b, 0, -1, 0)
|
||||
v.estack = v.newItemStack("estack")
|
||||
v.estack = newStack("evaluation", v.refs)
|
||||
ctx.estack = v.estack
|
||||
ctx.tryStack = NewStack("exception")
|
||||
ctx.tryStack = newStack("exception", nil)
|
||||
ctx.callFlag = f
|
||||
ctx.static = newSlot(v.refs)
|
||||
ctx.callingScriptHash = v.GetCurrentScriptHash()
|
||||
|
@ -1527,7 +1520,7 @@ func (v *VM) call(ctx *Context, offset int) {
|
|||
newCtx.RetCount = -1
|
||||
newCtx.local = nil
|
||||
newCtx.arguments = nil
|
||||
newCtx.tryStack = NewStack("exception")
|
||||
newCtx.tryStack = newStack("exception", nil)
|
||||
newCtx.NEF = ctx.NEF
|
||||
v.istack.PushVal(newCtx)
|
||||
v.Jump(newCtx, offset)
|
||||
|
|
Loading…
Reference in a new issue