From e0867d3b7b30706540b65dc333c6c4ba73cb0aae Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=BE=90=E5=BF=97=E5=BC=BA?= <652732310@qq.com>
Date: Wed, 27 Apr 2022 13:06:49 +0800
Subject: [PATCH] add ontevm
---
.../cross_chain_manager/common/param.go | 43 ++
.../native/cross_chain_manager/entrance.go | 3 +
.../ont/merkle/common/address.go | 152 +++++
.../ont/merkle/common/common.go | 57 ++
.../ont/merkle/common/safeMath.go | 41 ++
.../ont/merkle/common/uint256.go | 80 +++
.../ont/merkle/common/zero_copy_sink.go | 198 ++++++
.../ont/merkle/common/zero_copy_source.go | 241 +++++++
.../ont/merkle/file_hash_store.go | 152 +++++
.../ont/merkle/merkle_hasher.go | 239 +++++++
.../ont/merkle/merkle_tree.go | 607 ++++++++++++++++++
.../ont/merkle/merkle_tree_test.go | 326 ++++++++++
.../cross_chain_manager/ont/merkle/util.go | 50 ++
.../cross_chain_manager/ont/ont_handler.go | 99 +++
.../native/cross_chain_manager/ont/utils.go | 58 ++
contracts/native/header_sync/entrance.go | 3 +
go.mod | 4 +-
go.sum | 4 +
18 files changed, 2356 insertions(+), 1 deletion(-)
create mode 100644 contracts/native/cross_chain_manager/ont/merkle/common/address.go
create mode 100644 contracts/native/cross_chain_manager/ont/merkle/common/common.go
create mode 100644 contracts/native/cross_chain_manager/ont/merkle/common/safeMath.go
create mode 100644 contracts/native/cross_chain_manager/ont/merkle/common/uint256.go
create mode 100644 contracts/native/cross_chain_manager/ont/merkle/common/zero_copy_sink.go
create mode 100644 contracts/native/cross_chain_manager/ont/merkle/common/zero_copy_source.go
create mode 100644 contracts/native/cross_chain_manager/ont/merkle/file_hash_store.go
create mode 100644 contracts/native/cross_chain_manager/ont/merkle/merkle_hasher.go
create mode 100644 contracts/native/cross_chain_manager/ont/merkle/merkle_tree.go
create mode 100644 contracts/native/cross_chain_manager/ont/merkle/merkle_tree_test.go
create mode 100644 contracts/native/cross_chain_manager/ont/merkle/util.go
create mode 100644 contracts/native/cross_chain_manager/ont/ont_handler.go
create mode 100644 contracts/native/cross_chain_manager/ont/utils.go
diff --git a/contracts/native/cross_chain_manager/common/param.go b/contracts/native/cross_chain_manager/common/param.go
index 36df469e..ac47102e 100644
--- a/contracts/native/cross_chain_manager/common/param.go
+++ b/contracts/native/cross_chain_manager/common/param.go
@@ -23,6 +23,7 @@ import (
"math/big"
"github.com/ethereum/go-ethereum/accounts/abi"
+ "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/rlp"
@@ -108,6 +109,48 @@ func (m *MakeTxParam) DecodeRLP(s *rlp.Stream) error {
return nil
}
+type MakeTxParamWithSender struct {
+ Sender common.Address
+ MakeTxParam
+}
+
+//used for param from evm contract
+type MakeTxParamWithSenderShim struct {
+ Sender common.Address
+ MakeTxParam []byte
+}
+
+func (this *MakeTxParamWithSender) Deserialization(data []byte) (err error) {
+
+ BytesTy, _ := abi.NewType("bytes", "", nil)
+ AddrTy, _ := abi.NewType("address", "", nil)
+ // StringTy, _ := abi.NewType("string", "", nil)
+
+ TxParam := abi.Arguments{
+ {Type: AddrTy, Name: "sender"},
+ {Type: BytesTy, Name: "makeTxParam"},
+ }
+
+ args, err := TxParam.Unpack(data)
+ if err != nil {
+ return
+ }
+
+ shim := new(MakeTxParamWithSenderShim)
+ err = TxParam.Copy(shim, args)
+ if err != nil {
+ return
+ }
+
+ this.Sender = shim.Sender
+ makeTxParam, err := DecodeTxParam(shim.MakeTxParam)
+ if err != nil {
+ return
+ }
+ this.MakeTxParam = *makeTxParam
+ return
+}
+
//used for param from evm contract
type MakeTxParamShim struct {
TxHash []byte
diff --git a/contracts/native/cross_chain_manager/entrance.go b/contracts/native/cross_chain_manager/entrance.go
index 1bf6cdc5..e96c7c1f 100644
--- a/contracts/native/cross_chain_manager/entrance.go
+++ b/contracts/native/cross_chain_manager/entrance.go
@@ -30,6 +30,7 @@ import (
"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/okex"
+ "github.com/ethereum/go-ethereum/contracts/native/cross_chain_manager/ont"
"github.com/ethereum/go-ethereum/contracts/native/cross_chain_manager/polygon"
"github.com/ethereum/go-ethereum/contracts/native/cross_chain_manager/quorum"
"github.com/ethereum/go-ethereum/contracts/native/cross_chain_manager/zilliqa"
@@ -88,6 +89,8 @@ func GetChainHandler(router uint64) (scom.ChainHandler, error) {
return polygon.NewHandler(), nil
case utils.COSMOS_ROUTER:
return cosmos.NewCosmosHandler(), nil
+ case utils.ONT_ROUTER:
+ return ont.NewONTHandler(), nil
case utils.ZILLIQA_ROUTER:
return zilliqa.NewHandler(), nil
default:
diff --git a/contracts/native/cross_chain_manager/ont/merkle/common/address.go b/contracts/native/cross_chain_manager/ont/merkle/common/address.go
new file mode 100644
index 00000000..faa2f24a
--- /dev/null
+++ b/contracts/native/cross_chain_manager/ont/merkle/common/address.go
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2021 The Zion Authors
+ * This file is part of The Zion library.
+ *
+ * The Zion 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 Zion 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 Zion. If not, see .
+ */
+
+package common
+
+import (
+ "crypto/sha256"
+ "errors"
+ "fmt"
+ "io"
+ "math/big"
+
+ "github.com/itchyny/base58-go"
+ "golang.org/x/crypto/ripemd160"
+)
+
+const ADDR_LEN = 20
+
+type Address [ADDR_LEN]byte
+
+var ADDRESS_EMPTY = Address{}
+
+// ToHexString returns hex string representation of Address
+func (self *Address) ToHexString() string {
+ return fmt.Sprintf("%x", ToArrayReverse(self[:]))
+}
+
+// Serialize serialize Address into io.Writer
+func (self *Address) Serialization(sink *ZeroCopySink) {
+ sink.WriteAddress(*self)
+}
+
+// Deserialize deserialize Address from io.Reader
+func (self *Address) Deserialization(source *ZeroCopySource) error {
+ var eof bool
+ *self, eof = source.NextAddress()
+ if eof {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
+
+// Serialize serialize Address into io.Writer
+func (self *Address) Serialize(w io.Writer) error {
+ _, err := w.Write(self[:])
+ return err
+}
+
+// Deserialize deserialize Address from io.Reader
+func (self *Address) Deserialize(r io.Reader) error {
+ _, err := io.ReadFull(r, self[:])
+ if err != nil {
+ return errors.New("deserialize Address error")
+ }
+ return nil
+}
+
+// ToBase58 returns base58 encoded address string
+func (f *Address) ToBase58() string {
+ data := append([]byte{23}, f[:]...)
+ temp := sha256.Sum256(data)
+ temps := sha256.Sum256(temp[:])
+ data = append(data, temps[0:4]...)
+
+ bi := new(big.Int).SetBytes(data).String()
+ encoded, _ := base58.BitcoinEncoding.Encode([]byte(bi))
+ return string(encoded)
+}
+
+// AddressParseFromBytes returns parsed Address
+func AddressParseFromBytes(f []byte) (Address, error) {
+ if len(f) != ADDR_LEN {
+ return ADDRESS_EMPTY, errors.New("[Common]: AddressParseFromBytes err, len != 20")
+ }
+
+ var addr Address
+ copy(addr[:], f)
+ return addr, nil
+}
+
+// AddressParseFromHexString returns parsed Address
+func AddressFromHexString(s string) (Address, error) {
+ hx, err := HexToBytes(s)
+ if err != nil {
+ return ADDRESS_EMPTY, err
+ }
+ return AddressParseFromBytes(ToArrayReverse(hx))
+}
+
+const maxSize = 2048
+
+// AddressFromBase58 returns Address from encoded base58 string
+func AddressFromBase58(encoded string) (Address, error) {
+ if encoded == "" {
+ return ADDRESS_EMPTY, errors.New("invalid address")
+ }
+ if len(encoded) > maxSize {
+ return ADDRESS_EMPTY, errors.New("invalid address")
+ }
+ decoded, err := base58.BitcoinEncoding.Decode([]byte(encoded))
+ if err != nil {
+ return ADDRESS_EMPTY, err
+ }
+
+ x, ok := new(big.Int).SetString(string(decoded), 10)
+ if !ok {
+ return ADDRESS_EMPTY, errors.New("invalid address")
+ }
+
+ buf := x.Bytes()
+ if len(buf) != 1+ADDR_LEN+4 || buf[0] != byte(23) {
+ return ADDRESS_EMPTY, errors.New("wrong encoded address")
+ }
+
+ ph, err := AddressParseFromBytes(buf[1:21])
+ if err != nil {
+ return ADDRESS_EMPTY, err
+ }
+
+ addr := ph.ToBase58()
+
+ if addr != encoded {
+ return ADDRESS_EMPTY, errors.New("[AddressFromBase58]: decode encoded verify failed.")
+ }
+
+ return ph, nil
+}
+
+func AddressFromVmCode(code []byte) Address {
+ var addr Address
+ temp := sha256.Sum256(code)
+ md := ripemd160.New()
+ md.Write(temp[:])
+ md.Sum(addr[:0])
+
+ return addr
+}
diff --git a/contracts/native/cross_chain_manager/ont/merkle/common/common.go b/contracts/native/cross_chain_manager/ont/merkle/common/common.go
new file mode 100644
index 00000000..e843eaf5
--- /dev/null
+++ b/contracts/native/cross_chain_manager/ont/merkle/common/common.go
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2021 The Zion Authors
+ * This file is part of The Zion library.
+ *
+ * The Zion 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 Zion 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 Zion. If not, see .
+ */
+
+package common
+
+import (
+ "encoding/hex"
+ "math/rand"
+ "os"
+)
+
+// GetNonce returns random nonce
+func GetNonce() uint64 {
+ // Fixme replace with the real random number generator
+ nonce := uint64(rand.Uint32())<<32 + uint64(rand.Uint32())
+ return nonce
+}
+
+// ToHexString convert []byte to hex string
+func ToHexString(data []byte) string {
+ return hex.EncodeToString(data)
+}
+
+// HexToBytes convert hex string to []byte
+func HexToBytes(value string) ([]byte, error) {
+ return hex.DecodeString(value)
+}
+
+func ToArrayReverse(arr []byte) []byte {
+ l := len(arr)
+ x := make([]byte, 0)
+ for i := l - 1; i >= 0; i-- {
+ x = append(x, arr[i])
+ }
+ return x
+}
+
+// FileExisted checks whether filename exists in filesystem
+func FileExisted(filename string) bool {
+ _, err := os.Stat(filename)
+ return err == nil || os.IsExist(err)
+}
diff --git a/contracts/native/cross_chain_manager/ont/merkle/common/safeMath.go b/contracts/native/cross_chain_manager/ont/merkle/common/safeMath.go
new file mode 100644
index 00000000..9af6271f
--- /dev/null
+++ b/contracts/native/cross_chain_manager/ont/merkle/common/safeMath.go
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Zion Authors
+ * This file is part of The Zion library.
+ *
+ * The Zion 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 Zion 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 Zion. If not, see .
+ */
+
+package common
+
+import "math"
+
+const (
+ MAX_UINT64 = math.MaxUint64
+ MAX_INT64 = math.MaxInt64
+)
+
+func SafeSub(x, y uint64) (uint64, bool) {
+ return x - y, x < y
+}
+
+func SafeAdd(x, y uint64) (uint64, bool) {
+ return x + y, y > MAX_UINT64-x
+}
+
+func SafeMul(x, y uint64) (uint64, bool) {
+ if x == 0 || y == 0 {
+ return 0, false
+ }
+ return x * y, y > MAX_UINT64/x
+}
diff --git a/contracts/native/cross_chain_manager/ont/merkle/common/uint256.go b/contracts/native/cross_chain_manager/ont/merkle/common/uint256.go
new file mode 100644
index 00000000..58785397
--- /dev/null
+++ b/contracts/native/cross_chain_manager/ont/merkle/common/uint256.go
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2021 The Zion Authors
+ * This file is part of The Zion library.
+ *
+ * The Zion 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 Zion 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 Zion. If not, see .
+ */
+
+package common
+
+import (
+ "errors"
+ "fmt"
+ "io"
+)
+
+const (
+ UINT16_SIZE = 2
+ UINT32_SIZE = 4
+ UINT64_SIZE = 8
+ UINT256_SIZE = 32
+)
+
+type Uint256 [UINT256_SIZE]byte
+
+var UINT256_EMPTY = Uint256{}
+
+func (u *Uint256) ToArray() []byte {
+ x := make([]byte, UINT256_SIZE)
+ for i := 0; i < 32; i++ {
+ x[i] = byte(u[i])
+ }
+
+ return x
+}
+
+func (u *Uint256) ToHexString() string {
+ return fmt.Sprintf("%x", ToArrayReverse(u[:]))
+}
+
+func (u *Uint256) Serialize(w io.Writer) error {
+ _, err := w.Write(u[:])
+ return err
+}
+
+func (u *Uint256) Deserialize(r io.Reader) error {
+ _, err := io.ReadFull(r, u[:])
+ if err != nil {
+ return errors.New("deserialize Uint256 error")
+ }
+ return nil
+}
+
+func Uint256ParseFromBytes(f []byte) (Uint256, error) {
+ if len(f) != UINT256_SIZE {
+ return Uint256{}, errors.New("[Common]: Uint256ParseFromBytes err, len != 32")
+ }
+
+ var hash Uint256
+ copy(hash[:], f)
+ return hash, nil
+}
+
+func Uint256FromHexString(s string) (Uint256, error) {
+ hx, err := HexToBytes(s)
+ if err != nil {
+ return UINT256_EMPTY, err
+ }
+ return Uint256ParseFromBytes(ToArrayReverse(hx))
+}
diff --git a/contracts/native/cross_chain_manager/ont/merkle/common/zero_copy_sink.go b/contracts/native/cross_chain_manager/ont/merkle/common/zero_copy_sink.go
new file mode 100644
index 00000000..64ac3e39
--- /dev/null
+++ b/contracts/native/cross_chain_manager/ont/merkle/common/zero_copy_sink.go
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2021 The Zion Authors
+ * This file is part of The Zion library.
+ *
+ * The Zion 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 Zion 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 Zion. If not, see .
+ */
+package common
+
+import (
+ "bytes"
+ "encoding/binary"
+ "errors"
+)
+
+type ZeroCopySink struct {
+ buf []byte
+}
+
+// tryGrowByReslice is a inlineable version of grow for the fast-case where the
+// internal buffer only needs to be resliced.
+// It returns the index where bytes should be written and whether it succeeded.
+func (self *ZeroCopySink) tryGrowByReslice(n int) (int, bool) {
+ if l := len(self.buf); n <= cap(self.buf)-l {
+ self.buf = self.buf[:l+n]
+ return l, true
+ }
+ return 0, false
+}
+
+const maxInt = int(^uint(0) >> 1)
+
+// grow grows the buffer to guarantee space for n more bytes.
+// It returns the index where bytes should be written.
+// If the buffer can't grow it will panic with ErrTooLarge.
+func (self *ZeroCopySink) grow(n int) int {
+ // Try to grow by means of a reslice.
+ if i, ok := self.tryGrowByReslice(n); ok {
+ return i
+ }
+
+ l := len(self.buf)
+ c := cap(self.buf)
+ if c > maxInt-c-n {
+ panic(ErrTooLarge)
+ }
+ // Not enough space anywhere, we need to allocate.
+ buf := makeSlice(2*c + n)
+ copy(buf, self.buf)
+ self.buf = buf[:l+n]
+ return l
+}
+
+func (self *ZeroCopySink) WriteBytes(p []byte) {
+ data := self.NextBytes(uint64(len(p)))
+ copy(data, p)
+}
+
+func (self *ZeroCopySink) Size() uint64 { return uint64(len(self.buf)) }
+
+func (self *ZeroCopySink) NextBytes(n uint64) (data []byte) {
+ m, ok := self.tryGrowByReslice(int(n))
+ if !ok {
+ m = self.grow(int(n))
+ }
+ data = self.buf[m:]
+ return
+}
+
+// Backs up a number of bytes, so that the next call to NextXXX() returns data again
+// that was already returned by the last call to NextXXX().
+func (self *ZeroCopySink) BackUp(n uint64) {
+ l := len(self.buf) - int(n)
+ self.buf = self.buf[:l]
+}
+
+func (self *ZeroCopySink) WriteUint8(data uint8) {
+ buf := self.NextBytes(1)
+ buf[0] = data
+}
+
+func (self *ZeroCopySink) WriteByte(c byte) {
+ self.WriteUint8(c)
+}
+
+func (self *ZeroCopySink) WriteBool(data bool) {
+ if data {
+ self.WriteByte(1)
+ } else {
+ self.WriteByte(0)
+ }
+}
+
+func (self *ZeroCopySink) WriteUint16(data uint16) {
+ buf := self.NextBytes(2)
+ binary.LittleEndian.PutUint16(buf, data)
+}
+
+func (self *ZeroCopySink) WriteUint32(data uint32) {
+ buf := self.NextBytes(4)
+ binary.LittleEndian.PutUint32(buf, data)
+}
+
+func (self *ZeroCopySink) WriteUint64(data uint64) {
+ buf := self.NextBytes(8)
+ binary.LittleEndian.PutUint64(buf, data)
+}
+
+func (self *ZeroCopySink) WriteInt64(data int64) {
+ self.WriteUint64(uint64(data))
+}
+
+func (self *ZeroCopySink) WriteInt32(data int32) {
+ self.WriteUint32(uint32(data))
+}
+
+func (self *ZeroCopySink) WriteInt16(data int16) {
+ self.WriteUint16(uint16(data))
+}
+
+func (self *ZeroCopySink) WriteVarBytes(data []byte) (size uint64) {
+ l := uint64(len(data))
+ size = self.WriteVarUint(l) + l
+
+ self.WriteBytes(data)
+ return
+}
+
+func (self *ZeroCopySink) WriteString(data string) (size uint64) {
+ return self.WriteVarBytes([]byte(data))
+}
+
+func (self *ZeroCopySink) WriteAddress(addr Address) {
+ self.WriteBytes(addr[:])
+}
+
+func (self *ZeroCopySink) WriteHash(hash Uint256) {
+ self.WriteBytes(hash[:])
+}
+
+func (self *ZeroCopySink) WriteVarUint(data uint64) (size uint64) {
+ buf := self.NextBytes(9)
+ if data < 0xFD {
+ buf[0] = uint8(data)
+ size = 1
+ } else if data <= 0xFFFF {
+ buf[0] = 0xFD
+ binary.LittleEndian.PutUint16(buf[1:], uint16(data))
+ size = 3
+ } else if data <= 0xFFFFFFFF {
+ buf[0] = 0xFE
+ binary.LittleEndian.PutUint32(buf[1:], uint32(data))
+ size = 5
+ } else {
+ buf[0] = 0xFF
+ binary.LittleEndian.PutUint64(buf[1:], uint64(data))
+ size = 9
+ }
+
+ self.BackUp(9 - size)
+ return
+}
+
+// NewReader returns a new ZeroCopySink reading from b.
+func NewZeroCopySink(b []byte) *ZeroCopySink {
+ if b == nil {
+ b = make([]byte, 0, 512)
+ }
+ return &ZeroCopySink{b}
+}
+
+func (self *ZeroCopySink) Bytes() []byte { return self.buf }
+
+func (self *ZeroCopySink) Reset() { self.buf = self.buf[:0] }
+
+var ErrTooLarge = errors.New("bytes.Buffer: too large")
+
+// makeSlice allocates a slice of size n. If the allocation fails, it panics
+// with ErrTooLarge.
+func makeSlice(n int) []byte {
+ // If the make fails, give a known error.
+ defer func() {
+ if recover() != nil {
+ panic(bytes.ErrTooLarge)
+ }
+ }()
+ return make([]byte, n)
+}
diff --git a/contracts/native/cross_chain_manager/ont/merkle/common/zero_copy_source.go b/contracts/native/cross_chain_manager/ont/merkle/common/zero_copy_source.go
new file mode 100644
index 00000000..6b05c787
--- /dev/null
+++ b/contracts/native/cross_chain_manager/ont/merkle/common/zero_copy_source.go
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2021 The Zion Authors
+ * This file is part of The Zion library.
+ *
+ * The Zion 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 Zion 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 Zion. If not, see .
+ */
+
+package common
+
+import (
+ "encoding/binary"
+)
+
+type ZeroCopySource struct {
+ s []byte
+ off uint64 // current reading index
+}
+
+// Len returns the number of bytes of the unread portion of the
+// slice.
+func (self *ZeroCopySource) Len() uint64 {
+ length := uint64(len(self.s))
+ if self.off >= length {
+ return 0
+ }
+ return length - self.off
+}
+
+func (self *ZeroCopySource) Bytes() []byte {
+ return self.s
+}
+
+func (self *ZeroCopySource) OffBytes() []byte {
+ return self.s[self.off:]
+}
+
+func (self *ZeroCopySource) Pos() uint64 {
+ return self.off
+}
+
+// Size returns the original length of the underlying byte slice.
+// Size is the number of bytes available for reading via ReadAt.
+// The returned value is always the same and is not affected by calls
+// to any other method.
+func (self *ZeroCopySource) Size() uint64 { return uint64(len(self.s)) }
+
+// Read implements the io.ZeroCopySource interface.
+func (self *ZeroCopySource) NextBytes(n uint64) (data []byte, eof bool) {
+ m := uint64(len(self.s))
+ end, overflow := SafeAdd(self.off, n)
+ if overflow || end > m {
+ end = m
+ eof = true
+ }
+ data = self.s[self.off:end]
+ self.off = end
+
+ return
+}
+
+func (self *ZeroCopySource) Skip(n uint64) (eof bool) {
+ m := uint64(len(self.s))
+ end, overflow := SafeAdd(self.off, n)
+ if overflow || end > m {
+ end = m
+ eof = true
+ }
+ self.off = end
+
+ return
+}
+
+// ReadByte implements the io.ByteReader interface.
+func (self *ZeroCopySource) NextByte() (data byte, eof bool) {
+ if self.off >= uint64(len(self.s)) {
+ return 0, true
+ }
+
+ b := self.s[self.off]
+ self.off++
+ return b, false
+}
+
+func (self *ZeroCopySource) NextUint8() (data uint8, eof bool) {
+ var val byte
+ val, eof = self.NextByte()
+ return uint8(val), eof
+}
+
+func (self *ZeroCopySource) NextBool() (data bool, eof bool) {
+ val, eof := self.NextByte()
+ if val == 0 {
+ data = false
+ } else if val == 1 {
+ data = true
+ } else {
+ eof = true
+ }
+ return
+}
+
+// Backs up a number of bytes, so that the next call to NextXXX() returns data again
+// that was already returned by the last call to NextXXX().
+func (self *ZeroCopySource) BackUp(n uint64) {
+ self.off -= n
+}
+
+func (self *ZeroCopySource) NextUint16() (data uint16, eof bool) {
+ var buf []byte
+ buf, eof = self.NextBytes(UINT16_SIZE)
+ if eof {
+ return
+ }
+
+ return binary.LittleEndian.Uint16(buf), eof
+}
+
+func (self *ZeroCopySource) NextUint32() (data uint32, eof bool) {
+ var buf []byte
+ buf, eof = self.NextBytes(UINT32_SIZE)
+ if eof {
+ return
+ }
+
+ return binary.LittleEndian.Uint32(buf), eof
+}
+
+func (self *ZeroCopySource) NextUint64() (data uint64, eof bool) {
+ var buf []byte
+ buf, eof = self.NextBytes(UINT64_SIZE)
+ if eof {
+ return
+ }
+
+ return binary.LittleEndian.Uint64(buf), eof
+}
+
+func (self *ZeroCopySource) NextInt32() (data int32, eof bool) {
+ var val uint32
+ val, eof = self.NextUint32()
+ return int32(val), eof
+}
+
+func (self *ZeroCopySource) NextInt64() (data int64, eof bool) {
+ var val uint64
+ val, eof = self.NextUint64()
+ return int64(val), eof
+}
+
+func (self *ZeroCopySource) NextInt16() (data int16, eof bool) {
+ var val uint16
+ val, eof = self.NextUint16()
+ return int16(val), eof
+}
+
+func (self *ZeroCopySource) NextVarBytes() (data []byte, eof bool) {
+ count, eof := self.NextVarUint()
+ if eof {
+ return
+ }
+ data, eof = self.NextBytes(count)
+ return
+}
+
+func (self *ZeroCopySource) NextAddress() (data Address, eof bool) {
+ var buf []byte
+ buf, eof = self.NextBytes(ADDR_LEN)
+ if eof {
+ return
+ }
+ copy(data[:], buf)
+
+ return
+}
+
+func (self *ZeroCopySource) NextHash() (data Uint256, eof bool) {
+ var buf []byte
+ buf, eof = self.NextBytes(UINT256_SIZE)
+ if eof {
+ return
+ }
+ copy(data[:], buf)
+
+ return
+}
+
+func (self *ZeroCopySource) NextString() (data string, eof bool) {
+ var val []byte
+ val, eof = self.NextVarBytes()
+ data = string(val)
+ return
+}
+
+func (self *ZeroCopySource) NextVarUint() (data uint64, eof bool) {
+ var fb byte
+ fb, eof = self.NextByte()
+ if eof {
+ return
+ }
+
+ switch fb {
+ case 0xFD:
+ val, e := self.NextUint16()
+ if e {
+ eof = e
+ return
+ }
+ data = uint64(val)
+ case 0xFE:
+ val, e := self.NextUint32()
+ if e {
+ eof = e
+ return
+ }
+ data = uint64(val)
+ case 0xFF:
+ val, e := self.NextUint64()
+ if e {
+ eof = e
+ return
+ }
+ data = uint64(val)
+ default:
+ data = uint64(fb)
+ }
+ return
+}
+
+// NewReader returns a new ZeroCopySource reading from b.
+func NewZeroCopySource(b []byte) *ZeroCopySource { return &ZeroCopySource{b, 0} }
diff --git a/contracts/native/cross_chain_manager/ont/merkle/file_hash_store.go b/contracts/native/cross_chain_manager/ont/merkle/file_hash_store.go
new file mode 100644
index 00000000..b6ba678c
--- /dev/null
+++ b/contracts/native/cross_chain_manager/ont/merkle/file_hash_store.go
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2021 The Zion Authors
+ * This file is part of The Zion library.
+ *
+ * The Zion 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 Zion 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 Zion. If not, see .
+ */
+
+package merkle
+
+import (
+ "errors"
+ "io"
+ "os"
+
+ "github.com/ethereum/go-ethereum/contracts/native/cross_chain_manager/ont/merkle/common"
+)
+
+// HashStore is an interface for persist hash
+type HashStore interface {
+ Append(hash []common.Uint256) error
+ Flush() error
+ Close()
+ GetHash(pos uint32) (common.Uint256, error)
+}
+
+type fileHashStore struct {
+ file_name string
+ file *os.File
+}
+
+// NewFileHashStore returns a HashStore implement in file
+func NewFileHashStore(name string, tree_size uint32) (HashStore, error) {
+ f, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE, 0755)
+ if err != nil {
+ return nil, err
+ }
+ store := &fileHashStore{
+ file_name: name,
+ file: f,
+ }
+
+ err = store.checkConsistence(tree_size)
+ if err != nil {
+ return nil, err
+ }
+
+ num_hashes := getStoredHashNum(tree_size)
+ size := int64(num_hashes) * int64(common.UINT256_SIZE)
+
+ _, err = store.file.Seek(size, io.SeekStart)
+ if err != nil {
+ return nil, err
+ }
+ return store, nil
+}
+
+func getStoredHashNum(tree_size uint32) int64 {
+ subtreesize := getSubTreeSize(tree_size)
+ sum := int64(0)
+ for _, v := range subtreesize {
+ sum += int64(v)
+ }
+
+ return sum
+}
+
+func (self *fileHashStore) checkConsistence(tree_size uint32) error {
+ num_hashes := getStoredHashNum(tree_size)
+
+ stat, err := self.file.Stat()
+ if err != nil {
+ return err
+ } else if stat.Size() < int64(num_hashes)*int64(common.UINT256_SIZE) {
+ return errors.New("stored hashes are less than expected")
+ }
+
+ return nil
+}
+
+func (self *fileHashStore) Append(hash []common.Uint256) error {
+ if self == nil {
+ return nil
+ }
+ buf := make([]byte, 0, len(hash)*common.UINT256_SIZE)
+ for _, h := range hash {
+ buf = append(buf, h[:]...)
+ }
+ _, err := self.file.Write(buf)
+ return err
+}
+
+func (self *fileHashStore) Flush() error {
+ if self == nil {
+ return nil
+ }
+ return self.file.Sync()
+}
+
+func (self *fileHashStore) Close() {
+ if self == nil {
+ return
+ }
+ self.file.Close()
+}
+
+func (self *fileHashStore) GetHash(pos uint32) (common.Uint256, error) {
+ if self == nil {
+ return EMPTY_HASH, errors.New("FileHashstore is nil")
+ }
+ hash := EMPTY_HASH
+ _, err := self.file.ReadAt(hash[:], int64(pos)*int64(common.UINT256_SIZE))
+ if err != nil {
+ return EMPTY_HASH, err
+ }
+
+ return hash, nil
+}
+
+type memHashStore struct {
+ hashes []common.Uint256
+}
+
+// NewMemHashStore returns a HashStore implement in memory
+func NewMemHashStore() HashStore {
+ return &memHashStore{}
+}
+
+func (self *memHashStore) Append(hash []common.Uint256) error {
+ self.hashes = append(self.hashes, hash...)
+ return nil
+}
+
+func (self *memHashStore) GetHash(pos uint32) (common.Uint256, error) {
+ return self.hashes[pos], nil
+}
+
+func (self *memHashStore) Flush() error {
+ return nil
+}
+
+func (self *memHashStore) Close() {}
diff --git a/contracts/native/cross_chain_manager/ont/merkle/merkle_hasher.go b/contracts/native/cross_chain_manager/ont/merkle/merkle_hasher.go
new file mode 100644
index 00000000..fe255953
--- /dev/null
+++ b/contracts/native/cross_chain_manager/ont/merkle/merkle_hasher.go
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2021 The Zion Authors
+ * This file is part of The Zion library.
+ *
+ * The Zion 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 Zion 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 Zion. If not, see .
+ */
+
+package merkle
+
+import (
+ "bytes"
+ "crypto/sha256"
+ "errors"
+ "fmt"
+ "math"
+
+ "github.com/ethereum/go-ethereum/contracts/native/cross_chain_manager/ont/merkle/common"
+)
+
+const (
+ LEFT byte = iota
+ RIGHT
+)
+
+const (
+ MAX_SIZE = 1024 * 1024
+)
+
+var debugCheck = false
+
+type TreeHasher struct {
+}
+
+func (self TreeHasher) hash_empty() common.Uint256 {
+ return sha256.Sum256(nil)
+}
+
+func (self TreeHasher) hash_leaf(data []byte) common.Uint256 {
+ tmp := append([]byte{0}, data...)
+ return sha256.Sum256(tmp)
+}
+
+func (self TreeHasher) hash_children(left, right common.Uint256) common.Uint256 {
+ data := append([]byte{1}, left[:]...)
+ data = append(data, right[:]...)
+ return sha256.Sum256(data)
+}
+
+func (self TreeHasher) HashFullTreeWithLeafHash(leaves []common.Uint256) common.Uint256 {
+ length := uint32(len(leaves))
+ root_hash, hashes := self._hash_full(leaves, 0, length)
+
+ if uint(len(hashes)) != countBit(length) {
+ panic("hashes length mismatch")
+ }
+
+ if debugCheck {
+ root2 := self.hash_empty()
+ if len(hashes) != 0 {
+ root2 = self._hash_fold(hashes)
+ }
+
+ if root_hash != root2 {
+ panic("root hash mismatch")
+ }
+ }
+
+ // assert len(hashes) == countBit(len(leaves))
+ // assert self._hash_fold(hashes) == root_hash if hashes else root_hash == self.hash_empty()
+
+ return root_hash
+}
+
+func (self TreeHasher) HashFullTree(leaves [][]byte) common.Uint256 {
+ length := uint32(len(leaves))
+ leafhashes := make([]common.Uint256, length, length)
+ for i := range leaves {
+ leafhashes[i] = self.hash_leaf(leaves[i])
+ }
+
+ return self.HashFullTreeWithLeafHash(leafhashes)
+}
+
+func (self TreeHasher) _hash_full(leaves []common.Uint256, l_idx, r_idx uint32) (root_hash common.Uint256, hashes []common.Uint256) {
+ width := r_idx - l_idx
+ if width == 0 {
+ return self.hash_empty(), nil
+ } else if width == 1 {
+ leaf_hash := leaves[l_idx]
+ return leaf_hash, []common.Uint256{leaf_hash}
+ } else {
+ var split_width uint32 = 1 << (highBit(width-1) - 1)
+ l_root, l_hashes := self._hash_full(leaves, l_idx, l_idx+split_width)
+ if len(l_hashes) != 1 {
+ panic("left tree always full")
+ }
+ r_root, r_hashes := self._hash_full(leaves, l_idx+split_width, r_idx)
+ root_hash = self.hash_children(l_root, r_root)
+ var hashes []common.Uint256
+ if split_width*2 == width {
+ hashes = []common.Uint256{root_hash}
+ } else {
+ hashes = append(l_hashes, r_hashes[:]...)
+ }
+ return root_hash, hashes
+ }
+}
+
+func (self TreeHasher) _hash_fold(hashes []common.Uint256) common.Uint256 {
+ l := len(hashes)
+ accum := hashes[l-1]
+ for i := l - 2; i >= 0; i-- {
+ accum = self.hash_children(hashes[i], accum)
+ }
+
+ return accum
+}
+
+func HashLeaf(data []byte) common.Uint256 {
+ tmp := append([]byte{0}, data...)
+ return sha256.Sum256(tmp)
+}
+
+func HashChildren(left, right common.Uint256) common.Uint256 {
+ data := append([]byte{1}, left[:]...)
+ data = append(data, right[:]...)
+ return sha256.Sum256(data)
+}
+
+func MerkleLeafPath(data []byte, hashes []common.Uint256) ([]byte, error) {
+ size := len(hashes)*(common.UINT256_SIZE+1) + len(data) + 8
+ if size > MAX_SIZE {
+ return nil, fmt.Errorf("data length over max value:%d", MAX_SIZE)
+ }
+ index := getIndex(HashLeaf(data), hashes)
+ if index < 0 {
+ return nil, fmt.Errorf("%s", "values doesn't exist!")
+ }
+ sink := common.NewZeroCopySink(make([]byte, 0, size))
+ sink.WriteVarBytes(data)
+ d := depth(len(hashes))
+ merkleTree := MerkleHashes(hashes, d)
+ for i := d; i > 0; i-- {
+ subTree := merkleTree[i]
+ subLen := len(subTree)
+ nIndex := index / 2
+ if index == subLen-1 && subLen%2 != 0 {
+ index = nIndex
+ continue
+ }
+ if index%2 != 0 {
+ sink.WriteByte(LEFT)
+ sink.WriteHash(subTree[index-1])
+ } else {
+ sink.WriteByte(RIGHT)
+ sink.WriteHash(subTree[index+1])
+ }
+ index = nIndex
+ }
+ return sink.Bytes(), nil
+}
+
+func MerkleHashes(preLeaves []common.Uint256, depth int) [][]common.Uint256 {
+ levels := make([][]common.Uint256, depth+1, depth+1)
+ levels[depth] = preLeaves
+ for i := depth; i > 0; i -= 1 {
+ level := levels[i]
+ levelLen := len(level)
+ remainder := levelLen % 2
+ nextLevel := make([]common.Uint256, levelLen/2+remainder)
+ k := 0
+ for j := 0; j < len(level)-1; j += 2 {
+ left := level[j]
+ right := level[j+1]
+
+ nextLevel[k] = HashChildren(left, right)
+ k += 1
+ }
+ if remainder != 0 {
+ nextLevel[k] = level[len(level)-1]
+ }
+ levels[i-1] = nextLevel
+ }
+ return levels
+}
+
+func MerkleProve(path []byte, root []byte) ([]byte, error) {
+ source := common.NewZeroCopySource(path)
+ value, eof := source.NextVarBytes()
+ if eof {
+ return nil, errors.New("read bytes error")
+ }
+ hash := HashLeaf(value)
+ size := int((source.Size() - source.Pos()) / (common.UINT256_SIZE + 1))
+ for i := 0; i < size; i++ {
+ f, eof := source.NextByte()
+ if eof {
+ return nil, errors.New("read byte error")
+ }
+ v, eof := source.NextHash()
+ if eof {
+ return nil, errors.New("read hash error")
+ }
+ if f == LEFT {
+ hash = HashChildren(v, hash)
+ } else {
+ hash = HashChildren(hash, v)
+ }
+ }
+
+ if !bytes.Equal(hash[:], root) {
+ return nil, fmt.Errorf("expect root is not equal actual root, expect:%x, actual:%x", hash, root)
+ }
+ return value, nil
+}
+
+func depth(n int) int {
+ return int(math.Ceil(math.Log2(float64(n))))
+}
+
+func getIndex(leaf common.Uint256, hashes []common.Uint256) int {
+ for i, v := range hashes {
+ if bytes.Equal(v[:], leaf[:]) {
+ return i
+ }
+ }
+ return -1
+}
diff --git a/contracts/native/cross_chain_manager/ont/merkle/merkle_tree.go b/contracts/native/cross_chain_manager/ont/merkle/merkle_tree.go
new file mode 100644
index 00000000..45eb782f
--- /dev/null
+++ b/contracts/native/cross_chain_manager/ont/merkle/merkle_tree.go
@@ -0,0 +1,607 @@
+/*
+ * Copyright (C) 2021 The Zion Authors
+ * This file is part of The Zion library.
+ *
+ * The Zion 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 Zion 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 Zion. If not, see .
+ */
+
+package merkle
+
+import (
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "math"
+
+ "github.com/ethereum/go-ethereum/contracts/native/cross_chain_manager/ont/merkle/common"
+)
+
+// const UINT256_SIZE int = 32
+
+// type common.Uint256 [UINT256_SIZE]byte
+
+var EMPTY_HASH = common.Uint256{}
+
+// CompactMerkleTree calculate merkle tree with compact hash store in HashStore
+type CompactMerkleTree struct {
+ mintree_h uint
+ hashes []common.Uint256
+ hasher TreeHasher
+ hashStore HashStore
+ rootHash common.Uint256
+ treeSize uint32
+}
+
+// NewTree returns a CompactMerkleTree instance
+func NewTree(tree_size uint32, hashes []common.Uint256, store HashStore) *CompactMerkleTree {
+
+ tree := &CompactMerkleTree{
+ mintree_h: 0,
+ hashes: nil,
+ hasher: TreeHasher{},
+ hashStore: store,
+ rootHash: EMPTY_HASH,
+ }
+
+ tree._update(tree_size, hashes)
+ return tree
+}
+
+func (self *CompactMerkleTree) Hashes() []common.Uint256 {
+ return self.hashes
+}
+
+func (self *CompactMerkleTree) TreeSize() uint32 {
+ return self.treeSize
+}
+
+func (self *CompactMerkleTree) Marshal() ([]byte, error) {
+ length := 4 + len(self.hashes)*common.UINT256_SIZE
+ buf := make([]byte, 4, length)
+ binary.BigEndian.PutUint32(buf[0:], self.treeSize)
+ for _, h := range self.hashes {
+ buf = append(buf, h[:]...)
+ }
+
+ return buf, nil
+}
+
+func (self *CompactMerkleTree) UnMarshal(buf []byte) error {
+ tree_size := binary.BigEndian.Uint32(buf[0:4])
+ nhashes := countBit(tree_size)
+ if len(buf) < 4+int(nhashes)*common.UINT256_SIZE {
+ return errors.New("Too short input buf length")
+ }
+ hashes := make([]common.Uint256, nhashes, nhashes)
+ for i := 0; i < int(nhashes); i++ {
+ copy(hashes[i][:], buf[4+i*common.UINT256_SIZE:])
+ }
+
+ self._update(tree_size, hashes)
+
+ return nil
+}
+
+func (self *CompactMerkleTree) _update(tree_size uint32, hashes []common.Uint256) {
+ numBit := countBit(tree_size)
+ if len(hashes) != int(numBit) {
+ panic("number of hashes != num bit in tree_size")
+ }
+ self.treeSize = tree_size
+ self.hashes = hashes
+ self.mintree_h = lowBit(tree_size)
+ self.rootHash = EMPTY_HASH
+
+}
+
+// Root returns root hash of merkle tree
+func (self *CompactMerkleTree) Root() common.Uint256 {
+ if self.rootHash == EMPTY_HASH {
+ if len(self.hashes) != 0 {
+ self.rootHash = self.hasher._hash_fold(self.hashes)
+ } else {
+ self.rootHash = self.hasher.hash_empty()
+ }
+ }
+ return self.rootHash
+}
+
+// GetRootWithNewLeaf returns the new root hash if newLeaf is appended to the merkle tree
+func (self *CompactMerkleTree) GetRootWithNewLeaf(newLeaf common.Uint256) common.Uint256 {
+ hashes := append(self.hashes, self.hasher.hash_leaf(newLeaf.ToArray()))
+ root := self.hasher._hash_fold(hashes)
+
+ return root
+}
+
+// clone except internal hash storage
+func (self *CompactMerkleTree) cloneMem() CompactMerkleTree {
+ temp := CompactMerkleTree{mintree_h: self.mintree_h, hasher: self.hasher, hashStore: nil,
+ rootHash: self.rootHash, treeSize: self.treeSize,
+ }
+ temp.hashes = make([]common.Uint256, len(self.hashes))
+ for i, h := range self.hashes {
+ temp.hashes[i] = h
+ }
+
+ return temp
+}
+
+func (self *CompactMerkleTree) GetRootWithNewLeaves(newLeaf []common.Uint256) common.Uint256 {
+ tree := self.cloneMem()
+ for _, h := range newLeaf {
+ tree.Append(h.ToArray())
+ }
+
+ return tree.Root()
+}
+
+// Append appends a leaf to the merkle tree and returns the audit path
+func (self *CompactMerkleTree) Append(leafv []byte) []common.Uint256 {
+ leaf := self.hasher.hash_leaf(leafv)
+
+ return self.appendHash(leaf)
+}
+
+// AppendHash appends a leaf hash to the merkle tree and returns the audit path
+func (self *CompactMerkleTree) appendHash(leaf common.Uint256) []common.Uint256 {
+ size := len(self.hashes)
+ auditPath := make([]common.Uint256, size, size)
+ storehashes := make([]common.Uint256, 0)
+ // reverse
+ for i, v := range self.hashes {
+ auditPath[size-i-1] = v
+ }
+
+ storehashes = append(storehashes, leaf)
+ self.mintree_h = 1
+ for s := self.treeSize; s%2 == 1; s = s >> 1 {
+ self.mintree_h += 1
+ leaf = self.hasher.hash_children(self.hashes[size-1], leaf)
+ storehashes = append(storehashes, leaf)
+ size -= 1
+ }
+ if self.hashStore != nil {
+ self.hashStore.Append(storehashes)
+ self.hashStore.Flush()
+ }
+ self.treeSize += 1
+ self.hashes = self.hashes[0:size]
+ self.hashes = append(self.hashes, leaf)
+ self.rootHash = EMPTY_HASH
+
+ return auditPath
+}
+
+// 1 based n
+func getSubTreeSize(n uint32) []uint32 {
+ nhashes := countBit(n)
+ subtreesize := make([]uint32, nhashes, nhashes)
+ for i, id := nhashes-1, uint32(1); n != 0; n = n >> 1 {
+ id = id * 2
+ if n%2 == 1 {
+ subtreesize[i] = id - 1
+ i -= 1
+ }
+ }
+
+ return subtreesize
+}
+
+// 1-based n and return value
+func getSubTreePos(n uint32) []uint32 {
+ nhashes := countBit(n)
+ hashespos := make([]uint32, nhashes, nhashes)
+ for i, id := nhashes-1, uint32(1); n != 0; n = n >> 1 {
+ id = id * 2
+ if n%2 == 1 {
+ hashespos[i] = id - 1
+ i -= 1
+ }
+ }
+
+ for i := uint(1); i < nhashes; i++ {
+ hashespos[i] += hashespos[i-1]
+ }
+
+ return hashespos
+}
+
+// return merkle root of D[0:n] not include n
+func (self *CompactMerkleTree) merkleRoot(n uint32) common.Uint256 {
+ hashespos := getSubTreePos(n)
+ nhashes := uint(len(hashespos))
+
+ hashes := make([]common.Uint256, nhashes, nhashes)
+ for i := uint(0); i < nhashes; i++ {
+ hashes[i], _ = self.hashStore.GetHash(hashespos[i] - 1)
+ }
+ return self.hasher._hash_fold(hashes)
+}
+
+// ConsistencyProof returns consistency proof
+func (self *CompactMerkleTree) ConsistencyProof(m, n uint32) []common.Uint256 {
+ if m > n || self.treeSize < n || self.hashStore == nil {
+ return nil
+ }
+
+ return self.subproof(m, n, true)
+}
+
+// m, n 1-based
+func (self *CompactMerkleTree) subproof(m, n uint32, b bool) []common.Uint256 {
+ offset := uint32(0)
+ var hashes []common.Uint256
+ for m < n {
+ k := uint32(1 << (highBit(n-1) - 1))
+ if m <= k {
+ pos := getSubTreePos(n - k)
+ subhashes := make([]common.Uint256, len(pos), len(pos))
+ for p := range pos {
+ pos[p] += offset + k*2 - 1
+ subhashes[p], _ = self.hashStore.GetHash(pos[p] - 1)
+ }
+ rootk2n := self.hasher._hash_fold(subhashes)
+ hashes = append(hashes, rootk2n)
+ n = k
+ } else {
+ offset += k*2 - 1
+ root02k, _ := self.hashStore.GetHash(offset - 1)
+ hashes = append(hashes, root02k)
+ m -= k
+ n -= k
+ b = false
+ }
+ }
+
+ //assert m == n
+ if b == false {
+ pos := getSubTreePos(n)
+ //assert len(pos) == 1
+ if len(pos) != 1 {
+ panic("assert error")
+ }
+ root02n, _ := self.hashStore.GetHash(pos[0] + offset - 1)
+ hashes = append(hashes, root02n)
+ }
+
+ length := len(hashes)
+ reverse := make([]common.Uint256, length, length)
+ for k, _ := range reverse {
+ reverse[k] = hashes[length-k-1]
+ }
+
+ return reverse
+}
+
+// InclusionProof returns the proof d[m] in D[0:n]
+// m zero based index, n size 1-based
+// return sink.Bytes() of WriteVarBytes(hash_index_by_m) + loop of { WriteByte(PosInfo) + WriteByte(ProofPathNodeHash) }
+func (self *CompactMerkleTree) MerkleInclusionLeafPath(data []byte, m, n uint32) ([]byte, error) {
+ if m >= n {
+ return nil, errors.New("wrong parameters")
+ } else if self.treeSize < n {
+ return nil, errors.New("not available yet")
+ } else if self.hashStore == nil {
+ return nil, errors.New("hash store not available")
+ }
+
+ offset := uint32(0)
+ depth := int(math.Ceil(math.Log2(float64(n))))
+ hashes := make([]common.Uint256, 0, depth)
+ poses := make([]byte, 0, depth)
+ for n != 1 {
+ k := uint32(1 << (highBit(n-1) - 1))
+ if m < k {
+ pos := getSubTreePos(n - k)
+ subhashes := make([]common.Uint256, len(pos), len(pos))
+ for p := range pos {
+ pos[p] += offset + k*2 - 1
+ subhashes[p], _ = self.hashStore.GetHash(pos[p] - 1)
+ }
+ rootk2n := self.hasher._hash_fold(subhashes)
+ hashes = append(hashes, rootk2n)
+ poses = append(poses, byte(1))
+ n = k
+ } else {
+ offset += k*2 - 1
+ root02k, _ := self.hashStore.GetHash(offset - 1)
+ hashes = append(hashes, root02k)
+ poses = append(poses, byte(0))
+ m -= k
+ n -= k
+ }
+ }
+ length := len(hashes)
+ sink := common.NewZeroCopySink(nil)
+ sink.WriteVarBytes(data)
+ for k, _ := range hashes {
+ index := length - k - 1
+ sink.WriteByte(poses[index])
+ sink.WriteHash(hashes[index])
+ }
+ return sink.Bytes(), nil
+}
+
+// InclusionProof returns the proof d[m] in D[0:n]
+// m zero based index, n size 1-based
+func (self *CompactMerkleTree) InclusionProof(m, n uint32) ([]common.Uint256, error) {
+ if m >= n {
+ return nil, errors.New("wrong parameters")
+ } else if self.treeSize < n {
+ return nil, errors.New("not available yet")
+ } else if self.hashStore == nil {
+ return nil, errors.New("hash store not available")
+ }
+
+ offset := uint32(0)
+ var hashes []common.Uint256
+ for n != 1 {
+ k := uint32(1 << (highBit(n-1) - 1))
+ if m < k {
+ pos := getSubTreePos(n - k)
+ subhashes := make([]common.Uint256, len(pos), len(pos))
+ for p := range pos {
+ pos[p] += offset + k*2 - 1
+ subhashes[p], _ = self.hashStore.GetHash(pos[p] - 1)
+ }
+ rootk2n := self.hasher._hash_fold(subhashes)
+ hashes = append(hashes, rootk2n)
+ n = k
+ } else {
+ offset += k*2 - 1
+ root02k, _ := self.hashStore.GetHash(offset - 1)
+ hashes = append(hashes, root02k)
+ m -= k
+ n -= k
+ }
+ }
+
+ length := len(hashes)
+ reverse := make([]common.Uint256, length, length)
+ for k := range reverse {
+ reverse[k] = hashes[length-k-1]
+ }
+
+ return reverse, nil
+}
+
+// MerkleVerifier verify inclusion and consist proof
+type MerkleVerifier struct {
+ hasher TreeHasher
+}
+
+func NewMerkleVerifier() *MerkleVerifier {
+ return &MerkleVerifier{
+ hasher: TreeHasher{},
+ }
+}
+
+/*
+ Verify a Merkle Audit PATH.
+
+ leaf_hash: The hash of the leaf for which the proof was provided.
+ leaf_index: Index of the leaf in the tree.
+ proof: A list of SHA-256 hashes representing the Merkle audit path.
+ tree_size: The size of the tree
+ root_hash: The root hash of the tree
+
+ Returns:
+ nil when the proof is valid
+*/
+func (self *MerkleVerifier) VerifyLeafHashInclusion(leaf_hash common.Uint256,
+ leaf_index uint32, proof []common.Uint256, root_hash common.Uint256, tree_size uint32) error {
+
+ if tree_size <= leaf_index {
+ return errors.New("Wrong params: the tree size is smaller than the leaf index")
+ }
+
+ calculated_root_hash, err := self.calculate_root_hash_from_audit_path(leaf_hash,
+ leaf_index, proof, tree_size)
+ if err != nil {
+ return err
+ }
+ if calculated_root_hash != root_hash {
+ return errors.New(fmt.Sprintf("Constructed root hash differs from provided root hash. Constructed: %x, Expected: %x",
+ calculated_root_hash, root_hash))
+ }
+
+ return nil
+}
+
+/*
+ Verify a Merkle Audit PATH.
+
+ leaf: The leaf for which the proof is provided
+ leaf_index: Index of the leaf in the tree.
+ proof: A list of SHA-256 hashes representing the Merkle audit path.
+ tree_size: The size of the tree
+ root_hash: The root hash of the tree
+
+ Returns:
+ nil when the proof is valid
+*/
+func (self *MerkleVerifier) VerifyLeafInclusion(leaf []byte,
+ leaf_index uint32, proof []common.Uint256, root_hash common.Uint256, tree_size uint32) error {
+ leaf_hash := self.hasher.hash_leaf(leaf)
+ return self.VerifyLeafHashInclusion(leaf_hash, leaf_index, proof, root_hash, tree_size)
+}
+
+func (self *MerkleVerifier) calculate_root_hash_from_audit_path(leaf_hash common.Uint256,
+ node_index uint32, audit_path []common.Uint256, tree_size uint32) (common.Uint256, error) {
+ calculated_hash := leaf_hash
+ last_node := tree_size - 1
+ pos := 0
+ path_len := len(audit_path)
+ for last_node > 0 {
+ if pos >= path_len {
+ return EMPTY_HASH, errors.New(fmt.Sprintf("Proof too short. expected %d, got %d",
+ audit_path_length(node_index, tree_size), path_len))
+ }
+
+ if node_index%2 == 1 {
+ calculated_hash = self.hasher.hash_children(audit_path[pos], calculated_hash)
+ pos += 1
+ } else if node_index < last_node {
+ calculated_hash = self.hasher.hash_children(calculated_hash, audit_path[pos])
+ pos += 1
+ }
+ node_index /= 2
+ last_node /= 2
+ }
+
+ if pos < path_len {
+ return EMPTY_HASH, errors.New("Proof too long")
+ }
+ return calculated_hash, nil
+}
+
+func audit_path_length(index, tree_size uint32) int {
+ length := 0
+ last_node := tree_size - 1
+ for last_node > 0 {
+ if index%2 == 1 || index < last_node {
+ length += 1
+ }
+ index /= 2
+ last_node /= 2
+ }
+ return length
+}
+
+/*
+Verify the consistency between two root hashes.
+
+ old_tree_size must be <= new_tree_size.
+
+ Args:
+ old_tree_size: size of the older tree.
+ new_tree_size: size of the newer_tree.
+ old_root: the root hash of the older tree.
+ new_root: the root hash of the newer tree.
+ proof: the consistency proof.
+
+ Returns:
+ True. The return value is enforced by a decorator and need not be
+ checked by the caller.
+
+ Raises:
+ ConsistencyError: the proof indicates an inconsistency
+ (this is usually really serious!).
+ ProofError: the proof is invalid.
+ ValueError: supplied tree sizes are invalid.
+*/
+func (self *MerkleVerifier) VerifyConsistency(old_tree_size,
+ new_tree_size uint32, old_root, new_root common.Uint256, proof []common.Uint256) error {
+ old_size := old_tree_size
+ new_size := new_tree_size
+
+ if old_size > new_size {
+ return errors.New(fmt.Sprintf("Older tree has bigger size %d vs %d", old_size, new_size))
+ }
+ if old_root == new_root {
+ return nil
+ }
+ if old_size == 0 {
+ return nil
+ }
+ //assert o < old_size < new_size
+ /*
+ A consistency proof is essentially an audit proof for the node with
+ index old_size - 1 in the newer tree. The sole difference is that
+ the path is already hashed together into a single hash up until the
+ first audit node that occurs in the newer tree only.
+ */
+ node := old_size - 1
+ last_node := new_size - 1
+
+ // while we are the right child, everything is in both trees, so move one level up
+ for node%2 == 1 {
+ node /= 2
+ last_node /= 2
+ }
+
+ lenp := len(proof)
+ pos := 0
+ var new_hash, old_hash common.Uint256
+
+ if pos >= lenp {
+ return errors.New("Wrong proof length")
+ }
+ if node != 0 {
+ // compute the two root hashes in parallel.
+ new_hash = proof[pos]
+ old_hash = proof[pos]
+ pos += 1
+ } else {
+ // The old tree was balanced (2^k nodes), so we already have the first root hash
+ new_hash = old_root
+ old_hash = old_root
+ }
+
+ for node != 0 {
+ if node%2 == 1 {
+ if pos >= lenp {
+ return errors.New("Wrong proof length")
+ }
+ // node is a right child: left sibling exists in both trees
+ next_node := proof[pos]
+ pos += 1
+ old_hash = self.hasher.hash_children(next_node, old_hash)
+ new_hash = self.hasher.hash_children(next_node, new_hash)
+ } else if node < last_node {
+ if pos >= lenp {
+ return errors.New("Wrong proof length")
+ }
+ // node is a left child: right sibling only exists inthe newer tree
+ next_node := proof[pos]
+ pos += 1
+ new_hash = self.hasher.hash_children(new_hash, next_node)
+ }
+ // else node == last_node: node is a left child with no sibling in either tree
+
+ node /= 2
+ last_node /= 2
+ }
+
+ // Now old_hash is the hash of the first subtree. If the two trees have different
+ // height, continue the path until the new root.
+ for last_node != 0 {
+ if pos >= lenp {
+ return errors.New("Wrong proof length")
+ }
+ next_node := proof[pos]
+ pos += 1
+ new_hash = self.hasher.hash_children(new_hash, next_node)
+ last_node /= 2
+ }
+
+ /* If the second hash does not match, the proof is invalid for the given pair
+ If, on the other hand, the newer hash matches but the older one does not, then
+ the proof (together with the signatures on the hashes) is proof of inconsistency.
+ */
+ if new_hash != new_root {
+ return errors.New(fmt.Sprintf(`Bad Merkle proof: second root hash does not match.
+ Expected hash:%x, computed hash: %x`, new_root, new_hash))
+ } else if old_hash != old_root {
+ return errors.New(fmt.Sprintf(`Inconsistency: first root hash does not match."
+ "Expected hash: %x, computed hash:%x`, old_root, old_hash))
+ }
+
+ if pos != lenp {
+ return errors.New("Proof too long")
+ }
+
+ return nil
+}
diff --git a/contracts/native/cross_chain_manager/ont/merkle/merkle_tree_test.go b/contracts/native/cross_chain_manager/ont/merkle/merkle_tree_test.go
new file mode 100644
index 00000000..e74a4390
--- /dev/null
+++ b/contracts/native/cross_chain_manager/ont/merkle/merkle_tree_test.go
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2021 The Zion Authors
+ * This file is part of The Zion library.
+ *
+ * The Zion 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 Zion 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 Zion. If not, see .
+ */
+
+package merkle
+
+import (
+ "crypto/sha256"
+ "fmt"
+ "os"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/contracts/native/cross_chain_manager/ont/merkle/common"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestMerkleLeaf3(t *testing.T) {
+ hasher := TreeHasher{}
+ leafs := []common.Uint256{hasher.hash_leaf([]byte{1}),
+ hasher.hash_leaf([]byte{2}),
+ hasher.hash_leaf([]byte{3})}
+ store, _ := NewFileHashStore("merkletree.db", 0)
+ tree := NewTree(0, nil, store)
+ if tree.Root() != sha256.Sum256(nil) {
+ t.Fatal("root error")
+ }
+ for i := range leafs {
+ tree.Append([]byte{byte(i + 1)})
+ }
+
+ hashes := make([]common.Uint256, 5, 5)
+ for i := 0; i < 4; i++ {
+ hashes[i], _ = tree.hashStore.GetHash(uint32(i))
+ }
+ hashes[4] = tree.Root()
+
+ cmp := []common.Uint256{
+ leafs[0],
+ leafs[1],
+ hasher.hash_children(leafs[0], leafs[1]),
+ leafs[2],
+ hasher.hash_children(hasher.hash_children(leafs[0], leafs[1]),
+ leafs[2]),
+ }
+
+ for i := 0; i < 5; i++ {
+ if hashes[i] != cmp[i] {
+ t.Fatal(fmt.Sprintf("error: %d, expected %x, found %x", i, cmp[i], hashes[i]))
+ }
+ }
+
+}
+
+func TestCompactMerkleTree_GetRootWithNewLeaves(t *testing.T) {
+ N := 1000
+ tree1 := NewTree(0, nil, nil)
+ tree2 := NewTree(0, nil, nil)
+ leaves := make([]common.Uint256, N)
+ for i := 0; i < N; i++ {
+ leaves[i][:][0] = byte(i)
+ hash := leaves[i]
+ assert.Equal(t, tree1.GetRootWithNewLeaf(hash), tree2.GetRootWithNewLeaves([]common.Uint256{hash}))
+ tree1.Append(hash.ToArray())
+ tree2.Append(hash.ToArray())
+ }
+}
+
+func TestMerkle(t *testing.T) {
+ hasher := TreeHasher{}
+ leafs := []common.Uint256{hasher.hash_leaf([]byte{1}),
+ hasher.hash_leaf([]byte{2}),
+ hasher.hash_leaf([]byte{3}),
+ hasher.hash_leaf([]byte{4})}
+
+ store, _ := NewFileHashStore("merkletree.db", 0)
+ tree := NewTree(0, nil, store)
+ if tree.Root() != sha256.Sum256(nil) {
+ t.Fatal("root error")
+ }
+ for i, _ := range leafs {
+ tree.Append([]byte{byte(i + 1)})
+ }
+
+ hashes := make([]common.Uint256, 6, 6)
+ for i := 0; i < 6; i++ {
+ hashes[i], _ = tree.hashStore.GetHash(uint32(i))
+ }
+ cmp := []common.Uint256{
+ leafs[0],
+ leafs[1],
+ hasher.hash_children(leafs[0], leafs[1]),
+ leafs[2],
+ leafs[3],
+ hasher.hash_children(leafs[2], leafs[3]),
+ hasher.hash_children(hasher.hash_children(leafs[0], leafs[1]),
+ hasher.hash_children(leafs[2], leafs[3])),
+ }
+
+ for i := 0; i < 6; i++ {
+ if hashes[i] != cmp[i] {
+ fmt.Println(hashes)
+ fmt.Println(cmp)
+ t.Fatal(fmt.Sprintf("error: %d, expected %x, found %x", i, cmp[i], hashes[i]))
+ }
+ }
+
+}
+
+func TestMerkleHashes(t *testing.T) {
+ store, _ := NewFileHashStore("merkletree.db", 0)
+ tree := NewTree(0, nil, store)
+ for i := 0; i < 100; i++ {
+ tree.Append([]byte{byte(i + 1)})
+ }
+
+ // 100 == 110 0100
+ if len(tree.hashes) != 3 {
+ t.Fatal(fmt.Sprintf("error tree hashes size"))
+ }
+
+}
+
+// zero based return merkle root of D[0:n]
+func TestMerkleRoot(t *testing.T) {
+ n := 100
+ roots := make([]common.Uint256, n, n)
+ store, _ := NewFileHashStore("merkletree.db", 0)
+ tree := NewTree(0, nil, store)
+ for i := 0; i < n; i++ {
+ tree.Append([]byte{byte(i + 1)})
+ roots[i] = tree.Root()
+ }
+
+ cmp := make([]common.Uint256, n, n)
+ for i := 0; i < n; i++ {
+ cmp[i] = tree.merkleRoot(uint32(i) + 1)
+ if cmp[i] != roots[i] {
+ t.Error(fmt.Sprintf("error merkle root is not equal at %d", i))
+ }
+ }
+
+}
+
+func TestGetSubTreeSize(t *testing.T) {
+ sizes := getSubTreeSize(7)
+ fmt.Println("sub tree size", sizes)
+}
+
+// zero based return merkle root of D[0:n]
+func TestMerkleIncludeProof(t *testing.T) {
+ n := uint32(9)
+ store, _ := NewFileHashStore("merkletree.db", 0)
+ defer func() { os.Remove("merkletree.db") }()
+ tree := NewTree(0, nil, store)
+ for i := uint32(0); i < n; i++ {
+ tree.Append([]byte{byte(i + 1)})
+ }
+ verify := NewMerkleVerifier()
+ root := tree.Root()
+ for i := uint32(2); i < n; i++ {
+ proof, _ := tree.InclusionProof(i, n)
+ leaf_hash := tree.hasher.hash_leaf([]byte{byte(i + 1)})
+ res := verify.VerifyLeafHashInclusion(leaf_hash, i, proof, root, n)
+ if res != nil {
+ t.Fatal(res, i, proof)
+ }
+ }
+}
+
+func TestMerkleInclusionLeafPath(t *testing.T) {
+ n := uint32(10)
+ store, _ := NewFileHashStore("merkletree.db", 0)
+ defer func() { os.Remove("merkletree.db") }()
+ tree := NewTree(0, nil, store)
+ for i := uint32(0); i < n; i++ {
+ tree.Append([]byte{byte(i + 1)})
+ }
+ root := tree.Root()
+ for i := uint32(0); i < n; i++ {
+ data := []byte{byte(i + 1)}
+ path, err := tree.MerkleInclusionLeafPath(data, i, n)
+ assert.Nil(t, err)
+ val, err := MerkleProve(path, root.ToArray())
+ assert.Nil(t, err)
+ assert.Equal(t, data, val)
+ }
+}
+
+func TestMerkleConsistencyProofLen(t *testing.T) {
+ n := uint32(7)
+ store, _ := NewFileHashStore("merkletree.db", 0)
+ tree := NewTree(0, nil, store)
+ for i := uint32(0); i < n; i++ {
+ tree.Append([]byte{byte(i + 1)})
+ }
+
+ cmp := []int{3, 2, 4, 1, 4, 3, 0}
+ for i := uint32(0); i < n; i++ {
+ proof := tree.ConsistencyProof(i+1, n)
+ if len(proof) != cmp[i] {
+ t.Fatal("error: wrong proof length")
+ }
+ }
+
+}
+
+func TestMerkleConsistencyProof(t *testing.T) {
+ n := uint32(140)
+ roots := make([]common.Uint256, n, n)
+ store, _ := NewFileHashStore("merkletree.db", 0)
+ tree := NewTree(0, nil, store)
+ for i := uint32(0); i < n; i++ {
+ tree.Append([]byte{byte(i + 1)})
+ roots[i] = tree.Root()
+ }
+
+ verify := NewMerkleVerifier()
+
+ for i := uint32(0); i < n; i++ {
+ proof := tree.ConsistencyProof(i+1, n)
+ err := verify.VerifyConsistency(i+1, n, roots[i], roots[n-1], proof)
+ if err != nil {
+ t.Fatal("verify consistency error:", i, err)
+ }
+
+ }
+}
+
+//~70w
+func BenchmarkMerkleInsert(b *testing.B) {
+ store, _ := NewFileHashStore("merkletree.db", 0)
+ tree := NewTree(0, nil, store)
+ for i := 0; i < b.N; i++ {
+ //use b.N for looping
+ tree.Append([]byte(fmt.Sprintf("bench %d", i)))
+ }
+}
+
+var treeTest *CompactMerkleTree
+var storeTest HashStore
+var N = 100 //00
+
+func init() {
+ storeTest, _ = NewFileHashStore("merkletree.db", 0)
+ treeTest = NewTree(0, nil, storeTest)
+ for i := 0; i < N; i++ {
+ treeTest.Append([]byte(fmt.Sprintf("setup %d", i)))
+ }
+
+}
+
+/*
+// ~20w
+func BenchmarkMerkleInclusionProof(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ treeTest.InclusionProof(uint32(i), uint32(N))
+ }
+}
+
+// ~20w
+func BenchmarkMerkleConsistencyProof(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ treeTest.ConsistencyProof(uint32(i+1), uint32(N))
+ }
+}
+*/
+
+//~70w
+func BenchmarkMerkleInsert2(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ treeTest.Append([]byte(fmt.Sprintf("bench %d", i)))
+ }
+}
+
+func TestTreeHasher_HashFullTree(t *testing.T) {
+ debugCheck = true
+ leaves := make([][]byte, 0)
+ for i := byte(0); i < 200; i++ {
+ leaves = append(leaves, []byte{i})
+ TreeHasher{}.HashFullTree(leaves)
+ }
+}
+
+func TestTreeHasher(t *testing.T) {
+ tree := NewTree(0, nil, nil)
+ leaves := make([][]byte, 0)
+ for i := uint32(0); i < 1000; i++ {
+ leaf := []byte{byte(i + 1)}
+ leaves = append(leaves, leaf)
+ tree.Append(leaf)
+ root := TreeHasher{}.HashFullTree(leaves)
+ assert.Equal(t, root, tree.Root())
+ }
+}
+
+func TestAudit(t *testing.T) {
+ var hashes []common.Uint256
+ n := 100
+ tree := TreeHasher{}
+ for i := 0; i < n; i++ {
+ hashes = append(hashes, HashLeaf([]byte(fmt.Sprintf("%d", i))))
+ }
+ root := tree.HashFullTreeWithLeafHash(hashes)
+ treeHashes := MerkleHashes(hashes, depth(len(hashes)))
+ assert.Equal(t, root, treeHashes[0][0])
+ for i := 0; i < n; i++ {
+ auditPath, _ := MerkleLeafPath([]byte(fmt.Sprintf("%d", i)), hashes)
+ value, err := MerkleProve(auditPath, root[:])
+ assert.NoError(t, err)
+ assert.Equal(t, []byte(fmt.Sprintf("%d", i)), value)
+ }
+}
diff --git a/contracts/native/cross_chain_manager/ont/merkle/util.go b/contracts/native/cross_chain_manager/ont/merkle/util.go
new file mode 100644
index 00000000..216f330e
--- /dev/null
+++ b/contracts/native/cross_chain_manager/ont/merkle/util.go
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2021 The Zion Authors
+ * This file is part of The Zion library.
+ *
+ * The Zion 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 Zion 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 Zion. If not, see .
+ */
+
+package merkle
+
+// return the number of 1 bit
+func countBit(num uint32) uint {
+ var count uint
+ for num != 0 {
+ num &= (num - 1)
+ count += 1
+ }
+ return count
+}
+
+func isPower2(num uint32) bool {
+ return countBit(num) == 1
+}
+
+// return the position of the heightest 1 bit
+// 1-based index
+func highBit(num uint32) uint {
+ var hiBit uint
+ for num != 0 {
+ num >>= 1
+ hiBit += 1
+ }
+ return hiBit
+}
+
+// return the position of the lowest 1 bit
+// 1-based index
+func lowBit(num uint32) uint {
+ return highBit(num & -num)
+}
diff --git a/contracts/native/cross_chain_manager/ont/ont_handler.go b/contracts/native/cross_chain_manager/ont/ont_handler.go
new file mode 100644
index 00000000..7c9bcdc1
--- /dev/null
+++ b/contracts/native/cross_chain_manager/ont/ont_handler.go
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2021 The Zion Authors
+ * This file is part of The Zion library.
+ *
+ * The Zion 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 Zion 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 Zion. If not, see .
+ */
+
+package ont
+
+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/header_sync/ont"
+ "github.com/ethereum/go-ethereum/contracts/native/utils"
+ "github.com/ontio/ontology-crypto/keypair"
+ ocommon "github.com/ontio/ontology/common"
+ otypes "github.com/ontio/ontology/core/types"
+)
+
+type ONTHandler struct {
+}
+
+func NewONTHandler() *ONTHandler {
+ return &ONTHandler{}
+}
+
+func (this *ONTHandler) MakeDepositProposal(service *native.NativeContract) (*scom.MakeTxParam, error) {
+ ctx := service.ContractRef().CurrentContext()
+ params := &scom.EntranceParam{}
+ if err := utils.UnpackMethod(scom.ABI, scom.MethodImportOuterTransfer, params, ctx.Payload); err != nil {
+ return nil, err
+ }
+
+ crossChainMsg, err := ont.GetCrossChainMsg(service, params.SourceChainID, params.Height)
+ if crossChainMsg == nil {
+ source := ocommon.NewZeroCopySource(params.HeaderOrCrossChainMsg)
+ crossChainMsg = new(otypes.CrossChainMsg)
+ err := crossChainMsg.Deserialization(source)
+ if err != nil {
+ return nil, fmt.Errorf("ont MakeDepositProposal, deserialize crossChainMsg error: %v", err)
+ }
+ n, _, irr, eof := source.NextVarUint()
+ if irr || eof {
+ return nil, fmt.Errorf("ont MakeDepositProposal, deserialization bookkeeper length error")
+ }
+ var bookkeepers []keypair.PublicKey
+ for i := 0; uint64(i) < n; i++ {
+ v, _, irr, eof := source.NextVarBytes()
+ if irr || eof {
+ return nil, fmt.Errorf("ont MakeDepositProposal, deserialization bookkeeper error")
+ }
+ bookkeeper, err := keypair.DeserializePublicKey(v)
+ if err != nil {
+ return nil, fmt.Errorf("ont MakeDepositProposal, keypair.DeserializePublicKey error: %v", err)
+ }
+ bookkeepers = append(bookkeepers, bookkeeper)
+ }
+ err = ont.VerifyCrossChainMsg(service, params.SourceChainID, crossChainMsg, bookkeepers)
+ if err != nil {
+ return nil, fmt.Errorf("ont MakeDepositProposal, VerifyCrossChainMsg error: %v", err)
+ }
+ err = ont.PutCrossChainMsg(service, params.SourceChainID, crossChainMsg)
+ if err != nil {
+ return nil, fmt.Errorf("ont MakeDepositProposal, put PutCrossChainMsg error: %v", err)
+ }
+ }
+
+ //get registered side chain information from poly chain
+ sideChain, err := side_chain_manager.GetSideChain(service, params.SourceChainID)
+ if err != nil {
+ return nil, fmt.Errorf("ont MakeDepositProposal, side_chain_manager.GetSideChain error: %v", err)
+ }
+
+ value, err := VerifyFromOntTx(params.Proof, crossChainMsg, sideChain)
+ if err != nil {
+ return nil, fmt.Errorf("ont MakeDepositProposal, VerifyOntTx error: %v", err)
+ }
+ if err := scom.CheckDoneTx(service, value.CrossChainID, params.SourceChainID); err != nil {
+ return nil, fmt.Errorf("ont MakeDepositProposal, check done transaction error:%s", err)
+ }
+ if err = scom.PutDoneTx(service, value.CrossChainID, params.SourceChainID); err != nil {
+ return nil, fmt.Errorf("VerifyFromOntTx, putDoneTx error:%s", err)
+ }
+ return value, nil
+}
diff --git a/contracts/native/cross_chain_manager/ont/utils.go b/contracts/native/cross_chain_manager/ont/utils.go
new file mode 100644
index 00000000..ff6d35fa
--- /dev/null
+++ b/contracts/native/cross_chain_manager/ont/utils.go
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2021 The Zion Authors
+ * This file is part of The Zion library.
+ *
+ * The Zion 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 Zion 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 Zion. If not, see .
+ */
+
+package ont
+
+import (
+ "bytes"
+ "fmt"
+
+ scom "github.com/ethereum/go-ethereum/contracts/native/cross_chain_manager/common"
+ "github.com/ethereum/go-ethereum/contracts/native/cross_chain_manager/ont/merkle"
+ "github.com/ethereum/go-ethereum/contracts/native/governance/side_chain_manager"
+ otypes "github.com/ontio/ontology/core/types"
+)
+
+func VerifyFromOntTx(proof []byte, crossChainMsg *otypes.CrossChainMsg, sideChain *side_chain_manager.SideChain) (*scom.MakeTxParam, error) {
+ v, err := merkle.MerkleProve(proof, crossChainMsg.StatesRoot.ToArray())
+ if err != nil {
+ return nil, fmt.Errorf("VerifyFromOntTx, merkle.MerkleProve verify merkle proof error")
+ }
+
+ if len(sideChain.CCMCAddress) == 0 {
+ // old sideChain for ontology
+ txParam, err := scom.DecodeTxParam(v)
+ if err != nil {
+ return nil, fmt.Errorf("VerifyFromOntTx, deserialize MakeTxParam error:%s", err)
+ }
+ return txParam, nil
+ }
+
+ // new sideChain for ontology
+ txParam := new(scom.MakeTxParamWithSender)
+ if err := txParam.Deserialization(v); err != nil {
+ return nil, fmt.Errorf("VerifyFromOntTx, deserialize MakeTxParamWithSender error:%s", err)
+ }
+
+ if !bytes.Equal(txParam.Sender[:], sideChain.CCMCAddress) {
+ return nil, fmt.Errorf("VerifyFromOntTx, invalid sender:%s", err)
+ }
+
+ return &txParam.MakeTxParam, nil
+
+}
diff --git a/contracts/native/header_sync/entrance.go b/contracts/native/header_sync/entrance.go
index 01f2b2ec..4c16a38c 100644
--- a/contracts/native/header_sync/entrance.go
+++ b/contracts/native/header_sync/entrance.go
@@ -29,6 +29,7 @@ import (
"github.com/ethereum/go-ethereum/contracts/native/header_sync/heco"
"github.com/ethereum/go-ethereum/contracts/native/header_sync/msc"
"github.com/ethereum/go-ethereum/contracts/native/header_sync/okex"
+ "github.com/ethereum/go-ethereum/contracts/native/header_sync/ont"
"github.com/ethereum/go-ethereum/contracts/native/header_sync/polygon"
"github.com/ethereum/go-ethereum/contracts/native/header_sync/quorum"
"github.com/ethereum/go-ethereum/contracts/native/header_sync/zilliqa"
@@ -171,6 +172,8 @@ func GetChainHandler(router uint64) (hscommon.HeaderSyncHandler, error) {
return polygon.NewBorHandler(), nil
case utils.COSMOS_ROUTER:
return cosmos.NewCosmosHandler(), nil
+ case utils.ONT_ROUTER:
+ return ont.NewONTHandler(), nil
case utils.ZILLIQA_ROUTER:
return zilliqa.NewHandler(), nil
default:
diff --git a/go.mod b/go.mod
index 5d7de307..792dae86 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
@@ -37,6 +36,7 @@ require (
github.com/holiman/uint256 v1.2.0
github.com/huin/goupnp v1.0.2
github.com/influxdata/influxdb v1.8.3
+ github.com/itchyny/base58-go v0.1.0
github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458
github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e
github.com/julienschmidt/httprouter v1.2.0
@@ -45,6 +45,8 @@ require (
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..41fc1351 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=
@@ -516,7 +518,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=