Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions plugin/dapp/x2ethereum/executor/exec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ func (x *suiteX2Ethereum) Test_4_Eth2Chain33() {
_, err = x.x2eth.Query_GetEthProphecy(&types2.QueryEthProphecyParams{ID: "010x7B95B6EC7EbD73572298cEf32Bb54FA408207359"})
x.Equal(err, types.ErrNotFound)

ID := strconv.Itoa(0) + strconv.Itoa(0) + ethereumAddr + tokenContractAddress + "lock"
ID := calcProphecyID(payload, true)
x.queryGetEthProphecy(ID, types2.EthBridgeStatus_SuccessStatusText)
x.queryGetSymbolTotalAmountByTxType(1, symbol, "lock", "", "10")

Expand All @@ -214,7 +214,7 @@ func (x *suiteX2Ethereum) Test_4_Eth2Chain33() {
x.NoError(err)
x.setDb(receipt)

x.queryGetEthProphecy("000x7B95B6EC7EbD73572298cEf32Bb54FA4082073590x0000000000000000000000000000000000000000lock", types2.EthBridgeStatus_SuccessStatusText)
x.queryGetEthProphecy(calcProphecyID(payload, true), types2.EthBridgeStatus_SuccessStatusText)
x.queryGetSymbolTotalAmount(symbol, "", 7, true)
x.queryGetSymbolTotalAmountByTxType(2, symbol, "withdraw", "", "3")

Expand All @@ -231,7 +231,7 @@ func (x *suiteX2Ethereum) Test_4_Eth2Chain33() {
x.NoError(err)
x.setDb(receipt)

x.queryGetEthProphecy("010x7B95B6EC7EbD73572298cEf32Bb54FA4082073590x0000000000000000000000000000000000000000lock", types2.EthBridgeStatus_SuccessStatusText)
x.queryGetEthProphecy(calcProphecyID(payload, true), types2.EthBridgeStatus_SuccessStatusText)
x.queryGetSymbolTotalAmountByTxType(1, symbol, "lock", "", "11")
}

Expand Down Expand Up @@ -278,7 +278,7 @@ func (x *suiteX2Ethereum) Test_5_Chain33ToEth() {

x.queryGetSymbolTotalAmount("coins.bty", tokenContractAddress, 1, false)
x.queryGetSymbolTotalAmountByTxType(1, "coins.bty", "withdraw", tokenContractAddress, "4")
x.queryGetEthProphecy("020x7B95B6EC7EbD73572298cEf32Bb54FA4082073590x0000000000000000000000000000000000000000burn", types2.EthBridgeStatus_SuccessStatusText)
x.queryGetEthProphecy(calcProphecyID(payload, true), types2.EthBridgeStatus_SuccessStatusText)
}

func (x *suiteX2Ethereum) accountSetup() {
Expand Down
30 changes: 23 additions & 7 deletions plugin/dapp/x2ethereum/executor/prophecy.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package executor
import (
"fmt"
"strconv"
"strings"

"github.com/33cn/chain33/types"
x2eTy "github.com/33cn/plugin/plugin/dapp/x2ethereum/types"
Expand Down Expand Up @@ -86,17 +87,32 @@ func NewClaim(id string, validatorAddress string, content string) x2eTy.OracleCl
}
}

// calcProphecyID 通过ethchain33结构生成prophecy唯一ID
// 分叉后采用带分隔符的拼接,避免如(chainID=1,nonce=23)与(chainID=12,nonce=3)产生相同ID的碰撞
func calcProphecyID(ethClaim *x2eTy.Eth2Chain33, isFixFork bool) string {
claimType := "lock"
if ethClaim.ClaimType == int64(x2eTy.BurnClaimType) {
claimType = "burn"
}
if isFixFork {
return strings.Join([]string{
strconv.Itoa(int(ethClaim.EthereumChainID)),
strconv.Itoa(int(ethClaim.Nonce)),
ethClaim.EthereumSender,
ethClaim.TokenContractAddress,
claimType,
}, ":")
}
return strconv.Itoa(int(ethClaim.EthereumChainID)) + strconv.Itoa(int(ethClaim.Nonce)) +
ethClaim.EthereumSender + ethClaim.TokenContractAddress + claimType
}

// CreateOracleClaimFromEthClaim 通过ethchain33结构构造一个OracleClaim结构,包括生成唯一的ID
func CreateOracleClaimFromEthClaim(ethClaim x2eTy.Eth2Chain33) (x2eTy.OracleClaim, error) {
func CreateOracleClaimFromEthClaim(ethClaim x2eTy.Eth2Chain33, isFixFork bool) (x2eTy.OracleClaim, error) {
if ethClaim.ClaimType != int64(x2eTy.LockClaimType) && ethClaim.ClaimType != int64(x2eTy.BurnClaimType) {
return x2eTy.OracleClaim{}, x2eTy.ErrInvalidClaimType
}
oracleID := strconv.Itoa(int(ethClaim.EthereumChainID)) + strconv.Itoa(int(ethClaim.Nonce)) + ethClaim.EthereumSender + ethClaim.TokenContractAddress
if ethClaim.ClaimType == int64(x2eTy.LockClaimType) {
oracleID = oracleID + "lock"
} else if ethClaim.ClaimType == int64(x2eTy.BurnClaimType) {
oracleID = oracleID + "burn"
}
oracleID := calcProphecyID(&ethClaim, isFixFork)
claimContent := NewOracleClaimContent(ethClaim.Chain33Receiver, ethClaim.Amount, ethClaim.ClaimType, ethClaim.Decimals)
claimBytes := types.Encode(&claimContent)

Expand Down
12 changes: 12 additions & 0 deletions plugin/dapp/x2ethereum/executor/prophecy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,15 @@ func TestAddClaim(t *testing.T) {
assert.Contains(t, prophecy.ClaimValidators[1].Validators.Validators, "bb")

}

// TestCalcProphecyIDSeparator 确认分叉后 ID 带分隔符,不同(chainID,nonce)组合不再碰撞
func TestCalcProphecyIDSeparator(t *testing.T) {
claimA := &x2eTy.Eth2Chain33{EthereumChainID: 1, Nonce: 23, EthereumSender: "0xsender", TokenContractAddress: "0xtoken", ClaimType: int64(x2eTy.LockClaimType)}
claimB := &x2eTy.Eth2Chain33{EthereumChainID: 12, Nonce: 3, EthereumSender: "0xsender", TokenContractAddress: "0xtoken", ClaimType: int64(x2eTy.LockClaimType)}

// 旧格式无分隔符,两种组合拼出相同 ID
assert.Equal(t, calcProphecyID(claimA, false), calcProphecyID(claimB, false))
// 分叉后带分隔符,ID 不再碰撞
assert.NotEqual(t, calcProphecyID(claimA, true), calcProphecyID(claimB, true))
assert.NotEqual(t, calcProphecyID(claimA, true), calcProphecyID(claimA, false))
}
8 changes: 4 additions & 4 deletions plugin/dapp/x2ethereum/executor/x2ethereumaction.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package executor

import (
"strconv"
"strings"

"github.com/33cn/chain33/account"
Expand Down Expand Up @@ -29,9 +28,10 @@ type action struct {
func newAction(a *x2ethereum, tx *types.Transaction, index int32) *action {
hash := tx.Hash()
fromaddr := tx.From()
isFixFork := a.GetAPI().GetConfig().IsDappFork(a.GetHeight(), x2eTy.X2ethereumX, x2eTy.ForkX2EthFixReplayCollisionNormalize)

return &action{a.GetAPI(), a.GetCoinsAccount(), a.GetStateDB(), hash, fromaddr,
a.GetBlockTime(), a.GetHeight(), index, address.ExecAddress(string(tx.Execer)), NewOracle(a.GetStateDB(), x2eTy.DefaultConsensusNeeded)}
a.GetBlockTime(), a.GetHeight(), index, address.ExecAddress(string(tx.Execer)), NewOracle(a.GetStateDB(), x2eTy.DefaultConsensusNeeded, isFixFork)}
}

// ethereum ---> chain33
Expand All @@ -49,7 +49,7 @@ func (a *action) procEth2Chain33_lock(ethBridgeClaim *x2eTy.Eth2Chain33) (*types
return nil, err
}

ID := strconv.Itoa(int(ethBridgeClaim.EthereumChainID)) + strconv.Itoa(int(ethBridgeClaim.Nonce)) + ethBridgeClaim.EthereumSender + ethBridgeClaim.TokenContractAddress + "lock"
ID := calcProphecyID(ethBridgeClaim, a.oracle.isFixFork)

//记录ethProphecy
bz, err := a.db.Get(x2eTy.CalProphecyPrefix(ID))
Expand Down Expand Up @@ -209,7 +209,7 @@ func (a *action) procEth2Chain33_burn(withdrawEth *x2eTy.Eth2Chain33) (*types.Re
return nil, err
}

ID := strconv.Itoa(int(withdrawEth.EthereumChainID)) + strconv.Itoa(int(withdrawEth.Nonce)) + withdrawEth.EthereumSender + withdrawEth.TokenContractAddress + "burn"
ID := calcProphecyID(withdrawEth, a.oracle.isFixFork)

//记录ethProphecy
bz, err := a.db.Get(x2eTy.CalProphecyPrefix(ID))
Expand Down
11 changes: 9 additions & 2 deletions plugin/dapp/x2ethereum/executor/x2ethereumdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,18 @@ import (
type Oracle struct {
db dbm.KV
consensusThreshold int64
isFixFork bool
}

// NewOracle ...
func NewOracle(db dbm.KV, consensusThreshold int64) *Oracle {
func NewOracle(db dbm.KV, consensusThreshold int64, isFixFork bool) *Oracle {
if consensusThreshold <= 0 || consensusThreshold > 100 {
return nil
}
return &Oracle{
consensusThreshold: consensusThreshold,
db: db,
isFixFork: isFixFork,
}
}

Expand Down Expand Up @@ -292,7 +294,7 @@ func (o *Oracle) checkProphecy(prophecy *x2eTy.ReceiptEthProphecy) error {

// ProcessClaim 处理接收到的ethchain33请求
func (o *Oracle) ProcessClaim(claim x2eTy.Eth2Chain33) (*x2eTy.ProphecyStatus, error) {
oracleClaim, err := CreateOracleClaimFromEthClaim(claim)
oracleClaim, err := CreateOracleClaimFromEthClaim(claim, o.isFixFork)
if err != nil {
elog.Error("CreateEthClaimFromOracleString", "CreateOracleClaimFromOracleString error", err)
return nil, err
Expand All @@ -317,6 +319,11 @@ func (o *Oracle) ProcessClaim(claim x2eTy.Eth2Chain33) (*x2eTy.ProphecyStatus, e
}
prophecy = NewProphecy(oracleClaim.ID)
} else {
//分叉后prophecy进入Success/Failed即为终态,后续投票直接拒绝,避免重复铸币/解锁
if o.isFixFork && (prophecy.Status.Text == x2eTy.EthBridgeStatus_SuccessStatusText ||
prophecy.Status.Text == x2eTy.EthBridgeStatus_FailedStatusText) {
return nil, x2eTy.ErrProphecyFinalized
}
var exist bool
for _, vc := range prophecy.ValidatorClaims {
if vc.Claim == oracleClaim.Content {
Expand Down
9 changes: 9 additions & 0 deletions plugin/dapp/x2ethereum/types/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,15 @@ const (
// DefaultConsensusNeeded ...
const DefaultConsensusNeeded = int64(70)

// dapp fork
const (
// ForkX2EthFixReplayCollisionNormalize 修复三个安全问题:
// 1. prophecy达到Success/Failed后未进入终态, 后续validator投票会重复触发铸币/解锁
// 2. prophecy ID由chainID+nonce+sender+token无分隔符拼接, 不同(chainID, nonce)组合产生碰撞
// 3. procMsgTransfer的payload To未归一化, 大小写变体可绕过自我转账检查
ForkX2EthFixReplayCollisionNormalize = "ForkX2EthFixReplayCollisionNormalize"
)

// direct ...
const (
DirEth2Chain33 = "eth2chain33"
Expand Down
1 change: 1 addition & 0 deletions plugin/dapp/x2ethereum/types/x2ethereum.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ func init() {
// InitFork defines register fork
func InitFork(cfg *types.Chain33Config) {
cfg.RegisterDappFork(X2ethereumX, "Enable", 0)
cfg.RegisterDappFork(X2ethereumX, ForkX2EthFixReplayCollisionNormalize, 0)
}

// InitExecutor defines register executor
Expand Down
Loading