Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proxy exec #1310

Merged
merged 15 commits into from
Sep 15, 2023
1 change: 1 addition & 0 deletions cmd/chain33/chain33.fork.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ ForkTicketFundAddrV1=3350000
ForkRootHash=4500000
ForkFormatAddressKey=0
ForkCheckEthTxSort=0
ForkProxyExec=0

[fork.sub.none]
ForkUseTimeDelay=0
Expand Down
1 change: 1 addition & 0 deletions cmd/chain33/chain33.system.fork.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ ForkTicketFundAddrV1=3350000
ForkRootHash=4500000
ForkFormatAddressKey=0
ForkCheckEthTxSort=0
ForkProxyExec=0
6 changes: 6 additions & 0 deletions cmd/chain33/chain33.test.toml
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,14 @@ coinType="bty"
minerwhitelist=["*"]

[exec]
#交易费相关统一在mempool中配置
#是否开启stat插件
enableStat=false
#是否开启MVCC插件
enableMVCC=false
alias=["token1:token","token2:token","token3:token"]
#代理执行器地址
proxyExecAddress="0x0000000000000000000000000000000000200005"

[exec.sub.token]
saveTokenTxList=true
Expand Down
3 changes: 2 additions & 1 deletion cmd/chain33/chain33.toml
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,8 @@ enableStat=false
#是否开启MVCC插件
enableMVCC=false
alias=["token1:token","token2:token","token3:token"]

#代理执行器地址
proxyExecAddrss="0x0000000000000000000000000000000000200005"
[exec.sub.token]
#是否保存token交易信息
saveTokenTxList=true
Expand Down
51 changes: 49 additions & 2 deletions executor/execenv.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ package executor

import (
"bytes"

"errors"
"fmt"
"github.com/33cn/chain33/account"
"github.com/33cn/chain33/client"
"github.com/33cn/chain33/client/api"
Expand Down Expand Up @@ -598,6 +599,42 @@ func (e *executor) rollback() {
}
}

func (e *executor) proxyGetRealTx(tx *types.Transaction) (*types.Transaction, error) {
if string(types.GetRealExecName(tx.GetExecer())) != "evm" {
return nil, fmt.Errorf("execName %s not allowd", string(types.GetRealExecName(tx.GetExecer())))
}
var actionData types.EVMContractAction4Chain33
err := types.Decode(tx.GetPayload(), &actionData)
if err != nil {
return nil, err
}
if len(actionData.GetPara()) == 0 {
return nil, errors.New("empty tx")
}
var realTx types.Transaction
err = types.Decode(actionData.Para, &realTx)
if err != nil {
return nil, err
}

return &realTx, nil

}

func (e *executor) proxyExecTx(tx *types.Transaction) (*types.Transaction, error) {
var realTx = tx
var err error
if tx.To == e.cfg.GetModuleConfig().Exec.ProxyExecAddress {
realTx, err = e.proxyGetRealTx(tx)
if err != nil {
return realTx, err
}
realTx.Signature = tx.Signature

}
return realTx, nil
}

Comment on lines +602 to +637
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这部分代码建议增加单元测试覆盖

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok
plugin 也会增加ci 集成测试

