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

Add support for native kai #49

Open
wants to merge 26 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
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
12 changes: 7 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.14

require (
github.com/Zilliqa/gozilliqa-sdk v1.2.1-0.20210329093354-1b8e0a7a2e25
github.com/btcsuite/btcd v0.20.1-beta
github.com/btcsuite/btcd v0.21.0-beta
github.com/btcsuite/btcutil v1.0.2
github.com/cosmos/cosmos-sdk v0.39.1
github.com/ethereum/go-ethereum v1.9.15
Expand All @@ -16,17 +16,19 @@ require (
github.com/howeyc/gopass v0.0.0-20190910152052-7cb4b85ec19c
github.com/itchyny/base58-go v0.1.0
github.com/joeqian10/neo-gogogo v0.0.0-20200716075409-923bd4879b43
github.com/kardiachain/go-kardia v1.1.1-0.20210518073513-843096e91762 // indirect
github.com/ontio/ontology v1.11.1-0.20200812075204-26cf1fa5dd47
github.com/ontio/ontology-crypto v1.0.9
github.com/ontio/ontology-eventbus v0.9.1
github.com/pborman/uuid v1.2.0
github.com/polynetwork/poly-io-test v0.0.0-20200819093740-8cf514b07750
github.com/stretchr/testify v1.6.1
github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d
github.com/stretchr/testify v1.7.0
github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca
github.com/tendermint/tendermint v0.33.7
github.com/urfave/cli v1.22.4
github.com/valyala/bytebufferpool v1.0.0
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de
golang.org/x/net v0.0.0-20200822124328-c89045814202
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9
golang.org/x/net v0.0.0-20201021035429-f5854403a974
google.golang.org/grpc v1.30.0 // indirect
gotest.tools v2.2.0+incompatible
)
86 changes: 86 additions & 0 deletions go.sum

Large diffs are not rendered by default.

Binary file added merkle/merkletree.db
Binary file not shown.
3 changes: 3 additions & 0 deletions native/service/cross_chain_manager/entrance.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/polynetwork/poly/native/service/cross_chain_manager/cosmos"
"github.com/polynetwork/poly/native/service/cross_chain_manager/eth"
"github.com/polynetwork/poly/native/service/cross_chain_manager/heco"
"github.com/polynetwork/poly/native/service/cross_chain_manager/kai"
"github.com/polynetwork/poly/native/service/cross_chain_manager/msc"
"github.com/polynetwork/poly/native/service/cross_chain_manager/neo"
"github.com/polynetwork/poly/native/service/cross_chain_manager/okex"
Expand Down Expand Up @@ -80,6 +81,8 @@ func GetChainHandler(router uint64) (scom.ChainHandler, error) {
return msc.NewHandler(), nil
case utils.OKEX_ROUTER:
return okex.NewHandler(), nil
case utils.KAI_ROUTER:
return kai.NewHandler(), nil
default:
return nil, fmt.Errorf("not a supported router:%d", router)
}
Expand Down
260 changes: 260 additions & 0 deletions native/service/cross_chain_manager/kai/handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
/*
* Copyright (C) 2020 The poly network Authors
* This file is part of The poly network library.
*
* The poly network is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The poly network is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License
* along with The poly network . If not, see <http://www.gnu.org/licenses/>.
*/

package kai

import (
"bytes"
"encoding/hex"
"encoding/json"
"fmt"
"math/big"

ecommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/light"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
"github.com/kardiachain/go-kardia/types"
"github.com/polynetwork/poly/common"
"github.com/polynetwork/poly/common/log"
"github.com/polynetwork/poly/native"
scom "github.com/polynetwork/poly/native/service/cross_chain_manager/common"
"github.com/polynetwork/poly/native/service/governance/side_chain_manager"
hskai "github.com/polynetwork/poly/native/service/header_sync/kai"
)

// Handler ...
type Handler struct {
}

// NewHandler ...
func NewHandler() *Handler {
return &Handler{}
}

// MakeDepositProposal ...
func (h *Handler) MakeDepositProposal(service *native.NativeService) (*scom.MakeTxParam, error) {
params := new(scom.EntranceParam)
if err := params.Deserialization(common.NewZeroCopySource(service.GetInput())); err != nil {
return nil, fmt.Errorf("KAI MakeDepositProposal, contract params deserialize error: %s", err)
}
info, err := hskai.GetEpochSwitchInfo(service, params.SourceChainID)
if err != nil {
return nil, fmt.Errorf("KAI MakeDepositProposal, failed to get epoch switching height: %v", err)
}
if info.Height > int64(params.Height) {
return nil, fmt.Errorf("KAI MakeDepositProposal, the height %d of header is lower than epoch "+
"switching height %d", params.Height, info.Height)
}

if len(params.HeaderOrCrossChainMsg) == 0 {
return nil, fmt.Errorf("you must commit the header used to verify transaction's proof and get none")
}

var myHeader hskai.Header
if err := json.Unmarshal(params.HeaderOrCrossChainMsg, &myHeader); err != nil {
return nil, fmt.Errorf("KAI MakeDepositProposal, unmarshal cosmos header failed: %v", err)
}
if myHeader.Header.Height != uint64(params.Height) {
return nil, fmt.Errorf("KAI MakeDepositProposal, "+
"height of your header is %d not equal to %d in parameter", myHeader.Header.Height, params.Height)
}

if err = hskai.VerifyHeader(&myHeader, info); err != nil {
return nil, fmt.Errorf("KAI MakeDepositProposal, failed to verify KAI header: %v", err)
}

sideChain, err := side_chain_manager.GetSideChain(service, params.SourceChainID)
if err != nil {
return nil, fmt.Errorf("KAI MakeDepositProposal, side_chain_manager.GetSideChain error: %v", err)
}

if !myHeader.Header.ValidatorsHash.Equal(myHeader.Header.NextValidatorsHash) &&
int64(myHeader.Header.Height) > info.Height {
hskai.PutEpochSwitchInfo(service, params.SourceChainID, &hskai.EpochSwitchInfo{
Height: int64(myHeader.Header.Height),
BlockHash: myHeader.Header.Hash().Bytes(),
NextValidatorsHash: myHeader.Header.NextValidatorsHash.Bytes(),
ChainID: "",
})
}

value, err := verifyTx(myHeader.Header, service, params.Proof, params.Extra, params.SourceChainID, params.Height, sideChain)
if err != nil {
return nil, fmt.Errorf("KAI MakeDepositProposal, verifyFromEthTx error: %s", err)
}

if err := scom.CheckDoneTx(service, value.CrossChainID, params.SourceChainID); err != nil {
return nil, fmt.Errorf("KAI MakeDepositProposal, check done transaction error:%s", err)
}
if err := scom.PutDoneTx(service, value.CrossChainID, params.SourceChainID); err != nil {
return nil, fmt.Errorf("KAI MakeDepositProposal, PutDoneTx error:%s", err)
}

return nil, nil
}

func verifyTx(header *types.Header, native *native.NativeService, proof, extra []byte, fromChainID uint64, height uint32, sideChain *side_chain_manager.SideChain) (param *scom.MakeTxParam, err error) {
kaiProof := new(Proof)
err = json.Unmarshal(proof, kaiProof)
if err != nil {
return nil, fmt.Errorf("verifyTx, unmarshal proof error:%s", err)
}

if len(kaiProof.StorageProofs) != 1 {
return nil, fmt.Errorf("verifyTx, incorrect proof format")
}

proofResult, err := verifyMerkleProof(kaiProof, header, sideChain.CCMCAddress)
if err != nil {
return nil, fmt.Errorf("verifyTx, verifyMerkleProof error:%v", err)
}

if proofResult == nil {
return nil, fmt.Errorf("verifyTx, verifyMerkleProof failed")
}

if !checkProofResult(proofResult, extra) {
return nil, fmt.Errorf("verifyTx, verify proof value hash failed, proof result:%x, extra:%x", proofResult, extra)
}

data := common.NewZeroCopySource(extra)
txParam := new(scom.MakeTxParam)
if err := txParam.Deserialization(data); err != nil {
return nil, fmt.Errorf("verifyTx, deserialize merkleValue error:%s", err)
}
return txParam, nil
}

// Proof ...
type Proof struct {
Address string `json:"address"`
Balance string `json:"balance"`
CodeHash string `json:"codeHash"`
Nonce string `json:"nonce"`
StorageHash string `json:"storageHash"`
AccountProof []string `json:"accountProof"`
StorageProofs []StorageProof `json:"storageProof"`
}

// StorageProof ...
type StorageProof struct {
Key string `json:"key"`
Value string `json:"value"`
Proof []string `json:"proof"`
}

// ProofAccount ...
type ProofAccount struct {
Nounce *big.Int
Balance *big.Int
Storage ecommon.Hash
Codehash ecommon.Hash
}

func verifyMerkleProof(kaiProof *Proof, blockData *types.Header, contractAddr []byte) ([]byte, error) {
//1. prepare verify account
nodeList := new(light.NodeList)

for _, s := range kaiProof.AccountProof {
p := scom.Replace0x(s)
nodeList.Put(nil, ecommon.Hex2Bytes(p))
}
ns := nodeList.NodeSet()

addr := ecommon.Hex2Bytes(scom.Replace0x(kaiProof.Address))
if !bytes.Equal(addr, contractAddr) {
return nil, fmt.Errorf("verifyMerkleProof, contract address is error, proof address: %s, side chain address: %s", kaiProof.Address, hex.EncodeToString(contractAddr))
}
acctKey := crypto.Keccak256(addr)

//2. verify account proof
acctVal, err := trie.VerifyProof(ecommon.Hash(blockData.AppHash), acctKey, ns)
if err != nil {
return nil, fmt.Errorf("verifyMerkleProof, verify account proof error:%s", err)
}

nounce := new(big.Int)
_, ok := nounce.SetString(scom.Replace0x(kaiProof.Nonce), 16)
if !ok {
return nil, fmt.Errorf("verifyMerkleProof, invalid format of nounce:%s", kaiProof.Nonce)
}

balance := new(big.Int)
_, ok = balance.SetString(scom.Replace0x(kaiProof.Balance), 16)
if !ok {
return nil, fmt.Errorf("verifyMerkleProof, invalid format of balance:%s", kaiProof.Balance)
}

storageHash := ecommon.HexToHash(scom.Replace0x(kaiProof.StorageHash))
codeHash := ecommon.HexToHash(scom.Replace0x(kaiProof.CodeHash))

acct := &ProofAccount{
Nounce: nounce,
Balance: balance,
Storage: storageHash,
Codehash: codeHash,
}

acctrlp, err := rlp.EncodeToBytes(acct)
if err != nil {
return nil, err
}

if !bytes.Equal(acctrlp, acctVal) {
return nil, fmt.Errorf("verifyMerkleProof, verify account proof failed, wanted:%v, get:%v", acctrlp, acctVal)
}

//3.verify storage proof
nodeList = new(light.NodeList)
if len(kaiProof.StorageProofs) != 1 {
return nil, fmt.Errorf("verifyMerkleProof, invalid storage proof format")
}

sp := kaiProof.StorageProofs[0]
storageKey := crypto.Keccak256(ecommon.HexToHash(scom.Replace0x(sp.Key)).Bytes())

for _, prf := range sp.Proof {
nodeList.Put(nil, ecommon.Hex2Bytes(scom.Replace0x(prf)))
}

ns = nodeList.NodeSet()
val, err := trie.VerifyProof(storageHash, storageKey, ns)
if err != nil {
return nil, fmt.Errorf("verifyMerkleProof, verify storage proof error:%s", err)
}

return val, nil
}

func checkProofResult(result, value []byte) bool {
var tempBytes []byte
err := rlp.DecodeBytes(result, &tempBytes)
if err != nil {
log.Errorf("checkProofResult, rlp.DecodeBytes error:%s\n", err)
return false
}
//
var s []byte
for i := len(tempBytes); i < 32; i++ {
s = append(s, 0)
}
s = append(s, tempBytes...)
hash := crypto.Keccak256(value)
return bytes.Equal(s, hash)
}
1 change: 1 addition & 0 deletions native/service/cross_chain_manager/kai/handler_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package kai
75 changes: 75 additions & 0 deletions native/service/header_sync/kai/header_sync.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright (C) 2020 The poly network Authors
* This file is part of The poly network library.
*
* The poly network is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The poly network is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License
* along with The poly network . If not, see <http://www.gnu.org/licenses/>.
*/
package kai

import (
"encoding/json"
"fmt"

"github.com/polynetwork/poly/common"
"github.com/polynetwork/poly/common/log"
"github.com/polynetwork/poly/native"
hscommon "github.com/polynetwork/poly/native/service/header_sync/common"
)

// Handler ...
type Handler struct {
}

// SyncBlockHeader ...
func (h *Handler) SyncBlockHeader(native *native.NativeService) error {
params := new(hscommon.SyncBlockHeaderParam)
if err := params.Deserialization(common.NewZeroCopySource(native.GetInput())); err != nil {
return fmt.Errorf("SyncBlockHeader, contract params deserialize error: %v", err)
}
cnt := 0
info, err := GetEpochSwitchInfo(native, params.ChainID)
if err != nil {
return fmt.Errorf("SyncBlockHeader, get epoch switching height failed: %v", err)
}
for _, v := range params.Headers {
var myHeader Header
if err := json.Unmarshal(v, &myHeader); err != nil {
return fmt.Errorf("SyncBlockHeader failed to unmarshal header: %v", err)
}

if myHeader.Header.NextValidatorsHash.Equal(myHeader.Header.ValidatorsHash) {
continue
}
if info.Height >= int64(myHeader.Header.Height) {
log.Debugf("SyncBlockHeader, height %d is lower or equal than epoch switching height %d",
myHeader.Header.Height, info.Height)
continue
}
if err = VerifyHeader(&myHeader, info); err != nil {
return fmt.Errorf("SyncBlockHeader, failed to verify header: %v", err)
}
info.NextValidatorsHash = myHeader.Header.NextValidatorsHash.Bytes()
info.Height = int64(myHeader.Header.Height)
info.BlockHash = myHeader.Header.Hash().Bytes()
cnt++
}
if cnt == 0 {
return fmt.Errorf("no header you commited is useful")
}
PutEpochSwitchInfo(native, params.ChainID, info)
return nil
}

func (this *Handler) SyncCrossChainMsg(native *native.NativeService) error {
return nil
}
1 change: 1 addition & 0 deletions native/service/header_sync/kai/header_sync_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package kai
Loading