From 427905cb60b1f0f6e0deab1cc6ad6444843dbb67 Mon Sep 17 00:00:00 2001 From: joeqian Date: Fri, 2 Sep 2022 10:22:28 +0800 Subject: [PATCH] add neo3 handler --- .../cross_chain_manager/bsc/bsc_handler.go | 8 +- .../native/cross_chain_manager/entrance.go | 3 + .../cross_chain_manager/neo3/neo3_handler.go | 64 +++++ .../native/cross_chain_manager/neo3/states.go | 109 ++++++++ .../native/cross_chain_manager/neo3/utils.go | 106 ++++++++ .../cross_chain_manager/neo3/utils_test.go | 14 + .../governance/neo3_state_manager/abi.go | 56 ++-- .../governance/neo3_state_manager/abi_test.go | 29 ++ .../neo3_state_manager/neo3_state_manager.go | 132 +++++++-- .../neo3_state_manager_test.go | 172 ++++++++++++ .../governance/neo3_state_manager/utils.go | 257 ++++++++++++++++++ .../relayer_manager/relayer_manager.go | 12 +- .../relayer_manager/relayer_manager_test.go | 8 +- contracts/native/header_sync/entrance.go | 3 + .../native/header_sync/neo3/header_sync.go | 104 +++++++ contracts/native/header_sync/neo3/states.go | 49 ++++ contracts/native/header_sync/neo3/utils.go | 99 +++++++ go.mod | 4 +- go.sum | 6 + 19 files changed, 1185 insertions(+), 50 deletions(-) create mode 100644 contracts/native/cross_chain_manager/neo3/neo3_handler.go create mode 100644 contracts/native/cross_chain_manager/neo3/states.go create mode 100644 contracts/native/cross_chain_manager/neo3/utils.go create mode 100644 contracts/native/cross_chain_manager/neo3/utils_test.go create mode 100644 contracts/native/governance/neo3_state_manager/abi_test.go create mode 100644 contracts/native/governance/neo3_state_manager/neo3_state_manager_test.go create mode 100644 contracts/native/governance/neo3_state_manager/utils.go create mode 100644 contracts/native/header_sync/neo3/header_sync.go create mode 100644 contracts/native/header_sync/neo3/states.go create mode 100644 contracts/native/header_sync/neo3/utils.go diff --git a/contracts/native/cross_chain_manager/bsc/bsc_handler.go b/contracts/native/cross_chain_manager/bsc/bsc_handler.go index 5f39a840..fb3b8eb2 100644 --- a/contracts/native/cross_chain_manager/bsc/bsc_handler.go +++ b/contracts/native/cross_chain_manager/bsc/bsc_handler.go @@ -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 } diff --git a/contracts/native/cross_chain_manager/entrance.go b/contracts/native/cross_chain_manager/entrance.go index 1bf6cdc5..318fbb32 100644 --- a/contracts/native/cross_chain_manager/entrance.go +++ b/contracts/native/cross_chain_manager/entrance.go @@ -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" @@ -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: diff --git a/contracts/native/cross_chain_manager/neo3/neo3_handler.go b/contracts/native/cross_chain_manager/neo3/neo3_handler.go new file mode 100644 index 00000000..f9ca6c2d --- /dev/null +++ b/contracts/native/cross_chain_manager/neo3/neo3_handler.go @@ -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 +} diff --git a/contracts/native/cross_chain_manager/neo3/states.go b/contracts/native/cross_chain_manager/neo3/states.go new file mode 100644 index 00000000..6ae53fe2 --- /dev/null +++ b/contracts/native/cross_chain_manager/neo3/states.go @@ -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 +} diff --git a/contracts/native/cross_chain_manager/neo3/utils.go b/contracts/native/cross_chain_manager/neo3/utils.go new file mode 100644 index 00000000..749aae60 --- /dev/null +++ b/contracts/native/cross_chain_manager/neo3/utils.go @@ -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 +} diff --git a/contracts/native/cross_chain_manager/neo3/utils_test.go b/contracts/native/cross_chain_manager/neo3/utils_test.go new file mode 100644 index 00000000..9ec8849d --- /dev/null +++ b/contracts/native/cross_chain_manager/neo3/utils_test.go @@ -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) +} diff --git a/contracts/native/governance/neo3_state_manager/abi.go b/contracts/native/governance/neo3_state_manager/abi.go index 53957aef..76b49402 100644 --- a/contracts/native/governance/neo3_state_manager/abi.go +++ b/contracts/native/governance/neo3_state_manager/abi.go @@ -19,6 +19,8 @@ package neo3_state_manager import ( "fmt" + "github.com/ethereum/go-ethereum/rlp" + "io" "strings" "github.com/ethereum/go-ethereum/accounts/abi" @@ -26,23 +28,29 @@ import ( ) const ( - EventApproveRegisterStateValidator = "approveRegisterStateValidator" - EventApproveRemoveStateValidator = "approveRemoveStateValidator" + EventRegisterStateValidator = "evtRegisterStateValidator" + EventApproveRegisterStateValidator = "evtApproveRegisterStateValidator" + EventRemoveStateValidator = "evtRemoveStateValidator" + EventApproveRemoveStateValidator = "evtApproveRemoveStateValidator" + ) -const abijson = `[ - {"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"ID","type":"uint64"}],"name":"` + EventApproveRegisterStateValidator + `","type":"event"}, - {"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"ID","type":"uint64"}],"name":"` + EventApproveRemoveStateValidator + `","type":"event"}, - {"inputs":[{"internalType":"uint64","name":"ID","type":"uint64"},{"internalType":"address","name":"Address","type":"address"}],"name":"` + MethodApproveRegisterStateValidator + `","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"}, - {"inputs":[{"internalType":"uint64","name":"ID","type":"uint64"},{"internalType":"address","name":"Address","type":"address"}],"name":"` + MethodApproveRemoveStateValidator + `","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"}, - {"inputs":[],"name":"` + MethodContractName + `","outputs":[{"internalType":"string","name":"Name","type":"string"}],"stateMutability":"nonpayable","type":"function"}, - {"inputs":[],"name":"` + MethodGetCurrentStateValidator + `","outputs":[{"internalType":"bytes","name":"Validator","type":"bytes"}],"stateMutability":"nonpayable","type":"function"}, - {"inputs":[{"internalType":"string[]","name":"StateValidators","type":"string[]"},{"internalType":"address","name":"Address","type":"address"}],"name":"` + MethodRegisterStateValidator + `","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"}, - {"inputs":[{"internalType":"string[]","name":"StateValidators","type":"string[]"},{"internalType":"address","name":"Address","type":"address"}],"name":"` + MethodRemoveStateValidator + `","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"} +const abiJson = `[ + {"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"ID","type":"uint64"}],"name":"` + EventRegisterStateValidator + `","type":"event"}, + {"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"ID","type":"uint64"}],"name":"` + EventApproveRegisterStateValidator + `","type":"event"}, + {"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"ID","type":"uint64"}],"name":"` + EventRemoveStateValidator + `","type":"event"}, + {"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"ID","type":"uint64"}],"name":"` + EventApproveRemoveStateValidator + `","type":"event"}, + {"inputs":[],"name":"` + MethodContractName + `","outputs":[{"internalType":"string","name":"Name","type":"string"}],"stateMutability":"nonpayable","type":"function"}, + {"inputs":[],"name":"` + MethodGetCurrentStateValidator + `","outputs":[{"internalType":"bytes","name":"Validator","type":"bytes"}],"stateMutability":"nonpayable","type":"function"}, + {"inputs":[{"internalType":"uint64","name":"ID","type":"uint64"},{"internalType":"address","name":"Address","type":"address"}],"name":"` + MethodApproveRegisterStateValidator + `","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"}, + {"inputs":[{"internalType":"uint64","name":"ID","type":"uint64"},{"internalType":"address","name":"Address","type":"address"}],"name":"` + MethodApproveRemoveStateValidator + `","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"}, + {"inputs":[{"internalType":"string[]","name":"StateValidators","type":"string[]"},{"internalType":"address","name":"Address","type":"address"}],"name":"` + MethodRegisterStateValidator + `","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"}, + {"inputs":[{"internalType":"string[]","name":"StateValidators","type":"string[]"},{"internalType":"address","name":"Address","type":"address"}],"name":"` + MethodRemoveStateValidator + `","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"} ]` func GetABI() *abi.ABI { - ab, err := abi.JSON(strings.NewReader(abijson)) + //ab, err := abi.JSON(strings.NewReader(neo3_state_manager_abi.Neo3StateManagerABI)) + ab, err := abi.JSON(strings.NewReader(abiJson)) if err != nil { panic(fmt.Sprintf("failed to load abi json string: [%v]", err)) } @@ -50,11 +58,27 @@ func GetABI() *abi.ABI { } type StateValidatorListParam struct { - StateValidators []string // public key strings in encoded format, each is 33 bytes in []byte - Address common.Address // for check witness? + StateValidators []string // public key strings in encoded format, each is 33 bytes in []byte + Address common.Address +} + +func (this *StateValidatorListParam) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, []interface{}{this.StateValidators, this.Address}) +} +func (this *StateValidatorListParam) DecodeRLP(s *rlp.Stream) error { + var data struct { + StateValidators []string + Address common.Address + } + + if err := s.Decode(&data); err != nil { + return err + } + this.StateValidators, this.Address = data.StateValidators, data.Address + return nil } type ApproveStateValidatorParam struct { - ID uint64 // StateValidatorApproveID - Address common.Address // for check witness? + ID uint64 // StateValidatorApproveID + Address common.Address } diff --git a/contracts/native/governance/neo3_state_manager/abi_test.go b/contracts/native/governance/neo3_state_manager/abi_test.go new file mode 100644 index 00000000..f9338565 --- /dev/null +++ b/contracts/native/governance/neo3_state_manager/abi_test.go @@ -0,0 +1,29 @@ +package neo3_state_manager + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/rlp" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestStateValidatorListParam(t *testing.T) { + expect := &StateValidatorListParam{ + StateValidators: []string{ + "039b45040cc529966165ef5dff3d046a4960520ce616ae170e265d669e0e2de7f4", + "0345e2bbda8d3d9e24d1e9ee61df15d4f435f69a44fe012d86e9cf9377baaa42cd", + "023ccd59ec0fda27844984876ef2d440eca88e45c7401110210f7760cdcc73b5f7", + "0392fbd1d809a3c62f7dcde8f25454a1570830a21e4b014b3f362a79baf413e115", + }, + Address: common.HexToAddress("0x3"), + } + + blob, err := rlp.EncodeToBytes(expect) + assert.NoError(t, err) + + got := new(StateValidatorListParam) + err = rlp.DecodeBytes(blob, got) + assert.NoError(t, err) + + assert.Equal(t, expect.StateValidators[0], got.StateValidators[0]) +} diff --git a/contracts/native/governance/neo3_state_manager/neo3_state_manager.go b/contracts/native/governance/neo3_state_manager/neo3_state_manager.go index 288b8baa..e5695b4f 100644 --- a/contracts/native/governance/neo3_state_manager/neo3_state_manager.go +++ b/contracts/native/governance/neo3_state_manager/neo3_state_manager.go @@ -18,8 +18,11 @@ package neo3_state_manager import ( + "fmt" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/contracts/native" + "github.com/ethereum/go-ethereum/contracts/native/contract" + "github.com/ethereum/go-ethereum/contracts/native/governance/node_manager" "github.com/ethereum/go-ethereum/contracts/native/utils" ) @@ -32,6 +35,13 @@ const ( MethodApproveRegisterStateValidator = "approveRegisterStateValidator" MethodRemoveStateValidator = "removeStateValidator" MethodApproveRemoveStateValidator = "approveRemoveStateValidator" + + //key prefix + STATE_VALIDATOR = "stateValidator" + STATE_VALIDATOR_APPLY = "stateValidatorApply" + STATE_VALIDATOR_REMOVE = "stateValidatorRemove" + STATE_VALIDATOR_APPLY_ID = "stateValidatorApplyID" + STATE_VALIDATOR_REMOVE_ID = "stateValidatorRemoveID" ) var ( @@ -68,47 +78,131 @@ func Name(s *native.NativeContract) ([]byte, error) { return utils.PackOutputs(ABI, MethodContractName, contractName) } -func GetCurrentStateValidator(s *native.NativeContract) ([]byte, error) { - - return utils.PackOutputs(ABI, MethodGetCurrentStateValidator, []byte{}) +func GetCurrentStateValidator(native *native.NativeContract) ([]byte, error) { + data, err := getStateValidators(native) + if err != nil { + return nil, fmt.Errorf("GetCurrentStateValidator, getStateValidators error: %v", err) + } + return utils.PackOutputs(ABI, MethodGetCurrentStateValidator, data) } -func RegisterStateValidator(s *native.NativeContract) ([]byte, error) { - ctx := s.ContractRef().CurrentContext() - params := &StateValidatorListParam{} - if err := utils.UnpackMethod(ABI, MethodRegisterStateValidator, params, ctx.Payload); err != nil { +func RegisterStateValidator(native *native.NativeContract) ([]byte, error) { + ctx := native.ContractRef().CurrentContext() + param := &StateValidatorListParam{} + if err := utils.UnpackMethod(ABI, MethodRegisterStateValidator, param, ctx.Payload); err != nil { return nil, err } + //check witness + err := contract.ValidateOwner(native, param.Address) + if err != nil { + return nil, fmt.Errorf("RegisterStateValidator, ValidateOwner error: %v", err) + } + + if err := putStateValidatorApply(native, param); err != nil { + return nil, fmt.Errorf("RegisterStateValidator, putStateValidatorApply error: %v", err) + } + return utils.PackOutputs(ABI, MethodRegisterStateValidator, true) } -func ApproveRegisterStateValidator(s *native.NativeContract) ([]byte, error) { - ctx := s.ContractRef().CurrentContext() - params := &ApproveStateValidatorParam{} - if err := utils.UnpackMethod(ABI, MethodApproveRegisterStateValidator, params, ctx.Payload); err != nil { +func ApproveRegisterStateValidator(native *native.NativeContract) ([]byte, error) { + ctx := native.ContractRef().CurrentContext() + param := &ApproveStateValidatorParam{} + if err := utils.UnpackMethod(ABI, MethodApproveRegisterStateValidator, param, ctx.Payload); err != nil { return nil, err } + //check witness + err := contract.ValidateOwner(native, param.Address) + if err != nil { + return nil, fmt.Errorf("ApproveRegisterStateValidator, ValidateOwner error: %v", err) + } + + stateValidatorListParam, err := getStateValidatorApply(native, param.ID) + if err != nil { + return nil, fmt.Errorf("ApproveRegisterStateValidator, getStateValidatorApply error: %v", err) + } + + //check consensus signs + ok, err := node_manager.CheckConsensusSigns(native, MethodApproveRegisterStateValidator, utils.GetUint64Bytes(param.ID), param.Address) + if err != nil { + return nil, fmt.Errorf("ApproveRegisterStateValidator, CheckConsensusSigns error: %v", err) + } + if !ok { + return utils.PackOutputs(ABI, MethodApproveRegisterStateValidator, true) + } + + err = putStateValidators(native, stateValidatorListParam.StateValidators) + if err != nil { + return nil, fmt.Errorf("ApproveRegisterStateValidator, putStateValidators error: %v", err) + } + native.GetCacheDB().Delete(utils.ConcatKey(utils.Neo3StateManagerContractAddress, []byte(STATE_VALIDATOR_APPLY), utils.GetUint64Bytes(param.ID))) + + err=native.AddNotify(ABI, []string{EventApproveRegisterStateValidator}, param.ID) + if err != nil { + return nil, fmt.Errorf("ApproveRegisterStateValidator, AddNotify error: %v", err) + } return utils.PackOutputs(ABI, MethodApproveRegisterStateValidator, true) } -func RemoveStateValidator(s *native.NativeContract) ([]byte, error) { - ctx := s.ContractRef().CurrentContext() - params := &StateValidatorListParam{} - if err := utils.UnpackMethod(ABI, MethodRemoveStateValidator, params, ctx.Payload); err != nil { +func RemoveStateValidator(native *native.NativeContract) ([]byte, error) { + ctx := native.ContractRef().CurrentContext() + param := &StateValidatorListParam{} + if err := utils.UnpackMethod(ABI, MethodRemoveStateValidator, param, ctx.Payload); err != nil { return nil, err } + //check witness + err := contract.ValidateOwner(native, param.Address) + if err != nil { + return nil, fmt.Errorf("RemoveStateValidator, ValidateOwner error: %v", err) + } + + err = putStateValidatorRemove(native, param) + if err != nil { + return nil, fmt.Errorf("RemoveStateValidator, putStateValidatorRemove error: %v", err) + } + return utils.PackOutputs(ABI, MethodRemoveStateValidator, true) } -func ApproveRemoveStateValidator(s *native.NativeContract) ([]byte, error) { - ctx := s.ContractRef().CurrentContext() - params := &ApproveStateValidatorParam{} - if err := utils.UnpackMethod(ABI, MethodApproveRemoveStateValidator, params, ctx.Payload); err != nil { +func ApproveRemoveStateValidator(native *native.NativeContract) ([]byte, error) { + ctx := native.ContractRef().CurrentContext() + param := &ApproveStateValidatorParam{} + if err := utils.UnpackMethod(ABI, MethodApproveRemoveStateValidator, param, ctx.Payload); err != nil { return nil, err } + //check witness + err := contract.ValidateOwner(native, param.Address) + if err != nil { + return nil, fmt.Errorf("ApproveRemoveStateValidator, ValidateOwner error: %v", err) + } + //get sv list + svListParam, err := getStateValidatorRemove(native, param.ID) + if err != nil { + return nil, fmt.Errorf("ApproveRemoveStateValidator, getStateValidatorRemove error: %v", err) + } + //check consensus signs + ok, err := node_manager.CheckConsensusSigns(native, MethodApproveRemoveStateValidator, utils.GetUint64Bytes(param.ID), param.Address) + if err != nil { + return nil, fmt.Errorf("ApproveRemoveStateValidator, CheckConsensusSigns error: %v", err) + } + if !ok { + return utils.PackOutputs(ABI, MethodApproveRemoveStateValidator, true) + } + //remove sv + err = removeStateValidators(native, svListParam.StateValidators) + if err != nil { + return nil, fmt.Errorf("ApproveRemoveStateValidator, removeStateValidators error: %v", err) + } + //delete removed sv + native.GetCacheDB().Delete(utils.ConcatKey(utils.Neo3StateManagerContractAddress, []byte(STATE_VALIDATOR_REMOVE), utils.GetUint64Bytes(param.ID))) + err = native.AddNotify(ABI, []string{EventApproveRemoveStateValidator}, param.ID) + if err != nil { + return nil, fmt.Errorf("ApproveRemoveStateValidator, AddNofity error: %v", err) + } + return utils.PackOutputs(ABI, MethodApproveRemoveStateValidator, true) } diff --git a/contracts/native/governance/neo3_state_manager/neo3_state_manager_test.go b/contracts/native/governance/neo3_state_manager/neo3_state_manager_test.go new file mode 100644 index 00000000..51220783 --- /dev/null +++ b/contracts/native/governance/neo3_state_manager/neo3_state_manager_test.go @@ -0,0 +1,172 @@ +package neo3_state_manager + +import ( + "crypto/ecdsa" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/contracts/native" + "github.com/ethereum/go-ethereum/contracts/native/governance/node_manager" + "github.com/ethereum/go-ethereum/contracts/native/utils" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/crypto" + "github.com/stretchr/testify/assert" + "math/big" + "testing" +) + +func init() { + InitNeo3StateManager() + node_manager.InitNodeManager() + db := rawdb.NewMemoryDatabase() + sdb, _ = state.New(common.Hash{}, state.NewDatabase(db), nil) + + putPeerMapPoolAndView(sdb) +} + +func putPeerMapPoolAndView(db *state.StateDB) { + height := uint64(120) + epoch := node_manager.GenerateTestEpochInfo(1, height, 4) + peer := epoch.Peers.List[0] + rawPubKey, _ := hexutil.Decode(peer.PubKey) + pubkey, _ := crypto.DecompressPubkey(rawPubKey) + acct = pubkey + caller := peer.Address + + txhash := common.HexToHash("0x1234") + ref := native.NewContractRef(db, caller, caller, new(big.Int).SetUint64(height), txhash, 0, nil) + s := native.NewNativeContract(db, ref) + node_manager.StoreTestEpoch(s, epoch) +} + +var ( + sdb *state.StateDB + acct *ecdsa.PublicKey +) + +func TestRegisterStateValidator(t *testing.T) { + { + param := new(StateValidatorListParam) + param.StateValidators = []string{ + "039b45040cc529966165ef5dff3d046a4960520ce616ae170e265d669e0e2de7f4", + "0345e2bbda8d3d9e24d1e9ee61df15d4f435f69a44fe012d86e9cf9377baaa42cd", + "023ccd59ec0fda27844984876ef2d440eca88e45c7401110210f7760cdcc73b5f7", + "0392fbd1d809a3c62f7dcde8f25454a1570830a21e4b014b3f362a79baf413e115", + } + + input, err := utils.PackMethodWithStruct(ABI, MethodRegisterStateValidator, param) + assert.Nil(t, err) + + blockNumber := big.NewInt(1) + extra := uint64(10) + contractRef := native.NewContractRef(sdb, common.Address{}, common.Address{}, blockNumber, + common.Hash{}, gasTable[MethodRegisterStateValidator]+extra, nil) + + ret, leftOverGas, err := contractRef.NativeCall(common.Address{}, + utils.Neo3StateManagerContractAddress, input) + assert.Nil(t, err) + + result, err := utils.PackOutputs(ABI, MethodRegisterStateValidator, true) + assert.Nil(t, err) + assert.Equal(t, result, ret) + assert.Equal(t, extra, leftOverGas) + + contract := native.NewNativeContract(sdb, contractRef) + svListParam, err := getStateValidatorApply(contract, 0) + assert.Nil(t, err) + assert.Equal(t, param.StateValidators[0], svListParam.StateValidators[0]) + } + + // none consensus acct should not be able to approve register sv + { + caller := crypto.PubkeyToAddress(*acct) + param := new(ApproveStateValidatorParam) + param.ID = 0 + param.Address = caller + + input, err := utils.PackMethodWithStruct(ABI, MethodApproveRegisterStateValidator, param) + assert.Nil(t, err) + + blockNumber := big.NewInt(1) + extra := uint64(10) + contractRef := native.NewContractRef(sdb, caller, caller, blockNumber, + common.Hash{}, gasTable[MethodApproveRegisterStateValidator]+extra, nil) + + ret, leftOverGas, err := contractRef.NativeCall(caller, + utils.Neo3StateManagerContractAddress, input) + assert.Nil(t, err) + + result, err := utils.PackOutputs(ABI, MethodApproveRegisterStateValidator, true) + assert.Nil(t, err) + assert.Equal(t, result, ret) + assert.Equal(t, extra, leftOverGas) + + contract := native.NewNativeContract(sdb, contractRef) + ok, err := node_manager.CheckConsensusSigns(contract, MethodApproveRegisterStateValidator, utils.GetUint64Bytes(0), caller) + assert.NotNil(t, err, "duplicate signer") // the signer is already stored + assert.Equal(t, false, ok) + } +} + +func TestRemoveStateValidator(t *testing.T) { + { + param := new(StateValidatorListParam) + param.StateValidators = []string{ + "039b45040cc529966165ef5dff3d046a4960520ce616ae170e265d669e0e2de7f4", + "0345e2bbda8d3d9e24d1e9ee61df15d4f435f69a44fe012d86e9cf9377baaa42cd", + "023ccd59ec0fda27844984876ef2d440eca88e45c7401110210f7760cdcc73b5f7", + "0392fbd1d809a3c62f7dcde8f25454a1570830a21e4b014b3f362a79baf413e115", + } + + input, err := utils.PackMethodWithStruct(ABI, MethodRemoveStateValidator, param) + assert.Nil(t, err) + + blockNumber := big.NewInt(1) + extra := uint64(10) + contractRef := native.NewContractRef(sdb, common.Address{}, common.Address{}, blockNumber, + common.Hash{}, gasTable[MethodRemoveStateValidator]+extra, nil) + + ret, leftOverGas, err := contractRef.NativeCall(common.Address{}, + utils.Neo3StateManagerContractAddress, input) + assert.Nil(t, err) + + result, err := utils.PackOutputs(ABI, MethodRemoveStateValidator, true) + assert.Nil(t, err) + assert.Equal(t, result, ret) + assert.Equal(t, extra, leftOverGas) + + contract := native.NewNativeContract(sdb, contractRef) + svListParam, err := getStateValidatorRemove(contract, 0) + assert.Nil(t, err) + assert.Equal(t, param.StateValidators[0], svListParam.StateValidators[0]) + } + + { + caller := crypto.PubkeyToAddress(*acct) + param := new(ApproveStateValidatorParam) + param.ID = 0 + param.Address = caller + + input, err := utils.PackMethodWithStruct(ABI, MethodApproveRemoveStateValidator, param) + assert.Nil(t, err) + + blockNumber := big.NewInt(1) + extra := uint64(10) + contractRef := native.NewContractRef(sdb, caller, caller, blockNumber, + common.Hash{}, gasTable[MethodApproveRegisterStateValidator]+extra, nil) + + ret, leftOverGas, err := contractRef.NativeCall(caller, + utils.Neo3StateManagerContractAddress, input) + assert.Nil(t, err) + + result, err := utils.PackOutputs(ABI, MethodApproveRemoveStateValidator, true) + assert.Nil(t, err) + assert.Equal(t, result, ret) + assert.Equal(t, extra, leftOverGas) + + contract := native.NewNativeContract(sdb, contractRef) + ok, err := node_manager.CheckConsensusSigns(contract, MethodApproveRemoveStateValidator, utils.GetUint64Bytes(0), caller) + assert.NotNil(t, err, "duplicate signer") // the signer is already stored + assert.Equal(t, false, ok) + } +} diff --git a/contracts/native/governance/neo3_state_manager/utils.go b/contracts/native/governance/neo3_state_manager/utils.go new file mode 100644 index 00000000..9a947a0f --- /dev/null +++ b/contracts/native/governance/neo3_state_manager/utils.go @@ -0,0 +1,257 @@ +package neo3_state_manager + +import ( + "fmt" + "github.com/ethereum/go-ethereum/contracts/native" + "github.com/ethereum/go-ethereum/contracts/native/utils" + "github.com/ethereum/go-ethereum/rlp" + "github.com/joeqian10/neo3-gogogo/io" +) + +func SerializeStringArray(data []string) ([]byte, error) { + bw := io.NewBufBinaryWriter() + bw.WriteVarUInt(uint64(len(data))) + if bw.Err != nil { + return nil, fmt.Errorf("WriteVarUInt error: %v", bw.Err) + } + for _, v := range data { + bw.WriteVarString(v) + } + if bw.Err != nil { + return nil, fmt.Errorf("WriteVarString error: %v", bw.Err) + } + return bw.Bytes(), nil +} + +func DeserializeStringArray(source []byte) ([]string, error) { + if len(source) == 0 { + return []string{}, nil + } + br := io.NewBinaryReaderFromBuf(source) + n := br.ReadVarUInt() + if br.Err != nil { + return nil, fmt.Errorf("ReadVarUInt error: %v", br.Err) + } + result := make([]string, 0, n) + for i := 0; uint64(i) < n; i++ { + s := br.ReadVarString(128) + result = append(result, s) + } + return result, nil +} + +func getStateValidators(native *native.NativeContract) ([]byte, error) { + contract := utils.Neo3StateManagerContractAddress + svBytes, err := native.GetCacheDB().Get(utils.ConcatKey(contract, []byte(STATE_VALIDATOR))) + if err != nil { + return nil, fmt.Errorf("CacheDB.Get error: %v", err) + } + if svBytes == nil { + return []byte{}, nil + } + + return svBytes, nil +} + +func putStateValidators(native *native.NativeContract, stateValidators []string) error { + contract := utils.Neo3StateManagerContractAddress + // get current stored value + oldSvBytes, err := getStateValidators(native) + if err != nil { + return fmt.Errorf("getStateValidators error: %v", err) + } + oldSVs, err := DeserializeStringArray(oldSvBytes) + if err != nil { + return fmt.Errorf("DeserializeStringArray error: %v", err) + } + // max capacity = len(oldSVs)+len(stateValidators) + newSVs := make([]string, 0, len(oldSVs)+len(stateValidators)) + newSVs = append(newSVs, oldSVs...) + // filter duplicate svs + for _, sv := range stateValidators { + isInOld := false + for _, oldSv := range oldSVs { + if sv == oldSv { + isInOld = true + break + } + } + if !isInOld { + newSVs = append(newSVs, sv) + } + } + // convert back to []byte + data, err := SerializeStringArray(newSVs) + if err != nil { + return fmt.Errorf("SerializeStringArray error: %v", err) + } + native.GetCacheDB().Put(utils.ConcatKey(contract, []byte(STATE_VALIDATOR)), data) + return nil +} + +func removeStateValidators(native *native.NativeContract, stateValidators []string) error { + contract := utils.Neo3StateManagerContractAddress + // get current stored value + oldSvBytes, err := getStateValidators(native) + if err != nil { + return fmt.Errorf("getStateValidators error: %v", err) + } + oldSVs, err := DeserializeStringArray(oldSvBytes) + if err != nil { + return fmt.Errorf("DeserializeStringArray error: %v", err) + } + // remove in the slice + for _, sv := range stateValidators { + for i, oldSv := range oldSVs { + if sv == oldSv { + oldSVs = append(oldSVs[:i], oldSVs[i+1:]...) + break + } + } + } + // if no sv left, delete the storage, else put remaining back + if len(oldSVs) == 0 { + native.GetCacheDB().Delete(utils.ConcatKey(contract, []byte(STATE_VALIDATOR))) + return nil + } + // convert back to []byte + data, err := SerializeStringArray(oldSVs) + if err != nil { + return fmt.Errorf("SerializeStringArray error: %v", err) + } + native.GetCacheDB().Put(utils.ConcatKey(contract, []byte(STATE_VALIDATOR)), data) + return nil +} + +//----- +//below methods are for state validator apply +//----- + +func getStateValidatorApply(native *native.NativeContract, applyID uint64) (*StateValidatorListParam, error) { + contract := utils.Neo3StateManagerContractAddress + svListParamBytes, err := native.GetCacheDB().Get(utils.ConcatKey(contract, []byte(STATE_VALIDATOR_APPLY), utils.GetUint64Bytes(applyID))) + if err != nil { + return nil, fmt.Errorf("CacheDB.Get error: %v", err) + } + if len(svListParamBytes) == 0 { + return nil, fmt.Errorf("cannot find any record") + } + svListParam := new(StateValidatorListParam) + err = rlp.DecodeBytes(svListParamBytes, svListParam) + if err != nil { + return nil, fmt.Errorf("rlp.DecodeBytes error: %v", err) + } + return svListParam, nil +} + +func putStateValidatorApply(native *native.NativeContract, stateValidatorListParam *StateValidatorListParam) error { + contract := utils.Neo3StateManagerContractAddress + applyID, err := getStateValidatorApplyID(native) + if err != nil { + return fmt.Errorf("getStateValidatorApplyID error: %v", err) + } + newApplyID := applyID + 1 + err = putStateValidatorApplyID(native, newApplyID) + if err != nil { + return fmt.Errorf("putStateValidatorApplyID error: %v", err) + } + + blob, err := rlp.EncodeToBytes(stateValidatorListParam) + if err != nil { + return fmt.Errorf("rlp.EncodeToBytes error: %v", err) + } + native.GetCacheDB().Put(utils.ConcatKey(contract, []byte(STATE_VALIDATOR_APPLY), utils.GetUint64Bytes(applyID)), blob) + + err = native.AddNotify(ABI, []string{EventRegisterStateValidator}, applyID) + if err != nil { + return fmt.Errorf("AddNotify error: %v", err) + } + return nil +} + +func getStateValidatorApplyID(native *native.NativeContract) (uint64, error) { + contract := utils.Neo3StateManagerContractAddress + applyIDBytes, err := native.GetCacheDB().Get(utils.ConcatKey(contract, []byte(STATE_VALIDATOR_APPLY_ID))) + if err != nil { + return 0, fmt.Errorf("CacheDB.Get error: %v", err) + } + var applyID uint64 = 0 + if len(applyIDBytes) != 0 { + applyID = utils.GetBytesUint64(applyIDBytes) + } + return applyID, nil +} + +func putStateValidatorApplyID(native *native.NativeContract, applyID uint64) error { + contract := utils.Neo3StateManagerContractAddress + applyIDBytes := utils.GetUint64Bytes(applyID) + native.GetCacheDB().Put(utils.ConcatKey(contract, []byte(STATE_VALIDATOR_APPLY_ID)), applyIDBytes) + return nil +} + +//----- +//below methods are for state validator removal +//----- + +func getStateValidatorRemove(native *native.NativeContract, removeID uint64) (*StateValidatorListParam, error) { + contract := utils.Neo3StateManagerContractAddress + svListParamBytes, err := native.GetCacheDB().Get(utils.ConcatKey(contract, []byte(STATE_VALIDATOR_REMOVE), utils.GetUint64Bytes(removeID))) + if err != nil { + return nil, fmt.Errorf("CacheDB.Get error: %v", err) + } + if len(svListParamBytes) == 0 { + return nil, fmt.Errorf("cannot find any record") + } + + svListParam := new(StateValidatorListParam) + err = rlp.DecodeBytes(svListParamBytes, svListParam) + if err != nil { + return nil, fmt.Errorf("rlp.DecodeBytes error: %v", err) + } + return svListParam, nil +} + +func putStateValidatorRemove(native *native.NativeContract, svListParam *StateValidatorListParam) error { + contract := utils.Neo3StateManagerContractAddress + removeID, err := getStateValidatorRemoveID(native) + if err != nil { + return fmt.Errorf("getStateValidatorRemoveID error: %v", err) + } + newRemoveID := removeID + 1 + err = putStateValidatorRemoveID(native, newRemoveID) + if err != nil { + return fmt.Errorf("putStateValidatorRemoveID error: %v", err) + } + + blob, err := rlp.EncodeToBytes(svListParam) + if err != nil { + return fmt.Errorf("rlp.EncodeToBytes error: %v", err) + } + native.GetCacheDB().Put(utils.ConcatKey(contract, []byte(STATE_VALIDATOR_REMOVE), utils.GetUint64Bytes(removeID)), blob) + + err = native.AddNotify(ABI, []string{EventRemoveStateValidator}, removeID) + if err != nil { + return fmt.Errorf("AddNofity error: %v", err) + } + return nil +} + +func getStateValidatorRemoveID(native *native.NativeContract) (uint64, error) { + contract := utils.Neo3StateManagerContractAddress + removeIDBytes, err := native.GetCacheDB().Get(utils.ConcatKey(contract, []byte(STATE_VALIDATOR_REMOVE_ID))) + if err != nil { + return 0, fmt.Errorf("CacheDB.Get error: %v", err) + } + var removeID uint64 = 0 + if len(removeIDBytes) != 0 { + removeID = utils.GetBytesUint64(removeIDBytes) + } + return removeID, nil +} + +func putStateValidatorRemoveID(native *native.NativeContract, removeID uint64) error { + contract := utils.Neo3StateManagerContractAddress + removeIDBytes := utils.GetUint64Bytes(removeID) + native.GetCacheDB().Put(utils.ConcatKey(contract, []byte(STATE_VALIDATOR_REMOVE_ID)), removeIDBytes) + return nil +} diff --git a/contracts/native/governance/relayer_manager/relayer_manager.go b/contracts/native/governance/relayer_manager/relayer_manager.go index 626ea80c..ffefc278 100644 --- a/contracts/native/governance/relayer_manager/relayer_manager.go +++ b/contracts/native/governance/relayer_manager/relayer_manager.go @@ -87,11 +87,11 @@ func RegisterRelayer(native *native.NativeContract) ([]byte, error) { //check witness err := contract.ValidateOwner(native, params.Address) if err != nil { - return nil, fmt.Errorf("RegisterSideChain, checkWitness error: %v", err) + return nil, fmt.Errorf("RegisterRelayer, checkWitness error: %v", err) } if err := putRelayerApply(native, params); err != nil { - return nil, fmt.Errorf("RegisterRelayer, putRelayer error: %v", err) + return nil, fmt.Errorf("RegisterRelayer, putRelayerApply error: %v", err) } return utils.PackOutputs(ABI, MethodRegisterRelayer, true) @@ -107,7 +107,7 @@ func ApproveRegisterRelayer(native *native.NativeContract) ([]byte, error) { //check witness err := contract.ValidateOwner(native, params.Address) if err != nil { - return nil, fmt.Errorf("RegisterSideChain, checkWitness error: %v", err) + return nil, fmt.Errorf("ApproveRegisterRelayer, checkWitness error: %v", err) } relayerListParam, err := getRelayerApply(native, params.ID) @@ -150,12 +150,12 @@ func RemoveRelayer(native *native.NativeContract) ([]byte, error) { //check witness err := contract.ValidateOwner(native, params.Address) if err != nil { - return nil, fmt.Errorf("RegisterSideChain, checkWitness error: %v", err) + return nil, fmt.Errorf("RemoveRelayer, checkWitness error: %v", err) } err = putRelayerRemove(native, params) if err != nil { - return nil, fmt.Errorf("RemoveRelayer, putRelayer error: %v", err) + return nil, fmt.Errorf("RemoveRelayer, putRelayerRemove error: %v", err) } return utils.PackOutputs(ABI, MethodRemoveRelayer, true) @@ -171,7 +171,7 @@ func ApproveRemoveRelayer(native *native.NativeContract) ([]byte, error) { //check witness err := contract.ValidateOwner(native, params.Address) if err != nil { - return nil, fmt.Errorf("RegisterSideChain, checkWitness error: %v", err) + return nil, fmt.Errorf("ApproveRemoveRelayer, checkWitness error: %v", err) } relayerListParam, err := getRelayerRemove(native, params.ID) diff --git a/contracts/native/governance/relayer_manager/relayer_manager_test.go b/contracts/native/governance/relayer_manager/relayer_manager_test.go index 865317d6..c28fce7a 100644 --- a/contracts/native/governance/relayer_manager/relayer_manager_test.go +++ b/contracts/native/governance/relayer_manager/relayer_manager_test.go @@ -112,8 +112,8 @@ func TestRegisterRelayer(t *testing.T) { contract := native.NewNativeContract(sdb, contractRef) ok, err := node_manager.CheckConsensusSigns(contract, MethodApproveRegisterRelayer, utils.GetUint64Bytes(0), caller) - assert.Nil(t, err) - assert.Equal(t, true, ok) + assert.NotNil(t, err) // the signer is already stored + assert.Equal(t, false, ok) } } @@ -167,7 +167,7 @@ func TestRemoveRelayer(t *testing.T) { contract := native.NewNativeContract(sdb, contractRef) ok, err := node_manager.CheckConsensusSigns(contract, MethodApproveRemoveRelayer, utils.GetUint64Bytes(0), caller) - assert.Nil(t, err) - assert.Equal(t, true, ok) + assert.NotNil(t, err) // the signer is already stored + assert.Equal(t, false, ok) } } diff --git a/contracts/native/header_sync/entrance.go b/contracts/native/header_sync/entrance.go index 01f2b2ec..106dc46d 100644 --- a/contracts/native/header_sync/entrance.go +++ b/contracts/native/header_sync/entrance.go @@ -19,6 +19,7 @@ package header_sync import ( "fmt" + "github.com/ethereum/go-ethereum/contracts/native/header_sync/neo3" "github.com/ethereum/go-ethereum/contracts/native" "github.com/ethereum/go-ethereum/contracts/native/governance/side_chain_manager" @@ -161,6 +162,8 @@ func GetChainHandler(router uint64) (hscommon.HeaderSyncHandler, 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: diff --git a/contracts/native/header_sync/neo3/header_sync.go b/contracts/native/header_sync/neo3/header_sync.go new file mode 100644 index 00000000..2862bbd3 --- /dev/null +++ b/contracts/native/header_sync/neo3/header_sync.go @@ -0,0 +1,104 @@ +package neo3 + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/contracts/native" + "github.com/ethereum/go-ethereum/contracts/native/governance/node_manager" + "github.com/ethereum/go-ethereum/contracts/native/governance/side_chain_manager" + scom "github.com/ethereum/go-ethereum/contracts/native/header_sync/common" + "github.com/ethereum/go-ethereum/contracts/native/utils" + "github.com/joeqian10/neo3-gogogo/helper" +) + +// Handler ... +type Handler struct { +} + +// NewHandler ... +func NewHandler() *Handler { + return &Handler{} +} + +func (this *Handler) SyncGenesisHeader(native *native.NativeContract) error { + ctx := native.ContractRef().CurrentContext() + param := &scom.SyncGenesisHeaderParam{} + if err := utils.UnpackMethod(scom.ABI, scom.MethodSyncGenesisHeader, param, ctx.Payload); err != nil { + return fmt.Errorf("neo3 SyncGenesisHeader, contract params deserialize error: %v", err) + } + + // check consensus signs + ok, err := node_manager.CheckConsensusSigns(native, scom.MethodSyncGenesisHeader, ctx.Payload, native.ContractRef().MsgSender()) + if err != nil { + return fmt.Errorf("neo3 SyncGenesisHeader, CheckConsensusSigns error: %v", err) + } + if !ok { + return nil + } + + // can only store once + stored, err := isGenesisStored(native, param) + if err != nil { + return fmt.Errorf("neo3 SyncGenesisHeader, isGenesisStored error: %v", err) + } + if stored { + return fmt.Errorf("neo3 SyncGenesisHeader, genesis header had been initialized") + } + + // deserialize genesis header to verify + genesis, err := DeserializeNeo3Header(param.GenesisHeader) + if err != nil { + return fmt.Errorf("neo3 SyncGenesisHeader, DeserializeNeo3Header error: %v", err) + } + + err = putGenesis(native, param, genesis) + if err != nil { + return fmt.Errorf("neo3 SyncGenesisHeader, putGenesis error: %v", err) + } + + scom.NotifyPutHeader(native, param.ChainID, uint64(genesis.GetIndex()), genesis.GetHashString()) + return nil +} + +func (this *Handler) SyncBlockHeader(native *native.NativeContract) error { + param := &scom.SyncBlockHeaderParam{} + ctx := native.ContractRef().CurrentContext() + if err := utils.UnpackMethod(scom.ABI, scom.MethodSyncBlockHeader, param, ctx.Payload); err != nil { + return err + } + + sideChain, err := side_chain_manager.GetSideChain(native, param.ChainID) + if err != nil { + return fmt.Errorf("neo3 SyncBlockHeader, GetSideChain error: %v", err) + } + magicNum := helper.BytesToUInt32(sideChain.ExtraInfo) + + for _, v := range param.Headers { + header, err := DeserializeNeo3Header(v) + if err != nil { + return fmt.Errorf("neo3 SyncBlockHeader, DeserializeNeo3Header error: %v", err) + } + // get last stored header + lastHeader, err := getLastStoredHeader(native, param.ChainID) + if err != nil { + return fmt.Errorf("neo3 SyncBlockHeader, getLastStoredHeader error: %v", err) + } + // verify new header + if !header.GetNextConsensus().Equals(lastHeader.GetNextConsensus()) && + header.GetIndex() > lastHeader.GetIndex() { + if err = verifyHeader(native, header, lastHeader.GetNextConsensus(), magicNum); err != nil { + return fmt.Errorf("neo3 SyncBlockHeader, verifyHeader error: %v", err) + } + } + // update the last stored header + err = putHeader(native, param, header) + + scom.NotifyPutHeader(native, param.ChainID, uint64(header.GetIndex()), header.GetHash().String()) + } + return nil +} + +// SyncCrossChainMsg ... +func (h *Handler) SyncCrossChainMsg(native *native.NativeContract) error { + return nil +} diff --git a/contracts/native/header_sync/neo3/states.go b/contracts/native/header_sync/neo3/states.go new file mode 100644 index 00000000..403007b2 --- /dev/null +++ b/contracts/native/header_sync/neo3/states.go @@ -0,0 +1,49 @@ +package neo3 + +import ( + "fmt" + "github.com/joeqian10/neo3-gogogo/block" + "github.com/joeqian10/neo3-gogogo/crypto" + "github.com/joeqian10/neo3-gogogo/helper" + "github.com/joeqian10/neo3-gogogo/io" +) + +type BlockHeader struct { + *block.Header +} + +func (this *BlockHeader) GetMessage(magic uint32) ([]byte, error) { + buff2 := io.NewBufBinaryWriter() + this.SerializeUnsigned(buff2.BinaryWriter) + if buff2.Err != nil { + return nil, fmt.Errorf("neo3 Header SerializeUnsigned error: %v", 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("neo3 BlockHeader.GetMessage write hash error: %v", buf.Err) + } + return buf.Bytes(), nil +} + +func DeserializeNeo3Header(source []byte) (*BlockHeader, error) { + h := &BlockHeader{} + br := io.NewBinaryReaderFromBuf(source) + h.Deserialize(br) + if br.Err != nil { + return nil, fmt.Errorf("neo3 Header.Deserialize error: %v", br.Err) + } + return h, nil +} + +func SerializeNeo3Header(h *BlockHeader) ([]byte, error) { + bw := io.NewBufBinaryWriter() + h.Serialize(bw.BinaryWriter) + if bw.Err != nil { + return nil, fmt.Errorf("neo3 Header.Serialize error: %v", bw.Err) + } + return bw.Bytes(), nil +} diff --git a/contracts/native/header_sync/neo3/utils.go b/contracts/native/header_sync/neo3/utils.go new file mode 100644 index 00000000..3baaa502 --- /dev/null +++ b/contracts/native/header_sync/neo3/utils.go @@ -0,0 +1,99 @@ +package neo3 + +import ( + "fmt" + "github.com/ethereum/go-ethereum/contracts/native" + scom "github.com/ethereum/go-ethereum/contracts/native/header_sync/common" + "github.com/ethereum/go-ethereum/contracts/native/utils" + "github.com/joeqian10/neo3-gogogo/helper" + "github.com/joeqian10/neo3-gogogo/tx" +) + +func isGenesisStored(native *native.NativeContract, param *scom.SyncGenesisHeaderParam) (bool, error) { + genesis, err := getGenesis(native, param.ChainID) + if err != nil { + return false, fmt.Errorf("getGenesis error: %v", err) + } + + return genesis != nil, nil +} + +func getGenesis(native *native.NativeContract, chainID uint64) (*BlockHeader, error) { + genesisBytes, err := scom.GetGenesisHeader(native, chainID) + if err != nil { + return nil, fmt.Errorf("GetGenesisHeader error: %v", err) + } + + if genesisBytes == nil { + return nil, nil + } + + genesisHeader, err := DeserializeNeo3Header(genesisBytes) + if err != nil { + return nil, fmt.Errorf("getGenesis, DeserializeNeo3Header error: %v", err) + } + + return genesisHeader, nil +} + +func putGenesis(native *native.NativeContract, param *scom.SyncGenesisHeaderParam, genesis *BlockHeader) error { + raw, err := SerializeNeo3Header(genesis) + if err != nil { + return fmt.Errorf("SerializeNeo3Header error: %v", err) + } + + scom.SetGenesisHeader(native, param.ChainID, raw) + scom.SetHeaderIndex(native, param.ChainID, genesis.GetHash().ToByteArray(), raw) + scom.SetCurrentHeight(native, param.ChainID, utils.GetUint64Bytes(uint64(genesis.GetIndex()))) + scom.SetMainChain(native, param.ChainID, uint64(genesis.GetIndex()), raw) + + return nil +} + +func getLastStoredHeader(native *native.NativeContract, chainID uint64) (*BlockHeader, error) { + currentHeightBytes, err := scom.GetCurrentHeight(native, chainID) + if err != nil { + return nil, fmt.Errorf("GetCurrentHeight error: %v", err) + } + + currentHeight := utils.GetBytesUint64(currentHeightBytes) + raw, err := scom.GetMainChain(native, chainID, currentHeight) + if err != nil { + return nil, fmt.Errorf("GetMainChain error: %v", err) + } + + header, err := DeserializeNeo3Header(raw) + if err != nil { + return nil, fmt.Errorf("DeserializeNeo3Header error: %v", err) + } + return header, nil +} + +func putHeader(native *native.NativeContract, param *scom.SyncBlockHeaderParam, header *BlockHeader) error { + raw, err := SerializeNeo3Header(header) + if err != nil { + return fmt.Errorf("SerializeNeo3Header error: %v", err) + } + + scom.SetHeaderIndex(native, param.ChainID, header.GetHash().ToByteArray(), raw) + scom.SetCurrentHeight(native, param.ChainID, utils.GetUint64Bytes(uint64(header.GetIndex()))) + scom.SetMainChain(native, param.ChainID, uint64(header.GetIndex()), raw) + + return nil +} + +func verifyHeader(native *native.NativeContract, header *BlockHeader, lastNeoConsensus *helper.UInt160, magic uint32) error { + if !header.GetNextConsensus().Equals(lastNeoConsensus) { + return fmt.Errorf("invalid witness script hash, expected: %s, got: %s", lastNeoConsensus.String(), header.GetNextConsensus().String()) + } + // get hash + msg, err := header.GetMessage(magic) + if err != nil { + return fmt.Errorf("header.GetMessage error: %v", err) + } + // verify witness + if verified := tx.VerifyMultiSignatureWitness(msg, header.Witness); !verified { + return fmt.Errorf("VerifyMultiSignatureWitness error: %v, height: %d", err, header.GetIndex()) + } + return nil +} diff --git a/go.mod b/go.mod index 5d7de307..672f9e8d 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/ethereum/go-ethereum go 1.16 require ( - github.com/ontio/ontology v1.11.0 github.com/Azure/azure-storage-blob-go v0.7.0 github.com/VictoriaMetrics/fastcache v1.6.0 github.com/Zilliqa/gozilliqa-sdk v1.2.1-0.20210329093354-1b8e0a7a2e25 @@ -39,12 +38,15 @@ require ( github.com/influxdata/influxdb v1.8.3 github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458 github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e + github.com/joeqian10/neo3-gogogo v1.1.2 github.com/julienschmidt/httprouter v1.2.0 github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356 github.com/mattn/go-colorable v0.1.0 github.com/mattn/go-isatty v0.0.12 github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 github.com/olekukonko/tablewriter v0.0.5 + github.com/ontio/ontology v1.11.0 + github.com/ontio/ontology-crypto v1.0.9 github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 github.com/pkg/errors v0.9.1 github.com/prometheus/tsdb v0.7.1 diff --git a/go.sum b/go.sum index 43396d55..ea9f921d 100644 --- a/go.sum +++ b/go.sum @@ -44,6 +44,7 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d h1:nalkkPQcITbvhmL4+C4cKA87NW0tfm3Kl9VXRoPywFg= github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4= github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= +github.com/JohnCGriffin/overflow v0.0.0-20170615021017-4d914c927216 h1:2ZboyJ8vl75fGesnG9NpMTD2DyQI3FzMXy4x752rGF0= github.com/JohnCGriffin/overflow v0.0.0-20170615021017-4d914c927216/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= @@ -391,6 +392,7 @@ github.com/influxdata/promql/v2 v2.12.0/go.mod h1:fxOPu+DY0bqCTCECchSRtWfc+0X19y github.com/influxdata/roaring v0.4.13-0.20180809181101-fc520f41fab6/go.mod h1:bSgUQ7q5ZLSO+bKBGqJiCBGAl+9DxyW63zLTujjUlOE= github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mqiSBE6Ffsg94weZZ2c+v/ciT8QRHFOap7EKDrR0= github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1:Wbbw6tYNvwa5dlB6304Sd+82Z3f7PmVZHVKU637d4po= +github.com/itchyny/base58-go v0.1.0 h1:zF5spLDo956exUAD17o+7GamZTRkXOZlqJjRciZwd1I= github.com/itchyny/base58-go v0.1.0/go.mod h1:SrMWPE3DFuJJp1M/RUhu4fccp/y9AlB8AL3o3duPToU= github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458 h1:6OvNmYgJyexcZ3pYbTI9jWx5tHo1Dee/tWbLMfPe2TA= github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= @@ -403,6 +405,8 @@ github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHW github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= +github.com/joeqian10/neo3-gogogo v1.1.2 h1:sw2KRoBBW9tIWNpi/Nqn6xeCwf+oGRN02Xz1lUBXbSI= +github.com/joeqian10/neo3-gogogo v1.1.2/go.mod h1:k0wb1hcBjjspDpyHtEXIpDUEXAw5SfX7coi5AkNtxoU= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= @@ -516,7 +520,9 @@ github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/ontio/ontology v1.11.0 h1:0T/hxFDHQqRcs1+yEdgaym5YIvGx5yebOsHYdKVWgHI= github.com/ontio/ontology v1.11.0/go.mod h1:Qw74bfTBlIQka+jQX4nXuWvyOYGGt368/V7XFxaf4tY= +github.com/ontio/ontology-crypto v1.0.9 h1:6fxBsz3W4CcdJk4/9QO7j0Qq7NdlP2ixPrViu8XpzzM= github.com/ontio/ontology-crypto v1.0.9/go.mod h1:h/jeqqb9Ma/Leszxqh6zY3eTF2yks44hyRKikMni+YQ= github.com/ontio/ontology-eventbus v0.9.1/go.mod h1:hCQIlbdPckcfykMeVUdWrqHZ8d30TBdmLfXCVWGkYhM= github.com/ontio/wagon v0.4.1/go.mod h1:oTPdgWT7WfPlEyzVaHSn1vQPMSbOpQPv+WphxibWlhg=