func (e *executor) execTx(exec *Executor, tx *types.Transaction, index int) (*types.Receipt, error) {
if e.height == 0 { //genesis block 不检查手续费
receipt, err := e.Exec(tx, index)
Expand All @@ -609,10 +646,20 @@ func (e *executor) execTx(exec *Executor, tx *types.Transaction, index int) (*ty
}
return receipt, nil
}

var err error
//代理执行 EVM-->txpayload-->chain33 tx
if e.cfg.IsFork(e.height, "ForkProxyExec") {
tx, err = e.proxyExecTx(tx)
if err != nil {
return nil, err
}
}

//交易检查规则:
//1. mempool 检查区块,尽量检查更多的错误
//2. 打包的时候,尽量打包更多的交易,只要基本的签名,以及格式没有问题
err := e.checkTx(tx, index)
err = e.checkTx(tx, index)
if err != nil {
elog.Error("execTx.checkTx ", "txhash", common.ToHex(tx.Hash()), "err", err)
if e.cfg.IsPara() {
Expand Down
68 changes: 66 additions & 2 deletions executor/execenv_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,16 @@
package executor

import (
"github.com/33cn/chain33/common/crypto"
erpctypes "github.com/33cn/chain33/rpc/ethrpc/types"
drivers "github.com/33cn/chain33/system/dapp"
ecommon "github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"
ethcrypto "github.com/ethereum/go-ethereum/crypto"
"math/big"
"testing"
"time"

drivers "github.com/33cn/chain33/system/dapp"

"strings"

_ "github.com/33cn/chain33/system"
Expand All @@ -19,6 +24,7 @@ import (
)

func TestLoadDriverFork(t *testing.T) {

str := types.GetDefaultCfgstring()
new := strings.Replace(str, "Title=\"local\"", "Title=\"chain33\"", 1)
exec, _ := initEnv(types.MergeCfg(types.ReadFile("../cmd/chain33/chain33.fork.toml"), new))
Expand Down Expand Up @@ -97,3 +103,61 @@ func (app *notAllowApp) Allow(tx *types.Transaction, index int) error {
}
return types.ErrActionNotSupport
}

func TestProxyExec(t *testing.T) {
exec, _ := initEnv(types.GetDefaultCfgstring())
cfg := exec.client.GetConfig()
ctx := &executorCtx{
stateHash: nil,
height: 0,
blocktime: time.Now().Unix(),
difficulty: 1,
mainHash: nil,
parentHash: nil,
}

execute := newExecutor(ctx, exec, nil, nil, nil)
//测试解析代理地址
proxyAddr := ecommon.HexToAddress("0x0000000000000000000000000000000000200005")
var hexKey = "7939624566468cfa3cb2c9f39d5ad83bdc7cf4356bfd1a7b8094abda6b0699d1"
sk, err := ethcrypto.ToECDSA(ecommon.FromHex(hexKey))
assert.Nil(t, err)

signer := ethtypes.NewEIP155Signer(big.NewInt(3999))
//构建eth交易
to, _ := util.Genaddress()
//coins 转账,没有签名的裸交易
coinsTx := util.CreateCoinsTx(cfg, nil, to, 1000)
etx := ethtypes.NewTransaction(uint64(0), proxyAddr, big.NewInt(0), 3000000, big.NewInt(10e9), types.Encode(coinsTx))
signtx, err := ethtypes.SignTx(etx, signer, sk)
assert.Nil(t, err)
v, r, s := signtx.RawSignatureValues()
cv, err := erpctypes.CaculateRealV(v, signtx.ChainId().Uint64(), signtx.Type())
assert.Nil(t, err)
sig := make([]byte, 65)
copy(sig[32-len(r.Bytes()):32], r.Bytes())
copy(sig[64-len(s.Bytes()):64], s.Bytes())
sig[64] = cv
txSha3 := signer.Hash(signtx)
pubkey, err := ethcrypto.Ecrecover(txSha3.Bytes(), sig)
assert.Nil(t, err)
assembleTx := erpctypes.AssembleChain33Tx(signtx, sig, pubkey, cfg)
//checkSign
err = execute.checkTx(assembleTx, 0)
assert.Errorf(t, err, "ErrExecNameNotAllow")
types.AllowUserExec = append(types.AllowUserExec, []byte("evm"))
err = execute.checkTx(assembleTx, 0)
assert.Nil(t, err)

crypto.Init(cfg.GetModuleConfig().Crypto, cfg.GetSubConfig().Crypto)
assert.True(t, assembleTx.CheckSign(0))
//解析交易
realTx, err := execute.proxyExecTx(assembleTx)
assert.Nil(t, err)
assert.Equal(t, "0xa42431da868c58877a627cc71dc95f01bf40c196", realTx.From())
testTx := realTx.Clone()
testTx.Signature = nil

//与原始交易对比
assert.Equal(t, coinsTx.Hash(), testTx.Hash())
}
10 changes: 4 additions & 6 deletions executor/executor_real_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,6 @@ package executor_test
import (
"errors"
"fmt"
"net/http"
_ "net/http/pprof"
"testing"

"sync"

"github.com/33cn/chain33/common"
"github.com/33cn/chain33/common/address"
"github.com/33cn/chain33/common/merkle"
Expand All @@ -22,6 +16,10 @@ import (
"github.com/33cn/chain33/util"
"github.com/33cn/chain33/util/testnode"
"github.com/stretchr/testify/assert"
"net/http"
_ "net/http/pprof"
"sync"
"testing"
)

var runonce sync.Once
Expand Down
1 change: 1 addition & 0 deletions queue/queue_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,7 @@ func TestChannelClose(t *testing.T) {
go q.Start()
//rpc 模块 会向其他模块发送消息,自己本身不需要订阅消息
go func() {
time.Sleep(time.Millisecond * 100)
done <- struct{}{}
}()
for i := 0; i < 10000; i++ {
Expand Down
13 changes: 11 additions & 2 deletions rpc/ethrpc/eth/eth.go
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,17 @@ func (e *ethHandler) EstimateGas(callMsg *types.CallMsg) (hexutil.Uint64, error)
})

fee := properFee.GetProperFee()
//GetMinTxFeeRate 默认1e5
realFee, _ := tx.GetRealFee(e.cfg.GetMinTxFeeRate())
//判断是否是代理执行的地址,如果是,则直接返回,不会交给evm执行器去模拟执行计算gas.
if callMsg.To == e.cfg.GetModuleConfig().Exec.ProxyExecAddress {
rightFee := realFee
if realFee < fee {
rightFee = fee
}
return hexutil.Uint64(rightFee), nil
}

var minimumGas int64 = 21000
if callMsg.Data == nil || len(*callMsg.Data) == 0 {
if fee < e.cfg.GetMinTxFeeRate() {
Expand Down Expand Up @@ -597,8 +608,6 @@ func (e *ethHandler) EstimateGas(callMsg *types.CallMsg) (hexutil.Uint64, error)
}

bigGas, _ := new(big.Int).SetString(gas.Gas, 10)
//GetMinTxFeeRate 默认1e5
realFee, _ := tx.GetRealFee(e.cfg.GetMinTxFeeRate())

var finalFee = realFee
if bigGas.Uint64() > uint64(realFee) {
Expand Down
60 changes: 60 additions & 0 deletions system/mempool/check.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package mempool

import (
"bytes"
"errors"
"fmt"
"math/big"
"sort"
"sync/atomic"
"time"

Expand Down Expand Up @@ -198,6 +202,13 @@
}
}

//检查mempool内是否有相同的tx.nonce
err = mem.evmTxNonceCheck(tx.Tx())
if err != nil {
msg.Data = err
return msg
}

err = mem.PushTx(tx.Tx())
if err != nil {
if err == types.ErrMemFull {
Expand All @@ -209,3 +220,52 @@
}
return msg
}

//evmTxNonceCheck 检查eth noce 是否有重复值,如果有的话,需要比较txFee大小,用于替换较小的fee的那笔交易
func (mem *Mempool) evmTxNonceCheck(tx *types.Transaction) error {
if !types.IsEthSignID(tx.Tx().GetSignature().GetTy()) {
return nil
}

//较小的nonce 则返回错误,不被允许进入mempool
if tx.GetNonce() < mem.getCurrentNonce(tx.From()) {
return types.ErrLowNonce
}
details := mem.GetAccTxs(&types.ReqAddrs{Addrs: []string{tx.From()}})
txs := details.GetTxs()
txs = append(txs, &types.TransactionDetail{Tx: tx, Index: int64(len(txs))})
if len(txs) > 1 {
sort.SliceStable(txs, func(i, j int) bool { //nonce asc
return txs[i].Tx.GetNonce() < txs[j].Tx.GetNonce()
})
//遇到相同的Nonce ,较低的手续费的交易将被删除
for i, stx := range txs {
if bytes.Equal(stx.Tx.Hash(), tx.Hash()) {
continue

Check warning on line 244 in system/mempool/check.go

View check run for this annotation

Codecov / codecov/patch

system/mempool/check.go#L244

Added line #L244 was not covered by tests
}

if txs[i].GetTx().GetNonce() == tx.GetNonce() {
bnfee := big.NewInt(txs[i].GetTx().Fee)
//相同的nonce,fee 必须提升至1.1 倍 才能有效替换之前的交易
bnfee = bnfee.Mul(bnfee, big.NewInt(110))
bnfee = bnfee.Div(bnfee, big.NewInt(1e2))
if tx.Fee < bnfee.Int64() {
err := fmt.Errorf("requires at least 10 percent increase in handling fee,need more:%d", bnfee.Int64()-tx.Fee)
mlog.Error("checkTxNonce", "fee err", err, "txfee", tx.Fee, "mempooltx", txs[0].GetTx().Fee, "from:", tx.From())
return err
}

//删除Expire 较大的交易或者更低手续费的交易,确保先创建的交易留在mempool 中
mem.RemoveTxs(&types.TxHashList{
Hashes: [][]byte{txs[i].GetTx().Hash()},
})
mlog.Info("evmTxNonceCheck", "remote txhash:", common.ToHex(txs[i].GetTx().Hash()), "replace txHash:", common.ToHex(tx.Hash()))
return nil
}
}

}

return nil

}
Loading
Loading