Unverified Commit ef29dd82 authored by unpolaris's avatar unpolaris Committed by GitHub

Merge pull request #2 from unpolaris/feature_evmxgo

Feature: Add evmxgo contract
parents d8e3fdec d6fae69f
all:
bash build.sh $(OUT) $(FLAG)
#!/bin/bash
# 官方ci集成脚本
strpwd=$(pwd)
strcmd=${strpwd##*dapp/}
strapp=${strcmd%/cmd*}
OUT_DIR="${1}/$strapp"
#FLAG=$2
mkdir -p "${OUT_DIR}"
cp ./build/* "${OUT_DIR}"
#!/usr/bin/env bash
# shellcheck disable=SC2128
# shellcheck source=/dev/null
source ../dapp-test-common.sh
MAIN_HTTP=""
tokenExecName="token"
ExecName="evmxgo"
privateKey="0x4dcb00c7d01a3d377c0d5a14cd7ec91798a74c8b41896c5d21fc8b9bf4b40e42"
function updateConfig() {
unsignedTx=$(curl -s --data-binary '{"jsonrpc":"2.0","id":2,"method":"Chain33.CreateTransaction","params":[{"execer":"manage","actionName":"Modify","payload":{"key":"evmxgo-mint-DOG","value":"{\"address\":\"address1234\",\"precision\":4,\"introduction\":\"介绍\"}","op":"add","addr":""}}]}' -H 'content-type:text/plain;' ${MAIN_HTTP} | jq -r ".result")
if [ "${unsignedTx}" == "" ]; then
echo_rst "update config create tx" 1
return
fi
signRawTxAndQuery "$FUNCNAME" "$privateKey" "${unsignedTx}"
}
function evmxgo_mint() {
local data='{"jsonrpc":"2.0","id":1,"method":"Chain33.CreateTransaction","params":[{"execer":"evmxgo","actionName":"Mint","payload":{"symbol":"DOG","amount":10000000}}]}'
unsignedTx=$(curl -s --data-binary "$data" -H 'content-type:text/plain;' ${MAIN_HTTP} | jq -r ".result")
if [ "${unsignedTx}" == "" ]; then
echo_rst "evmxgo mint create tx" 1
return
fi
signRawTxAndQuery "$FUNCNAME" "$privateKey" "${unsignedTx}"
}
function evmxgo_burn() {
local data='{"jsonrpc":"2.0","id":2,"method":"Chain33.CreateTransaction","params":[{"execer":"evmxgo","actionName":"Burn","payload":{"symbol":"DOG","amount":10000000}}]}'
unsignedTx=$(curl -s --data-binary "$data" -H 'content-type:text/plain;' ${MAIN_HTTP} | jq -r ".result")
if [ "${unsignedTx}" == "" ]; then
echo_rst "evmxgo mint create tx" 1
return
fi
signRawTxAndQuery "$FUNCNAME" "$privateKey" "${unsignedTx}"
}
function evmxgo_transfer() {
addr=$1
symbol=$2
local data='{"jsonrpc":"2.0","id":2,"method":"Chain33.CreateTransaction","params":[{"execer": "'"${ExecName}"'","actionName":"Transfer","payload": {"cointoken":"'"${symbol}"'", "amount": "10000000", "note": "", "to": "'"${addr}"'"}}]}'
unsignedTx=$(curl -s --data-binary "${data}" -H 'content-type:text/plain;' ${MAIN_HTTP} | jq -r ".result")
if [ "${unsignedTx}" == "" ]; then
echo_rst "token transfer create tx" 1
return
fi
signRawTxAndQuery "$FUNCNAME" "$privateKey" "${unsignedTx}"
}
function evmxgo_transfer_exec() {
addr=$1
symbol=$2
local data='{"jsonrpc":"2.0","id":1,"method":"Chain33.CreateTransaction","params":[{"execer":"'"${ExecName}"'","actionName":"TransferToExec","payload":{"cointoken":"'"${symbol}"'","amount":10000,"note":"","execName":"token","to":"12hpJBHybh1mSyCijQ2MQJPk7z7kZ7jnQa"}}]}'
unsignedTx=$(curl -s --data-binary "${data}" -H 'content-type:text/plain;' ${MAIN_HTTP} | jq -r ".result")
if [ "${unsignedTx}" == "" ]; then
echo_rst "token transfer create tx" 1
return
fi
signRawTxAndQuery "$FUNCNAME" "$privateKey" "${unsignedTx}"
}
function evmxgo_withdraw() {
addr=$1
symbol=$2
local data='{"jsonrpc":"2.0","id":1,"method":"Chain33.CreateTransaction","params":[{"execer":"'"${ExecName}"'","actionName":"Withdraw","payload":{"cointoken":"'"${symbol}"'","amount":1000,"note":"","execName":"'"${tokenExecName}"'","to":"'"${addr}"'"}}]}'
unsignedTx=$(curl -s --data-binary "${data}" -H 'content-type:text/plain;' ${MAIN_HTTP} | jq -r ".result")
if [ "${unsignedTx}" == "" ]; then
echo_rst "token transfer create tx" 1
return
fi
signRawTxAndQuery "$FUNCNAME" "$privateKey" "${unsignedTx}"
}
# 查询交易的执行结果
# 根据传入的规则,校验查询的结果 (参数1: 校验规则 参数2: 预期匹配结果)
function queryTransaction() {
validator=$1
expectRes=$2
echo "txhash=${RAW_TX_HASH}"
res=$(curl -s --data-binary '{"jsonrpc":"2.0","id":2,"method":"Chain33.QueryTransaction","params":[{"hash":"'"${RAW_TX_HASH}"'"}]}' -H 'content-type:text/plain;' ${MAIN_HTTP} | jq -r "${validator}")
if [ "${res}" != "${expectRes}" ]; then
return 1
else
return 0
fi
}
function signRawTxAndQuery() {
chain33_SignAndSendTx "$3" "$2" "${MAIN_HTTP}"
queryTransaction ".error | not" "true"
echo_rst "$1 queryExecRes" "$?"
}
function init() {
updateConfig
}
function run_test() {
local ip=$1
evmxgo_mint
evmxgo_burn
evmxgo_transfer "17EVv6tW2HzE73TVB6YXQYThQJxa7kuZb8" "DOG"
evmxgo_transfer_exec "12hpJBHybh1mSyCijQ2MQJPk7z7kZ7jnQa" "DOG"
evmxgo_withdraw "12hpJBHybh1mSyCijQ2MQJPk7z7kZ7jnQa" "DOG"
}
function main() {
chain33_RpcTestBegin evmxgo
local ip=$1
MAIN_HTTP=$ip
echo "main_ip=$MAIN_HTTP"
init
run_test "$MAIN_HTTP"
chain33_RpcTestRst evmxgo "$CASE_ERR"
}
chain33_debug_function main "http://ip:port/"
This diff is collapsed.
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package commands
import (
"strings"
pt "github.com/33cn/plugin/plugin/dapp/paracross/types"
"github.com/33cn/chain33/common/address"
"github.com/33cn/chain33/types"
)
// GetExecAddr 获取执行器地址
func GetExecAddr(exec string) (string, error) {
if ok := types.IsAllowExecName([]byte(exec), []byte(exec)); !ok {
return "", types.ErrExecNameNotAllow
}
addrResult := address.ExecAddress(exec)
result := addrResult
return result, nil
}
func getRealExecName(paraName string, name string) string {
if strings.HasPrefix(name, pt.ParaPrefix) {
return name
}
return paraName + name
}
package executor
import (
"github.com/33cn/chain33/account"
"github.com/33cn/chain33/common/address"
log "github.com/33cn/chain33/common/log/log15"
drivers "github.com/33cn/chain33/system/dapp"
"github.com/33cn/chain33/types"
evmxgotypes "github.com/33cn/plugin/plugin/dapp/evmxgo/types"
)
/*
* 执行器相关定义
* 重载基类相关接口
*/
const (
mintPrefix = "evmxgo-mint-"
evmxgoAssetsPrefix = "LODB-evmxgo-assets:"
)
var (
//日志
elog = log.New("module", "evmxgo.executor")
)
var driverName = evmxgotypes.EvmxgoX
type subConfig struct {
SaveTokenTxList bool `json:"saveTokenTxList"`
}
var subCfg subConfig
// Init register dapp
func Init(name string, cfg *types.Chain33Config, sub []byte) {
if sub != nil {
types.MustDecode(sub, &subCfg)
}
drivers.Register(cfg, GetName(), newEvmxgo, cfg.GetDappFork(driverName, "Enable"))
InitExecType()
}
// InitExecType Init Exec Type
func InitExecType() {
ety := types.LoadExecutorType(driverName)
ety.InitFuncList(types.ListMethod(&evmxgo{}))
}
type evmxgo struct {
drivers.DriverBase
}
func newEvmxgo() drivers.Driver {
t := &evmxgo{}
t.SetChild(t)
t.SetExecutorType(types.LoadExecutorType(driverName))
return t
}
// GetName get driver name
func GetName() string {
return newEvmxgo().GetName()
}
func (e *evmxgo) GetDriverName() string {
return driverName
}
// CheckTx 实现自定义检验交易接口,供框架调用
func (e *evmxgo) CheckTx(tx *types.Transaction, index int) error {
// implement code
return nil
}
func (e *evmxgo) getTokens(req *evmxgotypes.ReqEvmxgos) (types.Message, error) {
replyTokens := &evmxgotypes.ReplyEvmxgos{}
tokens, err := e.listTokenKeys(req)
if err != nil {
return nil, err
}
elog.Error("token Query GetTokens", "get count", len(tokens))
for _, t1 := range tokens {
// delete impl by set nil
if len(t1) == 0 {
continue
}
var evmxgoValue evmxgotypes.LocalEvmxgo
err = types.Decode(t1, &evmxgoValue)
if err == nil {
replyTokens.Tokens = append(replyTokens.Tokens, &evmxgoValue)
}
}
//tokenlog.Info("token Query", "replyTokens", replyTokens)
return replyTokens, nil
}
func (e *evmxgo) listTokenKeys(req *evmxgotypes.ReqEvmxgos) ([][]byte, error) {
querydb := e.GetLocalDB()
if req.QueryAll {
keys, err := querydb.List(calcEvmxgoKeyLocal(), nil, 0, 0)
if err != nil && err != types.ErrNotFound {
return nil, err
}
if len(keys) == 0 {
return nil, types.ErrNotFound
}
elog.Debug("token Query GetTokens", "get count", len(keys))
return keys, nil
}
var keys [][]byte
for _, token := range req.Tokens {
keys1, err := querydb.List(calcEvmxgoStatusKeyLocal(token), nil, 0, 0)
if err != nil && err != types.ErrNotFound {
return nil, err
}
keys = append(keys, keys1...)
elog.Debug("token Query GetTokens", "get count", len(keys))
}
if len(keys) == 0 {
return nil, types.ErrNotFound
}
return keys, nil
}
func (e *evmxgo) makeTokenTxKvs(tx *types.Transaction, action *evmxgotypes.EvmxgoAction, receipt *types.ReceiptData, index int, isDel bool) ([]*types.KeyValue, error) {
var kvs []*types.KeyValue
var symbol string
if action.Ty == evmxgotypes.ActionTransfer {
symbol = action.GetTransfer().Cointoken
} else if action.Ty == evmxgotypes.ActionWithdraw {
symbol = action.GetWithdraw().Cointoken
} else if action.Ty == evmxgotypes.EvmxgoActionTransferToExec {
symbol = action.GetTransferToExec().Cointoken
} else {
return kvs, nil
}
kvs, err := tokenTxKvs(tx, symbol, e.GetHeight(), int64(index), isDel)
return kvs, err
}
func (e *evmxgo) getTokenInfo(symbol string) (types.Message, error) {
if symbol == "" {
return nil, types.ErrInvalidParam
}
key := calcEvmxgoStatusKeyLocal(symbol)
values, err := e.GetLocalDB().List(key, nil, 0, 0)
if err != nil {
return nil, err
}
if len(values) == 0 || values[0] == nil || len(values[0]) == 0 {
return nil, types.ErrNotFound
}
var tokenInfo evmxgotypes.LocalEvmxgo
err = types.Decode(values[0], &tokenInfo)
if err != nil {
return &tokenInfo, err
}
return &tokenInfo, nil
}
func (e *evmxgo) getBalance(req *types.ReqBalance) ([]*types.Account, error) {
cfg := e.GetAPI().GetConfig()
accountdb, err := account.NewAccountDB(cfg, evmxgotypes.EvmxgoX, req.GetAssetSymbol(), nil)
if err != nil {
return nil, err
}
switch req.GetExecer() {
case cfg.ExecName(evmxgotypes.EvmxgoX):
queryAddrs := req.GetAddresses()
accounts, err := accountdb.LoadAccounts(e.GetAPI(), queryAddrs)
if err != nil {
log.Error("GetTokenBalance", "err", err.Error(), "token symbol", req.GetAssetSymbol(), "address", queryAddrs)
return nil, err
}
return accounts, nil
default:
execaddress := address.ExecAddress(req.GetExecer())
addrs := req.GetAddresses()
var accounts []*types.Account
for _, addr := range addrs {
acc, err := accountdb.LoadExecAccountQueue(e.GetAPI(), addr, execaddress)
if err != nil {
log.Error("GetTokenBalance for exector", "err", err.Error(), "token symbol", req.GetAssetSymbol(),
"address", addr)
continue
}
accounts = append(accounts, acc)
}
return accounts, nil
}
}
package executor
import (
"encoding/json"
"fmt"
"github.com/33cn/chain33/account"
"github.com/33cn/chain33/client"
dbm "github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/system/dapp"
"github.com/33cn/chain33/types"
evmxgotypes "github.com/33cn/plugin/plugin/dapp/evmxgo/types"
)
type evmxgoDB struct {
evmxgo evmxgotypes.Evmxgo
}
func newEvmxgoDB(cfg *types.Chain33Config, mint *evmxgotypes.EvmxgoMint, creator string, height int64) *evmxgoDB {
e := &evmxgoDB{}
e.evmxgo.Symbol = mint.GetSymbol()
return e
}
func (e *evmxgoDB) save(db dbm.KV, key []byte) {
set := e.getKVSet(key)
for i := 0; i < len(set); i++ {
err := db.Set(set[i].GetKey(), set[i].Value)
if err != nil {
panic(err)
}
}
}
func (e *evmxgoDB) getLogs(ty int32, status int32) []*types.ReceiptLog {
var log []*types.ReceiptLog
value := types.Encode(&evmxgotypes.ReceiptEvmxgo{Symbol: e.evmxgo.Symbol})
log = append(log, &types.ReceiptLog{Ty: ty, Log: value})
return log
}
//key:mavl-create-token-addr-xxx or mavl-token-xxx <-----> value:token
func (e *evmxgoDB) getKVSet(key []byte) (kvset []*types.KeyValue) {
value := types.Encode(&e.evmxgo)
kvset = append(kvset, &types.KeyValue{Key: key, Value: value})
return kvset
}
func loadEvmxgoDB(db dbm.KV, symbol string) (*evmxgoDB, error) {
evmxgo, err := db.Get(calcEvmxgoKey(symbol))
if err != nil {
elog.Error("evmxgodb load ", "Can't get token form db for token", symbol)
return nil, evmxgotypes.ErrEvmxgoSymbolNotExist
}
var e evmxgotypes.Evmxgo
err = types.Decode(evmxgo, &e)
if err != nil {
elog.Error("evmxgodb load", "Can't decode token info", symbol)
return nil, err
}
return &evmxgoDB{e}, nil
}
func (e *evmxgoDB) mint(db dbm.KV, addr string, amount int64) ([]*types.KeyValue, []*types.ReceiptLog, error) {
if e.evmxgo.Total+amount > types.MaxTokenBalance {
return nil, nil, types.ErrAmount
}
prevEvmxgo := e.evmxgo
e.evmxgo.Total += amount
kvs := e.getKVSet(calcEvmxgoKey(e.evmxgo.Symbol))
logs := []*types.ReceiptLog{{Ty: evmxgotypes.TyLogEvmxgoMint, Log: types.Encode(&evmxgotypes.ReceiptEvmxgoAmount{Prev: &prevEvmxgo, Current: &e.evmxgo})}}
return kvs, logs, nil
}
func (e *evmxgoDB) burn(db dbm.KV, amount int64) ([]*types.KeyValue, []*types.ReceiptLog, error) {
if e.evmxgo.Total < amount {
return nil, nil, types.ErrNoBalance
}
prevToken := e.evmxgo
e.evmxgo.Total -= amount
kvs := e.getKVSet(calcEvmxgoKey(e.evmxgo.Symbol))
logs := []*types.ReceiptLog{{Ty: evmxgotypes.TyLogEvmxgoBurn, Log: types.Encode(&evmxgotypes.ReceiptEvmxgoAmount{Prev: &prevToken, Current: &e.evmxgo})}}
return kvs, logs, nil
}
type evmxgoAction struct {
coinsAccount *account.DB
db dbm.KV
txhash []byte
fromaddr string
toaddr string
blocktime int64
height int64
execaddr string
api client.QueueProtocolAPI
}
func newEvmxgoAction(e *evmxgo, toaddr string, tx *types.Transaction) *evmxgoAction {
hash := tx.Hash()
fromaddr := tx.From()
return &evmxgoAction{e.GetCoinsAccount(), e.GetStateDB(), hash, fromaddr, toaddr,
e.GetBlockTime(), e.GetHeight(), dapp.ExecAddress(string(tx.Execer)), e.GetAPI()}
}
func getManageKey(key string, db dbm.KV) ([]byte, error) {
manageKey := types.ManageKey(key)
value, err := db.Get([]byte(manageKey))
if err != nil {
elog.Info("evmxgodb", "get db key", "not found manageKey", "key", manageKey)
return getConfigKey(key, db)
}
return value, nil
}
func getConfigKey(key string, db dbm.KV) ([]byte, error) {
configKey := types.ConfigKey(key)
value, err := db.Get([]byte(configKey))
if err != nil {
elog.Info("evmxgodb", "get db key", "not found configKey", "key", configKey)
return nil, err
}
return value, nil
}
func hasConfiged(v1, key string, db dbm.KV) (bool, error) {
value, err := getManageKey(key, db)
if err != nil {
elog.Info("evmxgodb", "get db key", "not found", "key", key)
return false, err
}
if value == nil {
elog.Info("evmxgodb", "get db key", " found nil value", "key", key)
return false, nil
}
var item types.ConfigItem
err = types.Decode(value, &item)
if err != nil {
elog.Error("evmxgodb", "get db key", err)
return false, err // types.ErrBadConfigValue
}
for _, v := range item.GetArr().Value {
if v == v1 {
return true, nil
}
}
return false, nil
}
func loadEvmxgoMintConfig(db dbm.KV, symbol string) (*evmxgotypes.EvmxgoMintConfig, error) {
key := fmt.Sprintf(mintPrefix+"%s", symbol)
value, err := getManageKey(key, db)
if err != nil {
elog.Info("evmxgodb", "get db key", "not found", "key", key)
return nil, err
}
if value == nil {
elog.Info("evmxgodb", "get db key", " found nil value", "key", key)
return nil, nil
}
elog.Info("loadEvmxgoMintConfig", "value", string(value))
var item types.ConfigItem
err = types.Decode(value, &item)
if err != nil {
elog.Error("evmxgodb load loadEvmxgoMintConfig", "Can't decode ConfigItem", symbol)
return nil, err // types.ErrBadConfigValue
}
configValue := item.GetArr().Value
if len(configValue) <= 0 {
return nil, evmxgotypes.ErrEvmxgoSymbolNotConfigValue
}
var e evmxgotypes.EvmxgoMintConfig
err = json.Unmarshal([]byte(configValue[0]), &e)
if err != nil {
elog.Error("evmxgodb load", "Can't decode token info", symbol)
return nil, err
}
return &e, nil
}
func calcTokenAssetsKey(addr string) []byte {
return []byte(fmt.Sprintf(evmxgoAssetsPrefix+"%s", addr))
}
func getTokenAssetsKey(addr string, db dbm.KVDB) (*types.ReplyStrings, error) {
key := calcTokenAssetsKey(addr)
value, err := db.Get(key)
if err != nil && err != types.ErrNotFound {
elog.Error("evmxgodb", "GetTokenAssetsKey", err)
return nil, err
}
var assets types.ReplyStrings
if err == types.ErrNotFound {
return &assets, nil
}
err = types.Decode(value, &assets)
if err != nil {
elog.Error("evmxgodb", "GetTokenAssetsKey", err)
return nil, err
}
return &assets, nil
}
// AddTokenToAssets 添加个人资产列表
func AddTokenToAssets(addr string, db dbm.KVDB, symbol string) []*types.KeyValue {
tokenAssets, err := getTokenAssetsKey(addr, db)
if err != nil {
return nil
}
if tokenAssets == nil {
tokenAssets = &types.ReplyStrings{}
}
var found = false
for _, sym := range tokenAssets.Datas {
if sym == symbol {
found = true
break
}
}
if !found {
tokenAssets.Datas = append(tokenAssets.Datas, symbol)
}
var kv []*types.KeyValue
kv = append(kv, &types.KeyValue{Key: calcTokenAssetsKey(addr), Value: types.Encode(tokenAssets)})
return kv
}
// 铸币不可控, 也是麻烦。 2选1
// 1. 谁可以发起
// 2. 是否需要审核 这个会增加管理的成本
// 现在实现选择 1
func (action *evmxgoAction) mint(mint *evmxgotypes.EvmxgoMint) (*types.Receipt, error) {
if mint == nil {
return nil, types.ErrInvalidParam
}
if mint.GetAmount() < 0 || mint.GetAmount() > types.MaxTokenBalance || mint.GetSymbol() == "" {
return nil, types.ErrInvalidParam
}
cfg := action.api.GetConfig()
evmxgodb, err := loadEvmxgoDB(action.db, mint.GetSymbol())
if err != nil && err != evmxgotypes.ErrEvmxgoSymbolNotExist {
return nil, err
}
// evmxgo合约,只要配置了就可以铸币
if err == evmxgotypes.ErrEvmxgoSymbolNotExist {
configSynbol, err := loadEvmxgoMintConfig(action.db, mint.GetSymbol())
if err != nil || configSynbol == nil {
elog.Error("evmxgo mint ", "not config symbol", mint.GetSymbol(), "error", err)
return nil, evmxgotypes.ErrEvmxgoSymbolNotAllowedMint
}
evmxgodb = newEvmxgoDB(cfg, mint, action.fromaddr, action.height)
}
kvs, logs, err := evmxgodb.mint(action.db, action.fromaddr, mint.Amount)
if err != nil {
elog.Error("evmxgo mint ", "symbol", mint.GetSymbol(), "error", err, "from", action.fromaddr)
return nil, err
}
evmxgoAccount, err := account.NewAccountDB(cfg, "evmxgo", mint.GetSymbol(), action.db)
if err != nil {
return nil, err
}
elog.Debug("mint", "evmxgo.Symbol", mint.Symbol, "evmxgo.Amount", mint.Amount)
receipt, err := evmxgoAccount.Mint(action.fromaddr, mint.Amount)
if err != nil {
return nil, err
}
logs = append(logs, receipt.Logs...)
kvs = append(kvs, receipt.KV...)
return &types.Receipt{Ty: types.ExecOk, KV: kvs, Logs: logs}, nil
}
func (action *evmxgoAction) burn(burn *evmxgotypes.EvmxgoBurn) (*types.Receipt, error) {
if burn == nil {
return nil, types.ErrInvalidParam
}
if burn.GetAmount() < 0 || burn.GetAmount() > types.MaxTokenBalance || burn.GetSymbol() == "" {
return nil, types.ErrInvalidParam
}
evmxgodb, err := loadEvmxgoDB(action.db, burn.GetSymbol())
if err != nil {
return nil, err
}
kvs, logs, err := evmxgodb.burn(action.db, burn.Amount)
if err != nil {
elog.Error("evmxgo burn ", "symbol", burn.GetSymbol(), "error", err, "from", action.fromaddr)
return nil, err
}
chain33cfg := action.api.GetConfig()
evmxgoAccount, err := account.NewAccountDB(chain33cfg, "evmxgo", burn.GetSymbol(), action.db)
if err != nil {
return nil, err
}
elog.Debug("evmxgo burn", "burn.Symbol", burn.Symbol, "burn.Amount", burn.Amount)
receipt, err := evmxgoAccount.Burn(action.fromaddr, burn.Amount)
if err != nil {
return nil, err
}
logs = append(logs, receipt.Logs...)
kvs = append(kvs, receipt.KV...)
return &types.Receipt{Ty: types.ExecOk, KV: kvs, Logs: logs}, nil
}
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package executor
import (
"fmt"
"github.com/33cn/chain33/common/address"
"github.com/33cn/chain33/system/dapp"
"github.com/33cn/chain33/types"
tp "github.com/33cn/plugin/plugin/dapp/token/types"
)
const (
evmxgoTxPrefix = "LODB-evmxgo-txHash:"
evmxgoTxAddrPrefix = "LODB-evmxgo-txAddrHash:"
evmxgoTxAddrDirPrefix = "LODB-evmxgo-txAddrDirHash:"
)
func tokenTxKvs(tx *types.Transaction, symbol string, height, index int64, isDel bool) ([]*types.KeyValue, error) {
var kv []*types.KeyValue
from := address.PubKeyToAddress(tx.GetSignature().GetPubkey()).String()
to := tx.GetRealToAddr()
keys := tokenTxkeys(symbol, from, to, height, index)
var txInfo []byte
if !isDel {
txInfo = makeReplyTxInfo(tx, height, index, symbol)
}
for _, k := range keys {
kv = append(kv, &types.KeyValue{Key: k, Value: txInfo})
}
return kv, nil
}
func tokenTxkeys(symbol, from, to string, height, index int64) (result [][]byte) {
key := calcTokenTxKey(symbol, height, index)
result = append(result, key)
if len(from) > 0 {
fromKey1 := calcTokenAddrTxKey(symbol, from, height, index)
fromKey2 := calcTokenAddrTxDirKey(symbol, from, dapp.TxIndexFrom, height, index)
result = append(result, fromKey1)
result = append(result, fromKey2)
}
if len(to) > 0 {
toKey1 := calcTokenAddrTxKey(symbol, to, height, index)
toKey2 := calcTokenAddrTxDirKey(symbol, to, dapp.TxIndexTo, height, index)
result = append(result, toKey1)
result = append(result, toKey2)
}
return
}
// calcTokenTxKey evmxgo transaction entities in local DB
func calcTokenTxKey(symbol string, height, index int64) []byte {
if height == -1 {
return []byte(fmt.Sprintf(evmxgoTxPrefix+"%s:%s", symbol, ""))
}
return []byte(fmt.Sprintf(evmxgoTxPrefix+"%s:%s", symbol, dapp.HeightIndexStr(height, index)))
}
func calcTokenAddrTxKey(symbol, addr string, height, index int64) []byte {
if height == -1 {
return []byte(fmt.Sprintf(evmxgoTxAddrPrefix+"%s:%s:%s", symbol, addr, ""))
}
return []byte(fmt.Sprintf(evmxgoTxAddrPrefix+"%s:%s:%s", symbol, addr, dapp.HeightIndexStr(height, index)))
}
func calcTokenAddrTxDirKey(symbol, addr string, flag int32, height, index int64) []byte {
if height == -1 {
return []byte(fmt.Sprintf(evmxgoTxAddrDirPrefix+"%s:%s:%d:%s", symbol, addr, flag, ""))
}
return []byte(fmt.Sprintf(evmxgoTxAddrDirPrefix+"%s:%s:%d:%s", symbol, addr, flag,
dapp.HeightIndexStr(height, index)))
}
func makeReplyTxInfo(tx *types.Transaction, height, index int64, symbol string) []byte {
var info types.ReplyTxInfo
info.Hash = tx.Hash()
info.Height = height
info.Index = index
info.Assets = []*types.Asset{{Exec: tp.TokenX, Symbol: symbol}}
return types.Encode(&info)
}
package executor
import (
"github.com/33cn/chain33/account"
"github.com/33cn/chain33/types"
evmxgotypes "github.com/33cn/plugin/plugin/dapp/evmxgo/types"
)
/*
* 实现交易的链上执行接口
* 关键数据上链(statedb)并生成交易回执(log)
*/
func (e *evmxgo) Exec_Transfer(payload *types.AssetsTransfer, tx *types.Transaction, index int) (*types.Receipt, error) {
token := payload.GetCointoken()
cfg := e.GetAPI().GetConfig()
db, err := account.NewAccountDB(cfg, e.GetName(), token, e.GetStateDB())
if err != nil {
return nil, err
}
action := evmxgotypes.EvmxgoAction{
Ty: evmxgotypes.ActionTransfer,
Value: &evmxgotypes.EvmxgoAction_Transfer{
Transfer: payload,
},
}
return e.ExecTransWithdraw(db, tx, &action, index)
}
func (e *evmxgo) Exec_Withdraw(payload *types.AssetsWithdraw, tx *types.Transaction, index int) (*types.Receipt, error) {
token := payload.GetCointoken()
cfg := e.GetAPI().GetConfig()
db, err := account.NewAccountDB(cfg, e.GetName(), token, e.GetStateDB())
if err != nil {
return nil, err
}
action := evmxgotypes.EvmxgoAction{
Ty: evmxgotypes.ActionWithdraw,
Value: &evmxgotypes.EvmxgoAction_Withdraw{
Withdraw: payload,
},
}
return e.ExecTransWithdraw(db, tx, &action, index)
}
func (e *evmxgo) Exec_TransferToExec(payload *types.AssetsTransferToExec, tx *types.Transaction, index int) (*types.Receipt, error) {
token := payload.GetCointoken()
cfg := e.GetAPI().GetConfig()
db, err := account.NewAccountDB(cfg, e.GetName(), token, e.GetStateDB())
if err != nil {
return nil, err
}
action := evmxgotypes.EvmxgoAction{
Ty: evmxgotypes.EvmxgoActionTransferToExec,
Value: &evmxgotypes.EvmxgoAction_TransferToExec{
TransferToExec: payload,
},
}
return e.ExecTransWithdraw(db, tx, &action, index)
}
func (e *evmxgo) Exec_Mint(payload *evmxgotypes.EvmxgoMint, tx *types.Transaction, index int) (*types.Receipt, error) {
action:= newEvmxgoAction(e, "", tx)
return action.mint(payload)
}
func (e *evmxgo) Exec_Burn(payload *evmxgotypes.EvmxgoBurn, tx *types.Transaction, index int) (*types.Receipt, error) {
action:= newEvmxgoAction(e, "", tx)
return action.burn(payload)
}
package executor
import (
"github.com/33cn/chain33/system/dapp"
"github.com/33cn/chain33/types"
evmxgotypes "github.com/33cn/plugin/plugin/dapp/evmxgo/types"
)
/*
* 实现区块回退时本地执行的数据清除
*/
// ExecDelLocal localdb kv数据自动回滚接口
func (e *evmxgo) ExecDelLocal(tx *types.Transaction, receipt *types.ReceiptData, index int) (*types.LocalDBSet, error) {
kvs, err := e.DelRollbackKV(tx, tx.Execer)
if err != nil {
return nil, err
}
dbSet := &types.LocalDBSet{}
dbSet.KV = append(dbSet.KV, kvs...)
return dbSet, nil
}
func (e *evmxgo) ExecDelLocal_Transfer(payload *types.AssetsTransfer, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
set, err := e.ExecDelLocalLocalTransWithdraw(tx, receiptData, index)
if err != nil {
return nil, err
}
if subCfg.SaveTokenTxList {
action := evmxgotypes.EvmxgoAction{
Ty: evmxgotypes.ActionTransfer,
Value: &evmxgotypes.EvmxgoAction_Transfer{
Transfer: payload,
},
}
kvs, err := e.makeTokenTxKvs(tx, &action, receiptData, index, true)
if err != nil {
return nil, err
}
set.KV = append(set.KV, kvs...)
}
return set, nil
}
func (e *evmxgo) ExecDelLocal_Withdraw(payload *types.AssetsWithdraw, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
set, err := e.ExecDelLocalLocalTransWithdraw(tx, receiptData, index)
if err != nil {
return nil, err
}
if subCfg.SaveTokenTxList {
tokenAction := evmxgotypes.EvmxgoAction{
Ty: evmxgotypes.ActionWithdraw,
Value: &evmxgotypes.EvmxgoAction_Withdraw{
Withdraw: payload,
},
}
kvs, err := e.makeTokenTxKvs(tx, &tokenAction, receiptData, index, true)
if err != nil {
return nil, err
}
set.KV = append(set.KV, kvs...)
}
return set, nil
}
func (e *evmxgo) ExecDelLocal_TransferToExec(payload *types.AssetsTransferToExec, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
set, err := e.ExecDelLocalLocalTransWithdraw(tx, receiptData, index)
if err != nil {
return nil, err
}
if subCfg.SaveTokenTxList {
tokenAction := evmxgotypes.EvmxgoAction{
Ty: evmxgotypes.EvmxgoActionTransferToExec,
Value: &evmxgotypes.EvmxgoAction_TransferToExec{
TransferToExec: payload,
},
}
kvs, err := e.makeTokenTxKvs(tx, &tokenAction, receiptData, index, true)
if err != nil {
return nil, err
}
set.KV = append(set.KV, kvs...)
}
return set, nil
}
func resetMint(e *evmxgotypes.LocalEvmxgo, height, time, amount int64) *evmxgotypes.LocalEvmxgo {
e.Total = e.Total - amount
return e
}
func resetBurn(e *evmxgotypes.LocalEvmxgo, height, time, amount int64) *evmxgotypes.LocalEvmxgo {
e.Total = e.Total + amount
return e
}
func (e *evmxgo) ExecDelLocal_Mint(payload *evmxgotypes.EvmxgoMint, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
localToken, err := loadLocalToken(payload.Symbol, e.GetLocalDB())
if err != nil {
return nil, err
}
localToken = resetMint(localToken, e.GetHeight(), e.GetBlockTime(), payload.Amount)
key := calcEvmxgoStatusKeyLocal(payload.Symbol)
var set []*types.KeyValue
set = append(set, &types.KeyValue{Key: key, Value: types.Encode(localToken)})
table := NewLogsTable(e.GetLocalDB())
txIndex := dapp.HeightIndexStr(e.GetHeight(), int64(index))
err = table.Del([]byte(txIndex))
if err != nil {
return nil, err
}
kv, err := table.Save()
if err != nil {
return nil, err
}
set = append(set, kv...)
return &types.LocalDBSet{KV: set}, nil
}
func (e *evmxgo) ExecDelLocal_Burn(payload *evmxgotypes.EvmxgoBurn, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
localToken, err := loadLocalToken(payload.Symbol, e.GetLocalDB())
if err != nil {
return nil, err
}
localToken = resetBurn(localToken, e.GetHeight(), e.GetBlockTime(), payload.Amount)
key := calcEvmxgoStatusKeyLocal(payload.Symbol)
var set []*types.KeyValue
set = append(set, &types.KeyValue{Key: key, Value: types.Encode(localToken)})
table := NewLogsTable(e.GetLocalDB())
txIndex := dapp.HeightIndexStr(e.GetHeight(), int64(index))
err = table.Del([]byte(txIndex))
if err != nil {
return nil, err
}
kv, err := table.Save()
if err != nil {
return nil, err
}
set = append(set, kv...)
return &types.LocalDBSet{KV: set}, nil
}
package executor
import (
"encoding/hex"
"github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/system/dapp"
"github.com/33cn/chain33/types"
evmxgotypes "github.com/33cn/plugin/plugin/dapp/evmxgo/types"
)
/*
* 实现交易相关数据本地执行,数据不上链
* 非关键数据,本地存储(localDB), 用于辅助查询,效率高
*/
func (e *evmxgo) ExecLocal_Transfer(payload *types.AssetsTransfer, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
set, err := e.ExecLocalTransWithdraw(tx, receiptData, index)
if err != nil {
return nil, err
}
// 添加个人资产列表
kv := AddTokenToAssets(payload.To, e.GetLocalDB(), payload.Cointoken)
if kv != nil {
set.KV = append(set.KV, kv...)
}
if subCfg.SaveTokenTxList {
evmxgoAction := evmxgotypes.EvmxgoAction{
Ty: evmxgotypes.ActionTransfer,
Value: &evmxgotypes.EvmxgoAction_Transfer{
Transfer: payload,
},
}
kvs, err := e.makeTokenTxKvs(tx, &evmxgoAction, receiptData, index, false)
if err != nil {
return nil, err
}
set.KV = append(set.KV, kvs...)
}
return set, nil
}
func (e *evmxgo) ExecLocal_Withdraw(payload *types.AssetsWithdraw, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
set, err := e.ExecLocalTransWithdraw(tx, receiptData, index)
if err != nil {
return nil, err
}
// 添加个人资产列表
kv := AddTokenToAssets(tx.From(), e.GetLocalDB(), payload.Cointoken)
if kv != nil {
set.KV = append(set.KV, kv...)
}
if subCfg.SaveTokenTxList {
evmxgoAction := evmxgotypes.EvmxgoAction{
Ty: evmxgotypes.ActionWithdraw,
Value: &evmxgotypes.EvmxgoAction_Withdraw{
Withdraw: payload,
},
}
kvs, err := e.makeTokenTxKvs(tx, &evmxgoAction, receiptData, index, false)
if err != nil {
return nil, err
}
set.KV = append(set.KV, kvs...)
}
return set, nil
}
func (e *evmxgo) ExecLocal_TransferToExec(payload *types.AssetsTransferToExec, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
set, err := e.ExecLocalTransWithdraw(tx, receiptData, index)
if err != nil {
return nil, err
}
if subCfg.SaveTokenTxList {
evmxgoAction := evmxgotypes.EvmxgoAction{
Ty: evmxgotypes.EvmxgoActionTransferToExec,
Value: &evmxgotypes.EvmxgoAction_TransferToExec{
TransferToExec: payload,
},
}
kvs, err := e.makeTokenTxKvs(tx, &evmxgoAction, receiptData, index, false)
if err != nil {
return nil, err
}
set.KV = append(set.KV, kvs...)
}
return set, nil
}
func loadLocalToken(symbol string, db db.KVDB) (*evmxgotypes.LocalEvmxgo, error) {
key := calcEvmxgoStatusKeyLocal(symbol)
v, err := db.Get(key)
if err != nil {
return nil, evmxgotypes.ErrEvmxgoSymbolNotExist
}
var localToken evmxgotypes.LocalEvmxgo
err = types.Decode(v, &localToken)
if err != nil {
return nil, err
}
return &localToken, nil
}
func newLocalEvmxgo(mint *evmxgotypes.EvmxgoMint) *evmxgotypes.LocalEvmxgo {
e := evmxgotypes.LocalEvmxgo{}
e.Symbol = mint.GetSymbol()
return &e
}
func setMint(t *evmxgotypes.LocalEvmxgo, height, time, amount int64) *evmxgotypes.LocalEvmxgo {
t.Total = t.Total + amount
return t
}
func setBurn(t *evmxgotypes.LocalEvmxgo, height, time, amount int64) *evmxgotypes.LocalEvmxgo {
t.Total = t.Total - amount
return t
}
func (e *evmxgo) ExecLocal_Mint(payload *evmxgotypes.EvmxgoMint, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
localToken, err := loadLocalToken(payload.Symbol, e.GetLocalDB())
if err != nil && err != evmxgotypes.ErrEvmxgoSymbolNotExist {
return nil, err
}
// evmxgo合约,只要配置了就可以铸币
if err == evmxgotypes.ErrEvmxgoSymbolNotExist {
configSynbol, err := loadEvmxgoMintConfig(e.GetStateDB(), payload.GetSymbol())
if err != nil || configSynbol == nil {
elog.Error("evmxgo mint ", "not config symbol", payload.GetSymbol(), "error", err)
return nil, evmxgotypes.ErrEvmxgoSymbolNotAllowedMint
}
localToken = newLocalEvmxgo(payload)
localToken.Introduction = configSynbol.Introduction
localToken.Precision = configSynbol.Precision
}
localToken = setMint(localToken, e.GetHeight(), e.GetBlockTime(), payload.Amount)
var set []*types.KeyValue
key := calcEvmxgoStatusKeyLocal(payload.Symbol)
set = append(set, &types.KeyValue{Key: key, Value: types.Encode(localToken)})
table := NewLogsTable(e.GetLocalDB())
txIndex := dapp.HeightIndexStr(e.GetHeight(), int64(index))
err = table.Add(&evmxgotypes.LocalEvmxgoLogs{Symbol: payload.Symbol, TxIndex: txIndex, ActionType: evmxgotypes.EvmxgoActionMint, TxHash: "0x" + hex.EncodeToString(tx.Hash())})
if err != nil {
return nil, err
}
kv, err := table.Save()
if err != nil {
return nil, err
}
set = append(set, kv...)
return &types.LocalDBSet{KV: set}, nil
}
func (e *evmxgo) ExecLocal_Burn(payload *evmxgotypes.EvmxgoBurn, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
localToken, err := loadLocalToken(payload.Symbol, e.GetLocalDB())
if err != nil {
return nil, err
}
localToken = setBurn(localToken, e.GetHeight(), e.GetBlockTime(), payload.Amount)
var set []*types.KeyValue
key := calcEvmxgoStatusKeyLocal(payload.Symbol)
set = append(set, &types.KeyValue{Key: key, Value: types.Encode(localToken)})
table := NewLogsTable(e.GetLocalDB())
txIndex := dapp.HeightIndexStr(e.GetHeight(), int64(index))
err = table.Add(&evmxgotypes.LocalEvmxgoLogs{Symbol: payload.Symbol, TxIndex: txIndex, ActionType: evmxgotypes.EvmxgoActionBurn, TxHash: "0x" + hex.EncodeToString(tx.Hash())})
if err != nil {
return nil, err
}
kv, err := table.Save()
if err != nil {
return nil, err
}
set = append(set, kv...)
return &types.LocalDBSet{KV: set}, nil
}
//当区块回滚时,框架支持自动回滚localdb kv,需要对exec-local返回的kv进行封装
func (e *evmxgo) addAutoRollBack(tx *types.Transaction, kv []*types.KeyValue) *types.LocalDBSet {
dbSet := &types.LocalDBSet{}
dbSet.KV = e.AddRollbackKV(tx, tx.Execer, kv)
return dbSet
}
package executor
import "fmt"
/*
* 用户合约存取kv数据时,key值前缀需要满足一定规范
* 即key = keyPrefix + userKey
* 需要字段前缀查询时,使用’-‘作为分割符号
*/
var (
//KeyPrefixStateDB state db key必须前缀
KeyPrefixStateDB = "mavl-evmxgo-statedb"
//KeyPrefixLocalDB local db的key必须前缀
KeyPrefixLocalDB = "LODB-evmxgo-"
evmxgoCreatedSTONewLocal = "LODB-evmxgo-create-sto-"
)
func calcEvmxgoKey(value string) (key []byte) {
return []byte(fmt.Sprintf(KeyPrefixStateDB+"-%s", value))
}
func calcEvmxgoKeyLocal() []byte {
return []byte(evmxgoCreatedSTONewLocal)
}
func calcEvmxgoStatusKeyLocal(token string) []byte {
return []byte(fmt.Sprintf(evmxgoCreatedSTONewLocal+"%s", token))
}
//存储地址上收币的信息
func calcAddrKey(token string, addr string) []byte {
return []byte(fmt.Sprintf("LODB-evmxgo-%s-Addr:%s", token, addr))
}
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package executor
// 记录token 的更改记录,
// 包含创建完成, 铸币, 以后可能包含燃烧等
import (
dbm "github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/common/db/table"
"github.com/33cn/chain33/types"
evmxgotypes "github.com/33cn/plugin/plugin/dapp/evmxgo/types"
)
var opt_logs_table = &table.Option{
Prefix: "LODB-evmxgo",
Name: "logs",
Primary: "txIndex",
Index: []string{
"symbol",
},
}
// LogsRow row
type LogsRow struct {
*evmxgotypes.LocalEvmxgoLogs
}
// NewOrderRow create row
func NewOrderRow() *LogsRow {
return &LogsRow{LocalEvmxgoLogs: nil}
}
// CreateRow create row
func (r *LogsRow) CreateRow() *table.Row {
return &table.Row{Data: &evmxgotypes.LocalEvmxgoLogs{}}
}
// SetPayload set payload
func (r *LogsRow) SetPayload(data types.Message) error {
if d, ok := data.(*evmxgotypes.LocalEvmxgoLogs); ok {
r.LocalEvmxgoLogs = d
return nil
}
return types.ErrTypeAsset
}
// Get get index key
func (r *LogsRow) Get(key string) ([]byte, error) {
switch key {
case "txIndex":
return []byte(r.TxIndex), nil
case "symbol":
return []byte(r.Symbol), nil
default:
return nil, types.ErrNotFound
}
}
// NewLogsTable create table
func NewLogsTable(kvdb dbm.KV) *table.Table {
rowMeta := NewOrderRow()
err := rowMeta.SetPayload(&evmxgotypes.LocalEvmxgoLogs{})
if err != nil {
panic(err)
}
t, err := table.NewTable(rowMeta, kvdb, opt_logs_table)
if err != nil {
panic(err)
}
return t
}
func list(db dbm.KVDB, indexName string, data *evmxgotypes.LocalEvmxgoLogs, count, direction int32) ([]*table.Row, error) {
query := NewLogsTable(db).GetQuery(db)
var primary []byte
if len(data.TxIndex) > 0 {
primary = []byte(data.TxIndex)
}
cur := &LogsRow{LocalEvmxgoLogs: data}
index, err := cur.Get(indexName)
if err != nil {
elog.Error("query List failed", "key", string(primary), "param", data, "err", err)
return nil, err
}
elog.Debug("query List dbg", "indexName", indexName, "index", string(index), "primary", primary, "count", count, "direction", direction)
rows, err := query.ListIndex(indexName, index, primary, count, direction)
if err != nil {
elog.Error("query List failed", "key", string(primary), "param", data, "err", err)
return nil, err
}
if len(rows) == 0 {
return nil, types.ErrNotFound
}
return rows, nil
}
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package executor
import (
"github.com/33cn/chain33/types"
evmxgotypes "github.com/33cn/plugin/plugin/dapp/evmxgo/types"
)
// Query_GetTokens 获取evmxgo合约里面的币
func (e *evmxgo) Query_GetTokens(in *evmxgotypes.ReqEvmxgos) (types.Message, error) {
if in == nil {
return nil, types.ErrInvalidParam
}
return e.getTokens(in)
}
// Query_GetTokenInfo 获取evmxgo合约里指定的币
func (e *evmxgo) Query_GetTokenInfo(in *types.ReqString) (types.Message, error) {
if in == nil {
return nil, types.ErrInvalidParam
}
return e.getTokenInfo(in.GetData())
}
// Query_GetBalance 获取evmxgo合约里指定地址的币
func (e *evmxgo) Query_GetBalance(in *types.ReqBalance) (types.Message, error) {
if in == nil {
return nil, types.ErrInvalidParam
}
accounts, err := e.getBalance(in)
if err != nil {
return nil, err
}
reply := evmxgotypes.ReplyAccounts{}
reply.Accounts = accounts
return &reply, nil
}
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package executor
import (
"github.com/33cn/chain33/account"
"github.com/33cn/chain33/common/address"
dbm "github.com/33cn/chain33/common/db"
drivers "github.com/33cn/chain33/system/dapp"
"github.com/33cn/chain33/types"
evmxgotypes "github.com/33cn/plugin/plugin/dapp/evmxgo/types"
)
func (e *evmxgo) ExecTransWithdraw(accountDB *account.DB, tx *types.Transaction, action *evmxgotypes.EvmxgoAction, index int) (*types.Receipt, error) {
switch {
case action.Ty == evmxgotypes.ActionTransfer && action.GetTransfer() != nil:
transfer := action.GetTransfer()
from := tx.From()
//to 是 execs 合约地址
if drivers.IsDriverAddress(transfer.To, e.GetHeight()) {
return accountDB.TransferToExec(from, transfer.To, transfer.Amount)
}
return accountDB.Transfer(from, transfer.To, transfer.Amount)
case action.Ty == evmxgotypes.ActionWithdraw && action.GetWithdraw() != nil:
withdraw := action.GetWithdraw()
from := tx.From()
//to 是 execs 合约地址
if drivers.IsDriverAddress(withdraw.To, e.GetHeight()) || isExecAddrMatch(withdraw.ExecName, withdraw.To) {
return accountDB.TransferWithdraw(from, withdraw.To, withdraw.Amount)
}
return nil, types.ErrActionNotSupport
case action.Ty == evmxgotypes.EvmxgoActionTransferToExec && action.GetTransferToExec() != nil:
transfer := action.GetTransferToExec()
from := tx.From()
//to 是 execs 合约地址
if !isExecAddrMatch(transfer.ExecName, transfer.To) {
return nil, types.ErrToAddrNotSameToExecAddr
}
return accountDB.TransferToExec(from, transfer.To, transfer.Amount)
default:
return nil, types.ErrActionNotSupport
}
}
func isExecAddrMatch(name string, to string) bool {
toaddr := address.ExecAddress(name)
return toaddr == to
}
//0: all tx
//1: from tx
//2: to tx
func (e *evmxgo) ExecLocalTransWithdraw(tx *types.Transaction, receipt *types.ReceiptData, index int) (*types.LocalDBSet, error) {
set := &types.LocalDBSet{}
if receipt.GetTy() != types.ExecOk {
return set, nil
}
//执行成功
var action evmxgotypes.EvmxgoAction
err := types.Decode(tx.GetPayload(), &action)
if err != nil {
panic(err)
}
var kv *types.KeyValue
if action.Ty == evmxgotypes.ActionTransfer && action.GetTransfer() != nil {
transfer := action.GetTransfer()
kv, err = updateAddrReciver(e.GetLocalDB(), transfer.Cointoken, transfer.To, transfer.Amount, true)
} else if action.Ty == evmxgotypes.ActionWithdraw && action.GetWithdraw() != nil {
withdraw := action.GetWithdraw()
from := tx.From()
kv, err = updateAddrReciver(e.GetLocalDB(), withdraw.Cointoken, from, withdraw.Amount, true)
} else if action.Ty == evmxgotypes.EvmxgoActionTransferToExec && action.GetTransferToExec() != nil {
transfer := action.GetTransferToExec()
kv, err = updateAddrReciver(e.GetLocalDB(), transfer.Cointoken, transfer.To, transfer.Amount, true)
}
if err != nil {
return set, nil
}
if kv != nil {
set.KV = append(set.KV, kv)
}
return set, nil
}
func (e *evmxgo) ExecDelLocalLocalTransWithdraw(tx *types.Transaction, receipt *types.ReceiptData, index int) (*types.LocalDBSet, error) {
set := &types.LocalDBSet{}
if receipt.GetTy() != types.ExecOk {
return set, nil
}
//执行成功
var action evmxgotypes.EvmxgoAction
err := types.Decode(tx.GetPayload(), &action)
if err != nil {
panic(err)
}
var kv *types.KeyValue
if action.Ty == evmxgotypes.ActionTransfer && action.GetTransfer() != nil {
transfer := action.GetTransfer()
kv, err = updateAddrReciver(e.GetLocalDB(), transfer.Cointoken, transfer.To, transfer.Amount, false)
} else if action.Ty == evmxgotypes.ActionWithdraw && action.GetWithdraw() != nil {
withdraw := action.GetWithdraw()
from := tx.From()
kv, err = updateAddrReciver(e.GetLocalDB(), withdraw.Cointoken, from, withdraw.Amount, false)
} else if action.Ty == evmxgotypes.EvmxgoActionTransferToExec && action.GetTransferToExec() != nil {
transfer := action.GetTransferToExec()
kv, err = updateAddrReciver(e.GetLocalDB(), transfer.Cointoken, transfer.To, transfer.Amount, false)
}
if err != nil {
return set, nil
}
if kv != nil {
set.KV = append(set.KV, kv)
}
return set, nil
}
func getAddrReciverKV(token string, addr string, reciverAmount int64) *types.KeyValue {
reciver := &types.Int64{Data: reciverAmount}
amountbytes := types.Encode(reciver)
kv := &types.KeyValue{Key: calcAddrKey(token, addr), Value: amountbytes}
return kv
}
func getAddrReciver(db dbm.KVDB, token string, addr string) (int64, error) {
reciver := types.Int64{}
addrReciver, err := db.Get(calcAddrKey(token, addr))
if err != nil && err != types.ErrNotFound {
return 0, err
}
if len(addrReciver) == 0 {
return 0, nil
}
err = types.Decode(addrReciver, &reciver)
if err != nil {
return 0, err
}
return reciver.Data, nil
}
func setAddrReciver(db dbm.KVDB, token string, addr string, reciverAmount int64) error {
kv := getAddrReciverKV(addr, token, reciverAmount)
return db.Set(kv.Key, kv.Value)
}
func updateAddrReciver(cachedb dbm.KVDB, token string, addr string, amount int64, isadd bool) (*types.KeyValue, error) {
recv, err := getAddrReciver(cachedb, token, addr)
if err != nil && err != types.ErrNotFound {
return nil, err
}
if isadd {
recv += amount
} else {
recv -= amount
}
err = setAddrReciver(cachedb, token, addr, recv)
if err != nil {
return nil, err
}
//keyvalue
return getAddrReciverKV(token, addr, recv), nil
}
package types
import (
"github.com/33cn/chain33/pluginmgr"
"github.com/33cn/plugin/plugin/dapp/evmxgo/commands"
"github.com/33cn/plugin/plugin/dapp/evmxgo/executor"
"github.com/33cn/plugin/plugin/dapp/evmxgo/rpc"
evmxgotypes "github.com/33cn/plugin/plugin/dapp/evmxgo/types"
)
/*
* 初始化dapp相关的组件
*/
func init() {
pluginmgr.Register(&pluginmgr.PluginBase{
Name: evmxgotypes.EvmxgoX,
ExecName: executor.GetName(),
Exec: executor.Init,
Cmd: commands.Cmd,
RPC: rpc.Init,
})
}
all:
bash ./create_protobuf.sh
#!/bin/bash
# proto生成命令,将pb.go文件生成到types/目录下, chain33_path支持引用chain33框架的proto文件
chain33_path=$(go list -f '{{.Dir}}' "github.com/33cn/chain33")
protoc --go_out=plugins=grpc:../types ./*.proto --proto_path=. --proto_path="${chain33_path}/types/proto/"
syntax = "proto3";
import "transaction.proto";
import "account.proto";
package types;
option go_package = "../types";
// action
message EvmxgoAction {
oneof value {
AssetsTransfer transfer = 1;
AssetsWithdraw withdraw = 2;
AssetsTransferToExec transferToExec = 3;
EvmxgoMint mint = 4;
EvmxgoBurn burn = 5;
}
int32 Ty = 6;
}
message EvmxgoMint {
string symbol = 1;
int64 amount = 2;
}
message EvmxgoBurn {
string symbol = 1;
int64 amount = 2;
}
// state db
message Evmxgo {
string symbol = 1;
string introduction = 2;
int64 total = 3;
}
// config mint synbol
message EvmxgoMintConfig {
string address =1;
int32 precision =2;
string introduction =3;
}
// log
message ReceiptEvmxgo {
string symbol = 1;
}
message ReceiptEvmxgoAmount {
Evmxgo prev = 1;
Evmxgo current = 2;
}
// local
message LocalEvmxgo {
string symbol = 1;
string introduction = 2;
int64 total = 3;
int32 precision = 4;
}
message LocalEvmxgoLogs {
string symbol = 1;
string txIndex = 2;
int32 actionType = 3;
string txHash = 4;
}
// query
message ReqEvmxgos {
bool queryAll = 1;
repeated string tokens = 2;
}
message ReplyEvmxgos {
repeated LocalEvmxgo tokens = 1;
}
message EvmxgoRecv {
string evmxgo = 1;
int64 recv = 2;
}
message ReplyAddrRecvForEvmxgos {
repeated EvmxgoRecv evmxgoRecvs = 1;
}
message ReplyAccounts {
repeated Account accounts =1;
}
message ReqAccountEvmxgoAssets {
string address = 1;
string execer = 2;
}
message EvmxgoAsset {
string symbol = 1;
Account account = 2;
}
message ReplyAccountEvmxgoAssets {
repeated EvmxgoAsset evmxgoAssets = 1;
}
message ReqAddrEvmxgos {
string addr = 1;
int32 status = 2;
repeated string evmxgo = 3;
int32 direction = 4;
int32 count = 5;
string fromKey = 6;
}
message ReqEvmxgoTx {
string symbol = 1;
//表示取所有/from/to/其他的hash列表
int32 flag = 2;
int32 count = 3;
int32 direction = 4;
int64 height = 5;
int64 index = 6;
string addr = 7;
}
message ReplyEvmxgoLogs {
repeated LocalEvmxgoLogs logs = 1;
}
service evmxgo {}
package rpc
/*
* 实现json rpc和grpc service接口
* json rpc用Jrpc结构作为接收实例
* grpc使用channelClient结构作为接收实例
*/
package rpc
import (
rpctypes "github.com/33cn/chain33/rpc/types"
)
/*
* rpc相关结构定义和初始化
*/
// 实现grpc的service接口
type channelClient struct {
rpctypes.ChannelClient
}
// Jrpc 实现json rpc调用实例
type Jrpc struct {
cli *channelClient
}
// Grpc grpc
type Grpc struct {
*channelClient
}
// Init init rpc
func Init(name string, s rpctypes.RPCServer) {
cli := &channelClient{}
grpc := &Grpc{channelClient: cli}
cli.Init(name, s, &Jrpc{cli: cli}, grpc)
}
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package types
const (
// ActionTransfer for transfer
ActionTransfer = 4
// ActionGenesis for genesis
ActionGenesis = 5
// ActionWithdraw for Withdraw
ActionWithdraw = 6
// EvmxgoActionTransferToExec for evmxgo transfer to exec
EvmxgoActionTransferToExec = 11
// EvmxgoActionMint for evmxgo mint
EvmxgoActionMint = 12
// EvmxgoActionBurn for evmxgo burn
EvmxgoActionBurn = 13
)
const (
// TyLogEvmxgoTransfer log for token tranfer
TyLogEvmxgoTransfer = 313
// TyLogEvmxgoDeposit log for token deposit
TyLogEvmxgoDeposit = 315
// TyLogEvmxgoExecTransfer log for token exec transfer
TyLogEvmxgoExecTransfer = 316
// TyLogEvmxgoExecWithdraw log for token exec withdraw
TyLogEvmxgoExecWithdraw = 317
// TyLogEvmxgoExecDeposit log for token exec deposit
TyLogEvmxgoExecDeposit = 318
// TyLogEvmxgoExecFrozen log for token exec frozen
TyLogEvmxgoExecFrozen = 319
// TyLogEvmxgoExecActive log for token exec active
TyLogEvmxgoExecActive = 320
// TyLogEvmxgoGenesisTransfer log for token genesis rransfer
TyLogEvmxgoGenesisTransfer = 321
// TyLogEvmxgoGenesisDeposit log for token genesis deposit
TyLogEvmxgoGenesisDeposit = 322
// TyLogEvmxgoMint log for evmxgo mint
TyLogEvmxgoMint = 323
// TyLogEvmxgoBurn log for evmxgo burn
TyLogEvmxgoBurn = 324
)
package types
import "errors"
var (
// ErrEvmxgoSymbolNotExist error evmxgo symbol not exist
ErrEvmxgoSymbolNotExist = errors.New("ErrEvmxgoSymbolNotExist")
// ErrEvmxgoSymbolNotAllowedMint error evmxgo symbol not allowed mint
ErrEvmxgoSymbolNotAllowedMint = errors.New("ErrEvmxgoSymbolNotAllowedMint")
// ErrEvmxgoSymbolNotConfigValue error evmxgo symbol not config value
ErrEvmxgoSymbolNotConfigValue = errors.New("ErrEvmxgoSymbolNotConfigValue")
)
package types
import (
log "github.com/33cn/chain33/common/log/log15"
"github.com/33cn/chain33/types"
"reflect"
)
/*
* 交易相关类型定义
* 交易action通常有对应的log结构,用于交易回执日志记录
* 每一种action和log需要用id数值和name名称加以区分
*/
// action类型id和name,这些常量可以自定义修改
const (
TyUnknowAction = iota + 100
TyTransferAction
TyWithdrawAction
TyTransferToExecAction
TyMintAction
TyBurnAction
NameTransferAction = "Transfer"
NameWithdrawAction = "Withdraw"
NameTransferToExecAction = "TransferToExec"
NameMintAction = "Mint"
NameBurnAction = "Burn"
)
// log类型id值
const (
TyUnknownLog = iota + 100
TyTransferLog
TyWithdrawLog
TyTransferToExecLog
TyMintLog
TyBurnLog
)
var (
//EvmxgoX 执行器名称定义
EvmxgoX = "evmxgo"
//定义actionMap
actionMap = map[string]int32{
NameTransferAction: TyTransferAction,
NameWithdrawAction: TyWithdrawAction,
NameTransferToExecAction: TyTransferToExecAction,
NameMintAction: TyMintAction,
NameBurnAction: TyBurnAction,
}
//定义log的id和具体log类型及名称,填入具体自定义log类型
logMap = map[int64]*types.LogInfo{
//LogID: {Ty: reflect.TypeOf(LogStruct), Name: LogName},
}
tlog = log.New("module", "evmxgo.types")
)
// init defines a register function
func init() {
types.AllowUserExec = append(types.AllowUserExec, []byte(EvmxgoX))
//注册合约启用高度
types.RegFork(EvmxgoX, InitFork)
types.RegExec(EvmxgoX, InitExecutor)
}
// InitFork defines register fork
func InitFork(cfg *types.Chain33Config) {
cfg.RegisterDappFork(EvmxgoX, "Enable", 0)
}
// InitExecutor defines register executor
func InitExecutor(cfg *types.Chain33Config) {
types.RegistorExecutor(EvmxgoX, NewType(cfg))
}
type evmxgoType struct {
types.ExecTypeBase
}
func NewType(cfg *types.Chain33Config) *evmxgoType {
c := &evmxgoType{}
c.SetChild(c)
c.SetConfig(cfg)
return c
}
// GetPayload 获取合约action结构
func (e *evmxgoType) GetPayload() types.Message {
return &EvmxgoAction{}
}
// GetTypeMap 获取合约action的id和name信息
func (e *evmxgoType) GetTypeMap() map[string]int32 {
return map[string]int32{
"Transfer": ActionTransfer,
"Withdraw": ActionWithdraw,
"TransferToExec": EvmxgoActionTransferToExec,
"Mint": EvmxgoActionMint,
"Burn": EvmxgoActionBurn,
}
}
// GetLogMap 获取合约log相关信息
func (e *evmxgoType) GetLogMap() map[int64]*types.LogInfo {
return map[int64]*types.LogInfo{
TyLogEvmxgoTransfer: {Ty: reflect.TypeOf(types.ReceiptAccountTransfer{}), Name: "LogTokenTransfer"},
TyLogEvmxgoDeposit: {Ty: reflect.TypeOf(types.ReceiptAccountTransfer{}), Name: "LogTokenDeposit"},
TyLogEvmxgoExecTransfer: {Ty: reflect.TypeOf(types.ReceiptExecAccountTransfer{}), Name: "LogTokenExecTransfer"},
TyLogEvmxgoExecWithdraw: {Ty: reflect.TypeOf(types.ReceiptExecAccountTransfer{}), Name: "LogTokenExecWithdraw"},
TyLogEvmxgoExecDeposit: {Ty: reflect.TypeOf(types.ReceiptExecAccountTransfer{}), Name: "LogTokenExecDeposit"},
TyLogEvmxgoExecFrozen: {Ty: reflect.TypeOf(types.ReceiptExecAccountTransfer{}), Name: "LogTokenExecFrozen"},
TyLogEvmxgoExecActive: {Ty: reflect.TypeOf(types.ReceiptExecAccountTransfer{}), Name: "LogTokenExecActive"},
TyLogEvmxgoGenesisTransfer: {Ty: reflect.TypeOf(types.ReceiptAccountTransfer{}), Name: "LogTokenGenesisTransfer"},
TyLogEvmxgoGenesisDeposit: {Ty: reflect.TypeOf(types.ReceiptExecAccountTransfer{}), Name: "LogTokenGenesisDeposit"},
TyLogEvmxgoMint: {Ty: reflect.TypeOf(ReceiptEvmxgoAmount{}), Name: "LogMintToken"},
TyLogEvmxgoBurn: {Ty: reflect.TypeOf(ReceiptEvmxgoAmount{}), Name: "LogBurnToken"},
}
}
This diff is collapsed.
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package types
// EvmxgoAccountResult about evmxgo account result
type EvmxgoAccountResult struct {
Token string `json:"Token,omitempty"`
Currency int32 `json:"currency,omitempty"`
Balance string `json:"balance,omitempty"`
Frozen string `json:"frozen,omitempty"`
Addr string `json:"addr,omitempty"`
}
...@@ -10,6 +10,7 @@ import ( ...@@ -10,6 +10,7 @@ import (
_ "github.com/33cn/plugin/plugin/dapp/dposvote" //auto gen _ "github.com/33cn/plugin/plugin/dapp/dposvote" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/echo" //auto gen _ "github.com/33cn/plugin/plugin/dapp/echo" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/evm" //auto gen _ "github.com/33cn/plugin/plugin/dapp/evm" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/evmxgo" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/exchange" //auto gen _ "github.com/33cn/plugin/plugin/dapp/exchange" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/game" //auto gen _ "github.com/33cn/plugin/plugin/dapp/game" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/guess" //auto gen _ "github.com/33cn/plugin/plugin/dapp/guess" //auto gen
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment