github.com/turingchain2020/turingchain@v1.1.21/executor/executor_real_test.go (about)

     1  // Copyright Turing Corp. 2018 All Rights Reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package executor_test
     6  
     7  import (
     8  	"errors"
     9  	"fmt"
    10  	"net/http"
    11  	_ "net/http/pprof"
    12  	"testing"
    13  
    14  	"sync"
    15  
    16  	"github.com/turingchain2020/turingchain/common"
    17  	"github.com/turingchain2020/turingchain/common/address"
    18  	"github.com/turingchain2020/turingchain/common/merkle"
    19  	_ "github.com/turingchain2020/turingchain/system"
    20  	drivers "github.com/turingchain2020/turingchain/system/dapp"
    21  	"github.com/turingchain2020/turingchain/types"
    22  	"github.com/turingchain2020/turingchain/util"
    23  	"github.com/turingchain2020/turingchain/util/testnode"
    24  	"github.com/stretchr/testify/assert"
    25  )
    26  
    27  var runonce sync.Once
    28  
    29  func init() {
    30  	types.AllowUserExec = append(types.AllowUserExec, []byte("demo2"))
    31  	go func() {
    32  		http.ListenAndServe("localhost:6060", nil)
    33  	}()
    34  }
    35  
    36  func TestExecGenesisBlock(t *testing.T) {
    37  	mock33 := newMockNode()
    38  	defer mock33.Close()
    39  	mock33.WaitHeight(0)
    40  	block := mock33.GetBlock(0)
    41  	assert.Equal(t, block.Height, int64(0))
    42  }
    43  
    44  func newMockNode() *testnode.TuringchainMock {
    45  	cfg := testnode.GetDefaultConfig()
    46  	cfg.GetModuleConfig().Consensus.Minerstart = false
    47  	runonce.Do(func() {
    48  		drivers.Register(cfg, "demo2", newdemoApp, 1)
    49  	})
    50  	mock33 := testnode.NewWithConfig(cfg, nil)
    51  	return mock33
    52  }
    53  
    54  func TestTxGroup(t *testing.T) {
    55  	mock33 := newMockNode()
    56  	defer mock33.Close()
    57  	cfg := mock33.GetClient().GetConfig()
    58  	prev := cfg.GetMinTxFeeRate()
    59  	cfg.SetMinFee(100000)
    60  	defer cfg.SetMinFee(prev)
    61  	mcfg := mock33.GetCfg()
    62  	genkey := mock33.GetGenesisKey()
    63  	mock33.WaitHeight(0)
    64  	block := mock33.GetBlock(0)
    65  	acc := mock33.GetAccount(block.StateHash, mcfg.Consensus.Genesis)
    66  	assert.Equal(t, acc.Balance, 100000000*types.Coin)
    67  	var txs []*types.Transaction
    68  	addr2, priv2 := util.Genaddress()
    69  	addr3, priv3 := util.Genaddress()
    70  	addr4, _ := util.Genaddress()
    71  	txs = append(txs, util.CreateCoinsTx(cfg, genkey, addr2, types.Coin))
    72  	txs = append(txs, util.CreateCoinsTx(cfg, priv2, addr3, types.Coin))
    73  	txs = append(txs, util.CreateCoinsTx(cfg, priv3, addr4, types.Coin))
    74  	//执行三笔交易: 全部正确
    75  	feeRate := cfg.GetMinTxFeeRate()
    76  	txgroup, err := types.CreateTxGroup(txs, feeRate)
    77  	assert.Nil(t, err)
    78  	//重新签名
    79  	txgroup.SignN(0, types.SECP256K1, genkey)
    80  	txgroup.SignN(1, types.SECP256K1, priv2)
    81  	txgroup.SignN(2, types.SECP256K1, priv3)
    82  	//返回新的区块
    83  	block, err = util.ExecAndCheckBlock(mock33.GetClient(), block, txgroup.GetTxs(), []int{types.ExecOk, types.ExecOk, types.ExecOk})
    84  	assert.Nil(t, err)
    85  	assert.Equal(t, mock33.GetAccount(block.StateHash, mock33.GetGenesisAddress()).Balance, int64(9999999899700000))
    86  	assert.Equal(t, mock33.GetAccount(block.StateHash, addr2).Balance, int64(0))
    87  	assert.Equal(t, mock33.GetAccount(block.StateHash, addr3).Balance, int64(0))
    88  	assert.Equal(t, mock33.GetAccount(block.StateHash, addr4).Balance, 1*types.Coin)
    89  
    90  	//执行三笔交易:第一比错误
    91  	txs = nil
    92  	txs = append(txs, util.CreateCoinsTx(cfg, priv2, addr3, 2*types.Coin))
    93  	txs = append(txs, util.CreateCoinsTx(cfg, genkey, addr4, types.Coin))
    94  	txs = append(txs, util.CreateCoinsTx(cfg, genkey, addr4, types.Coin))
    95  
    96  	txgroup, err = types.CreateTxGroup(txs, feeRate)
    97  	assert.Nil(t, err)
    98  	//重新签名
    99  	txgroup.SignN(0, types.SECP256K1, priv2)
   100  	txgroup.SignN(1, types.SECP256K1, genkey)
   101  	txgroup.SignN(2, types.SECP256K1, genkey)
   102  
   103  	_, err = util.ExecAndCheckBlock(mock33.GetClient(), block, txgroup.GetTxs(), []int{types.ExecErr, types.ExecErr, types.ExecErr})
   104  	assert.Nil(t, err)
   105  	//执行三笔交易:第二比错误
   106  	txs = nil
   107  	txs = append(txs, util.CreateCoinsTx(cfg, genkey, addr2, types.Coin))
   108  	txs = append(txs, util.CreateCoinsTx(cfg, priv2, addr4, 2*types.Coin))
   109  	txs = append(txs, util.CreateCoinsTx(cfg, genkey, addr4, types.Coin))
   110  
   111  	txgroup, err = types.CreateTxGroup(txs, feeRate)
   112  	assert.Nil(t, err)
   113  	//重新签名
   114  	txgroup.SignN(0, types.SECP256K1, genkey)
   115  	txgroup.SignN(1, types.SECP256K1, priv2)
   116  	txgroup.SignN(2, types.SECP256K1, genkey)
   117  
   118  	_, err = util.ExecAndCheckBlock(mock33.GetClient(), block, txgroup.GetTxs(), []int{types.ExecPack, types.ExecPack, types.ExecPack})
   119  	assert.Nil(t, err)
   120  	//执行三笔交易: 第三比错误
   121  	txs = nil
   122  	txs = append(txs, util.CreateCoinsTx(cfg, genkey, addr2, types.Coin))
   123  	txs = append(txs, util.CreateCoinsTx(cfg, genkey, addr4, types.Coin))
   124  	txs = append(txs, util.CreateCoinsTx(cfg, priv2, addr4, 10*types.Coin))
   125  
   126  	txgroup, err = types.CreateTxGroup(txs, feeRate)
   127  	assert.Nil(t, err)
   128  	//重新签名
   129  	txgroup.SignN(0, types.SECP256K1, genkey)
   130  	txgroup.SignN(1, types.SECP256K1, genkey)
   131  	txgroup.SignN(2, types.SECP256K1, priv2)
   132  
   133  	_, err = util.ExecAndCheckBlock(mock33.GetClient(), block, txgroup.GetTxs(), []int{types.ExecPack, types.ExecPack, types.ExecPack})
   134  	assert.Nil(t, err)
   135  	//执行三笔交易:其中有一笔是user.xxx的执行器
   136  	txs = nil
   137  	txs = append(txs, util.CreateCoinsTx(cfg, genkey, addr2, types.Coin))
   138  	txs = append(txs, util.CreateCoinsTx(cfg, genkey, addr4, types.Coin))
   139  	txs = append(txs, util.CreateCoinsTx(cfg, priv2, addr4, 10*types.Coin))
   140  	txs[2].Execer = []byte("user.xxx")
   141  	txs[2].To = address.ExecAddress("user.xxx")
   142  	txgroup, err = types.CreateTxGroup(txs, feeRate)
   143  	assert.Nil(t, err)
   144  	//重新签名
   145  	txgroup.SignN(0, types.SECP256K1, genkey)
   146  	txgroup.SignN(1, types.SECP256K1, genkey)
   147  	txgroup.SignN(2, types.SECP256K1, priv2)
   148  
   149  	_, err = util.ExecAndCheckBlock(mock33.GetClient(), block, txgroup.GetTxs(), []int{2, 2, 1})
   150  	assert.Nil(t, err)
   151  }
   152  
   153  func TestExecAllow(t *testing.T) {
   154  	mock33 := newMockNode()
   155  	defer mock33.Close()
   156  	cfg := mock33.GetClient().GetConfig()
   157  	prev := cfg.GetMinTxFeeRate()
   158  	cfg.SetMinFee(100000)
   159  	defer cfg.SetMinFee(prev)
   160  	genkey := mock33.GetGenesisKey()
   161  	genaddr := mock33.GetGenesisAddress()
   162  	mock33.WaitHeight(0)
   163  	block := mock33.GetBlock(0)
   164  	tx1 := util.CreateTxWithExecer(cfg, genkey, "user.evm")       //allow
   165  	tx2 := util.CreateTxWithExecer(cfg, genkey, "coins")          //allow
   166  	tx3 := util.CreateTxWithExecer(cfg, genkey, "evmxx")          //not allow
   167  	tx4 := util.CreateTxWithExecer(cfg, genkey, "user.evmxx.xxx") //allow
   168  	assert.Equal(t, mock33.GetAccount(block.StateHash, genaddr).Balance, 100000000*types.Coin)
   169  	txs := []*types.Transaction{tx1, tx2, tx3, tx4}
   170  	var err error
   171  	block, err = util.ExecAndCheckBlockCB(mock33.GetClient(), block, txs, func(index int, receipt *types.ReceiptData) error {
   172  		if index == 0 && receipt.GetTy() != 1 {
   173  			return errors.New("user.evm is allow")
   174  		}
   175  		if index == 1 && receipt.GetTy() != 2 {
   176  			return errors.New("coins exec ok")
   177  		}
   178  		if index == 2 && receipt != nil {
   179  			return errors.New("evmxx is not allow")
   180  		}
   181  		if index == 3 && receipt.GetTy() != 1 {
   182  			return errors.New("user.evmxx.xxx is allow")
   183  		}
   184  		return nil
   185  	})
   186  	assert.Nil(t, err)
   187  }
   188  
   189  func TestExecBlock2(t *testing.T) {
   190  	mock33 := newMockNode()
   191  	defer mock33.Close()
   192  	cfg := mock33.GetClient().GetConfig()
   193  	genkey := mock33.GetGenesisKey()
   194  	genaddr := mock33.GetGenesisAddress()
   195  	mock33.WaitHeight(0)
   196  	block := mock33.GetBlock(0)
   197  	assert.Equal(t, mock33.GetAccount(block.StateHash, genaddr).Balance, 100000000*types.Coin)
   198  	txs := util.GenCoinsTxs(cfg, genkey, 2)
   199  
   200  	block2 := util.CreateNewBlock(cfg, block, txs)
   201  	detail, _, err := util.ExecBlock(mock33.GetClient(), block.StateHash, block2, false, true, false)
   202  	if err != nil {
   203  		t.Error(err)
   204  		return
   205  	}
   206  	for _, Receipt := range detail.Receipts {
   207  		if Receipt.GetTy() != 2 {
   208  			t.Errorf("exec expect true, but now false")
   209  		}
   210  	}
   211  
   212  	N := 1000
   213  	done := make(chan struct{}, N)
   214  	for i := 0; i < N; i++ {
   215  		go func() {
   216  			txs := util.GenCoinsTxs(cfg, genkey, 2)
   217  			block3 := util.CreateNewBlock(cfg, block2, txs)
   218  			detail, _, err := util.ExecBlock(mock33.GetClient(), block2.StateHash, block3, false, true, false)
   219  			assert.Nil(t, err)
   220  			for _, Receipt := range detail.Receipts {
   221  				if Receipt.GetTy() != 2 {
   222  					t.Errorf("exec expect true, but now false")
   223  				}
   224  			}
   225  			done <- struct{}{}
   226  		}()
   227  	}
   228  	for n := 0; n < N; n++ {
   229  		<-done
   230  	}
   231  }
   232  
   233  var zeroHash [32]byte
   234  
   235  func TestSameTx(t *testing.T) {
   236  	mock33 := newMockNode()
   237  	defer mock33.Close()
   238  	cfg := mock33.GetClient().GetConfig()
   239  	newblock := &types.Block{}
   240  	newblock.Height = 1
   241  	newblock.BlockTime = types.Now().Unix()
   242  	newblock.ParentHash = zeroHash[:]
   243  	newblock.Txs = util.GenNoneTxs(cfg, mock33.GetGenesisKey(), 3)
   244  	hash1 := merkle.CalcMerkleRoot(cfg, newblock.Height, newblock.Txs)
   245  	newblock.Txs = append(newblock.Txs, newblock.Txs[2])
   246  	newblock.TxHash = merkle.CalcMerkleRoot(cfg, newblock.Height, newblock.Txs)
   247  	assert.Equal(t, hash1, newblock.TxHash)
   248  	_, _, err := util.ExecBlock(mock33.GetClient(), nil, newblock, true, true, false)
   249  	assert.Equal(t, types.ErrTxDup, err)
   250  
   251  	//情况2
   252  	//[tx1,xt2,tx3,tx4,tx5,tx6] and [tx1,xt2,tx3,tx4,tx5,tx6,tx5,tx6]
   253  	newblock.Txs = util.GenNoneTxs(cfg, mock33.GetGenesisKey(), 6)
   254  	hash1 = merkle.CalcMerkleRoot(cfg, newblock.Height, newblock.Txs)
   255  	newblock.Txs = append(newblock.Txs, newblock.Txs[4:]...)
   256  	newblock.TxHash = merkle.CalcMerkleRoot(cfg, newblock.Height, newblock.Txs)
   257  	assert.Equal(t, hash1, newblock.TxHash)
   258  	_, _, err = util.ExecBlock(mock33.GetClient(), nil, newblock, true, true, false)
   259  	assert.Equal(t, types.ErrTxDup, err)
   260  }
   261  
   262  func TestExecBlock(t *testing.T) {
   263  	mock33 := newMockNode()
   264  	defer mock33.Close()
   265  	cfg := mock33.GetClient().GetConfig()
   266  	mock33.WaitHeight(0)
   267  	block0 := mock33.GetBlock(0)
   268  	block := util.CreateCoinsBlock(cfg, mock33.GetGenesisKey(), 10)
   269  	util.ExecBlock(mock33.GetClient(), block0.StateHash, block, false, true, false)
   270  }
   271  
   272  //区块执行性能更好的一个测试
   273  //1. 先生成 10万个账户,每个账户转1000个币
   274  //2. 每个区块随机取1万比交易,然后执行
   275  //3. 执行1000个块,看性能曲线的变化
   276  //4. 排除掉网络掉影响
   277  //5. 先对leveldb 做一个性能的测试
   278  
   279  //区块执行新能测试
   280  func BenchmarkExecBlock(b *testing.B) {
   281  	b.ReportAllocs()
   282  	mock33 := newMockNode()
   283  	defer mock33.Close()
   284  	cfg := mock33.GetClient().GetConfig()
   285  	block := util.CreateCoinsBlock(cfg, mock33.GetGenesisKey(), 10000)
   286  	mock33.WaitHeight(0)
   287  	block0 := mock33.GetBlock(0)
   288  	account := mock33.GetAccount(block0.StateHash, mock33.GetGenesisAddress())
   289  	assert.Equal(b, int64(10000000000000000), account.Balance)
   290  	b.ResetTimer()
   291  	for i := 0; i < b.N; i++ {
   292  		util.ExecBlock(mock33.GetClient(), block0.StateHash, block, false, true, false)
   293  	}
   294  }
   295  
   296  /*
   297  ExecLocalSameTime test
   298  */
   299  type demoApp struct {
   300  	*drivers.DriverBase
   301  }
   302  
   303  func newdemoApp() drivers.Driver {
   304  	demo := &demoApp{DriverBase: &drivers.DriverBase{}}
   305  	demo.SetChild(demo)
   306  	return demo
   307  }
   308  
   309  func (demo *demoApp) GetDriverName() string {
   310  	return "demo2"
   311  }
   312  
   313  var orderflag = drivers.ExecLocalSameTime
   314  
   315  func (demo *demoApp) ExecutorOrder() int64 {
   316  	return orderflag
   317  }
   318  
   319  func (demo *demoApp) Exec(tx *types.Transaction, index int) (receipt *types.Receipt, err error) {
   320  	addr := tx.From()
   321  	id := common.ToHex(tx.Hash())
   322  	values, err := demo.GetLocalDB().List(demoCalcLocalKey(addr, ""), nil, 0, 0)
   323  	if err != nil && err != types.ErrNotFound {
   324  		return nil, err
   325  	}
   326  	if seterrkey {
   327  		println("set err key value")
   328  		err = demo.GetLocalDB().Set([]byte("key1"), []byte("value1"))
   329  		if err != nil {
   330  			return nil, err
   331  		}
   332  	}
   333  	receipt = &types.Receipt{Ty: types.ExecOk}
   334  	receipt.KV = append(receipt.KV, &types.KeyValue{
   335  		Key:   demoCalcStateKey(addr, id),
   336  		Value: []byte(fmt.Sprint(len(values))),
   337  	})
   338  	k := []byte("LODB-demo2-localkey")
   339  	data, err := demo.GetLocalDB().Get(k)
   340  	if err != nil && err != types.ErrNotFound {
   341  		return nil, err
   342  	}
   343  	count := &types.Int64{Data: 0}
   344  	if err == nil {
   345  		err = types.Decode(data, count)
   346  		if err != nil {
   347  			return nil, err
   348  		}
   349  	}
   350  	count.Data++
   351  	receipt.Logs = append(receipt.Logs, &types.ReceiptLog{Ty: int32(len(values)),
   352  		Log: types.Encode(count)})
   353  	return receipt, nil
   354  }
   355  
   356  func (demo *demoApp) ExecLocal(tx *types.Transaction, receipt *types.ReceiptData, index int) (localkv *types.LocalDBSet, err error) {
   357  	localkv = &types.LocalDBSet{}
   358  	var count types.Int64
   359  	err = types.Decode(receipt.Logs[0].Log, &count)
   360  	if err != nil {
   361  		return nil, err
   362  	}
   363  	localkv.KV = append(localkv.KV, &types.KeyValue{
   364  		Key:   []byte("LODB-demo2-localkey"),
   365  		Value: receipt.Logs[0].Log,
   366  	})
   367  	localkv.KV = append(localkv.KV, &types.KeyValue{
   368  		Key:   []byte("LODB-demo2-localkey" + fmt.Sprint(count.GetData())),
   369  		Value: receipt.Logs[0].Log,
   370  	})
   371  	if count.GetData() > 0 {
   372  		localkv.KV = append(localkv.KV, &types.KeyValue{
   373  			Key:   []byte("LODB-demo2-localkey" + fmt.Sprint(count.GetData()-1)),
   374  			Value: nil,
   375  		})
   376  	}
   377  	return localkv, nil
   378  }
   379  
   380  func demoCalcStateKey(addr string, id string) []byte {
   381  	key := append([]byte("mavl-demo2-"), []byte(addr)...)
   382  	key = append(key, []byte(":")...)
   383  	key = append(key, []byte(id)...)
   384  	return key
   385  }
   386  
   387  func demoCalcLocalKey(addr string, id string) []byte {
   388  	key := append([]byte("LODB-demo2-"), []byte(addr)...)
   389  	key = append(key, []byte(":")...)
   390  	if len(id) > 0 {
   391  		key = append(key, []byte(id)...)
   392  	}
   393  	return key
   394  }
   395  
   396  func TestExecLocalSameTime1(t *testing.T) {
   397  	mock33 := newMockNode()
   398  	defer mock33.Close()
   399  	cfg := mock33.GetClient().GetConfig()
   400  	orderflag = 1
   401  	genkey := mock33.GetGenesisKey()
   402  	genaddr := mock33.GetGenesisAddress()
   403  	mock33.WaitHeight(0)
   404  	block := mock33.GetBlock(0)
   405  	assert.Equal(t, mock33.GetAccount(block.StateHash, genaddr).Balance, 100000000*types.Coin)
   406  	var txs []*types.Transaction
   407  	addr1, priv1 := util.Genaddress()
   408  	txs = append(txs, util.CreateCoinsTx(cfg, genkey, addr1, 1e8))
   409  	txs = append(txs, util.CreateTxWithExecer(cfg, priv1, "demo2"))
   410  	txs = append(txs, util.CreateTxWithExecer(cfg, priv1, "demo2"))
   411  	block2 := util.CreateNewBlock(cfg, block, txs)
   412  	detail, _, err := util.ExecBlock(mock33.GetClient(), block.StateHash, block2, false, true, false)
   413  	if err != nil {
   414  		t.Error(err)
   415  		return
   416  	}
   417  	for i, receipt := range detail.Receipts {
   418  		assert.Equal(t, receipt.GetTy(), int32(2), fmt.Sprint(i))
   419  	}
   420  	receipt1 := detail.Receipts[1]
   421  	assert.Equal(t, receipt1.Logs[1].Log, types.Encode(&types.Int64{Data: 1}))
   422  	receipt2 := detail.Receipts[2]
   423  	assert.Equal(t, receipt2.Logs[1].Log, types.Encode(&types.Int64{Data: 2}))
   424  }
   425  
   426  func TestExecLocalSameTime0(t *testing.T) {
   427  	mock33 := newMockNode()
   428  	defer mock33.Close()
   429  	cfg := mock33.GetClient().GetConfig()
   430  	orderflag = 0
   431  	genkey := mock33.GetGenesisKey()
   432  	genaddr := mock33.GetGenesisAddress()
   433  	mock33.WaitHeight(0)
   434  	block := mock33.GetBlock(0)
   435  	assert.Equal(t, mock33.GetAccount(block.StateHash, genaddr).Balance, 100000000*types.Coin)
   436  	var txs []*types.Transaction
   437  	addr1, priv1 := util.Genaddress()
   438  	txs = append(txs, util.CreateCoinsTx(cfg, genkey, addr1, 1e8))
   439  	txs = append(txs, util.CreateTxWithExecer(cfg, priv1, "demo2"))
   440  	txs = append(txs, util.CreateTxWithExecer(cfg, priv1, "demo2"))
   441  	block2 := util.CreateNewBlock(cfg, block, txs)
   442  	detail, _, err := util.ExecBlock(mock33.GetClient(), block.StateHash, block2, false, true, false)
   443  	if err != nil {
   444  		t.Error(err)
   445  		return
   446  	}
   447  	assert.Equal(t, detail.Receipts[0].GetTy(), int32(2))
   448  	receipt1 := detail.Receipts[1]
   449  	assert.Equal(t, receipt1.Logs[1].Log, []byte(types.ErrDisableRead.Error()))
   450  	receipt2 := detail.Receipts[2]
   451  	assert.Equal(t, receipt2.Logs[1].Log, []byte(types.ErrDisableRead.Error()))
   452  }
   453  
   454  var seterrkey = false
   455  
   456  func TestExecLocalSameTimeSetErrKey(t *testing.T) {
   457  	mock33 := newMockNode()
   458  	defer mock33.Close()
   459  	cfg := mock33.GetClient().GetConfig()
   460  	orderflag = 1
   461  	seterrkey = true
   462  	genkey := mock33.GetGenesisKey()
   463  	genaddr := mock33.GetGenesisAddress()
   464  	mock33.WaitHeight(0)
   465  	block := mock33.GetBlock(0)
   466  	assert.Equal(t, mock33.GetAccount(block.StateHash, genaddr).Balance, 100000000*types.Coin)
   467  	var txs []*types.Transaction
   468  	addr1, priv1 := util.Genaddress()
   469  	txs = append(txs, util.CreateCoinsTx(cfg, genkey, addr1, 1e8))
   470  	txs = append(txs, util.CreateTxWithExecer(cfg, priv1, "demo2"))
   471  	txs = append(txs, util.CreateTxWithExecer(cfg, priv1, "demo2"))
   472  	block2 := util.CreateNewBlock(cfg, block, txs)
   473  	detail, _, err := util.ExecBlock(mock33.GetClient(), block.StateHash, block2, false, true, false)
   474  	if err != nil {
   475  		t.Error(err)
   476  		return
   477  	}
   478  	for i, receipt := range detail.Receipts {
   479  		if i == 0 {
   480  			assert.Equal(t, receipt.GetTy(), int32(2))
   481  		}
   482  		if i >= 1 {
   483  			assert.Equal(t, receipt.GetTy(), int32(1))
   484  			assert.Equal(t, len(receipt.Logs), 2)
   485  			assert.Equal(t, receipt.Logs[1].Ty, int32(1))
   486  		}
   487  	}
   488  }