bitxhub/internal/executor/contracts/interchain.go

441 lines
12 KiB
Go
Raw Normal View History

2020-03-29 21:32:01 +08:00
package contracts
import (
"crypto/sha256"
2020-03-29 21:32:01 +08:00
"encoding/json"
"fmt"
"strings"
2020-03-29 21:32:01 +08:00
2020-10-22 13:49:05 +08:00
appchainMgr "github.com/meshplus/bitxhub-core/appchain-mgr"
"github.com/meshplus/bitxhub-core/boltvm"
"github.com/meshplus/bitxhub-kit/crypto"
"github.com/meshplus/bitxhub-kit/crypto/asym"
2020-03-29 21:32:01 +08:00
"github.com/meshplus/bitxhub-kit/types"
"github.com/meshplus/bitxhub-model/constant"
2020-03-29 21:32:01 +08:00
"github.com/meshplus/bitxhub-model/pb"
)
type InterchainManager struct {
2020-03-29 21:32:01 +08:00
boltvm.Stub
}
type BxhValidators struct {
Addresses []string `json:"addresses"`
}
func (x *InterchainManager) Register(chainId string) *boltvm.Response {
interchain, ok := x.getInterchain(chainId)
2020-09-29 15:13:05 +08:00
if !ok {
interchain = &pb.Interchain{
ID: chainId,
2020-09-29 15:13:05 +08:00
InterchainCounter: make(map[string]uint64),
ReceiptCounter: make(map[string]uint64),
SourceReceiptCounter: make(map[string]uint64),
}
x.setInterchain(chainId, interchain)
2020-03-29 21:32:01 +08:00
}
2020-09-29 15:13:05 +08:00
body, err := interchain.Marshal()
if err != nil {
return boltvm.Error(err.Error())
2020-03-29 21:32:01 +08:00
}
2020-09-29 15:13:05 +08:00
return boltvm.Success(body)
}
func (x *InterchainManager) DeleteInterchain(id string) *boltvm.Response {
x.Delete(AppchainKey(id))
return boltvm.Success(nil)
}
// GetInterchain returns information of the interchain count, Receipt count and SourceReceipt count by id
func (x *InterchainManager) getInterchain(id string) (*pb.Interchain, bool) {
interchain := &pb.Interchain{}
ok, data := x.Get(AppchainKey(id))
if !ok {
return nil, false
2020-03-29 21:32:01 +08:00
}
2020-09-29 15:13:05 +08:00
if err := interchain.Unmarshal(data); err != nil {
panic(err)
2020-03-29 21:32:01 +08:00
}
2020-09-29 15:13:05 +08:00
if interchain.InterchainCounter == nil {
interchain.InterchainCounter = make(map[string]uint64)
}
2020-03-29 21:32:01 +08:00
2020-09-29 15:13:05 +08:00
if interchain.ReceiptCounter == nil {
interchain.ReceiptCounter = make(map[string]uint64)
2020-03-29 21:32:01 +08:00
}
2020-09-29 15:13:05 +08:00
if interchain.SourceReceiptCounter == nil {
interchain.SourceReceiptCounter = make(map[string]uint64)
2020-03-29 21:32:01 +08:00
}
2020-09-29 15:13:05 +08:00
return interchain, true
2020-03-29 21:32:01 +08:00
}
2020-09-29 15:13:05 +08:00
// GetInterchain returns information of the interchain count, Receipt count and SourceReceipt count by id
func (x *InterchainManager) setInterchain(id string, interchain *pb.Interchain) {
data, err := interchain.Marshal()
if err != nil {
panic(err)
}
x.Set(AppchainKey(id), data)
2020-03-29 21:32:01 +08:00
}
2020-05-09 15:07:55 +08:00
// Interchain returns information of the interchain count, Receipt count and SourceReceipt count
func (x *InterchainManager) Interchain() *boltvm.Response {
ok, data := x.Get(AppchainKey(x.Caller()))
2020-05-09 15:07:55 +08:00
if !ok {
return boltvm.Error(fmt.Errorf("this appchain does not exist").Error())
}
return boltvm.Success(data)
}
// GetInterchain returns information of the interchain count, Receipt count and SourceReceipt count by id
func (x *InterchainManager) GetInterchain(id string) *boltvm.Response {
ok, data := x.Get(AppchainKey(id))
if !ok {
return boltvm.Error(fmt.Errorf("this appchain does not exist").Error())
}
return boltvm.Success(data)
}
func (x *InterchainManager) HandleIBTP(ibtp *pb.IBTP) *boltvm.Response {
2021-01-21 15:38:13 +08:00
if len(strings.Split(ibtp.From, "-")) == 2 {
return x.handleUnionIBTP(ibtp)
}
2020-09-29 15:13:05 +08:00
interchain, ok := x.getInterchain(ibtp.From)
if !ok {
return boltvm.Error("this appchain does not exist")
}
if err := x.checkIBTP(ibtp, interchain); err != nil {
return boltvm.Error(err.Error())
}
res := boltvm.Success(nil)
if pb.IBTP_INTERCHAIN == ibtp.Type {
res = x.beginTransaction(ibtp)
2020-07-24 20:19:32 +08:00
} else if pb.IBTP_RECEIPT_SUCCESS == ibtp.Type || pb.IBTP_RECEIPT_FAILURE == ibtp.Type {
res = x.reportTransaction(ibtp)
2020-07-24 20:19:32 +08:00
} else if pb.IBTP_ASSET_EXCHANGE_INIT == ibtp.Type ||
pb.IBTP_ASSET_EXCHANGE_REDEEM == ibtp.Type ||
pb.IBTP_ASSET_EXCHANGE_REFUND == ibtp.Type {
res = x.handleAssetExchange(ibtp)
2020-03-29 21:32:01 +08:00
}
if !res.Ok {
return res
}
x.ProcessIBTP(ibtp, interchain)
return res
}
func (x *InterchainManager) HandleIBTPs(data []byte) *boltvm.Response {
ok := x.Has(AppchainKey(x.Caller()))
2020-03-29 21:32:01 +08:00
if !ok {
return boltvm.Error("this appchain does not exist")
}
ibtps := &pb.IBTPs{}
if err := ibtps.Unmarshal(data); err != nil {
return boltvm.Error(err.Error())
2020-03-29 21:32:01 +08:00
}
2020-09-29 15:13:05 +08:00
interchain := &pb.Interchain{}
x.GetObject(AppchainKey(x.Caller()), interchain)
2020-09-22 11:35:55 +08:00
for _, ibtp := range ibtps.Ibtps {
if err := x.checkIBTP(ibtp, interchain); err != nil {
return boltvm.Error(err.Error())
}
}
if res := x.beginMultiTargetsTransaction(ibtps); !res.Ok {
return res
}
2020-09-22 11:35:55 +08:00
for _, ibtp := range ibtps.Ibtps {
x.ProcessIBTP(ibtp, interchain)
}
return boltvm.Success(nil)
}
2020-09-29 15:13:05 +08:00
func (x *InterchainManager) checkIBTP(ibtp *pb.IBTP, interchain *pb.Interchain) error {
2021-02-01 11:08:26 +08:00
isRelayIBTP := x.isRelayIBTP(ibtp.From)
2021-01-21 15:38:13 +08:00
if ibtp.To == "" {
return fmt.Errorf("empty destination chain id")
}
2020-09-29 15:13:05 +08:00
if _, ok := x.getInterchain(ibtp.To); !ok {
x.Logger().WithField("chain_id", ibtp.To).Debug("target appchain does not exist")
}
2020-03-29 21:32:01 +08:00
2020-07-24 20:19:32 +08:00
if pb.IBTP_INTERCHAIN == ibtp.Type ||
pb.IBTP_ASSET_EXCHANGE_INIT == ibtp.Type ||
pb.IBTP_ASSET_EXCHANGE_REDEEM == ibtp.Type ||
pb.IBTP_ASSET_EXCHANGE_REFUND == ibtp.Type {
2021-02-01 11:08:26 +08:00
if !isRelayIBTP {
2021-01-21 15:38:13 +08:00
if ibtp.From != x.Caller() {
return fmt.Errorf("ibtp from != caller")
}
2020-03-29 21:32:01 +08:00
}
idx := interchain.InterchainCounter[ibtp.To]
2020-11-18 10:36:12 +08:00
if ibtp.Index <= idx {
return fmt.Errorf(fmt.Sprintf("index already exists, required %d, but %d", idx+1, ibtp.Index))
}
if ibtp.Index > idx+1 {
return fmt.Errorf(fmt.Sprintf("wrong index, required %d, but %d", idx+1, ibtp.Index))
}
} else {
2021-02-01 11:08:26 +08:00
if !isRelayIBTP {
2021-01-21 15:38:13 +08:00
if ibtp.To != x.Caller() {
return fmt.Errorf("ibtp to != caller")
}
2020-03-29 21:32:01 +08:00
}
idx := interchain.ReceiptCounter[ibtp.To]
2020-11-18 10:36:12 +08:00
if ibtp.Index <= idx {
return fmt.Errorf(fmt.Sprintf("receipt index already exists, required %d, but %d", idx+1, ibtp.Index))
}
if ibtp.Index > idx+1 {
return fmt.Errorf(fmt.Sprintf("wrong receipt index, required %d, but %d", idx+1, ibtp.Index))
}
}
return nil
}
2020-03-29 21:32:01 +08:00
2021-02-01 11:08:26 +08:00
// isRelayIBTP returns whether ibtp.from is relaychain type
func (x *InterchainManager) isRelayIBTP(from string) bool {
srcChain := &appchainMgr.Appchain{}
res := x.CrossInvoke(constant.AppchainMgrContractAddr.String(), "GetAppchain", pb.String(from))
if err := json.Unmarshal(res.Result, srcChain); err != nil {
return false
}
return srcChain.ChainType == appchainMgr.RelaychainType
}
2020-09-29 15:13:05 +08:00
func (x *InterchainManager) ProcessIBTP(ibtp *pb.IBTP, interchain *pb.Interchain) {
m := make(map[string]uint64)
2020-07-24 20:19:32 +08:00
if pb.IBTP_INTERCHAIN == ibtp.Type ||
pb.IBTP_ASSET_EXCHANGE_INIT == ibtp.Type ||
pb.IBTP_ASSET_EXCHANGE_REDEEM == ibtp.Type ||
pb.IBTP_ASSET_EXCHANGE_REFUND == ibtp.Type {
2021-01-21 15:38:13 +08:00
if interchain.InterchainCounter == nil {
x.Logger().Info("interchain counter is nil, make one")
interchain.InterchainCounter = make(map[string]uint64)
}
interchain.InterchainCounter[ibtp.To]++
2020-09-29 15:13:05 +08:00
x.setInterchain(ibtp.From, interchain)
x.AddObject(x.indexMapKey(ibtp.ID()), x.GetTxHash())
m[ibtp.To] = x.GetTxIndex()
} else {
interchain.ReceiptCounter[ibtp.To] = ibtp.Index
2020-09-29 15:13:05 +08:00
x.setInterchain(ibtp.From, interchain)
2020-03-29 21:32:01 +08:00
m[ibtp.From] = x.GetTxIndex()
2020-09-29 15:13:05 +08:00
ic, _ := x.getInterchain(ibtp.To)
ic.SourceReceiptCounter[ibtp.From] = ibtp.Index
2020-09-29 15:13:05 +08:00
x.setInterchain(ibtp.To, ic)
2020-11-18 10:36:12 +08:00
x.SetObject(x.indexReceiptMapKey(ibtp.ID()), x.GetTxHash())
}
x.PostInterchainEvent(m)
}
2020-03-29 21:32:01 +08:00
func (x *InterchainManager) beginMultiTargetsTransaction(ibtps *pb.IBTPs) *boltvm.Response {
args := make([]*pb.Arg, 0)
globalId := fmt.Sprintf("%s-%s", x.Caller(), x.GetTxHash())
args = append(args, pb.String(globalId))
2020-09-22 11:35:55 +08:00
for _, ibtp := range ibtps.Ibtps {
if ibtp.Type != pb.IBTP_INTERCHAIN {
return boltvm.Error("ibtp type != IBTP_INTERCHAIN")
}
childTxId := fmt.Sprintf("%s-%s-%d", ibtp.From, ibtp.To, ibtp.Index)
args = append(args, pb.String(childTxId))
2020-03-29 21:32:01 +08:00
}
return x.CrossInvoke(constant.TransactionMgrContractAddr.String(), "BeginMultiTXs", args...)
}
func (x *InterchainManager) beginTransaction(ibtp *pb.IBTP) *boltvm.Response {
txId := fmt.Sprintf("%s-%s-%d", ibtp.From, ibtp.To, ibtp.Index)
return x.CrossInvoke(constant.TransactionMgrContractAddr.String(), "Begin", pb.String(txId))
}
func (x *InterchainManager) reportTransaction(ibtp *pb.IBTP) *boltvm.Response {
txId := fmt.Sprintf("%s-%s-%d", ibtp.From, ibtp.To, ibtp.Index)
result := int32(0)
if ibtp.Type == pb.IBTP_RECEIPT_FAILURE {
result = 1
}
return x.CrossInvoke(constant.TransactionMgrContractAddr.String(), "Report", pb.String(txId), pb.Int32(result))
2020-03-29 21:32:01 +08:00
}
2020-07-24 20:19:32 +08:00
func (x *InterchainManager) handleAssetExchange(ibtp *pb.IBTP) *boltvm.Response {
var method string
switch ibtp.Type {
case pb.IBTP_ASSET_EXCHANGE_INIT:
method = "Init"
case pb.IBTP_ASSET_EXCHANGE_REDEEM:
method = "Redeem"
case pb.IBTP_ASSET_EXCHANGE_REFUND:
method = "Refund"
default:
return boltvm.Error("unsupported asset exchange type")
}
return x.CrossInvoke(constant.AssetExchangeContractAddr.String(), method, pb.String(ibtp.From),
pb.String(ibtp.To), pb.Bytes(ibtp.Extra))
}
func (x *InterchainManager) GetIBTPByID(id string) *boltvm.Response {
arr := strings.Split(id, "-")
if len(arr) != 3 {
return boltvm.Error("wrong ibtp id")
}
caller := x.Caller()
2020-04-16 00:37:53 +08:00
if caller != arr[0] && caller != arr[1] {
return boltvm.Error("The caller does not have access to this ibtp")
}
2020-03-29 21:32:01 +08:00
var hash types.Hash
exist := x.GetObject(x.indexMapKey(id), &hash)
if !exist {
return boltvm.Error("this id is not existed")
}
return boltvm.Success(hash.Bytes())
}
func (x *InterchainManager) handleUnionIBTP(ibtp *pb.IBTP) *boltvm.Response {
srcRelayChainID := strings.Split(ibtp.From, "-")[0]
ok := x.Has(AppchainKey(srcRelayChainID))
if !ok {
return boltvm.Error("this relay chain does not exist")
}
if ibtp.To == "" {
return boltvm.Error("empty destination chain id")
}
if ok := x.Has(AppchainKey(ibtp.To)); !ok {
return boltvm.Error(fmt.Sprintf("target appchain does not exist: %s", ibtp.To))
}
app := &appchainMgr.Appchain{}
res := x.CrossInvoke(constant.AppchainMgrContractAddr.String(), "GetAppchain", pb.String(srcRelayChainID))
if err := json.Unmarshal(res.Result, app); err != nil {
return boltvm.Error(err.Error())
}
2021-01-21 15:38:13 +08:00
interchain, ok := x.getInterchain(ibtp.From)
if !ok {
2020-09-29 15:13:05 +08:00
interchain = &pb.Interchain{
ID: ibtp.From,
}
x.setInterchain(ibtp.From, interchain)
}
if err := x.checkUnionIBTP(app, ibtp, interchain); err != nil {
return boltvm.Error(err.Error())
}
x.ProcessIBTP(ibtp, interchain)
return boltvm.Success(nil)
}
2020-09-29 15:13:05 +08:00
func (x *InterchainManager) checkUnionIBTP(app *appchainMgr.Appchain, ibtp *pb.IBTP, interchain *pb.Interchain) error {
if pb.IBTP_INTERCHAIN == ibtp.Type ||
pb.IBTP_ASSET_EXCHANGE_INIT == ibtp.Type ||
pb.IBTP_ASSET_EXCHANGE_REDEEM == ibtp.Type ||
pb.IBTP_ASSET_EXCHANGE_REFUND == ibtp.Type {
idx := interchain.InterchainCounter[ibtp.To]
if idx+1 != ibtp.Index {
return fmt.Errorf(fmt.Sprintf("wrong index, required %d, but %d", idx+1, ibtp.Index))
}
} else {
idx := interchain.ReceiptCounter[ibtp.To]
if idx+1 != ibtp.Index {
if interchain.SourceReceiptCounter[ibtp.To]+1 != ibtp.Index {
return fmt.Errorf("wrong receipt index, required %d, but %d", idx+1, ibtp.Index)
}
}
}
2021-02-24 15:35:08 +08:00
_, err := verifyMultiSign(app, ibtp, ibtp.Proof)
return err
2021-01-21 15:38:13 +08:00
}
// verifyMultiSign .
func verifyMultiSign(app *appchainMgr.Appchain, ibtp *pb.IBTP, proof []byte) (bool, error) {
if "" == app.Validators {
2021-01-21 15:38:13 +08:00
return false, fmt.Errorf("empty validators in relay chain:%s", app.ID)
}
var validators BxhValidators
if err := json.Unmarshal([]byte(app.Validators), &validators); err != nil {
2021-01-21 15:38:13 +08:00
return false, err
}
m := make(map[string]struct{}, 0)
for _, validator := range validators.Addresses {
m[validator] = struct{}{}
}
var signs pb.SignResponse
if err := signs.Unmarshal(ibtp.Proof); err != nil {
2021-01-21 15:38:13 +08:00
return false, err
}
2021-01-21 15:38:13 +08:00
threshold := (len(validators.Addresses) - 1) / 3 // TODO be dynamic
counter := 0
ibtpHash := ibtp.Hash()
hash := sha256.Sum256([]byte(ibtpHash.String()))
for v, sign := range signs.Sign {
if _, ok := m[v]; !ok {
2021-01-21 15:38:13 +08:00
return false, fmt.Errorf("wrong validator: %s", v)
}
delete(m, v)
addr := types.NewAddressByStr(v)
ok, _ := asym.Verify(crypto.Secp256k1, sign, hash[:], *addr)
if ok {
counter++
}
if counter > threshold {
2021-01-21 15:38:13 +08:00
return true, nil
}
}
2021-01-21 15:38:13 +08:00
return false, fmt.Errorf("multi signs verify fail, counter: %d", counter)
}
func AppchainKey(id string) string {
return appchainMgr.PREFIX + id
2020-03-29 21:32:01 +08:00
}
func (x *InterchainManager) indexMapKey(id string) string {
2020-03-29 21:32:01 +08:00
return fmt.Sprintf("index-tx-%s", id)
}
2020-11-18 10:36:12 +08:00
func (x *InterchainManager) indexReceiptMapKey(id string) string {
return fmt.Sprintf("index-receipt-tx-%s", id)
}