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 neo3 handler #89

Open
wants to merge 1 commit into
base: storage
Choose a base branch
from
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 contracts/native/cross_chain_manager/bsc/bsc_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,19 +57,19 @@ func (h *Handler) MakeDepositProposal(service *native.NativeContract) (*scom.Mak

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

value, err := verifyFromTx(service, params.Proof, params.Extra, params.SourceChainID, params.Height, sideChain)
if err != nil {
return nil, fmt.Errorf("eth MakeDepositProposal, verifyFromEthTx error: %s", err)
return nil, fmt.Errorf("bsc MakeDepositProposal, verifyFromEthTx error: %v", err)
}

if err := scom.CheckDoneTx(service, value.CrossChainID, params.SourceChainID); err != nil {
return nil, fmt.Errorf("eth MakeDepositProposal, check done transaction error:%s", err)
return nil, fmt.Errorf("bsc MakeDepositProposal, check done transaction error: %v", err)
}
if err := scom.PutDoneTx(service, value.CrossChainID, params.SourceChainID); err != nil {
return nil, fmt.Errorf("eth MakeDepositProposal, PutDoneTx error:%s", err)
return nil, fmt.Errorf("bsc MakeDepositProposal, PutDoneTx error: %v", err)
}
return value, nil
}
Expand Down
3 changes: 3 additions & 0 deletions contracts/native/cross_chain_manager/entrance.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"github.com/ethereum/go-ethereum/contracts/native/cross_chain_manager/eth"
"github.com/ethereum/go-ethereum/contracts/native/cross_chain_manager/heco"
"github.com/ethereum/go-ethereum/contracts/native/cross_chain_manager/msc"
"github.com/ethereum/go-ethereum/contracts/native/cross_chain_manager/neo3"
"github.com/ethereum/go-ethereum/contracts/native/cross_chain_manager/okex"
"github.com/ethereum/go-ethereum/contracts/native/cross_chain_manager/polygon"
"github.com/ethereum/go-ethereum/contracts/native/cross_chain_manager/quorum"
Expand Down Expand Up @@ -80,6 +81,8 @@ func GetChainHandler(router uint64) (scom.ChainHandler, error) {
return heco.NewHecoHandler(), nil
case utils.MSC_ROUTER:
return msc.NewHandler(), nil
case utils.NEO3_ROUTER:
return neo3.NewHandler(), nil
case utils.OKEX_ROUTER:
return okex.NewHandler(), nil
case utils.QUORUM_ROUTER:
Expand Down
64 changes: 64 additions & 0 deletions contracts/native/cross_chain_manager/neo3/neo3_handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package neo3

import (
"fmt"
"github.com/ethereum/go-ethereum/contracts/native"
scom "github.com/ethereum/go-ethereum/contracts/native/cross_chain_manager/common"
"github.com/ethereum/go-ethereum/contracts/native/governance/side_chain_manager"
"github.com/ethereum/go-ethereum/contracts/native/utils"
"github.com/joeqian10/neo3-gogogo/helper"
)

// Handler ...
type Handler struct {
}

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

// MakeDepositProposal ...
func (h *Handler) MakeDepositProposal(native *native.NativeContract) (*scom.MakeTxParam, error) {
ctx := native.ContractRef().CurrentContext()
param := &scom.EntranceParam{}
if err := utils.UnpackMethod(scom.ABI, scom.MethodImportOuterTransfer, param, ctx.Payload); err != nil {
return nil, err
}

// deserialize neo3 state root
ccm, err := DeserializeCrossChainMsg(param.HeaderOrCrossChainMsg)
if err != nil {
return nil, fmt.Errorf("neo3 MakeDepositProposal, DeserializeCrossChainMsg error: %v", err)
}
sideChain, err := side_chain_manager.GetSideChain(native, param.SourceChainID)
if err != nil {
return nil, fmt.Errorf("neo3 MakeDepositProposal, side_chain_manager.GetSideChain error: %v", err)
}
magicNum := helper.BytesToUInt32(sideChain.ExtraInfo)
// verify its signature
err = verifyCrossChainMsg(native, ccm, magicNum)
if err != nil {
return nil, fmt.Errorf("neo3 MakeDepositProposal, VerifyCrossChainMsg error: %v", err)
}

// when register neo N3, convert ccmc id to []byte
// change neo3 contract address bytes to id, it is different from other chains
// need to store "int" in a []byte, contract id is available from "getcontractstate" api
// neo3 native contracts have negative ids, while custom contracts have positive ones
id := int(int32(helper.BytesToUInt32(sideChain.CCMCAddress)))

// get cross chain param
makeTxParam, err := verifyFromNeoTx(param.Proof, ccm, id)
if err != nil {
return nil, fmt.Errorf("neo3 MakeDepositProposal, verifyFromNeoTx error: %v", err)
}
// check done tx
if err := scom.CheckDoneTx(native, makeTxParam.CrossChainID, param.SourceChainID); err != nil {
return nil, fmt.Errorf("neo3 MakeDepositProposal, CheckDoneTx error:%s", err)
}
if err = scom.PutDoneTx(native, makeTxParam.CrossChainID, param.SourceChainID); err != nil {
return nil, fmt.Errorf("neo3 MakeDepositProposal, PutDoneTx error:%s", err)
}
return makeTxParam, nil
}
109 changes: 109 additions & 0 deletions contracts/native/cross_chain_manager/neo3/states.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package neo3

import (
"fmt"
scom "github.com/ethereum/go-ethereum/contracts/native/cross_chain_manager/common"
"github.com/joeqian10/neo3-gogogo/crypto"
"github.com/joeqian10/neo3-gogogo/helper"
"github.com/joeqian10/neo3-gogogo/io"
"github.com/joeqian10/neo3-gogogo/mpt"
)

type CrossChainMsg struct {
*mpt.StateRoot
}

func (this *CrossChainMsg) GetScriptHash() (*helper.UInt160, error) {
if len(this.Witnesses) == 0 {
return nil, fmt.Errorf("NeoCrossChainMsg.Witness incorrect length")
}
verificationScriptBs, err := crypto.Base64Decode(this.Witnesses[0].Verification) // base64
if err != nil {
return nil, fmt.Errorf("NeoCrossChainMsg.Witness.Verification decode error: %s", err)
}
if len(verificationScriptBs) == 0 {
return nil, fmt.Errorf("NeoCrossChainMsg.Witness.VerificationScript is empty")
}
scriptHash := helper.UInt160FromBytes(crypto.Hash160(verificationScriptBs))
return scriptHash, nil
}

func (this *CrossChainMsg) GetMessage(magic uint32) ([]byte, error) {
buff2 := io.NewBufBinaryWriter()
this.SerializeUnsigned(buff2.BinaryWriter)
if buff2.Err != nil {
return nil, fmt.Errorf("neo3-gogogo mpt.StateRoot SerializeUnsigned error: %s", buff2.Err)
}
hash := helper.UInt256FromBytes(crypto.Sha256(buff2.Bytes()))

buf := io.NewBufBinaryWriter()
buf.BinaryWriter.WriteLE(magic)
buf.BinaryWriter.WriteLE(hash)
if buf.Err != nil {
return nil, fmt.Errorf("NeoCrossChainMsg.GetMessage write hash error: %s", buf.Err)
}
return buf.Bytes(), nil
}

func DeserializeCrossChainMsg(source []byte) (*CrossChainMsg, error) {
ccm := &CrossChainMsg{}
br := io.NewBinaryReaderFromBuf(source)
ccm.Deserialize(br)
if br.Err != nil {
return nil, fmt.Errorf("neo3 StateRoot.Deserialzie error: %v", br.Err)
}
return ccm, nil
}

func SerializeCrossChainMsg(ccm *CrossChainMsg) ([]byte, error) {
bw := io.NewBufBinaryWriter()
ccm.Serialize(bw.BinaryWriter)
if bw.Err != nil {
return nil, fmt.Errorf("neo3 StateRoot.Serialize error: %v", bw.Err)
}
return bw.Bytes(), nil
}

type NeoMakeTxParam struct {
*scom.MakeTxParam
}

func (this *NeoMakeTxParam) Deserialize(br *io.BinaryReader) {
this.TxHash = br.ReadVarBytes()
this.CrossChainID = br.ReadVarBytes()
this.FromContractAddress = br.ReadVarBytes()
br.ReadLE(&this.ToChainID)
this.ToContractAddress = br.ReadVarBytes()
this.Method = string(br.ReadVarBytes())
this.Args = br.ReadVarBytes()
}

func (this *NeoMakeTxParam) Serialize(bw *io.BinaryWriter) {
bw.WriteVarBytes(this.TxHash)
bw.WriteVarBytes(this.CrossChainID)
bw.WriteVarBytes(this.FromContractAddress)
bw.WriteLE(this.ToChainID)
bw.WriteVarBytes(this.ToContractAddress)
bw.WriteVarBytes([]byte(this.Method))
bw.WriteVarBytes(this.Args)
}

func DeserializeNeoMakeTxParam(source []byte) (*NeoMakeTxParam, error) {
param := new(NeoMakeTxParam)
param.MakeTxParam = new(scom.MakeTxParam)
br := io.NewBinaryReaderFromBuf(source)
param.Deserialize(br)
if br.Err != nil {
return nil, br.Err
}
return param, nil
}

func SerializeNeoMakeTxParam(param *NeoMakeTxParam) ([]byte, error) {
bbw := io.NewBufBinaryWriter()
param.Serialize(bbw.BinaryWriter)
if bbw.Err != nil {
return nil, bbw.Err
}
return bbw.Bytes(), nil
}
106 changes: 106 additions & 0 deletions contracts/native/cross_chain_manager/neo3/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package neo3

import (
"fmt"
"github.com/ethereum/go-ethereum/contracts/native"
scom "github.com/ethereum/go-ethereum/contracts/native/cross_chain_manager/common"
"github.com/ethereum/go-ethereum/contracts/native/governance/neo3_state_manager"
"github.com/joeqian10/neo3-gogogo/crypto"
"github.com/joeqian10/neo3-gogogo/helper"
"github.com/joeqian10/neo3-gogogo/mpt"
"github.com/joeqian10/neo3-gogogo/sc"
"github.com/joeqian10/neo3-gogogo/tx"
)

func verifyCrossChainMsg(native *native.NativeContract, ccm *CrossChainMsg, magic uint32) error {
// get neo3 state validator from native contract
svListBytes, err := neo3_state_manager.GetCurrentStateValidator(native)
if err != nil {
return fmt.Errorf("verifyCrossChainMsg, neo3_state_manager.GetCurrentStateValidator error: %v", err)
}
svStrings, err := neo3_state_manager.DeserializeStringArray(svListBytes)
if err != nil {
return fmt.Errorf("verifyCrossChainMsg, neo3_state_manager.DeserializeStringArray error: %v", err)
}
pubKeys := make([]crypto.ECPoint, len(svStrings), len(svStrings))
for i, v := range svStrings {
pubKey, err := crypto.NewECPointFromString(v)
if err != nil {
return fmt.Errorf("verifyCrossChainMsg, crypto.NewECPointFromString error: %v", err)
}
pubKeys[i] = *pubKey
}
n := len(pubKeys)
m := n - (n-1)/3
msc, err := sc.CreateMultiSigContract(m, pubKeys) // sort public keys inside
if err != nil {
return fmt.Errorf("verifyCrossChainMsg, sc.CreateMultiSigContract error: %v", err)
}
expected := msc.GetScriptHash()
got, err := ccm.GetScriptHash()
if err != nil {
return fmt.Errorf("verifyCrossChainMsg, getScripthash error: %v", err)
}
// compare state validator
if !expected.Equals(got) {
return fmt.Errorf("verifyCrossChainMsg, invalid script hash in NeoCrossChainMsg error, expected: %s, got: %s", expected.String(), got.String())
}
msg, err := ccm.GetMessage(magic)
if err != nil {
return fmt.Errorf("verifyCrossChainMsg, unable to get unsigned message of neo crossChainMsg")
}
// verify witness
if len(ccm.Witnesses) == 0 {
return fmt.Errorf("verifyCrossChainMsg, incorrect witness length")
}
invScript, err := crypto.Base64Decode(ccm.Witnesses[0].Invocation)
if err != nil {
return fmt.Errorf("crypto.Base64Decode, decode invocation script error: %v", err)
}
verScript, err := crypto.Base64Decode(ccm.Witnesses[0].Verification)
if err != nil {
return fmt.Errorf("crypto.Base64Decode, decode verification script error: %v", err)
}
witness := &tx.Witness{
InvocationScript: invScript,
VerificationScript: verScript,
}
v1 := tx.VerifyMultiSignatureWitness(msg, witness)
if !v1 {
return fmt.Errorf("verifyCrossChainMsg, verify witness failed, height: %d", ccm.Index)
}
return nil
}

func verifyFromNeoTx(proof []byte, ccm *CrossChainMsg, contractId int) (*scom.MakeTxParam, error) {
root, err := helper.UInt256FromString(ccm.RootHash)
if err != nil {
return nil, fmt.Errorf("UInt256FromString error: %v", err)
}
value, err := verifyNeoCrossChainProof(proof, root.ToByteArray(), contractId)
if err != nil {
return nil, fmt.Errorf("verifyNeoCrossChainProof error: %v", err)
}

txParam, err := DeserializeNeoMakeTxParam(value)
if err != nil {
return nil, fmt.Errorf("DecodeTxParam error: %v", err)
}
return txParam.MakeTxParam, nil
}

func verifyNeoCrossChainProof(proof []byte, stateRoot []byte, contractId int) ([]byte, error) {
id, key, proofs, err := mpt.ResolveProof(proof)
if err != nil {
return nil, fmt.Errorf("VerifyNeoCrossChainProof, neo3-gogogo mpt.ResolveProof error: %v", err)
}
if id != contractId {
return nil, fmt.Errorf("VerifyNeoCrossChainProof, error: id is not CCMC contract id, expected: %d, but got: %d", contractId, id)
}
root := helper.UInt256FromBytes(stateRoot)
value, err := mpt.VerifyProof(root, contractId, key, proofs)
if err != nil {
return nil, fmt.Errorf("VerifyNeoCrossChainProof, neo3-gogogo mpt.VerifyProof error: %v", err)
}
return value, nil
}
14 changes: 14 additions & 0 deletions contracts/native/cross_chain_manager/neo3/utils_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package neo3

import (
"github.com/joeqian10/neo3-gogogo/helper"
"github.com/stretchr/testify/assert"
"testing"
)

func TestDeserializeNeoMakeTxParam(t *testing.T) {
value := helper.HexToBytes("20000000000000000000000000000000000000000000000000000000000000e269204df8f0fc252d2bd3f21c474510fed914cf5fb5ba98510ddfe83b3d6d5a3715ff14250e76987d838a75310c34bf422ea9f1ac4cc9060e0000000000000014cb569453781497dcb067b73d95b28802cb01553806756e6c6f636b4a149328aec1e84c93855e2fb4a01f5eb7ec15e1abd614e9cdc1efd22c74b5706f0068f79b69b46fa85a0d2035f50500000000000000000000000000000000000000000000000000000000")
txParam, err := DeserializeNeoMakeTxParam(value)
assert.Nil(t, err)
assert.Equal(t, uint64(14), txParam.MakeTxParam.ToChainID)
}
Loading