commit
5e18a6141e
11 changed files with 100 additions and 61 deletions
|
@ -8,12 +8,12 @@ import (
|
||||||
func Opcode(base int64, opcodes ...opcode.Opcode) int64 {
|
func Opcode(base int64, opcodes ...opcode.Opcode) int64 {
|
||||||
var result int64
|
var result int64
|
||||||
for _, op := range opcodes {
|
for _, op := range opcodes {
|
||||||
result += coefficients[op]
|
result += int64(coefficients[op])
|
||||||
}
|
}
|
||||||
return result * base
|
return result * base
|
||||||
}
|
}
|
||||||
|
|
||||||
var coefficients = map[opcode.Opcode]int64{
|
var coefficients = [256]uint16{
|
||||||
opcode.PUSHINT8: 1 << 0,
|
opcode.PUSHINT8: 1 << 0,
|
||||||
opcode.PUSHINT16: 1 << 0,
|
opcode.PUSHINT16: 1 << 0,
|
||||||
opcode.PUSHINT32: 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
|
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)
|
acc, err := state.NEP17BalanceFromBytes(*si)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if sign := amount.Sign(); sign == 0 {
|
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 {
|
} else if sign == -1 && acc.Balance.Cmp(new(big.Int).Neg(amount)) == -1 {
|
||||||
return errors.New("insufficient funds")
|
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 {
|
if err := g.nep17TokenNative.Initialize(ic); err != nil {
|
||||||
return err
|
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")
|
return errors.New("already initialized")
|
||||||
}
|
}
|
||||||
h, err := getStandbyValidatorsHash(ic)
|
h, err := getStandbyValidatorsHash(ic)
|
||||||
|
|
|
@ -189,7 +189,8 @@ func (n *NEO) Initialize(ic *interop.Context) error {
|
||||||
return err
|
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")
|
return errors.New("already initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -391,12 +392,13 @@ func (n *NEO) getGASPerVote(d dao.DAO, key []byte, index ...uint32) []big.Int {
|
||||||
return reward
|
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)
|
acc, err := state.NEOBalanceFromBytes(*si)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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")
|
return errors.New("insufficient funds")
|
||||||
}
|
}
|
||||||
if err := n.distributeGas(ic, h, acc); err != nil {
|
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 := bigint.FromBytes(si)
|
||||||
// votersCount / totalSupply must be >= 0.2
|
// votersCount / totalSupply must be >= 0.2
|
||||||
votersCount.Mul(votersCount, big.NewInt(effectiveVoterTurnout))
|
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()
|
sbVals := bc.GetStandByCommittee()
|
||||||
count := len(sbVals)
|
count := len(sbVals)
|
||||||
|
|
|
@ -33,7 +33,7 @@ type nep17TokenNative struct {
|
||||||
symbol string
|
symbol string
|
||||||
decimals int64
|
decimals int64
|
||||||
factor 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)
|
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 {
|
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)
|
si := d.GetStorageItem(c.ID, totalSupplyKey)
|
||||||
if si == nil {
|
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 {
|
func (c *nep17TokenNative) saveTotalSupply(d dao.DAO, si state.StorageItem, supply *big.Int) error {
|
||||||
return d.PutStorageItem(c.ID, totalSupplyKey, bigint.ToBytes(supply))
|
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 {
|
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")
|
return errors.New("insufficient funds")
|
||||||
}
|
}
|
||||||
si = state.StorageItem{}
|
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 err != nil {
|
||||||
if si != nil && amount.Sign() < 0 {
|
if si != nil && amount.Sign() <= 0 {
|
||||||
_ = ic.DAO.PutStorageItem(c.ID, key, si)
|
_ = ic.DAO.PutStorageItem(c.ID, key, si)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
|
@ -288,7 +272,7 @@ func (c *nep17TokenNative) addTokens(ic *interop.Context, h util.Uint160, amount
|
||||||
if si == nil {
|
if si == nil {
|
||||||
si = state.StorageItem{}
|
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)
|
panic(err)
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
|
@ -301,9 +285,9 @@ func (c *nep17TokenNative) addTokens(ic *interop.Context, h util.Uint160, amount
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
supply := c.getTotalSupply(ic.DAO)
|
buf, supply := c.getTotalSupply(ic.DAO)
|
||||||
supply.Add(supply, amount)
|
supply.Add(supply, amount)
|
||||||
err = c.saveTotalSupply(ic.DAO, supply)
|
err = c.saveTotalSupply(ic.DAO, buf, supply)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,15 +91,18 @@ func (s *NEOBalance) Bytes() []byte {
|
||||||
|
|
||||||
// ToStackItem implements stackitem.Convertible interface. It never returns an error.
|
// ToStackItem implements stackitem.Convertible interface. It never returns an error.
|
||||||
func (s *NEOBalance) ToStackItem() (stackitem.Item, error) {
|
func (s *NEOBalance) ToStackItem() (stackitem.Item, error) {
|
||||||
resItem, _ := s.NEP17Balance.ToStackItem()
|
var voteItem stackitem.Item
|
||||||
result := resItem.(*stackitem.Struct)
|
|
||||||
result.Append(stackitem.NewBigInteger(big.NewInt(int64(s.BalanceHeight))))
|
|
||||||
if s.VoteTo != nil {
|
if s.VoteTo != nil {
|
||||||
result.Append(stackitem.NewByteArray(s.VoteTo.Bytes()))
|
voteItem = stackitem.NewByteArray(s.VoteTo.Bytes())
|
||||||
} else {
|
} 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.
|
// 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
|
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).
|
// IsValid returns true if the opcode passed is valid (defined in the VM).
|
||||||
func IsValid(op Opcode) bool {
|
func IsValid(op Opcode) bool {
|
||||||
_, ok := _Opcode_map[op] // We rely on stringer here, it has a map anyway.
|
return validCodes[int(op)]
|
||||||
return ok
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,9 @@ func newRefCounter() *refCounter {
|
||||||
|
|
||||||
// Add adds an item to the reference counter.
|
// Add adds an item to the reference counter.
|
||||||
func (r *refCounter) Add(item stackitem.Item) {
|
func (r *refCounter) Add(item stackitem.Item) {
|
||||||
|
if r == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
r.size++
|
r.size++
|
||||||
|
|
||||||
switch item.(type) {
|
switch item.(type) {
|
||||||
|
@ -41,6 +44,9 @@ func (r *refCounter) Add(item stackitem.Item) {
|
||||||
|
|
||||||
// Remove removes item from the reference counter.
|
// Remove removes item from the reference counter.
|
||||||
func (r *refCounter) Remove(item stackitem.Item) {
|
func (r *refCounter) Remove(item stackitem.Item) {
|
||||||
|
if r == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
r.size--
|
r.size--
|
||||||
|
|
||||||
switch item.(type) {
|
switch item.(type) {
|
||||||
|
|
|
@ -158,13 +158,16 @@ type Stack struct {
|
||||||
|
|
||||||
// NewStack returns a new stack name by the given name.
|
// NewStack returns a new stack name by the given name.
|
||||||
func NewStack(n string) *Stack {
|
func NewStack(n string) *Stack {
|
||||||
|
return newStack(n, newRefCounter())
|
||||||
|
}
|
||||||
|
|
||||||
|
func newStack(n string, refc *refCounter) *Stack {
|
||||||
s := &Stack{
|
s := &Stack{
|
||||||
name: n,
|
name: n,
|
||||||
|
refs: refc,
|
||||||
}
|
}
|
||||||
s.top.next = &s.top
|
s.top.next = &s.top
|
||||||
s.top.prev = &s.top
|
s.top.prev = &s.top
|
||||||
s.len = 0
|
|
||||||
s.refs = newRefCounter()
|
|
||||||
return s
|
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 {
|
func NewWithTrigger(t trigger.Type) *VM {
|
||||||
vm := &VM{
|
vm := &VM{
|
||||||
state: NoneState,
|
state: NoneState,
|
||||||
istack: NewStack("invocation"),
|
istack: newStack("invocation", nil),
|
||||||
refs: newRefCounter(),
|
refs: newRefCounter(),
|
||||||
trigger: t,
|
trigger: t,
|
||||||
|
|
||||||
|
@ -107,17 +107,10 @@ func NewWithTrigger(t trigger.Type) *VM {
|
||||||
Invocations: make(map[util.Uint160]int),
|
Invocations: make(map[util.Uint160]int),
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.estack = vm.newItemStack("evaluation")
|
vm.estack = newStack("evaluation", vm.refs)
|
||||||
return vm
|
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.
|
// SetPriceGetter registers the given PriceGetterFunc in v.
|
||||||
// f accepts vm's Context, current instruction and instruction parameter.
|
// f accepts vm's Context, current instruction and instruction parameter.
|
||||||
func (v *VM) SetPriceGetter(f func(opcode.Opcode, []byte) int64) {
|
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) {
|
func (v *VM) LoadScriptWithFlags(b []byte, f callflag.CallFlag) {
|
||||||
v.checkInvocationStackSize()
|
v.checkInvocationStackSize()
|
||||||
ctx := NewContextWithParams(b, 0, -1, 0)
|
ctx := NewContextWithParams(b, 0, -1, 0)
|
||||||
v.estack = v.newItemStack("estack")
|
v.estack = newStack("evaluation", v.refs)
|
||||||
ctx.estack = v.estack
|
ctx.estack = v.estack
|
||||||
ctx.tryStack = NewStack("exception")
|
ctx.tryStack = newStack("exception", nil)
|
||||||
ctx.callFlag = f
|
ctx.callFlag = f
|
||||||
ctx.static = newSlot(v.refs)
|
ctx.static = newSlot(v.refs)
|
||||||
ctx.callingScriptHash = v.GetCurrentScriptHash()
|
ctx.callingScriptHash = v.GetCurrentScriptHash()
|
||||||
|
@ -1527,7 +1520,7 @@ func (v *VM) call(ctx *Context, offset int) {
|
||||||
newCtx.RetCount = -1
|
newCtx.RetCount = -1
|
||||||
newCtx.local = nil
|
newCtx.local = nil
|
||||||
newCtx.arguments = nil
|
newCtx.arguments = nil
|
||||||
newCtx.tryStack = NewStack("exception")
|
newCtx.tryStack = newStack("exception", nil)
|
||||||
newCtx.NEF = ctx.NEF
|
newCtx.NEF = ctx.NEF
|
||||||
v.istack.PushVal(newCtx)
|
v.istack.PushVal(newCtx)
|
||||||
v.Jump(newCtx, offset)
|
v.Jump(newCtx, offset)
|
||||||
|
|
Loading…
Reference in a new issue