bitxhub/internal/coreapi/broker.go

320 lines
8.2 KiB
Go

package coreapi
import (
"crypto/sha256"
"encoding/json"
"fmt"
"strconv"
"sync"
"github.com/meshplus/bitxhub-kit/types"
"github.com/meshplus/bitxhub-model/constant"
"github.com/meshplus/bitxhub-model/pb"
"github.com/meshplus/bitxhub/internal/coreapi/api"
"github.com/meshplus/bitxhub/internal/executor/contracts"
"github.com/meshplus/bitxhub/internal/model"
"github.com/sirupsen/logrus"
)
type BrokerAPI CoreAPI
var _ api.BrokerAPI = (*BrokerAPI)(nil)
func (b *BrokerAPI) HandleTransaction(tx *pb.Transaction) error {
if tx.TransactionHash == nil {
if tx.Hash() != nil {
tx.TransactionHash = tx.Hash()
} else {
return fmt.Errorf("transaction hash is nil")
}
}
b.logger.WithFields(logrus.Fields{
"hash": tx.TransactionHash.String(),
}).Debugf("Receive tx")
go func() {
if err := b.bxh.Order.Prepare(tx); err != nil {
b.logger.Error(err)
}
}()
return nil
}
func (b *BrokerAPI) HandleView(tx *pb.Transaction) (*pb.Receipt, error) {
if tx.TransactionHash == nil {
if tx.Hash() != nil {
tx.TransactionHash = tx.Hash()
} else {
return nil, fmt.Errorf("transaction hash is nil")
}
}
b.logger.WithFields(logrus.Fields{
"hash": tx.TransactionHash.String(),
}).Debugf("Receive view")
receipts := b.bxh.ViewExecutor.ApplyReadonlyTransactions([]*pb.Transaction{tx})
return receipts[0], nil
}
func (b *BrokerAPI) GetTransaction(hash *types.Hash) (*pb.Transaction, error) {
return b.bxh.Ledger.GetTransaction(hash)
}
func (b *BrokerAPI) GetTransactionMeta(hash *types.Hash) (*pb.TransactionMeta, error) {
return b.bxh.Ledger.GetTransactionMeta(hash)
}
func (b *BrokerAPI) GetReceipt(hash *types.Hash) (*pb.Receipt, error) {
return b.bxh.Ledger.GetReceipt(hash)
}
func (b *BrokerAPI) AddPier(pid string, isUnion bool) (chan *pb.InterchainTxWrappers, error) {
return b.bxh.Router.AddPier(pid, isUnion)
}
func (b *BrokerAPI) GetBlockHeader(begin, end uint64, ch chan<- *pb.BlockHeader) error {
return b.bxh.Router.GetBlockHeader(begin, end, ch)
}
func (b *BrokerAPI) GetInterchainTxWrappers(pid string, begin, end uint64, ch chan<- *pb.InterchainTxWrappers) error {
return b.bxh.Router.GetInterchainTxWrappers(pid, begin, end, ch)
}
func (b *BrokerAPI) GetBlock(mode string, value string) (*pb.Block, error) {
switch mode {
case "HEIGHT":
height, err := strconv.ParseUint(value, 10, 64)
if err != nil {
return nil, fmt.Errorf("wrong block number: %s", value)
}
return b.bxh.Ledger.GetBlock(height)
case "HASH":
hash := types.NewHashByStr(value)
if hash == nil {
return nil, fmt.Errorf("invalid format of block hash for querying block")
}
return b.bxh.Ledger.GetBlockByHash(hash)
default:
return nil, fmt.Errorf("wrong args about getting block: %s", mode)
}
}
func (b *BrokerAPI) GetBlocks(start uint64, end uint64) ([]*pb.Block, error) {
meta := b.bxh.Ledger.GetChainMeta()
var blocks []*pb.Block
if meta.Height < end {
end = meta.Height
}
for i := start; i > 0 && i <= end; i++ {
b, err := b.GetBlock("HEIGHT", strconv.Itoa(int(i)))
if err != nil {
continue
}
blocks = append(blocks, b)
}
return blocks, nil
}
func (b *BrokerAPI) GetBlockHeaders(start uint64, end uint64) ([]*pb.BlockHeader, error) {
meta := b.bxh.Ledger.GetChainMeta()
var blockHeaders []*pb.BlockHeader
if meta.Height < end {
end = meta.Height
}
for i := start; i > 0 && i <= end; i++ {
b, err := b.GetBlock("HEIGHT", strconv.Itoa(int(i)))
if err != nil {
continue
}
blockHeaders = append(blockHeaders, b.BlockHeader)
}
return blockHeaders, nil
}
func (b *BrokerAPI) RemovePier(pid string, isUnion bool) {
b.bxh.Router.RemovePier(pid, isUnion)
}
func (b *BrokerAPI) OrderReady() error {
return b.bxh.Order.Ready()
}
func (b *BrokerAPI) FetchSignsFromOtherPeers(id string, typ pb.GetMultiSignsRequest_Type) map[string][]byte {
var (
result = make(map[string][]byte)
wg = sync.WaitGroup{}
lock = sync.Mutex{}
)
wg.Add(len(b.bxh.PeerMgr.OtherPeers()))
for pid := range b.bxh.PeerMgr.OtherPeers() {
go func(pid uint64, result map[string][]byte, wg *sync.WaitGroup, lock *sync.Mutex) {
var (
address string
sign []byte
err error
)
switch typ {
case pb.GetMultiSignsRequest_ASSET_EXCHANGE:
address, sign, err = b.requestAssetExchangeSignFromPeer(pid, id)
case pb.GetMultiSignsRequest_IBTP:
address, sign, err = b.requestIBTPSignPeer(pid, id)
case pb.GetMultiSignsRequest_BLOCK_HEADER:
address, sign, err = b.requestBlockHeaderSignFromPeer(pid, id)
}
if err != nil {
b.logger.WithFields(logrus.Fields{
"pid": pid,
"err": err.Error(),
}).Warnf("Get asset exchange sign with error")
} else {
lock.Lock()
result[address] = sign
lock.Unlock()
}
wg.Done()
}(pid, result, &wg, &lock)
}
wg.Wait()
return result
}
func (b *BrokerAPI) requestAssetExchangeSignFromPeer(peerId uint64, assetExchangeId string) (string, []byte, error) {
req := pb.Message{
Type: pb.Message_FETCH_ASSET_EXCHANEG_SIGN,
Data: []byte(assetExchangeId),
}
resp, err := b.bxh.PeerMgr.Send(peerId, &req)
if err != nil {
return "", nil, err
}
if resp == nil || resp.Type != pb.Message_FETCH_ASSET_EXCHANGE_SIGN_ACK {
return "", nil, fmt.Errorf("invalid asset exchange sign resp")
}
data := model.MerkleWrapperSign{}
if err := data.Unmarshal(resp.Data); err != nil {
return "", nil, err
}
return data.Address, data.Signature, nil
}
func (b *BrokerAPI) requestIBTPSignPeer(pid uint64, ibtpHash string) (string, []byte, error) {
req := pb.Message{
Type: pb.Message_FETCH_IBTP_SIGN,
Data: []byte(ibtpHash),
}
resp, err := b.bxh.PeerMgr.Send(pid, &req)
if err != nil {
return "", nil, err
}
if resp == nil || resp.Type != pb.Message_FETCH_IBTP_SIGN_ACK {
return "", nil, fmt.Errorf("invalid fetch ibtp sign resp")
}
data := model.MerkleWrapperSign{}
if err := data.Unmarshal(resp.Data); err != nil {
return "", nil, err
}
return data.Address, data.Signature, nil
}
func (b *BrokerAPI) requestBlockHeaderSignFromPeer(pid uint64, height string) (string, []byte, error) {
req := pb.Message{
Type: pb.Message_FETCH_BLOCK_SIGN,
Data: []byte(height),
}
resp, err := b.bxh.PeerMgr.Send(pid, &req)
if err != nil {
return "", nil, err
}
if resp == nil || resp.Type != pb.Message_FETCH_BLOCK_SIGN_ACK {
return "", nil, fmt.Errorf("invalid fetch block header sign resp")
}
data := model.MerkleWrapperSign{}
if err := data.Unmarshal(resp.Data); err != nil {
return "", nil, err
}
return data.Address, data.Signature, nil
}
func (b *BrokerAPI) GetSign(content string, typ pb.GetMultiSignsRequest_Type) (string, []byte, error) {
switch typ {
case pb.GetMultiSignsRequest_ASSET_EXCHANGE:
id := content
ok, record := b.bxh.Ledger.GetState(constant.AssetExchangeContractAddr.Address(), []byte(contracts.AssetExchangeKey(id)))
if !ok {
return "", nil, fmt.Errorf("cannot find asset exchange record with id %s", id)
}
aer := contracts.AssetExchangeRecord{}
if err := json.Unmarshal(record, &aer); err != nil {
return "", nil, err
}
addr, sign, err := b.getSign(fmt.Sprintf("%s-%d", id, aer.Status))
if err != nil {
return "", nil, fmt.Errorf("fetch asset exchange sign: %w", err)
}
return addr, sign, nil
case pb.GetMultiSignsRequest_IBTP:
addr, sign, err := b.getSign(content)
if err != nil {
return "", nil, fmt.Errorf("get ibtp sign: %w", err)
}
return addr, sign, nil
case pb.GetMultiSignsRequest_BLOCK_HEADER:
height, err := strconv.ParseUint(content, 10, 64)
if err != nil {
return "", nil, fmt.Errorf("get block header sign: %w", err)
}
sign, err := b.bxh.Ledger.GetBlockSign(height)
if err != nil {
return "", nil, fmt.Errorf("get block sign: %w", err)
}
return b.bxh.GetPrivKey().Address, sign, nil
default:
return "", nil, fmt.Errorf("unsupported get sign type")
}
}
func (b *BrokerAPI) getSign(content string) (string, []byte, error) {
hash := sha256.Sum256([]byte(content))
key := b.bxh.GetPrivKey()
sign, err := key.PrivKey.Sign(hash[:])
if err != nil {
return "", nil, fmt.Errorf("bitxhub sign: %w", err)
}
return key.Address, sign, nil
}
func (b BrokerAPI) GetPendingNonceByAccount(account string) uint64 {
return b.bxh.Order.GetPendingNonceByAccount(account)
}
func (b BrokerAPI) DelVPNode(delID uint64) error {
return b.bxh.Order.DelNode(delID)
}