native: replace NEP-5 with NEP-17
This commit is contained in:
parent
a5914f89fa
commit
b97dfae8d8
27 changed files with 195 additions and 134 deletions
|
@ -93,7 +93,7 @@ func TestSignMultisigTx(t *testing.T) {
|
||||||
e.Chain.GoverningTokenHash().StringLE(), "transfer",
|
e.Chain.GoverningTokenHash().StringLE(), "transfer",
|
||||||
"bytes:"+multisigHash.StringBE(),
|
"bytes:"+multisigHash.StringBE(),
|
||||||
"bytes:"+priv.GetScriptHash().StringBE(),
|
"bytes:"+priv.GetScriptHash().StringBE(),
|
||||||
"int:1",
|
"int:1", "bytes:",
|
||||||
"--", strings.Join([]string{multisigHash.StringLE(), ":", "Global"}, ""))
|
"--", strings.Join([]string{multisigHash.StringLE(), ":", "Global"}, ""))
|
||||||
|
|
||||||
e.In.WriteString("pass\r")
|
e.In.WriteString("pass\r")
|
||||||
|
|
3
cli/testdata/verify.go
vendored
3
cli/testdata/verify.go
vendored
|
@ -3,3 +3,6 @@ package testdata
|
||||||
func Verify() bool {
|
func Verify() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func OnPayment(from []byte, amount int, data interface{}) {
|
||||||
|
}
|
||||||
|
|
2
cli/testdata/verify.manifest.json
vendored
2
cli/testdata/verify.manifest.json
vendored
|
@ -1 +1 @@
|
||||||
{"name":"Verify","abi":{"hash":"0x8dff9f223e4622961f410c015dd37052a59892bb","methods":[{"name":"verify","offset":0,"parameters":[],"returntype":"Boolean"}],"events":[]},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"safemethods":[],"extra":null}
|
{"name":"verify","abi":{"hash":"0xbf214a7551e50d6fbe0bef05271719325d9fc1ef","methods":[{"name":"verify","offset":0,"parameters":[],"returntype":"Boolean"},{"name":"onPayment","offset":5,"parameters":[{"name":"from","type":"ByteArray"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Void"}],"events":[]},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"safemethods":[],"extra":null}
|
BIN
cli/testdata/verify.nef
vendored
BIN
cli/testdata/verify.nef
vendored
Binary file not shown.
|
@ -133,14 +133,6 @@ func checkOwnerWitness() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Name returns the token name
|
|
||||||
func Name() interface{} {
|
|
||||||
if trigger != runtime.Application {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return token.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decimals returns the token decimals
|
// Decimals returns the token decimals
|
||||||
func Decimals() interface{} {
|
func Decimals() interface{} {
|
||||||
if trigger != runtime.Application {
|
if trigger != runtime.Application {
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
name: "My awesome token"
|
name: "My awesome token"
|
||||||
supportedstandards: ["NEP-5"]
|
supportedstandards: ["NEP-17"]
|
||||||
events: []
|
events: []
|
||||||
|
|
|
@ -44,7 +44,7 @@ func (t Token) BalanceOf(ctx storage.Context, holder []byte) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transfer token from one user to another
|
// Transfer token from one user to another
|
||||||
func (t Token) Transfer(ctx storage.Context, from []byte, to []byte, amount int) bool {
|
func (t Token) Transfer(ctx storage.Context, from []byte, to []byte, amount int, data interface{}) bool {
|
||||||
amountFrom := t.CanTransfer(ctx, from, to, amount)
|
amountFrom := t.CanTransfer(ctx, from, to, amount)
|
||||||
if amountFrom == -1 {
|
if amountFrom == -1 {
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -32,11 +32,6 @@ func init() {
|
||||||
ctx = storage.GetContext()
|
ctx = storage.GetContext()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Name returns the token name
|
|
||||||
func Name() string {
|
|
||||||
return token.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
// Symbol returns the token symbol
|
// Symbol returns the token symbol
|
||||||
func Symbol() string {
|
func Symbol() string {
|
||||||
return token.Symbol
|
return token.Symbol
|
||||||
|
@ -58,8 +53,8 @@ func BalanceOf(holder interop.Hash160) interface{} {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transfer token from one user to another
|
// Transfer token from one user to another
|
||||||
func Transfer(from interop.Hash160, to interop.Hash160, amount int) bool {
|
func Transfer(from interop.Hash160, to interop.Hash160, amount int, data interface{}) bool {
|
||||||
return token.Transfer(ctx, from, to, amount)
|
return token.Transfer(ctx, from, to, amount, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mint initial supply of tokens
|
// Mint initial supply of tokens
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
name: "Awesome NEO Token"
|
name: "Awesome NEO Token"
|
||||||
supportedstandards: ["NEP-5"]
|
supportedstandards: ["NEP-17"]
|
||||||
events:
|
events:
|
||||||
- name: transfer
|
- name: Transfer
|
||||||
parameters:
|
parameters:
|
||||||
- name: from
|
- name: from
|
||||||
type: ByteString
|
type: ByteString
|
||||||
|
|
|
@ -54,10 +54,10 @@ func initServiceNextConsensus(t *testing.T, newAcc *wallet.Account, offset uint3
|
||||||
// Transfer funds to new validator.
|
// Transfer funds to new validator.
|
||||||
w := io.NewBufBinWriter()
|
w := io.NewBufBinWriter()
|
||||||
emit.AppCallWithOperationAndArgs(w.BinWriter, bc.GoverningTokenHash(), "transfer",
|
emit.AppCallWithOperationAndArgs(w.BinWriter, bc.GoverningTokenHash(), "transfer",
|
||||||
acc.Contract.ScriptHash().BytesBE(), newPriv.GetScriptHash().BytesBE(), int64(native.NEOTotalSupply))
|
acc.Contract.ScriptHash().BytesBE(), newPriv.GetScriptHash().BytesBE(), int64(native.NEOTotalSupply), nil)
|
||||||
emit.Opcodes(w.BinWriter, opcode.ASSERT)
|
emit.Opcodes(w.BinWriter, opcode.ASSERT)
|
||||||
emit.AppCallWithOperationAndArgs(w.BinWriter, bc.UtilityTokenHash(), "transfer",
|
emit.AppCallWithOperationAndArgs(w.BinWriter, bc.UtilityTokenHash(), "transfer",
|
||||||
acc.Contract.ScriptHash().BytesBE(), newPriv.GetScriptHash().BytesBE(), int64(1_000_000_000))
|
acc.Contract.ScriptHash().BytesBE(), newPriv.GetScriptHash().BytesBE(), int64(1_000_000_000), nil)
|
||||||
emit.Opcodes(w.BinWriter, opcode.ASSERT)
|
emit.Opcodes(w.BinWriter, opcode.ASSERT)
|
||||||
require.NoError(t, w.Err)
|
require.NoError(t, w.Err)
|
||||||
|
|
||||||
|
|
|
@ -758,7 +758,7 @@ func (bc *Blockchain) runPersist(script []byte, block *block.Block, cache *dao.C
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bc *Blockchain) handleNotification(note *state.NotificationEvent, d *dao.Cached, b *block.Block, h util.Uint256) {
|
func (bc *Blockchain) handleNotification(note *state.NotificationEvent, d *dao.Cached, b *block.Block, h util.Uint256) {
|
||||||
if note.Name != "transfer" && note.Name != "Transfer" {
|
if note.Name != "Transfer" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
arr, ok := note.Item.Value().([]stackitem.Item)
|
arr, ok := note.Item.Value().([]stackitem.Item)
|
||||||
|
|
|
@ -265,12 +265,12 @@ func TestVerifyTx(t *testing.T) {
|
||||||
amount = 1_000_000_000
|
amount = 1_000_000_000
|
||||||
}
|
}
|
||||||
emit.AppCallWithOperationAndArgs(w.BinWriter, sc, "transfer",
|
emit.AppCallWithOperationAndArgs(w.BinWriter, sc, "transfer",
|
||||||
neoOwner, a.Contract.ScriptHash(), amount)
|
neoOwner, a.Contract.ScriptHash(), amount, nil)
|
||||||
emit.Opcodes(w.BinWriter, opcode.ASSERT)
|
emit.Opcodes(w.BinWriter, opcode.ASSERT)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
emit.AppCallWithOperationAndArgs(w.BinWriter, gasHash, "transfer",
|
emit.AppCallWithOperationAndArgs(w.BinWriter, gasHash, "transfer",
|
||||||
neoOwner, testchain.CommitteeScriptHash(), int64(1_000_000_000))
|
neoOwner, testchain.CommitteeScriptHash(), int64(1_000_000_000), nil)
|
||||||
emit.Opcodes(w.BinWriter, opcode.ASSERT)
|
emit.Opcodes(w.BinWriter, opcode.ASSERT)
|
||||||
require.NoError(t, w.Err)
|
require.NoError(t, w.Err)
|
||||||
|
|
||||||
|
|
|
@ -244,7 +244,7 @@ func TestCreateBasicChain(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Push some contract into the chain.
|
// Push some contract into the chain.
|
||||||
txDeploy, avm := newDeployTx(t, prefix+"test_contract.go")
|
txDeploy, avm := newDeployTx(t, prefix+"test_contract.go", "Rubl")
|
||||||
txDeploy.Nonce = getNextNonce()
|
txDeploy.Nonce = getNextNonce()
|
||||||
txDeploy.ValidUntilBlock = validUntilBlock
|
txDeploy.ValidUntilBlock = validUntilBlock
|
||||||
txDeploy.Signers = []transaction.Signer{{Account: priv0ScriptHash}}
|
txDeploy.Signers = []transaction.Signer{{Account: priv0ScriptHash}}
|
||||||
|
@ -332,7 +332,7 @@ func TestCreateBasicChain(t *testing.T) {
|
||||||
t.Logf("sendRublesTx: %v", transferTx.Hash().StringLE())
|
t.Logf("sendRublesTx: %v", transferTx.Hash().StringLE())
|
||||||
|
|
||||||
// Push verification contract into the chain.
|
// Push verification contract into the chain.
|
||||||
txDeploy2, _ := newDeployTx(t, prefix+"verification_contract.go")
|
txDeploy2, _ := newDeployTx(t, prefix+"verification_contract.go", "Verify")
|
||||||
txDeploy2.Nonce = getNextNonce()
|
txDeploy2.Nonce = getNextNonce()
|
||||||
txDeploy2.ValidUntilBlock = validUntilBlock
|
txDeploy2.ValidUntilBlock = validUntilBlock
|
||||||
txDeploy2.Signers = []transaction.Signer{{Account: priv0ScriptHash}}
|
txDeploy2.Signers = []transaction.Signer{{Account: priv0ScriptHash}}
|
||||||
|
@ -382,14 +382,14 @@ func TestCreateBasicChain(t *testing.T) {
|
||||||
|
|
||||||
func newNEP5Transfer(sc, from, to util.Uint160, amount int64) *transaction.Transaction {
|
func newNEP5Transfer(sc, from, to util.Uint160, amount int64) *transaction.Transaction {
|
||||||
w := io.NewBufBinWriter()
|
w := io.NewBufBinWriter()
|
||||||
emit.AppCallWithOperationAndArgs(w.BinWriter, sc, "transfer", from, to, amount)
|
emit.AppCallWithOperationAndArgs(w.BinWriter, sc, "transfer", from, to, amount, nil)
|
||||||
emit.Opcodes(w.BinWriter, opcode.ASSERT)
|
emit.Opcodes(w.BinWriter, opcode.ASSERT)
|
||||||
|
|
||||||
script := w.Bytes()
|
script := w.Bytes()
|
||||||
return transaction.New(testchain.Network(), script, 10000000)
|
return transaction.New(testchain.Network(), script, 10000000)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDeployTx(t *testing.T, name string) (*transaction.Transaction, []byte) {
|
func newDeployTx(t *testing.T, name, ctrName string) (*transaction.Transaction, []byte) {
|
||||||
c, err := ioutil.ReadFile(name)
|
c, err := ioutil.ReadFile(name)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
avm, di, err := compiler.CompileWithDebugInfo(name, bytes.NewReader(c))
|
avm, di, err := compiler.CompileWithDebugInfo(name, bytes.NewReader(c))
|
||||||
|
@ -398,7 +398,7 @@ func newDeployTx(t *testing.T, name string) (*transaction.Transaction, []byte) {
|
||||||
t.Logf("contractScript: %x", avm)
|
t.Logf("contractScript: %x", avm)
|
||||||
|
|
||||||
script := io.NewBufBinWriter()
|
script := io.NewBufBinWriter()
|
||||||
m, err := di.ConvertToManifest("Test", nil)
|
m, err := di.ConvertToManifest(ctrName, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
bs, err := json.Marshal(m)
|
bs, err := json.Marshal(m)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
|
@ -299,16 +299,7 @@ func runtimeLog(ic *interop.Context) error {
|
||||||
// runtimeGetTime returns timestamp of the block being verified, or the latest
|
// runtimeGetTime returns timestamp of the block being verified, or the latest
|
||||||
// one in the blockchain if no block is given to Context.
|
// one in the blockchain if no block is given to Context.
|
||||||
func runtimeGetTime(ic *interop.Context) error {
|
func runtimeGetTime(ic *interop.Context) error {
|
||||||
var header *block.Header
|
header := ic.Block.Header()
|
||||||
if ic.Block == nil {
|
|
||||||
var err error
|
|
||||||
header, err = ic.Chain.GetHeader(ic.Chain.CurrentBlockHash())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
header = ic.Block.Header()
|
|
||||||
}
|
|
||||||
ic.VM.Estack().PushVal(header.Timestamp)
|
ic.VM.Estack().PushVal(header.Timestamp)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -408,6 +408,13 @@ func getTestContractState() (*state.Contract, *state.Contract) {
|
||||||
emit.String(w.BinWriter, "initial")
|
emit.String(w.BinWriter, "initial")
|
||||||
emit.Syscall(w.BinWriter, interopnames.SystemStorageGetContext)
|
emit.Syscall(w.BinWriter, interopnames.SystemStorageGetContext)
|
||||||
emit.Syscall(w.BinWriter, interopnames.SystemStorageGet)
|
emit.Syscall(w.BinWriter, interopnames.SystemStorageGet)
|
||||||
|
emit.Opcodes(w.BinWriter, opcode.RET)
|
||||||
|
onPaymentOff := w.Len()
|
||||||
|
emit.Int(w.BinWriter, 3)
|
||||||
|
emit.Opcodes(w.BinWriter, opcode.PACK)
|
||||||
|
emit.String(w.BinWriter, "LastPayment")
|
||||||
|
emit.Syscall(w.BinWriter, interopnames.SystemRuntimeNotify)
|
||||||
|
emit.Opcodes(w.BinWriter, opcode.RET)
|
||||||
|
|
||||||
script := w.Bytes()
|
script := w.Bytes()
|
||||||
h := hash.Hash160(script)
|
h := hash.Hash160(script)
|
||||||
|
@ -482,6 +489,16 @@ func getTestContractState() (*state.Contract, *state.Contract) {
|
||||||
},
|
},
|
||||||
ReturnType: smartcontract.VoidType,
|
ReturnType: smartcontract.VoidType,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "onPayment",
|
||||||
|
Offset: onPaymentOff,
|
||||||
|
Parameters: []manifest.Parameter{
|
||||||
|
manifest.NewParameter("from", smartcontract.Hash160Type),
|
||||||
|
manifest.NewParameter("amount", smartcontract.IntegerType),
|
||||||
|
manifest.NewParameter("data", smartcontract.AnyType),
|
||||||
|
},
|
||||||
|
ReturnType: smartcontract.VoidType,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
cs := &state.Contract{
|
cs := &state.Contract{
|
||||||
Script: script,
|
Script: script,
|
||||||
|
|
|
@ -13,7 +13,7 @@ import (
|
||||||
|
|
||||||
// GAS represents GAS native contract.
|
// GAS represents GAS native contract.
|
||||||
type GAS struct {
|
type GAS struct {
|
||||||
nep5TokenNative
|
nep17TokenNative
|
||||||
NEO *NEO
|
NEO *NEO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,15 +27,15 @@ const initialGAS = 30000000
|
||||||
// newGAS returns GAS native contract.
|
// newGAS returns GAS native contract.
|
||||||
func newGAS() *GAS {
|
func newGAS() *GAS {
|
||||||
g := &GAS{}
|
g := &GAS{}
|
||||||
nep5 := newNEP5Native(gasName)
|
nep17 := newNEP17Native(gasName)
|
||||||
nep5.symbol = "gas"
|
nep17.symbol = "gas"
|
||||||
nep5.decimals = 8
|
nep17.decimals = 8
|
||||||
nep5.factor = GASFactor
|
nep17.factor = GASFactor
|
||||||
nep5.onPersist = chainOnPersist(nep5.OnPersist, g.OnPersist)
|
nep17.onPersist = chainOnPersist(nep17.OnPersist, g.OnPersist)
|
||||||
nep5.incBalance = g.increaseBalance
|
nep17.incBalance = g.increaseBalance
|
||||||
nep5.ContractID = gasContractID
|
nep17.ContractID = gasContractID
|
||||||
|
|
||||||
g.nep5TokenNative = *nep5
|
g.nep17TokenNative = *nep17
|
||||||
|
|
||||||
onp := g.Methods["onPersist"]
|
onp := g.Methods["onPersist"]
|
||||||
onp.Func = getOnPersistWrapper(g.onPersist)
|
onp.Func = getOnPersistWrapper(g.onPersist)
|
||||||
|
@ -65,10 +65,10 @@ func (g *GAS) increaseBalance(_ *interop.Context, _ util.Uint160, si *state.Stor
|
||||||
|
|
||||||
// Initialize initializes GAS contract.
|
// Initialize initializes GAS contract.
|
||||||
func (g *GAS) Initialize(ic *interop.Context) error {
|
func (g *GAS) Initialize(ic *interop.Context) error {
|
||||||
if err := g.nep5TokenNative.Initialize(ic); err != nil {
|
if err := g.nep17TokenNative.Initialize(ic); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if g.nep5TokenNative.getTotalSupply(ic.DAO).Sign() != 0 {
|
if g.nep17TokenNative.getTotalSupply(ic.DAO).Sign() != 0 {
|
||||||
return errors.New("already initialized")
|
return errors.New("already initialized")
|
||||||
}
|
}
|
||||||
h, err := getStandbyValidatorsHash(ic)
|
h, err := getStandbyValidatorsHash(ic)
|
||||||
|
|
|
@ -28,7 +28,7 @@ import (
|
||||||
|
|
||||||
// NEO represents NEO native contract.
|
// NEO represents NEO native contract.
|
||||||
type NEO struct {
|
type NEO struct {
|
||||||
nep5TokenNative
|
nep17TokenNative
|
||||||
GAS *GAS
|
GAS *GAS
|
||||||
|
|
||||||
// gasPerBlock represents current value of generated gas per block.
|
// gasPerBlock represents current value of generated gas per block.
|
||||||
|
@ -93,16 +93,16 @@ func makeValidatorKey(key *keys.PublicKey) []byte {
|
||||||
// newNEO returns NEO native contract.
|
// newNEO returns NEO native contract.
|
||||||
func newNEO() *NEO {
|
func newNEO() *NEO {
|
||||||
n := &NEO{}
|
n := &NEO{}
|
||||||
nep5 := newNEP5Native(neoName)
|
nep17 := newNEP17Native(neoName)
|
||||||
nep5.symbol = "neo"
|
nep17.symbol = "neo"
|
||||||
nep5.decimals = 0
|
nep17.decimals = 0
|
||||||
nep5.factor = 1
|
nep17.factor = 1
|
||||||
nep5.onPersist = chainOnPersist(nep5.OnPersist, n.OnPersist)
|
nep17.onPersist = chainOnPersist(nep17.OnPersist, n.OnPersist)
|
||||||
nep5.postPersist = chainOnPersist(nep5.postPersist, n.PostPersist)
|
nep17.postPersist = chainOnPersist(nep17.postPersist, n.PostPersist)
|
||||||
nep5.incBalance = n.increaseBalance
|
nep17.incBalance = n.increaseBalance
|
||||||
nep5.ContractID = neoContractID
|
nep17.ContractID = neoContractID
|
||||||
|
|
||||||
n.nep5TokenNative = *nep5
|
n.nep17TokenNative = *nep17
|
||||||
n.votesChanged.Store(true)
|
n.votesChanged.Store(true)
|
||||||
n.nextValidators.Store(keys.PublicKeys(nil))
|
n.nextValidators.Store(keys.PublicKeys(nil))
|
||||||
n.validators.Store(keys.PublicKeys(nil))
|
n.validators.Store(keys.PublicKeys(nil))
|
||||||
|
@ -165,11 +165,11 @@ func newNEO() *NEO {
|
||||||
|
|
||||||
// Initialize initializes NEO contract.
|
// Initialize initializes NEO contract.
|
||||||
func (n *NEO) Initialize(ic *interop.Context) error {
|
func (n *NEO) Initialize(ic *interop.Context) error {
|
||||||
if err := n.nep5TokenNative.Initialize(ic); err != nil {
|
if err := n.nep17TokenNative.Initialize(ic); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if n.nep5TokenNative.getTotalSupply(ic.DAO).Sign() != 0 {
|
if n.nep17TokenNative.getTotalSupply(ic.DAO).Sign() != 0 {
|
||||||
return errors.New("already initialized")
|
return errors.New("already initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/interop/contract"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/runtime"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop/runtime"
|
||||||
"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/encoding/bigint"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
|
||||||
|
@ -14,6 +15,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -28,8 +30,8 @@ func makeAccountKey(h util.Uint160) []byte {
|
||||||
return k
|
return k
|
||||||
}
|
}
|
||||||
|
|
||||||
// nep5TokenNative represents NEP-5 token contract.
|
// nep17TokenNative represents NEP-17 token contract.
|
||||||
type nep5TokenNative struct {
|
type nep17TokenNative struct {
|
||||||
interop.ContractMD
|
interop.ContractMD
|
||||||
symbol string
|
symbol string
|
||||||
decimals int64
|
decimals int64
|
||||||
|
@ -42,15 +44,15 @@ type nep5TokenNative struct {
|
||||||
// totalSupplyKey is the key used to store totalSupply value.
|
// totalSupplyKey is the key used to store totalSupply value.
|
||||||
var totalSupplyKey = []byte{11}
|
var totalSupplyKey = []byte{11}
|
||||||
|
|
||||||
func (c *nep5TokenNative) Metadata() *interop.ContractMD {
|
func (c *nep17TokenNative) Metadata() *interop.ContractMD {
|
||||||
return &c.ContractMD
|
return &c.ContractMD
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ interop.Contract = (*nep5TokenNative)(nil)
|
var _ interop.Contract = (*nep17TokenNative)(nil)
|
||||||
|
|
||||||
func newNEP5Native(name string) *nep5TokenNative {
|
func newNEP17Native(name string) *nep17TokenNative {
|
||||||
n := &nep5TokenNative{ContractMD: *interop.NewContractMD(name)}
|
n := &nep17TokenNative{ContractMD: *interop.NewContractMD(name)}
|
||||||
n.Manifest.SupportedStandards = []string{manifest.NEP5StandardName}
|
n.Manifest.SupportedStandards = []string{manifest.NEP17StandardName}
|
||||||
|
|
||||||
desc := newDescriptor("name", smartcontract.StringType)
|
desc := newDescriptor("name", smartcontract.StringType)
|
||||||
md := newMethodAndPrice(nameMethod(name), 0, smartcontract.NoneFlag)
|
md := newMethodAndPrice(nameMethod(name), 0, smartcontract.NoneFlag)
|
||||||
|
@ -77,6 +79,7 @@ func newNEP5Native(name string) *nep5TokenNative {
|
||||||
manifest.NewParameter("from", smartcontract.Hash160Type),
|
manifest.NewParameter("from", smartcontract.Hash160Type),
|
||||||
manifest.NewParameter("to", smartcontract.Hash160Type),
|
manifest.NewParameter("to", smartcontract.Hash160Type),
|
||||||
manifest.NewParameter("amount", smartcontract.IntegerType),
|
manifest.NewParameter("amount", smartcontract.IntegerType),
|
||||||
|
manifest.NewParameter("data", smartcontract.AnyType),
|
||||||
)
|
)
|
||||||
md = newMethodAndPrice(n.Transfer, 8000000, smartcontract.AllowModifyStates)
|
md = newMethodAndPrice(n.Transfer, 8000000, smartcontract.AllowModifyStates)
|
||||||
n.AddMethod(md, desc, false)
|
n.AddMethod(md, desc, false)
|
||||||
|
@ -94,23 +97,23 @@ func newNEP5Native(name string) *nep5TokenNative {
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *nep5TokenNative) Initialize(_ *interop.Context) error {
|
func (c *nep17TokenNative) Initialize(_ *interop.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *nep5TokenNative) Symbol(_ *interop.Context, _ []stackitem.Item) stackitem.Item {
|
func (c *nep17TokenNative) Symbol(_ *interop.Context, _ []stackitem.Item) stackitem.Item {
|
||||||
return stackitem.NewByteArray([]byte(c.symbol))
|
return stackitem.NewByteArray([]byte(c.symbol))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *nep5TokenNative) Decimals(_ *interop.Context, _ []stackitem.Item) stackitem.Item {
|
func (c *nep17TokenNative) Decimals(_ *interop.Context, _ []stackitem.Item) stackitem.Item {
|
||||||
return stackitem.NewBigInteger(big.NewInt(c.decimals))
|
return stackitem.NewBigInteger(big.NewInt(c.decimals))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *nep5TokenNative) 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))
|
return stackitem.NewBigInteger(c.getTotalSupply(ic.DAO))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *nep5TokenNative) getTotalSupply(d dao.DAO) *big.Int {
|
func (c *nep17TokenNative) getTotalSupply(d dao.DAO) *big.Int {
|
||||||
si := d.GetStorageItem(c.ContractID, totalSupplyKey)
|
si := d.GetStorageItem(c.ContractID, totalSupplyKey)
|
||||||
if si == nil {
|
if si == nil {
|
||||||
return big.NewInt(0)
|
return big.NewInt(0)
|
||||||
|
@ -118,16 +121,16 @@ func (c *nep5TokenNative) getTotalSupply(d dao.DAO) *big.Int {
|
||||||
return bigint.FromBytes(si.Value)
|
return bigint.FromBytes(si.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *nep5TokenNative) saveTotalSupply(d dao.DAO, supply *big.Int) error {
|
func (c *nep17TokenNative) saveTotalSupply(d dao.DAO, supply *big.Int) error {
|
||||||
si := &state.StorageItem{Value: bigint.ToBytes(supply)}
|
si := &state.StorageItem{Value: bigint.ToBytes(supply)}
|
||||||
return d.PutStorageItem(c.ContractID, totalSupplyKey, si)
|
return d.PutStorageItem(c.ContractID, totalSupplyKey, si)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *nep5TokenNative) Transfer(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
func (c *nep17TokenNative) Transfer(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||||
from := toUint160(args[0])
|
from := toUint160(args[0])
|
||||||
to := toUint160(args[1])
|
to := toUint160(args[1])
|
||||||
amount := toBigInt(args[2])
|
amount := toBigInt(args[2])
|
||||||
err := c.TransferInternal(ic, from, to, amount)
|
err := c.TransferInternal(ic, from, to, amount, args[3])
|
||||||
return stackitem.NewBool(err == nil)
|
return stackitem.NewBool(err == nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,7 +141,31 @@ func addrToStackItem(u *util.Uint160) stackitem.Item {
|
||||||
return stackitem.NewByteArray(u.BytesBE())
|
return stackitem.NewByteArray(u.BytesBE())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *nep5TokenNative) emitTransfer(ic *interop.Context, from, to *util.Uint160, amount *big.Int) {
|
func (c *nep17TokenNative) postTransfer(ic *interop.Context, from, to *util.Uint160, amount *big.Int, data stackitem.Item) {
|
||||||
|
c.emitTransfer(ic, from, to, amount)
|
||||||
|
if to == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cs, err := ic.DAO.GetContractState(*to)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fromArg := stackitem.Item(stackitem.Null{})
|
||||||
|
if from != nil {
|
||||||
|
fromArg = stackitem.NewByteArray((*from).BytesBE())
|
||||||
|
}
|
||||||
|
args := []stackitem.Item{
|
||||||
|
fromArg,
|
||||||
|
stackitem.NewBigInteger(amount),
|
||||||
|
data,
|
||||||
|
}
|
||||||
|
if err := contract.CallExInternal(ic, cs, manifest.MethodOnPayment, args, smartcontract.All, vm.EnsureIsEmpty); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *nep17TokenNative) emitTransfer(ic *interop.Context, from, to *util.Uint160, amount *big.Int) {
|
||||||
ne := state.NotificationEvent{
|
ne := state.NotificationEvent{
|
||||||
ScriptHash: c.Hash,
|
ScriptHash: c.Hash,
|
||||||
Name: "Transfer",
|
Name: "Transfer",
|
||||||
|
@ -151,7 +178,7 @@ func (c *nep5TokenNative) emitTransfer(ic *interop.Context, from, to *util.Uint1
|
||||||
ic.Notifications = append(ic.Notifications, ne)
|
ic.Notifications = append(ic.Notifications, ne)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *nep5TokenNative) updateAccBalance(ic *interop.Context, acc util.Uint160, amount *big.Int) error {
|
func (c *nep17TokenNative) updateAccBalance(ic *interop.Context, acc util.Uint160, amount *big.Int) error {
|
||||||
key := makeAccountKey(acc)
|
key := makeAccountKey(acc)
|
||||||
si := ic.DAO.GetStorageItem(c.ContractID, key)
|
si := ic.DAO.GetStorageItem(c.ContractID, key)
|
||||||
if si == nil {
|
if si == nil {
|
||||||
|
@ -174,7 +201,7 @@ func (c *nep5TokenNative) updateAccBalance(ic *interop.Context, acc util.Uint160
|
||||||
}
|
}
|
||||||
|
|
||||||
// TransferInternal transfers NEO between accounts.
|
// TransferInternal transfers NEO between accounts.
|
||||||
func (c *nep5TokenNative) TransferInternal(ic *interop.Context, from, to util.Uint160, amount *big.Int) error {
|
func (c *nep17TokenNative) TransferInternal(ic *interop.Context, from, to util.Uint160, amount *big.Int, data stackitem.Item) error {
|
||||||
if amount.Sign() == -1 {
|
if amount.Sign() == -1 {
|
||||||
return errors.New("negative amount")
|
return errors.New("negative amount")
|
||||||
}
|
}
|
||||||
|
@ -205,11 +232,11 @@ func (c *nep5TokenNative) TransferInternal(ic *interop.Context, from, to util.Ui
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.emitTransfer(ic, &from, &to, amount)
|
c.postTransfer(ic, &from, &to, amount, data)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *nep5TokenNative) balanceOf(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
func (c *nep17TokenNative) balanceOf(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||||
h := toUint160(args[0])
|
h := toUint160(args[0])
|
||||||
bs, err := ic.DAO.GetNEP5Balances(h)
|
bs, err := ic.DAO.GetNEP5Balances(h)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -219,23 +246,23 @@ func (c *nep5TokenNative) balanceOf(ic *interop.Context, args []stackitem.Item)
|
||||||
return stackitem.NewBigInteger(&balance)
|
return stackitem.NewBigInteger(&balance)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *nep5TokenNative) mint(ic *interop.Context, h util.Uint160, amount *big.Int) {
|
func (c *nep17TokenNative) mint(ic *interop.Context, h util.Uint160, amount *big.Int) {
|
||||||
if amount.Sign() == 0 {
|
if amount.Sign() == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.addTokens(ic, h, amount)
|
c.addTokens(ic, h, amount)
|
||||||
c.emitTransfer(ic, nil, &h, amount)
|
c.postTransfer(ic, nil, &h, amount, stackitem.Null{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *nep5TokenNative) burn(ic *interop.Context, h util.Uint160, amount *big.Int) {
|
func (c *nep17TokenNative) burn(ic *interop.Context, h util.Uint160, amount *big.Int) {
|
||||||
if amount.Sign() == 0 {
|
if amount.Sign() == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.addTokens(ic, h, new(big.Int).Neg(amount))
|
c.addTokens(ic, h, new(big.Int).Neg(amount))
|
||||||
c.emitTransfer(ic, &h, nil, amount)
|
c.postTransfer(ic, &h, nil, amount, stackitem.Null{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *nep5TokenNative) addTokens(ic *interop.Context, h util.Uint160, amount *big.Int) {
|
func (c *nep17TokenNative) addTokens(ic *interop.Context, h util.Uint160, amount *big.Int) {
|
||||||
if amount.Sign() == 0 {
|
if amount.Sign() == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -260,7 +287,7 @@ func (c *nep5TokenNative) addTokens(ic *interop.Context, h util.Uint160, amount
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *nep5TokenNative) OnPersist(ic *interop.Context) error {
|
func (c *nep17TokenNative) OnPersist(ic *interop.Context) error {
|
||||||
if ic.Trigger != trigger.OnPersist {
|
if ic.Trigger != trigger.OnPersist {
|
||||||
return errors.New("onPersist must be triggerred by system")
|
return errors.New("onPersist must be triggerred by system")
|
||||||
}
|
}
|
|
@ -125,6 +125,13 @@ func newOracle() *Oracle {
|
||||||
md = newMethodAndPrice(o.verify, 100_0000, smartcontract.NoneFlag)
|
md = newMethodAndPrice(o.verify, 100_0000, smartcontract.NoneFlag)
|
||||||
o.AddMethod(md, desc, false)
|
o.AddMethod(md, desc, false)
|
||||||
|
|
||||||
|
desc = newDescriptor("onPayment", smartcontract.VoidType,
|
||||||
|
manifest.NewParameter("from", smartcontract.Hash160Type),
|
||||||
|
manifest.NewParameter("amount", smartcontract.IntegerType),
|
||||||
|
manifest.NewParameter("data", smartcontract.AnyType))
|
||||||
|
md = newMethodAndPrice(o.onPayment, 0, smartcontract.NoneFlag)
|
||||||
|
o.AddMethod(md, desc, false)
|
||||||
|
|
||||||
pp := chainOnPersist(postPersistBase, o.PostPersist)
|
pp := chainOnPersist(postPersistBase, o.PostPersist)
|
||||||
desc = newDescriptor("postPersist", smartcontract.VoidType)
|
desc = newDescriptor("postPersist", smartcontract.VoidType)
|
||||||
md = newMethodAndPrice(getOnPersistWrapper(pp), 0, smartcontract.AllowModifyStates)
|
md = newMethodAndPrice(getOnPersistWrapper(pp), 0, smartcontract.AllowModifyStates)
|
||||||
|
@ -299,6 +306,7 @@ func (o *Oracle) RequestInternal(ic *interop.Context, url string, filter *string
|
||||||
if !ic.VM.AddGas(gas.Int64()) {
|
if !ic.VM.AddGas(gas.Int64()) {
|
||||||
return ErrNotEnoughGas
|
return ErrNotEnoughGas
|
||||||
}
|
}
|
||||||
|
callingHash := ic.VM.GetCallingScriptHash()
|
||||||
o.GAS.mint(ic, o.Hash, gas)
|
o.GAS.mint(ic, o.Hash, gas)
|
||||||
si := ic.DAO.GetStorageItem(o.ContractID, prefixRequestID)
|
si := ic.DAO.GetStorageItem(o.ContractID, prefixRequestID)
|
||||||
id := binary.LittleEndian.Uint64(si.Value) + 1
|
id := binary.LittleEndian.Uint64(si.Value) + 1
|
||||||
|
@ -344,7 +352,7 @@ func (o *Oracle) RequestInternal(ic *interop.Context, url string, filter *string
|
||||||
GasForResponse: gas.Uint64(),
|
GasForResponse: gas.Uint64(),
|
||||||
URL: url,
|
URL: url,
|
||||||
Filter: filter,
|
Filter: filter,
|
||||||
CallbackContract: ic.VM.GetCallingScriptHash(),
|
CallbackContract: callingHash,
|
||||||
CallbackMethod: cb,
|
CallbackMethod: cb,
|
||||||
UserData: data,
|
UserData: data,
|
||||||
}
|
}
|
||||||
|
@ -402,6 +410,14 @@ func (o *Oracle) verify(ic *interop.Context, _ []stackitem.Item) stackitem.Item
|
||||||
return stackitem.NewBool(ic.Tx.HasAttribute(transaction.OracleResponseT))
|
return stackitem.NewBool(ic.Tx.HasAttribute(transaction.OracleResponseT))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *Oracle) onPayment(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
|
||||||
|
// FIXME when calling native transfer directly, context is not provided.
|
||||||
|
if h := ic.VM.GetCallingScriptHash(); h != o.Hash && h != o.GAS.Hash {
|
||||||
|
panic("only GAS can be accepted")
|
||||||
|
}
|
||||||
|
return stackitem.Null{}
|
||||||
|
}
|
||||||
|
|
||||||
func (o *Oracle) getOriginalTxID(d dao.DAO, tx *transaction.Transaction) util.Uint256 {
|
func (o *Oracle) getOriginalTxID(d dao.DAO, tx *transaction.Transaction) util.Uint256 {
|
||||||
for i := range tx.Attributes {
|
for i := range tx.Attributes {
|
||||||
if tx.Attributes[i].Type == transaction.OracleResponseT {
|
if tx.Attributes[i].Type == transaction.OracleResponseT {
|
||||||
|
|
|
@ -16,6 +16,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
@ -76,11 +77,11 @@ func TestNEO_Vote(t *testing.T) {
|
||||||
w := io.NewBufBinWriter()
|
w := io.NewBufBinWriter()
|
||||||
emit.AppCallWithOperationAndArgs(w.BinWriter, bc.contracts.NEO.Hash, "transfer",
|
emit.AppCallWithOperationAndArgs(w.BinWriter, bc.contracts.NEO.Hash, "transfer",
|
||||||
neoOwner.BytesBE(), to.BytesBE(),
|
neoOwner.BytesBE(), to.BytesBE(),
|
||||||
big.NewInt(int64(sz-i)*1000000).Int64())
|
big.NewInt(int64(sz-i)*1000000).Int64(), nil)
|
||||||
emit.Opcodes(w.BinWriter, opcode.ASSERT)
|
emit.Opcodes(w.BinWriter, opcode.ASSERT)
|
||||||
emit.AppCallWithOperationAndArgs(w.BinWriter, bc.contracts.GAS.Hash, "transfer",
|
emit.AppCallWithOperationAndArgs(w.BinWriter, bc.contracts.GAS.Hash, "transfer",
|
||||||
neoOwner.BytesBE(), to.BytesBE(),
|
neoOwner.BytesBE(), to.BytesBE(),
|
||||||
int64(1_000_000_000))
|
int64(1_000_000_000), nil)
|
||||||
emit.Opcodes(w.BinWriter, opcode.ASSERT)
|
emit.Opcodes(w.BinWriter, opcode.ASSERT)
|
||||||
require.NoError(t, w.Err)
|
require.NoError(t, w.Err)
|
||||||
tx := transaction.New(netmode.UnitTestNet, w.Bytes(), 1000_000_000)
|
tx := transaction.New(netmode.UnitTestNet, w.Bytes(), 1000_000_000)
|
||||||
|
@ -141,7 +142,7 @@ func TestNEO_Vote(t *testing.T) {
|
||||||
gasBalance[i] = bc.GetUtilityTokenBalance(h)
|
gasBalance[i] = bc.GetUtilityTokenBalance(h)
|
||||||
neoBalance[i], _ = bc.GetGoverningTokenBalance(h)
|
neoBalance[i], _ = bc.GetGoverningTokenBalance(h)
|
||||||
emit.AppCallWithOperationAndArgs(w.BinWriter, bc.contracts.NEO.Hash, "transfer",
|
emit.AppCallWithOperationAndArgs(w.BinWriter, bc.contracts.NEO.Hash, "transfer",
|
||||||
h.BytesBE(), h.BytesBE(), int64(1))
|
h.BytesBE(), h.BytesBE(), int64(1), nil)
|
||||||
emit.Opcodes(w.BinWriter, opcode.ASSERT)
|
emit.Opcodes(w.BinWriter, opcode.ASSERT)
|
||||||
require.NoError(t, w.Err)
|
require.NoError(t, w.Err)
|
||||||
tx := transaction.New(netmode.UnitTestNet, w.Bytes(), 0)
|
tx := transaction.New(netmode.UnitTestNet, w.Bytes(), 0)
|
||||||
|
@ -303,3 +304,31 @@ func TestNEO_CommitteeBountyOnPersist(t *testing.T) {
|
||||||
checkBalances()
|
checkBalances()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNEO_TransferOnPayment(t *testing.T) {
|
||||||
|
bc := newTestChain(t)
|
||||||
|
defer bc.Close()
|
||||||
|
|
||||||
|
cs, _ := getTestContractState()
|
||||||
|
require.NoError(t, bc.dao.PutContractState(cs))
|
||||||
|
|
||||||
|
const amount = 2
|
||||||
|
tx := newNEP5Transfer(bc.contracts.NEO.Hash, neoOwner, cs.ScriptHash(), amount)
|
||||||
|
tx.SystemFee += 1_000_000
|
||||||
|
tx.NetworkFee = 10_000_000
|
||||||
|
tx.ValidUntilBlock = bc.BlockHeight() + 1
|
||||||
|
addSigners(tx)
|
||||||
|
require.NoError(t, signTx(bc, tx))
|
||||||
|
require.NoError(t, bc.AddBlock(bc.newBlock(tx)))
|
||||||
|
|
||||||
|
aer, err := bc.GetAppExecResults(tx.Hash(), trigger.Application)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, vm.HaltState, aer[0].VMState)
|
||||||
|
require.Len(t, aer[0].Events, 3) // transfer + auto GAS claim + onPayment
|
||||||
|
|
||||||
|
e := aer[0].Events[2]
|
||||||
|
require.Equal(t, "LastPayment", e.Name)
|
||||||
|
arr := e.Item.Value().([]stackitem.Item)
|
||||||
|
require.Equal(t, neoOwner.BytesBE(), arr[0].Value())
|
||||||
|
require.Equal(t, big.NewInt(amount), arr[1].Value())
|
||||||
|
}
|
||||||
|
|
|
@ -37,20 +37,6 @@ func (c *Client) NEP5Decimals(tokenHash util.Uint160) (int64, error) {
|
||||||
return topIntFromStack(result.Stack)
|
return topIntFromStack(result.Stack)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NEP5Name invokes `name` NEP5 method on a specified contract.
|
|
||||||
func (c *Client) NEP5Name(tokenHash util.Uint160) (string, error) {
|
|
||||||
result, err := c.InvokeFunction(tokenHash, "name", []smartcontract.Parameter{}, nil)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
err = getInvocationError(result)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("failed to get NEP5 name: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return topStringFromStack(result.Stack)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NEP5Symbol invokes `symbol` NEP5 method on a specified contract.
|
// NEP5Symbol invokes `symbol` NEP5 method on a specified contract.
|
||||||
func (c *Client) NEP5Symbol(tokenHash util.Uint160) (string, error) {
|
func (c *Client) NEP5Symbol(tokenHash util.Uint160) (string, error) {
|
||||||
result, err := c.InvokeFunction(tokenHash, "symbol", []smartcontract.Parameter{}, nil)
|
result, err := c.InvokeFunction(tokenHash, "symbol", []smartcontract.Parameter{}, nil)
|
||||||
|
@ -98,7 +84,7 @@ func (c *Client) NEP5BalanceOf(tokenHash, acc util.Uint160) (int64, error) {
|
||||||
|
|
||||||
// NEP5TokenInfo returns full NEP5 token info.
|
// NEP5TokenInfo returns full NEP5 token info.
|
||||||
func (c *Client) NEP5TokenInfo(tokenHash util.Uint160) (*wallet.Token, error) {
|
func (c *Client) NEP5TokenInfo(tokenHash util.Uint160) (*wallet.Token, error) {
|
||||||
name, err := c.NEP5Name(tokenHash)
|
cs, err := c.GetContractStateByHash(tokenHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -110,7 +96,7 @@ func (c *Client) NEP5TokenInfo(tokenHash util.Uint160) (*wallet.Token, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return wallet.NewToken(tokenHash, name, symbol, decimals), nil
|
return wallet.NewToken(tokenHash, cs.Manifest.Name, symbol, decimals), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateNEP5TransferTx creates an invocation transaction for the 'transfer'
|
// CreateNEP5TransferTx creates an invocation transaction for the 'transfer'
|
||||||
|
@ -135,7 +121,7 @@ func (c *Client) CreateNEP5MultiTransferTx(acc *wallet.Account, gas int64, recip
|
||||||
w := io.NewBufBinWriter()
|
w := io.NewBufBinWriter()
|
||||||
for i := range recipients {
|
for i := range recipients {
|
||||||
emit.AppCallWithOperationAndArgs(w.BinWriter, recipients[i].Token, "transfer", from,
|
emit.AppCallWithOperationAndArgs(w.BinWriter, recipients[i].Token, "transfer", from,
|
||||||
recipients[i].Address, recipients[i].Amount)
|
recipients[i].Address, recipients[i].Amount, nil)
|
||||||
emit.Opcodes(w.BinWriter, opcode.ASSERT)
|
emit.Opcodes(w.BinWriter, opcode.ASSERT)
|
||||||
}
|
}
|
||||||
return c.CreateTxFromScript(w.Bytes(), acc, -1, gas, transaction.Signer{
|
return c.CreateTxFromScript(w.Bytes(), acc, -1, gas, transaction.Signer{
|
||||||
|
|
|
@ -41,11 +41,6 @@ func TestClient_NEP5(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.EqualValues(t, 1_000_000, s)
|
require.EqualValues(t, 1_000_000, s)
|
||||||
})
|
})
|
||||||
t.Run("Name", func(t *testing.T) {
|
|
||||||
name, err := c.NEP5Name(h)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, "Rubl", name)
|
|
||||||
})
|
|
||||||
t.Run("Symbol", func(t *testing.T) {
|
t.Run("Symbol", func(t *testing.T) {
|
||||||
sym, err := c.NEP5Symbol(h)
|
sym, err := c.NEP5Symbol(h)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
|
@ -1095,7 +1095,7 @@ func (s *Server) invokeFunction(reqParams request.Params) (interface{}, *respons
|
||||||
return nil, response.NewInternalServerError("can't create invocation script", err)
|
return nil, response.NewInternalServerError("can't create invocation script", err)
|
||||||
}
|
}
|
||||||
tx.Script = script
|
tx.Script = script
|
||||||
return s.runScriptInVM(script, tx), nil
|
return s.runScriptInVM(script, tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// invokescript implements the `invokescript` RPC call.
|
// invokescript implements the `invokescript` RPC call.
|
||||||
|
@ -1121,16 +1121,27 @@ func (s *Server) invokescript(reqParams request.Params) (interface{}, *response.
|
||||||
tx.Signers = []transaction.Signer{{Account: util.Uint160{}, Scopes: transaction.None}}
|
tx.Signers = []transaction.Signer{{Account: util.Uint160{}, Scopes: transaction.None}}
|
||||||
}
|
}
|
||||||
tx.Script = script
|
tx.Script = script
|
||||||
return s.runScriptInVM(script, tx), nil
|
return s.runScriptInVM(script, tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// runScriptInVM runs given script in a new test VM and returns the invocation
|
// runScriptInVM runs given script in a new test VM and returns the invocation
|
||||||
// result.
|
// result.
|
||||||
func (s *Server) runScriptInVM(script []byte, tx *transaction.Transaction) *result.Invoke {
|
func (s *Server) runScriptInVM(script []byte, tx *transaction.Transaction) (*result.Invoke, *response.Error) {
|
||||||
vm := s.chain.GetTestVM(tx, nil)
|
// When transfering funds, script execution does no auto GAS claim,
|
||||||
|
// because it depends on persisting tx height.
|
||||||
|
// This is why we provide block here.
|
||||||
|
b := block.New(s.network, s.stateRootEnabled)
|
||||||
|
b.Index = s.chain.BlockHeight() + 1
|
||||||
|
hdr, err := s.chain.GetHeader(s.chain.GetHeaderHash(int(s.chain.BlockHeight())))
|
||||||
|
if err != nil {
|
||||||
|
return nil, response.NewInternalServerError("can't get last block", err)
|
||||||
|
}
|
||||||
|
b.Timestamp = hdr.Timestamp + uint64(s.chain.GetConfig().SecondsPerBlock*int(time.Second/time.Millisecond))
|
||||||
|
|
||||||
|
vm := s.chain.GetTestVM(tx, b)
|
||||||
vm.GasLimit = int64(s.config.MaxGasInvoke)
|
vm.GasLimit = int64(s.config.MaxGasInvoke)
|
||||||
vm.LoadScriptWithFlags(script, smartcontract.All)
|
vm.LoadScriptWithFlags(script, smartcontract.All)
|
||||||
err := vm.Run()
|
err = vm.Run()
|
||||||
var faultException string
|
var faultException string
|
||||||
if err != nil {
|
if err != nil {
|
||||||
faultException = err.Error()
|
faultException = err.Error()
|
||||||
|
@ -1142,7 +1153,7 @@ func (s *Server) runScriptInVM(script []byte, tx *transaction.Transaction) *resu
|
||||||
Stack: vm.Estack().ToArray(),
|
Stack: vm.Estack().ToArray(),
|
||||||
FaultException: faultException,
|
FaultException: faultException,
|
||||||
}
|
}
|
||||||
return result
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// submitBlock broadcasts a raw block over the NEO network.
|
// submitBlock broadcasts a raw block over the NEO network.
|
||||||
|
|
|
@ -56,8 +56,8 @@ type rpcTestCase struct {
|
||||||
check func(t *testing.T, e *executor, result interface{})
|
check func(t *testing.T, e *executor, result interface{})
|
||||||
}
|
}
|
||||||
|
|
||||||
const testContractHash = "b0fda4dd46b8e5d207e86e774a4a133c6db69ee7"
|
const testContractHash = "55b692ecc09f240355e042c6c07e8f3fe57546b1"
|
||||||
const deploymentTxHash = "59f7b22b90e26f883a56b916c1580e3ee4f13caded686353cd77577e6194c173"
|
const deploymentTxHash = "99e40e5d169eb9a2b6faebc6fc596c050cf3f8a70ad25de8f44309bc8ccbfbfb"
|
||||||
const genesisBlockHash = "a496577895eb8c227bb866dc44f99f21c0cf06417ca8f2a877cc5d761a50dac0"
|
const genesisBlockHash = "a496577895eb8c227bb866dc44f99f21c0cf06417ca8f2a877cc5d761a50dac0"
|
||||||
|
|
||||||
const verifyContractHash = "c1213693b22cb0454a436d6e0bd76b8c0a3bfdf7"
|
const verifyContractHash = "c1213693b22cb0454a436d6e0bd76b8c0a3bfdf7"
|
||||||
|
@ -1345,7 +1345,7 @@ func checkNep5Balances(t *testing.T, e *executor, acc interface{}) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Asset: e.chain.UtilityTokenHash(),
|
Asset: e.chain.UtilityTokenHash(),
|
||||||
Amount: "80009641770",
|
Amount: "80009744770",
|
||||||
LastUpdated: 7,
|
LastUpdated: 7,
|
||||||
}},
|
}},
|
||||||
Address: testchain.PrivateKeyByID(0).GetScriptHash().StringLE(),
|
Address: testchain.PrivateKeyByID(0).GetScriptHash().StringLE(),
|
||||||
|
|
10
pkg/rpc/server/testdata/test_contract.go
vendored
10
pkg/rpc/server/testdata/test_contract.go
vendored
|
@ -15,11 +15,11 @@ func Init() bool {
|
||||||
h := runtime.GetExecutingScriptHash()
|
h := runtime.GetExecutingScriptHash()
|
||||||
amount := totalSupply
|
amount := totalSupply
|
||||||
storage.Put(ctx, h, amount)
|
storage.Put(ctx, h, amount)
|
||||||
runtime.Notify("transfer", []byte{}, h, amount)
|
runtime.Notify("Transfer", []byte{}, h, amount)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func Transfer(from, to []byte, amount int) bool {
|
func Transfer(from, to []byte, amount int, data interface{}) bool {
|
||||||
ctx := storage.GetContext()
|
ctx := storage.GetContext()
|
||||||
if len(from) != 20 {
|
if len(from) != 20 {
|
||||||
runtime.Log("invalid 'from' address")
|
runtime.Log("invalid 'from' address")
|
||||||
|
@ -54,7 +54,7 @@ func Transfer(from, to []byte, amount int) bool {
|
||||||
toBalance += amount
|
toBalance += amount
|
||||||
storage.Put(ctx, to, toBalance)
|
storage.Put(ctx, to, toBalance)
|
||||||
|
|
||||||
runtime.Notify("transfer", from, to, amount)
|
runtime.Notify("Transfer", from, to, amount)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -74,10 +74,6 @@ func BalanceOf(addr []byte) int {
|
||||||
return amount
|
return amount
|
||||||
}
|
}
|
||||||
|
|
||||||
func Name() string {
|
|
||||||
return "Rubl"
|
|
||||||
}
|
|
||||||
|
|
||||||
func Symbol() string {
|
func Symbol() string {
|
||||||
return "RUB"
|
return "RUB"
|
||||||
}
|
}
|
||||||
|
|
BIN
pkg/rpc/server/testdata/testblocks.acc
vendored
BIN
pkg/rpc/server/testdata/testblocks.acc
vendored
Binary file not shown.
|
@ -20,10 +20,13 @@ const (
|
||||||
// MethodVerify is a name for default verification method.
|
// MethodVerify is a name for default verification method.
|
||||||
MethodVerify = "verify"
|
MethodVerify = "verify"
|
||||||
|
|
||||||
// NEP5StandardName represents the name of NEP5 smartcontract standard.
|
// MethodOnPayment is name of the method which is called when contract receives funds.
|
||||||
NEP5StandardName = "NEP-5"
|
MethodOnPayment = "onPayment"
|
||||||
|
|
||||||
// NEP10StandardName represents the name of NEP10 smartcontract standard.
|
// NEP10StandardName represents the name of NEP10 smartcontract standard.
|
||||||
NEP10StandardName = "NEP-10"
|
NEP10StandardName = "NEP-10"
|
||||||
|
// NEP17StandardName represents the name of NEP17 smartcontract standard.
|
||||||
|
NEP17StandardName = "NEP-17"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ABI represents a contract application binary interface.
|
// ABI represents a contract application binary interface.
|
||||||
|
|
Loading…
Reference in a new issue