+ * 该引擎设置有同步处理器、异步处理器,根据需求选择。
+ */
+@Service
+public class ReceiverService {
+
+ /**
+ * 同步receiver处理器
+ */
+ @Resource
+ private SyncReceiveHandler syncReceiveHandler;
+
+ /**
+ * 异步receiver处理器
+ */
+ @Resource
+ private AsyncReceiveHandler asyncReceiveHandler;
+
+ @Resource
+ private BlockchainIdleDCache blockchainIdleDCache;
+
+ /**
+ * 链外请求receive接口
+ *
+ * @param authMsg
+ * @param authMsg
+ * @return
+ */
+ public void receiveOffChainAMRequest(String domainName, String authMsg, String udagProof, String ledgerInfo) {
+
+ AuthMsgWrapper authMsgWrapper = new AuthMsgWrapper();
+ authMsgWrapper.setDomain(domainName);
+ authMsgWrapper.setLedgerInfo(ledgerInfo);
+ authMsgWrapper.setAuthMessage(
+ AuthMessageFactory.createAuthMessage(
+ Base64.getDecoder().decode(authMsg)
+ )
+ );
+
+ try {
+ syncReceiveHandler.receiveOffChainAMRequest(authMsgWrapper, udagProof);
+ } catch (AntChainBridgeRelayerException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new AntChainBridgeRelayerException(
+ RelayerErrorCodeEnum.SERVICE_MULTI_ANCHOR_PROCESS_REMOTE_AM_PROCESS_FAILED,
+ e,
+ "failed to process remote am request from blockchain {}",
+ domainName
+ );
+ }
+ }
+
+ /**
+ * 接收am消息的接口
+ *
+ * @param authMsgWrappers
+ * @return
+ */
+ public void receiveAM(List authMsgWrappers) {
+ asyncReceiveHandler.receiveAuthMessages(authMsgWrappers);
+ if (!authMsgWrappers.isEmpty()) {
+ blockchainIdleDCache.setLastAMReceiveTime(
+ authMsgWrappers.get(0).getProduct(),
+ authMsgWrappers.get(0).getBlockchainId()
+ );
+ }
+ }
+
+ /**
+ * receive am client eceipt接口
+ *
+ * @param commitResults
+ * @return
+ */
+ public boolean receiveAMClientReceipts(List commitResults) {
+
+ if (!commitResults.isEmpty()) {
+ blockchainIdleDCache.setLastAMResponseTime(
+ commitResults.get(0).getReceiveProduct(),
+ commitResults.get(0).getReceiveBlockchainId()
+ );
+ }
+ return asyncReceiveHandler.receiveAMClientReceipt(commitResults);
+ }
+}
diff --git a/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/service/receiver/handler/AsyncReceiveHandler.java b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/service/receiver/handler/AsyncReceiveHandler.java
new file mode 100644
index 0000000..b21329e
--- /dev/null
+++ b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/service/receiver/handler/AsyncReceiveHandler.java
@@ -0,0 +1,83 @@
+package com.alipay.antchain.bridge.relayer.core.service.receiver.handler;
+
+import java.util.List;
+import javax.annotation.Resource;
+
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import com.alipay.antchain.bridge.relayer.commons.constant.AuthMsgProcessStateEnum;
+import com.alipay.antchain.bridge.relayer.commons.model.AuthMsgWrapper;
+import com.alipay.antchain.bridge.relayer.commons.model.SDPMsgCommitResult;
+import com.alipay.antchain.bridge.relayer.dal.repository.ICrossChainMessageRepository;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.support.TransactionTemplate;
+
+@Component
+@Slf4j
+public class AsyncReceiveHandler {
+
+ @Resource
+ private ICrossChainMessageRepository crossChainMessageRepository;
+
+ @Resource
+ private TransactionTemplate transactionTemplate;
+
+ public void receiveAuthMessages(List authMsgWrappers) {
+
+ for (AuthMsgWrapper am : authMsgWrappers) {
+ log.info("receive a AuthenticMessage from {} with upper protocol {}", am.getDomain(), am.getProtocolType());
+ am.setProcessState(AuthMsgProcessStateEnum.PENDING);
+ }
+
+ int rowsNum = crossChainMessageRepository.putAuthMessages(authMsgWrappers);
+ if (authMsgWrappers.size() != rowsNum) {
+ throw new RuntimeException(
+ StrUtil.format(
+ "failed to save auth messages: rows number {} inserted not equal to list size {}",
+ rowsNum, authMsgWrappers.size()
+ )
+ );
+ }
+ log.info("[asyncReceiver] put am_pending AuthenticMessage to pool success");
+ }
+
+ public boolean receiveAMClientReceipt(List commitResults) {
+
+ // 处理空值
+ if (commitResults.isEmpty()) {
+ return true;
+ }
+
+ List results = transactionTemplate.execute(
+ status -> crossChainMessageRepository.updateSDPMessageResults(commitResults)
+ );
+
+ if (ObjectUtil.isEmpty(results)) {
+ return false;
+ }
+
+ for (int i = 0; i < results.size(); i++) {
+ if (0 == results.get(i)) {
+ // sql变更行数为0,表示tx hash在DB不存在,可能有多种原因导致,可以跳过,打个warn
+ log.warn(
+ "sdp msg to blockchain {} processed failed: ( tx: {}, fail_reason: {} )",
+ commitResults.get(i).getReceiveBlockchainId(),
+ commitResults.get(i).getTxHash(),
+ commitResults.get(i).getFailReason()
+ );
+ } else {
+ log.info(
+ "sdp msg to blockchain {}-{} processed success: ( tx: {}, committed: {}, confirm: {}, )",
+ commitResults.get(i).getReceiveProduct(),
+ commitResults.get(i).getReceiveBlockchainId(),
+ commitResults.get(i).getTxHash(),
+ commitResults.get(i).isCommitSuccess(),
+ commitResults.get(i).isConfirmed()
+ );
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/service/receiver/handler/SyncReceiveHandler.java b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/service/receiver/handler/SyncReceiveHandler.java
new file mode 100644
index 0000000..c370ed2
--- /dev/null
+++ b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/service/receiver/handler/SyncReceiveHandler.java
@@ -0,0 +1,65 @@
+package com.alipay.antchain.bridge.relayer.core.service.receiver.handler;
+
+import javax.annotation.Resource;
+
+import cn.hutool.core.util.StrUtil;
+import com.alipay.antchain.bridge.relayer.commons.exception.AntChainBridgeRelayerException;
+import com.alipay.antchain.bridge.relayer.commons.exception.RelayerErrorCodeEnum;
+import com.alipay.antchain.bridge.relayer.commons.model.AuthMsgWrapper;
+import com.alipay.antchain.bridge.relayer.core.service.process.ProcessService;
+import com.alipay.antchain.bridge.relayer.dal.repository.ICrossChainMessageRepository;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.TransactionStatus;
+import org.springframework.transaction.support.TransactionCallbackWithoutResult;
+import org.springframework.transaction.support.TransactionTemplate;
+
+@Component
+@Slf4j
+public class SyncReceiveHandler {
+
+ @Resource
+ private ProcessService processService;
+
+ @Resource
+ private TransactionTemplate transactionTemplate;
+
+ @Resource
+ private ICrossChainMessageRepository crossChainMessageRepository;
+
+ public void receiveOffChainAMRequest(AuthMsgWrapper authMsg, String ptcProof) {
+
+ log.info("receive a off-chain am request from blockchain {}", authMsg.getDomain());
+
+ authMsg.setNetworkAM(true);
+
+ try {
+ transactionTemplate.execute(
+ new TransactionCallbackWithoutResult() {
+ @Override
+ protected void doInTransactionWithoutResult(TransactionStatus status) {
+ authMsg.setAuthMsgId(
+ crossChainMessageRepository.putAuthMessageWithIdReturned(authMsg)
+ );
+ if (
+ !processService.getAuthenticMessageProcess().doProcess(authMsg)
+ ) {
+ throw new RuntimeException(StrUtil.format("process off-chain am request from blockchain {} failed", authMsg.getDomain()));
+ }
+ log.info("process off-chain am request from blockchain {} success", authMsg.getDomain());
+ }
+ }
+ );
+ } catch (AntChainBridgeRelayerException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new AntChainBridgeRelayerException(
+ RelayerErrorCodeEnum.SERVICE_MULTI_ANCHOR_PROCESS_REMOTE_AM_PROCESS_FAILED,
+ e,
+ "failed to process remote am request from blockchain {}",
+ authMsg.getDomain()
+ );
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/blockchain/AbstractBlock.java b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/blockchain/AbstractBlock.java
new file mode 100644
index 0000000..d26d9cb
--- /dev/null
+++ b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/blockchain/AbstractBlock.java
@@ -0,0 +1,27 @@
+package com.alipay.antchain.bridge.relayer.core.types.blockchain;
+
+import com.alibaba.fastjson.annotation.JSONField;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+public abstract class AbstractBlock {
+
+ @JSONField
+ private String product;
+
+ @JSONField
+ private String blockchainId;
+
+ @JSONField
+ private long height;
+
+ public abstract byte[] encode();
+
+ public abstract void decode(byte[] data);
+}
diff --git a/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/blockchain/AbstractBlockHeader.java b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/blockchain/AbstractBlockHeader.java
new file mode 100644
index 0000000..c1f80ed
--- /dev/null
+++ b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/blockchain/AbstractBlockHeader.java
@@ -0,0 +1,23 @@
+package com.alipay.antchain.bridge.relayer.core.types.blockchain;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+public class AbstractBlockHeader {
+
+ private String product;
+
+ private String blockchainId;
+
+ private long height;
+
+ private byte[] blockHeader;
+
+ private byte[] ext;
+}
diff --git a/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/blockchain/AbstractBlockchainClient.java b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/blockchain/AbstractBlockchainClient.java
new file mode 100644
index 0000000..4b39c9d
--- /dev/null
+++ b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/blockchain/AbstractBlockchainClient.java
@@ -0,0 +1,67 @@
+package com.alipay.antchain.bridge.relayer.core.types.blockchain;
+
+import com.alipay.antchain.bridge.commons.bbc.AbstractBBCContext;
+import com.alipay.antchain.bridge.commons.core.base.CrossChainMessageReceipt;
+import com.alipay.antchain.bridge.relayer.commons.model.BlockchainMeta;
+import com.alipay.antchain.bridge.relayer.core.manager.bbc.IAMClientContract;
+import com.alipay.antchain.bridge.relayer.core.manager.bbc.ISDPMsgClientContract;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@AllArgsConstructor
+public abstract class AbstractBlockchainClient {
+
+ private BlockchainMeta blockchainMeta;
+
+ private String domain;
+
+ public abstract boolean start();
+
+ public abstract boolean shutdown();
+
+ public abstract boolean ifHasDeployedAMClientContract();
+
+ public abstract long getLastBlockHeight();
+
+ public abstract AbstractBlock getEssentialHeaderByHeight(long height);
+
+ public abstract IAMClientContract getAMClientContract();
+
+ public abstract ISDPMsgClientContract getSDPMsgClientContract();
+
+ public abstract CrossChainMessageReceipt queryCommittedTxReceipt(String txhash);
+
+ public abstract AbstractBBCContext queryBBCContext();
+
+ @Getter
+ @Setter
+ public static class SendResponseResult {
+ private String txId;
+ private boolean confirmed;
+ private boolean success;
+ private String errorCode;
+ private String errorMessage;
+
+ public SendResponseResult(String txId, boolean success, String errorCode, String errorMessage) {
+ this.txId = txId;
+ this.success = success;
+ this.errorCode = errorCode;
+ this.errorMessage = errorMessage;
+ }
+
+ public SendResponseResult(String txId, boolean confirmed, boolean success, String errorCode, String errorMessage) {
+ this.txId = txId;
+ this.confirmed = confirmed;
+ this.success = success;
+ this.errorCode = errorCode;
+ this.errorMessage = errorMessage;
+ }
+
+ public SendResponseResult(String txId, boolean success, int errorCode, String errorMessage) {
+ this(txId, success, String.valueOf(errorCode), errorMessage);
+ }
+ }
+}
\ No newline at end of file
diff --git a/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/blockchain/AbstractTransactionWithProof.java b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/blockchain/AbstractTransactionWithProof.java
new file mode 100644
index 0000000..dbd5186
--- /dev/null
+++ b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/blockchain/AbstractTransactionWithProof.java
@@ -0,0 +1,25 @@
+package com.alipay.antchain.bridge.relayer.core.types.blockchain;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+public class AbstractTransactionWithProof {
+
+ private String product;
+
+ private String blockchainId;
+
+ private long blockHeight;
+
+ private String txHash;
+
+ private byte[] tx;
+
+ private byte[] ext;
+}
diff --git a/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/blockchain/BlockchainAnchorProcess.java b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/blockchain/BlockchainAnchorProcess.java
new file mode 100644
index 0000000..db0179d
--- /dev/null
+++ b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/blockchain/BlockchainAnchorProcess.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2023 Ant Group
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alipay.antchain.bridge.relayer.core.types.blockchain;
+
+import java.util.Date;
+import java.util.Map;
+
+import cn.hutool.core.date.DatePattern;
+import cn.hutool.core.date.DateUtil;
+import com.alipay.antchain.bridge.relayer.commons.model.AnchorProcessHeights;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class BlockchainAnchorProcess {
+
+ private static final String ANCHOR_PROCESS_LATEST_HEIGHT = "polling";
+
+ private static final String ANCHOR_PROCESS_SPV_HEIGHT = "notify_CONTRACT_SYSTEM";
+
+ private static final String ANCHOR_PROCESS_DATA_REQ_HEIGHT = "notify_CONTRACT_ORACLE";
+
+ private static final String ANCHOR_PROCESS_MSG_HEIGHT = "notify_CONTRACT_AM_CLIENT";
+
+ public static BlockchainAnchorProcess convertFrom(AnchorProcessHeights heights) {
+ BlockchainAnchorProcess process = new BlockchainAnchorProcess();
+
+ for (Map.Entry entry : heights.getProcessHeights().entrySet()) {
+ TaskBlockHeight taskBlockHeight = new TaskBlockHeight(
+ entry.getValue(),
+ DateUtil.format(
+ new Date(heights.getModifiedTimeMap().get(entry.getKey())),
+ DatePattern.NORM_DATETIME_PATTERN
+ )
+ );
+ switch (entry.getKey()) {
+ case ANCHOR_PROCESS_LATEST_HEIGHT:
+ process.setLatestBlockHeight(taskBlockHeight);
+ break;
+ case ANCHOR_PROCESS_SPV_HEIGHT:
+ process.setSpvTaskBlockHeight(taskBlockHeight);
+ break;
+ case ANCHOR_PROCESS_DATA_REQ_HEIGHT:
+ process.setDataReqTaskBlockHeight(taskBlockHeight);
+ break;
+ case ANCHOR_PROCESS_MSG_HEIGHT:
+ process.setCrosschainTaskBlockHeight(taskBlockHeight);
+ break;
+ }
+ }
+ return process;
+ }
+
+ @Getter
+ @Setter
+ @AllArgsConstructor
+ public static class TaskBlockHeight {
+ private long height;
+ private String gmtModified;
+ }
+
+ private TaskBlockHeight latestBlockHeight;
+
+ private TaskBlockHeight spvTaskBlockHeight;
+
+ private TaskBlockHeight dataReqTaskBlockHeight;
+
+ private TaskBlockHeight crosschainTaskBlockHeight;
+}
diff --git a/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/blockchain/BlockchainClientPool.java b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/blockchain/BlockchainClientPool.java
new file mode 100644
index 0000000..5306b9f
--- /dev/null
+++ b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/blockchain/BlockchainClientPool.java
@@ -0,0 +1,126 @@
+package com.alipay.antchain.bridge.relayer.core.types.blockchain;
+
+import java.util.List;
+import java.util.Map;
+import javax.annotation.Resource;
+
+import cn.hutool.core.collection.ListUtil;
+import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import com.alipay.antchain.bridge.relayer.commons.exception.AntChainBridgeRelayerException;
+import com.alipay.antchain.bridge.relayer.commons.exception.RelayerErrorCodeEnum;
+import com.alipay.antchain.bridge.relayer.commons.model.BlockchainMeta;
+import com.alipay.antchain.bridge.relayer.core.manager.bbc.IBBCPluginManager;
+import com.alipay.antchain.bridge.relayer.core.types.pluginserver.IBBCServiceClient;
+import com.alipay.antchain.bridge.relayer.dal.repository.IBlockchainRepository;
+import lombok.Getter;
+import lombok.Synchronized;
+import org.springframework.stereotype.Component;
+
+@Component
+@Getter
+public class BlockchainClientPool {
+
+ @Resource
+ private IBlockchainRepository blockchainRepository;
+
+ @Resource
+ private IBBCPluginManager bbcPluginManager;
+
+ private final Map clients = MapUtil.newConcurrentHashMap();
+
+ public AbstractBlockchainClient createClient(String blockchainProduct, String blockchainId) {
+
+ String key = blockchainProduct + "_" + blockchainId;
+
+ if (clients.containsKey(key)) {
+ return clients.get(key);
+ }
+
+ BlockchainMeta blockchainMeta = blockchainRepository.getBlockchainMeta(blockchainProduct, blockchainId);
+ if (ObjectUtil.isNull(blockchainMeta)) {
+ throw new AntChainBridgeRelayerException(
+ RelayerErrorCodeEnum.CORE_BLOCKCHAIN_ERROR,
+ "none meta found for blockchain {}-{}",
+ blockchainProduct, blockchainId
+ );
+ }
+
+ return createClient(blockchainMeta);
+ }
+
+ @Synchronized
+ public AbstractBlockchainClient createClient(BlockchainMeta chainMeta) {
+ String clientKey = chainMeta.getMetaKey();
+ if (clients.containsKey(clientKey)) {
+ AbstractBlockchainClient client = clients.get(clientKey);
+ client.setBlockchainMeta(chainMeta);
+ return client;
+ }
+
+ String domain = blockchainRepository.getBlockchainDomain(
+ chainMeta.getProduct(),
+ chainMeta.getBlockchainId()
+ );
+ if (StrUtil.isEmpty(domain)) {
+ throw new AntChainBridgeRelayerException(
+ RelayerErrorCodeEnum.CORE_BLOCKCHAIN_CLIENT_INIT_ERROR,
+ "none domain found for {}",
+ clientKey
+ );
+ }
+
+ IBBCServiceClient bbcClient = bbcPluginManager.createBBCClient(
+ chainMeta.getProperties().getPluginServerId(),
+ chainMeta.getProduct(),
+ domain
+ );
+ HeteroBlockchainClient heteroBcClient = new HeteroBlockchainClient(
+ bbcClient,
+ chainMeta
+ );
+ if (!heteroBcClient.start()) {
+ throw new AntChainBridgeRelayerException(
+ RelayerErrorCodeEnum.CORE_BLOCKCHAIN_CLIENT_INIT_ERROR,
+ "Blockchain client start failed for product {} and domain {}",
+ heteroBcClient.getBlockchainMeta().getProduct(),
+ heteroBcClient.getDomain()
+ );
+ }
+ clients.put(clientKey, heteroBcClient);
+
+ return heteroBcClient;
+ }
+
+ public AbstractBlockchainClient getClient(String product, String blockchainId) {
+ String key = BlockchainMeta.createMetaKey(product, blockchainId);
+ if (clients.containsKey(key)) {
+ return clients.get(key);
+ }
+ return null;
+ }
+
+ public synchronized List getAllClient() {
+ return ListUtil.toList(clients.keySet());
+ }
+
+ @Synchronized
+ public boolean hasClient(String blockchainProduct, String blockchainId) {
+ String key = blockchainProduct + "_" + blockchainId;
+ return clients.containsKey(key);
+ }
+
+ @Synchronized
+ public void shutdownClient(String blockchainProduct, String blockchainId) {
+
+ String key = blockchainProduct + "_" + blockchainId;
+
+ if (!clients.containsKey(key)) {
+ return;
+ }
+
+ clients.get(key).shutdown();
+ clients.remove(key);
+ }
+}
diff --git a/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/blockchain/HeteroBlockchainClient.java b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/blockchain/HeteroBlockchainClient.java
new file mode 100644
index 0000000..bfd308c
--- /dev/null
+++ b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/blockchain/HeteroBlockchainClient.java
@@ -0,0 +1,169 @@
+package com.alipay.antchain.bridge.relayer.core.types.blockchain;
+
+import java.util.List;
+
+import cn.hutool.core.util.ObjectUtil;
+import com.alipay.antchain.bridge.commons.bbc.AbstractBBCContext;
+import com.alipay.antchain.bridge.commons.bbc.DefaultBBCContext;
+import com.alipay.antchain.bridge.commons.bbc.syscontract.ContractStatusEnum;
+import com.alipay.antchain.bridge.commons.core.base.CrossChainMessage;
+import com.alipay.antchain.bridge.commons.core.base.CrossChainMessageReceipt;
+import com.alipay.antchain.bridge.relayer.commons.model.BlockchainMeta;
+import com.alipay.antchain.bridge.relayer.core.manager.bbc.AMClientContractHeteroBlockchainImpl;
+import com.alipay.antchain.bridge.relayer.core.manager.bbc.IAMClientContract;
+import com.alipay.antchain.bridge.relayer.core.manager.bbc.ISDPMsgClientContract;
+import com.alipay.antchain.bridge.relayer.core.manager.bbc.SDPMsgClientHeteroBlockchainImpl;
+import com.alipay.antchain.bridge.relayer.core.types.pluginserver.IBBCServiceClient;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class HeteroBlockchainClient extends AbstractBlockchainClient {
+
+ private final IBBCServiceClient bbcClient;
+
+ private final IAMClientContract amClientContract;
+
+ private final ISDPMsgClientContract sdpMsgClient;
+
+ public HeteroBlockchainClient(IBBCServiceClient bbcClient, BlockchainMeta blockchainMeta) {
+ super(blockchainMeta, bbcClient.getDomain());
+ this.bbcClient = bbcClient;
+ this.amClientContract = new AMClientContractHeteroBlockchainImpl(bbcClient);
+ this.sdpMsgClient = new SDPMsgClientHeteroBlockchainImpl(bbcClient);
+ }
+
+ @Override
+ public boolean start() {
+
+ DefaultBBCContext bbcContext = getBlockchainMeta().getProperties().getBbcContext();
+ if (ObjectUtil.isNull(bbcContext)) {
+ log.error(
+ "bbcContext is null for ( plugin_server: {}, product: {}, domain: {} )",
+ getBlockchainMeta().getProperties().getPluginServerId(),
+ bbcClient.getProduct(),
+ bbcClient.getDomain()
+ );
+ return false;
+ }
+ try {
+ this.bbcClient.startup(bbcContext);
+ } catch (Exception e) {
+ log.error(
+ "failed to start heterogeneous blockchain client ( plugin_server: {}, product: {}, domain: {} )",
+ getBlockchainMeta().getProperties().getPluginServerId(),
+ bbcClient.getProduct(),
+ bbcClient.getDomain(),
+ e
+ );
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean shutdown() {
+ try {
+ this.bbcClient.shutdown();
+ } catch (Exception e) {
+ log.error(
+ "failed to shutdown heterogeneous blockchain client ( plugin_server: {}, product: {}, domain: {} )",
+ getBlockchainMeta().getProperties().getPluginServerId(),
+ this.bbcClient.getProduct(),
+ this.bbcClient.getDomain(),
+ e
+ );
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean ifHasDeployedAMClientContract() {
+ if (checkIfHasAMDeployedLocally()) {
+ return true;
+ }
+ return checkIfHasAMDeployedRemotely();
+ }
+
+ private boolean checkContractsStatus(AbstractBBCContext bbcContext) {
+ if (ObjectUtil.isNull(bbcContext.getAuthMessageContract()) || ObjectUtil.isNull(bbcContext.getSdpContract())) {
+ log.info(
+ "local bbc context for ( product: {}, domain: {} ) is incomplete",
+ getBlockchainMeta().getProduct(), getDomain()
+ );
+ return false;
+ }
+
+ boolean ifAMPrepared = ContractStatusEnum.CONTRACT_READY == bbcContext.getAuthMessageContract().getStatus();
+ if (!ifAMPrepared) {
+ log.info(
+ "AM contract of heterogeneous blockchain client ( product: {} , domain: {} ) is {} but ready",
+ getBlockchainMeta().getProduct(), getDomain(), bbcContext.getAuthMessageContract().getStatus()
+ );
+ return false;
+ }
+ boolean ifSDPPrepared = ContractStatusEnum.CONTRACT_READY == bbcContext.getSdpContract().getStatus();
+ if (!ifSDPPrepared) {
+ log.info(
+ "SDP contract of heterogeneous blockchain client ( product: {} , domain: {} ) is not ready",
+ getBlockchainMeta().getProduct(), getDomain()
+ );
+ return false;
+ }
+ return true;
+ }
+
+ private boolean checkIfHasAMDeployedLocally() {
+ return checkContractsStatus(
+ getBlockchainMeta().getProperties().getBbcContext()
+ );
+ }
+
+ private boolean checkIfHasAMDeployedRemotely() {
+ AbstractBBCContext bbcContext = this.bbcClient.getContext();
+ if (ObjectUtil.isNull(bbcContext)) {
+ log.error("get null bbc context for {}-{}", getBlockchainMeta().getProduct(), getDomain());
+ return false;
+ }
+ getBlockchainMeta().getProperties().setBbcContext((DefaultBBCContext) bbcContext);
+ return checkContractsStatus(bbcContext);
+ }
+
+ @Override
+ public long getLastBlockHeight() {
+ return this.bbcClient.queryLatestHeight();
+ }
+
+ @Override
+ public AbstractBlock getEssentialHeaderByHeight(long height) {
+ List messages = this.bbcClient.readCrossChainMessagesByHeight(height);
+ return new HeterogeneousBlock(
+ getBlockchainMeta().getProduct(),
+ getDomain(),
+ getBlockchainMeta().getBlockchainId(),
+ height,
+ messages
+ );
+ }
+
+ @Override
+ public IAMClientContract getAMClientContract() {
+ return this.amClientContract;
+ }
+
+ @Override
+ public ISDPMsgClientContract getSDPMsgClientContract() {
+ return this.sdpMsgClient;
+ }
+
+ @Override
+ public CrossChainMessageReceipt queryCommittedTxReceipt(String txhash) {
+ return this.bbcClient.readCrossChainMessageReceipt(txhash);
+ }
+
+ public AbstractBBCContext queryBBCContext() {
+ return this.bbcClient.getContext();
+ }
+}
diff --git a/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/blockchain/HeterogeneousBlock.java b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/blockchain/HeterogeneousBlock.java
new file mode 100644
index 0000000..be572e9
--- /dev/null
+++ b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/blockchain/HeterogeneousBlock.java
@@ -0,0 +1,103 @@
+package com.alipay.antchain.bridge.relayer.core.types.blockchain;
+
+import java.util.List;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.util.HexUtil;
+import cn.hutool.core.util.StrUtil;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.annotation.JSONField;
+import com.alipay.antchain.bridge.commons.core.base.CrossChainMessage;
+import com.alipay.antchain.bridge.relayer.commons.model.AuthMsgWrapper;
+import com.alipay.antchain.bridge.relayer.commons.model.UniformCrosschainPacketContext;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+@Getter
+@Slf4j
+@NoArgsConstructor
+public class HeterogeneousBlock extends AbstractBlock {
+
+ @JSONField(serialize = false, deserialize = false)
+ private List uniformCrosschainPacketContexts;
+
+ @JSONField
+ private List crossChainMessages;
+
+ @JSONField
+ private String domain;
+
+ public HeterogeneousBlock(String product, String domain, String blockchainId, Long height, List crossChainMessages) {
+ super(
+ product,
+ blockchainId,
+ height
+ );
+ this.crossChainMessages = crossChainMessages;
+ this.domain = domain;
+ }
+
+ @Override
+ public byte[] encode() {
+ return JSON.toJSONBytes(this);
+ }
+
+ @Override
+ public void decode(byte[] data) {
+ BeanUtil.copyProperties(JSON.parseObject(data, HeterogeneousBlock.class), this);
+ }
+
+ public List toAuthMsgWrappers() {
+ return this.crossChainMessages.stream()
+ .filter(crossChainMessage -> crossChainMessage.getType() == CrossChainMessage.CrossChainMessageType.AUTH_MSG)
+ .map(
+ crossChainMessage -> {
+
+ AuthMsgWrapper wrapper = AuthMsgWrapper.buildFrom(
+ getProduct(),
+ getBlockchainId(),
+ domain,
+ crossChainMessage
+ );
+
+ wrapper.addLedgerInfo(
+ AuthMsgWrapper.AM_BLOCK_HEIGHT,
+ Long.toString(getHeight())
+ );
+ wrapper.addLedgerInfo(
+ AuthMsgWrapper.AM_BLOCK_HASH,
+ HexUtil.encodeHexStr(crossChainMessage.getProvableData().getBlockHash())
+ );
+ wrapper.addLedgerInfo(
+ AuthMsgWrapper.AM_BLOCK_TIMESTAMP,
+ String.valueOf(crossChainMessage.getProvableData().getTimestamp())
+ );
+ wrapper.addLedgerInfo(
+ AuthMsgWrapper.AM_CAPTURE_TIMESTAMP,
+ String.valueOf(System.currentTimeMillis())
+ );
+ wrapper.addLedgerInfo(
+ AuthMsgWrapper.AM_SENDER_GAS_USED,
+ "0"
+ );
+ wrapper.addLedgerInfo(
+ AuthMsgWrapper.AM_HINTS,
+ StrUtil.EMPTY
+ );
+ wrapper.addLedgerInfo(
+ AuthMsgWrapper.AM_TX_ID,
+ HexUtil.encodeHexStr(crossChainMessage.getProvableData().getTxHash())
+ );
+ wrapper.addLedgerInfo(
+ AuthMsgWrapper.AM_HETEROGENEOUS_RANDOM_UUID,
+ UUID.randomUUID().toString()
+ );
+
+ return wrapper;
+ }
+ ).collect(Collectors.toList());
+ }
+}
diff --git a/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/BaseRelayerClient.java b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/BaseRelayerClient.java
new file mode 100644
index 0000000..d8b88ab
--- /dev/null
+++ b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/BaseRelayerClient.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2023 Ant Group
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alipay.antchain.bridge.relayer.core.types.network;
+
+import cn.hutool.core.codec.Base64;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import com.alipay.antchain.bridge.relayer.commons.model.RelayerBlockchainContent;
+import com.alipay.antchain.bridge.relayer.commons.model.RelayerBlockchainInfo;
+import com.alipay.antchain.bridge.relayer.commons.model.RelayerNodeInfo;
+import com.alipay.antchain.bridge.relayer.core.manager.network.IRelayerCredentialManager;
+import com.alipay.antchain.bridge.relayer.core.types.network.request.*;
+import com.alipay.antchain.bridge.relayer.core.types.network.response.HandshakeRespPayload;
+import com.alipay.antchain.bridge.relayer.core.types.network.response.RelayerResponse;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Endpoint Client基类
+ */
+@Getter
+@Setter
+@Slf4j
+public abstract class BaseRelayerClient implements RelayerClient {
+
+ private RelayerNodeInfo remoteNodeInfo;
+
+ private IRelayerCredentialManager relayerCredentialManager;
+
+ private String defaultNetworkId;
+
+ public BaseRelayerClient(
+ RelayerNodeInfo remoteNodeInfo,
+ IRelayerCredentialManager relayerCredentialManager,
+ String defaultNetworkId
+ ) {
+ this.remoteNodeInfo = remoteNodeInfo;
+ this.relayerCredentialManager = relayerCredentialManager;
+ this.defaultNetworkId = defaultNetworkId;
+ }
+
+ /**
+ * 发送请求
+ *
+ * @param relayerRequest
+ * @return
+ */
+ public abstract RelayerResponse sendRequest(RelayerRequest relayerRequest);
+
+ public abstract void startup();
+
+ public abstract boolean shutdown();
+
+ @Override
+ public RelayerNodeInfo getRelayerNodeInfo() {
+ RelayerRequest request = new GetRelayerNodeInfoRelayerRequest();
+ relayerCredentialManager.signRelayerRequest(request);
+
+ RelayerResponse response = validateRelayerResponse(sendRequest(request));
+ if (ObjectUtil.isNull(response) || !response.isSuccess()) {
+ throw new RuntimeException(
+ StrUtil.format(
+ "failed to getRelayerNodeInfo: {} - {}",
+ response.getResponseCode(), response.getResponseMessage()
+ )
+ );
+ }
+
+ return RelayerNodeInfo.decode(Base64.decode(response.getResponsePayload()));
+ }
+
+ @Override
+ public RelayerBlockchainInfo getRelayerBlockchainInfo(String domainToQuery) {
+ RelayerRequest request = new GetRelayerBlockchainInfoRelayerRequest(domainToQuery);
+ relayerCredentialManager.signRelayerRequest(request);
+
+ RelayerResponse response = validateRelayerResponse(sendRequest(request));
+ if (ObjectUtil.isNull(response) || !response.isSuccess()) {
+ throw new RuntimeException(
+ StrUtil.format(
+ "getRelayerBlockchainInfo for domain {} failed: {} - {}",
+ domainToQuery,
+ response.getResponseCode(),
+ response.getResponseMessage()
+ )
+ );
+ }
+
+ return RelayerBlockchainInfo.decode(response.getResponsePayload());
+ }
+
+ @Override
+ public RelayerBlockchainContent getRelayerBlockchainContent() {
+ RelayerRequest request = new GetRelayerBlockchainContentRelayerRequest();
+ relayerCredentialManager.signRelayerRequest(request);
+
+ RelayerResponse response = validateRelayerResponse(sendRequest(request));
+ if (ObjectUtil.isNull(response) || !response.isSuccess()) {
+ throw new RuntimeException(
+ StrUtil.format(
+ "getRelayerBlockchainContent from relayer {} failed: {} - {}",
+ remoteNodeInfo.getNodeId(),
+ response.getResponseCode(),
+ response.getResponseMessage()
+ )
+ );
+ }
+
+ return RelayerBlockchainContent.decodeFromJson(response.getResponsePayload());
+ }
+
+ @Override
+ public void amRequest(String domainName, String authMsg, String udagProof, String ledgerInfo) {
+ RelayerRequest request = new AMRelayerRequest(
+ udagProof,
+ authMsg,
+ domainName,
+ ledgerInfo
+ );
+ relayerCredentialManager.signRelayerRequest(request);
+
+ RelayerResponse response = validateRelayerResponse(sendRequest(request));
+ if (ObjectUtil.isNull(response)) {
+ throw new RuntimeException(
+ StrUtil.format(
+ "am request from domain {} failed: empty response found",
+ domainName
+ )
+ );
+ } else if (!response.isSuccess()) {
+ throw new RuntimeException(
+ StrUtil.format("am request from domain {} failed: (code: {}, msg: {})",
+ domainName, response.getResponseCode(), response.getResponseMessage()
+ )
+ );
+ }
+ }
+
+ @Override
+ public RelayerNodeInfo handshake(RelayerNodeInfo senderNodeInfo, String networkId) {
+ RelayerRequest request = new HandshakeRelayerRequest(
+ senderNodeInfo,
+ defaultNetworkId
+ );
+
+ RelayerResponse response = validateRelayerResponse(sendRequest(request));
+ if (ObjectUtil.isNull(response)) {
+ throw new RuntimeException(
+ StrUtil.format(
+ "handshake with relayer {} failed: response is empty",
+ senderNodeInfo.getNodeId()
+ )
+ );
+ } else if (!response.isSuccess()) {
+ throw new RuntimeException(
+ String.format("handshake with relayer {} failed: (code: %d, msg: %s)",
+ response.getResponseCode(), response.getResponseMessage()
+ )
+ );
+ }
+
+ HandshakeRespPayload handshakeRespPayload = HandshakeRespPayload.decodeFromJson(response.getResponsePayload());
+ RelayerNodeInfo remoteNodeInfo = RelayerNodeInfo.decode(
+ Base64.decode(handshakeRespPayload.getRemoteNodeInfo())
+ );
+
+ remoteNodeInfo.getProperties().getProperties().put(
+ "network_id",
+ ObjectUtil.defaultIfEmpty(
+ handshakeRespPayload.getRemoteNetworkId(),
+ defaultNetworkId
+ )
+ );
+
+ log.debug("handshake with relayer {} success with response: {}", this.remoteNodeInfo.getNodeId(), response.getResponsePayload());
+
+ return remoteNodeInfo;
+ }
+
+ private RelayerResponse validateRelayerResponse(RelayerResponse relayerResponse) {
+ if (relayerCredentialManager.validateRelayerResponse(relayerResponse)) {
+ throw new RuntimeException(
+ StrUtil.format(
+ "response from relayer {} sig is invalid",
+ relayerResponse.calcRelayerNodeId()
+ )
+ );
+ }
+ return relayerResponse;
+ }
+}
diff --git a/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/IRelayerClientPool.java b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/IRelayerClientPool.java
new file mode 100644
index 0000000..95e7a78
--- /dev/null
+++ b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/IRelayerClientPool.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2023 Ant Group
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alipay.antchain.bridge.relayer.core.types.network;
+
+import com.alipay.antchain.bridge.relayer.commons.model.RelayerNodeInfo;
+
+public interface IRelayerClientPool {
+
+ RelayerClient getRelayerClient(RelayerNodeInfo remoteRelayerNodeInfo);
+}
diff --git a/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/RelayerClient.java b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/RelayerClient.java
new file mode 100644
index 0000000..ae87210
--- /dev/null
+++ b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/RelayerClient.java
@@ -0,0 +1,50 @@
+package com.alipay.antchain.bridge.relayer.core.types.network;
+
+import com.alipay.antchain.bridge.relayer.commons.model.RelayerBlockchainContent;
+import com.alipay.antchain.bridge.relayer.commons.model.RelayerBlockchainInfo;
+import com.alipay.antchain.bridge.relayer.commons.model.RelayerNodeInfo;
+
+/**
+ * 实现节点endpoint客通讯客户端、服务端
+ *
+ * @author honglin.qhl
+ */
+public interface RelayerClient {
+
+ /**
+ * 获取Relayer基本信息
+ *
+ * @return
+ */
+ RelayerNodeInfo getRelayerNodeInfo();
+
+ /**
+ * 获取支持指定domain的区块链信息,包括oracle等信任根
+ *
+ * @param supportedDomain
+ * @return
+ */
+ RelayerBlockchainInfo getRelayerBlockchainInfo(String supportedDomain);
+
+ /**
+ *
+ * @return
+ */
+ RelayerBlockchainContent getRelayerBlockchainContent();
+
+ /**
+ * 发送AM请求
+ *
+ * @param authMsg
+ * @param udagProof
+ * @return
+ */
+ void amRequest(String domainName, String authMsg, String udagProof, String ledgerInfo);
+
+ /**
+ *
+ * @param nodeInfo
+ * @return
+ */
+ RelayerNodeInfo handshake(RelayerNodeInfo nodeInfo, String networkId);
+}
\ No newline at end of file
diff --git a/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/RelayerClientPool.java b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/RelayerClientPool.java
new file mode 100644
index 0000000..89e4d4f
--- /dev/null
+++ b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/RelayerClientPool.java
@@ -0,0 +1,62 @@
+package com.alipay.antchain.bridge.relayer.core.types.network;
+
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import javax.annotation.Resource;
+
+import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.StrUtil;
+import com.alipay.antchain.bridge.relayer.commons.model.RelayerNodeInfo;
+import com.alipay.antchain.bridge.relayer.core.manager.network.IRelayerCredentialManager;
+import com.alipay.antchain.bridge.relayer.core.manager.network.IRelayerNetworkManager;
+import com.alipay.antchain.bridge.relayer.core.types.network.ws.WsSslFactory;
+import com.alipay.antchain.bridge.relayer.core.types.network.ws.client.WSRelayerClient;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+@Component
+public class RelayerClientPool implements IRelayerClientPool {
+
+ @Resource
+ private IRelayerCredentialManager relayerCredentialManager;
+
+ @Resource(name = "wsRelayerClientThreadsPool")
+ private ExecutorService wsRelayerClientThreadsPool;
+
+ @Value("#{systemConfigRepository.defaultNetworkId}")
+ private String defaultNetworkId;
+
+ @Resource
+ private WsSslFactory wsSslFactory;
+
+ private final Map clientMap = MapUtil.newConcurrentHashMap();
+
+ public RelayerClient getRelayerClient(RelayerNodeInfo remoteRelayerNodeInfo) {
+
+ try {
+ if (!clientMap.containsKey(remoteRelayerNodeInfo.getNodeId())) {
+ WSRelayerClient client = new WSRelayerClient(
+ remoteRelayerNodeInfo,
+ relayerCredentialManager,
+ defaultNetworkId,
+ wsRelayerClientThreadsPool,
+ wsSslFactory.getSslContext().getSocketFactory()
+ );
+ client.startup();
+ clientMap.put(remoteRelayerNodeInfo.getNodeId(), client);
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(
+ StrUtil.format("failed to create relayer client for {}: ", remoteRelayerNodeInfo.getNodeId()),
+ e
+ );
+ }
+
+
+ return clientMap.get(remoteRelayerNodeInfo.getNodeId());
+ }
+
+ public void addRelayerClient(String nodeId, RelayerClient client) {
+ clientMap.put(nodeId, client);
+ }
+}
diff --git a/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/exception/RejectRequestException.java b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/exception/RejectRequestException.java
new file mode 100644
index 0000000..102ece2
--- /dev/null
+++ b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/exception/RejectRequestException.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2023 Ant Group
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alipay.antchain.bridge.relayer.core.types.network.exception;
+
+import cn.hutool.core.util.StrUtil;
+import com.alipay.antchain.bridge.relayer.commons.exception.AntChainBridgeRelayerException;
+import com.alipay.antchain.bridge.relayer.commons.exception.RelayerErrorCodeEnum;
+import lombok.Getter;
+
+@Getter
+public class RejectRequestException extends AntChainBridgeRelayerException {
+
+ private final int errorCode;
+
+ private final String errorMsg;
+
+ public RejectRequestException(int errorCode, String errorMsg) {
+ super(
+ RelayerErrorCodeEnum.SERVER_REQUEST_FROM_RELAYER_REJECT,
+ "request reject: " + errorMsg
+ );
+ this.errorCode = errorCode;
+ this.errorMsg = errorMsg;
+ }
+
+ public RejectRequestException(String message, int errorCode, String errorMsg) {
+ super(
+ RelayerErrorCodeEnum.SERVER_REQUEST_FROM_RELAYER_REJECT,
+ StrUtil.format("request reject: {} - {}", errorMsg, message)
+ );
+ this.errorCode = errorCode;
+ this.errorMsg = errorMsg;
+ }
+}
diff --git a/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/request/AMRelayerRequest.java b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/request/AMRelayerRequest.java
new file mode 100644
index 0000000..fd24750
--- /dev/null
+++ b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/request/AMRelayerRequest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2023 Ant Group
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alipay.antchain.bridge.relayer.core.types.network.request;
+
+import cn.hutool.core.bean.BeanUtil;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.annotation.JSONField;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class AMRelayerRequest extends RelayerRequest {
+
+ public static AMRelayerRequest createFrom(RelayerRequest relayerRequest) {
+ AMRelayerRequest request = JSON.parseObject(relayerRequest.getRequestPayload(), AMRelayerRequest.class);
+ BeanUtil.copyProperties(relayerRequest, request);
+ return request;
+ }
+
+ @JSONField
+ private String udagProof;
+
+ @JSONField
+ private String authMsg;
+
+ @JSONField
+ private String domainName;
+
+ @JSONField
+ private String ledgerInfo;
+
+ public AMRelayerRequest(
+ String udagProof,
+ String authMsg,
+ String domainName,
+ String ledgerInfo
+ ) {
+ super(
+ RelayerRequestType.AM_REQUEST
+ );
+ this.udagProof = udagProof;
+ this.authMsg = authMsg;
+ this.domainName = domainName;
+ this.ledgerInfo = ledgerInfo;
+
+ setRequestPayload(
+ JSON.toJSONBytes(this)
+ );
+ }
+}
diff --git a/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/request/GetRelayerBlockchainContentRelayerRequest.java b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/request/GetRelayerBlockchainContentRelayerRequest.java
new file mode 100644
index 0000000..e71c4df
--- /dev/null
+++ b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/request/GetRelayerBlockchainContentRelayerRequest.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2023 Ant Group
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alipay.antchain.bridge.relayer.core.types.network.request;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.util.StrUtil;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class GetRelayerBlockchainContentRelayerRequest extends RelayerRequest {
+
+ public static GetRelayerBlockchainContentRelayerRequest createFrom(RelayerRequest relayerRequest) {
+ return BeanUtil.copyProperties(relayerRequest, GetRelayerBlockchainContentRelayerRequest.class);
+ }
+
+ public GetRelayerBlockchainContentRelayerRequest() {
+ super(
+ RelayerRequestType.GET_RELAYER_BLOCKCHAIN_CONTENT
+ );
+ setRequestPayload(StrUtil.EMPTY.getBytes());
+ }
+}
diff --git a/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/request/GetRelayerBlockchainInfoRelayerRequest.java b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/request/GetRelayerBlockchainInfoRelayerRequest.java
new file mode 100644
index 0000000..69e660c
--- /dev/null
+++ b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/request/GetRelayerBlockchainInfoRelayerRequest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2023 Ant Group
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alipay.antchain.bridge.relayer.core.types.network.request;
+
+import cn.hutool.core.bean.BeanUtil;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@NoArgsConstructor
+public class GetRelayerBlockchainInfoRelayerRequest extends RelayerRequest {
+
+ private String domainToQuery;
+
+ public static GetRelayerBlockchainInfoRelayerRequest createFrom(RelayerRequest relayerRequest) {
+ GetRelayerBlockchainInfoRelayerRequest request = BeanUtil.copyProperties(
+ relayerRequest,
+ GetRelayerBlockchainInfoRelayerRequest.class
+ );
+ request.setDomainToQuery(new String(relayerRequest.getRequestPayload()));
+ return request;
+ }
+
+ public GetRelayerBlockchainInfoRelayerRequest(
+ String domainToQuery
+ ) {
+ super(
+ RelayerRequestType.GET_RELAYER_BLOCKCHAIN_INFO
+ );
+ this.setRequestPayload(domainToQuery.getBytes());
+ }
+}
diff --git a/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/request/GetRelayerNodeInfoRelayerRequest.java b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/request/GetRelayerNodeInfoRelayerRequest.java
new file mode 100644
index 0000000..fbfb0f2
--- /dev/null
+++ b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/request/GetRelayerNodeInfoRelayerRequest.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2023 Ant Group
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alipay.antchain.bridge.relayer.core.types.network.request;
+
+import cn.hutool.core.util.StrUtil;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class GetRelayerNodeInfoRelayerRequest extends RelayerRequest {
+
+ public GetRelayerNodeInfoRelayerRequest() {
+ super(
+ RelayerRequestType.GET_RELAYER_NODE_INFO
+ );
+ setRequestPayload(StrUtil.EMPTY.getBytes());
+ }
+}
diff --git a/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/request/HandshakeRelayerRequest.java b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/request/HandshakeRelayerRequest.java
new file mode 100644
index 0000000..05ce37d
--- /dev/null
+++ b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/request/HandshakeRelayerRequest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2023 Ant Group
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alipay.antchain.bridge.relayer.core.types.network.request;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.map.MapUtil;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.alipay.antchain.bridge.relayer.commons.model.RelayerNodeInfo;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import lombok.experimental.FieldNameConstants;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@FieldNameConstants
+public class HandshakeRelayerRequest extends RelayerRequest {
+
+ public static HandshakeRelayerRequest createFrom(RelayerRequest relayerRequest) {
+ HandshakeRelayerRequest request = BeanUtil.copyProperties(
+ relayerRequest,
+ HandshakeRelayerRequest.class
+ );
+
+ JSONObject jsonObject = JSON.parseObject(new String(relayerRequest.getRequestPayload()));
+ request.setNetworkId(jsonObject.getString(Fields.networkId));
+ request.setSenderNodeInfo(RelayerNodeInfo.decode(jsonObject.getBytes(Fields.senderNodeInfo)));
+ return request;
+ }
+
+ public static byte[] createHandshakePayload(
+ String networkId,
+ RelayerNodeInfo relayerNodeInfo
+ ) {
+ return JSON.toJSONBytes(
+ MapUtil.builder()
+ .put(Fields.networkId, networkId)
+ .put(Fields.senderNodeInfo, relayerNodeInfo.encodeWithProperties())
+ .build()
+ );
+ }
+
+ private String networkId;
+
+ private RelayerNodeInfo senderNodeInfo;
+
+ public HandshakeRelayerRequest(
+ RelayerNodeInfo senderNodeInfo,
+ String networkId
+ ) {
+ super(
+ RelayerRequestType.HANDSHAKE
+ );
+
+ this.networkId = networkId;
+ this.senderNodeInfo = senderNodeInfo;
+ setRequestPayload(
+ createHandshakePayload(networkId, senderNodeInfo)
+ );
+ }
+}
diff --git a/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/request/RelayerRequest.java b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/request/RelayerRequest.java
new file mode 100644
index 0000000..9c9480b
--- /dev/null
+++ b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/request/RelayerRequest.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2023 Ant Group
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alipay.antchain.bridge.relayer.core.types.network.request;
+
+import java.security.PublicKey;
+import java.security.Signature;
+
+import com.alipay.antchain.bridge.commons.bcdns.AbstractCrossChainCertificate;
+import com.alipay.antchain.bridge.commons.bcdns.RelayerCredentialSubject;
+import com.alipay.antchain.bridge.commons.bcdns.utils.ObjectIdentityUtil;
+import com.alipay.antchain.bridge.commons.utils.codec.tlv.TLVTypeEnum;
+import com.alipay.antchain.bridge.commons.utils.codec.tlv.TLVUtils;
+import com.alipay.antchain.bridge.commons.utils.codec.tlv.annotation.TLVField;
+import com.alipay.antchain.bridge.relayer.commons.model.RelayerNodeInfo;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Relayer请求
+ */
+@Getter
+@Setter
+@Slf4j
+@NoArgsConstructor
+public class RelayerRequest {
+
+ public static final short TLV_TYPE_RELAYER_REQUEST_TYPE = 0;
+
+ public static final short TLV_TYPE_RELAYER_REQUEST_NODE_ID = 1;
+
+ public static final short TLV_TYPE_RELAYER_REQUEST_RELAYER_CERT = 2;
+
+ public static final short TLV_TYPE_RELAYER_REQUEST_PAYLOAD = 3;
+
+ public static final short TLV_TYPE_RELAYER_REQUEST_SIG_ALGO = 4;
+
+ public static final short TLV_TYPE_RELAYER_REQUEST_SIGNATURE = 5;
+
+ public static RelayerRequest decode(byte[] rawData, Class extends RelayerRequest> requestClass) {
+ return TLVUtils.decode(rawData, requestClass);
+ }
+
+ public static RelayerRequest decode(byte[] rawData) {
+ return TLVUtils.decode(rawData, RelayerRequest.class);
+ }
+
+ public RelayerRequest(
+ RelayerRequestType relayerRequestType
+ ) {
+ this.requestType = relayerRequestType;
+ }
+
+ @TLVField(tag = TLV_TYPE_RELAYER_REQUEST_TYPE, type = TLVTypeEnum.UINT8)
+ private RelayerRequestType requestType;
+
+ @TLVField(tag = TLV_TYPE_RELAYER_REQUEST_NODE_ID, type = TLVTypeEnum.STRING, order = TLV_TYPE_RELAYER_REQUEST_NODE_ID)
+ private String nodeId;
+
+ @TLVField(tag = TLV_TYPE_RELAYER_REQUEST_RELAYER_CERT, type = TLVTypeEnum.BYTES, order = TLV_TYPE_RELAYER_REQUEST_RELAYER_CERT)
+ private AbstractCrossChainCertificate senderRelayerCertificate;
+
+ @TLVField(tag = TLV_TYPE_RELAYER_REQUEST_PAYLOAD, type = TLVTypeEnum.BYTES, order = TLV_TYPE_RELAYER_REQUEST_PAYLOAD)
+ private byte[] requestPayload;
+
+ @TLVField(tag = TLV_TYPE_RELAYER_REQUEST_SIG_ALGO, type = TLVTypeEnum.STRING, order = TLV_TYPE_RELAYER_REQUEST_SIG_ALGO)
+ private String sigAlgo;
+
+ @TLVField(tag = TLV_TYPE_RELAYER_REQUEST_SIGNATURE, type = TLVTypeEnum.BYTES, order = TLV_TYPE_RELAYER_REQUEST_SIGNATURE)
+ private byte[] signature;
+
+ public byte[] rawEncode() {
+ return TLVUtils.encode(this, TLV_TYPE_RELAYER_REQUEST_SIGNATURE);
+ }
+
+ public byte[] encode() {
+ return TLVUtils.encode(this);
+ }
+
+ /**
+ * 验签
+ *
+ * @return
+ */
+ public boolean verify() {
+
+ try {
+ RelayerCredentialSubject relayerCredentialSubject = RelayerCredentialSubject.decode(
+ senderRelayerCertificate.getCredentialSubject()
+ );
+ PublicKey publicKey = ObjectIdentityUtil.getPublicKeyFromSubject(
+ relayerCredentialSubject.getApplicant(),
+ relayerCredentialSubject.getSubjectInfo()
+ );
+
+ Signature verifier = Signature.getInstance(sigAlgo);
+ verifier.initVerify(publicKey);
+ verifier.update(rawEncode());
+
+ return verifier.verify(signature);
+
+ } catch (Exception e) {
+ throw new RuntimeException("failed to verify request sig", e);
+ }
+ }
+
+ public void setRequestTypeCode(String requestType) {
+ this.requestType = RelayerRequestType.parseFromValue(requestType);
+ }
+
+ public String calcRelayerNodeId() {
+ return RelayerNodeInfo.calculateNodeId(senderRelayerCertificate);
+ }
+}
diff --git a/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/request/RelayerRequestType.java b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/request/RelayerRequestType.java
new file mode 100644
index 0000000..451ba56
--- /dev/null
+++ b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/request/RelayerRequestType.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2023 Ant Group
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alipay.antchain.bridge.relayer.core.types.network.request;
+
+import cn.hutool.core.util.StrUtil;
+import com.alipay.antchain.bridge.relayer.commons.exception.AntChainBridgeRelayerException;
+import com.alipay.antchain.bridge.relayer.commons.exception.RelayerErrorCodeEnum;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * Relayer请求类型
+ */
+@Getter
+@AllArgsConstructor
+public enum RelayerRequestType {
+
+ // relayer之间的请求
+ GET_RELAYER_NODE_INFO("getRelayerNodeInfo"),
+
+ GET_RELAYER_BLOCKCHAIN_INFO("getBlockchainInfo"),
+
+ AM_REQUEST("amRequest"),
+
+ /**
+ * 建立可信连接,进行握手
+ */
+ HANDSHAKE("handshake"),
+
+ /**
+ * 获取指定的域名信息
+ */
+ GET_RELAYER_FOR_DOMAIN("getRelayerForDomain"),
+
+ /**
+ * 注册域名
+ */
+ REGISTER_DOMAIN("registerDomain"),
+
+ /**
+ * 更新指定域名
+ */
+ UPDATE_DOMAIN("updateDomain"),
+
+ /**
+ * 让relayer配置中心删除对应域名
+ */
+ DELETE_DOMAIN("deleteDomain"),
+
+ GET_RELAYER_BLOCKCHAIN_CONTENT("getRelayerBlockChainContent");
+
+ private final String code;
+
+ public static RelayerRequestType parseFromValue(String value) {
+ if (StrUtil.equals(value, GET_RELAYER_NODE_INFO.code)) {
+ return GET_RELAYER_NODE_INFO;
+ } else if (StrUtil.equals(value, GET_RELAYER_BLOCKCHAIN_INFO.code)) {
+ return GET_RELAYER_BLOCKCHAIN_INFO;
+ } else if (StrUtil.equals(value, AM_REQUEST.code)) {
+ return AM_REQUEST;
+ } else if (StrUtil.equals(value, HANDSHAKE.code)) {
+ return HANDSHAKE;
+ } else if (StrUtil.equals(value, GET_RELAYER_FOR_DOMAIN.code)) {
+ return GET_RELAYER_FOR_DOMAIN;
+ } else if (StrUtil.equals(value, REGISTER_DOMAIN.code)) {
+ return REGISTER_DOMAIN;
+ } else if (StrUtil.equals(value, UPDATE_DOMAIN.code)) {
+ return UPDATE_DOMAIN;
+ } else if (StrUtil.equals(value, DELETE_DOMAIN.code)) {
+ return DELETE_DOMAIN;
+ } else if (StrUtil.equals(value, GET_RELAYER_BLOCKCHAIN_CONTENT.code)) {
+ return GET_RELAYER_BLOCKCHAIN_CONTENT;
+ }
+ throw new AntChainBridgeRelayerException(
+ RelayerErrorCodeEnum.UNKNOWN_INTERNAL_ERROR,
+ "Invalid value for relayer request type: " + value
+ );
+ }
+
+ public static RelayerRequestType valueOf(Byte value) {
+ switch (value) {
+ case 0:
+ return GET_RELAYER_NODE_INFO;
+ case 1:
+ return GET_RELAYER_BLOCKCHAIN_INFO;
+ case 2:
+ return AM_REQUEST;
+ case 3:
+ return HANDSHAKE;
+ case 4:
+ return GET_RELAYER_FOR_DOMAIN;
+ case 5:
+ return REGISTER_DOMAIN;
+ case 6:
+ return UPDATE_DOMAIN;
+ case 7:
+ return DELETE_DOMAIN;
+ case 8:
+ return GET_RELAYER_BLOCKCHAIN_CONTENT;
+ default:
+ return null;
+ }
+ }
+}
diff --git a/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/response/HandshakeRespPayload.java b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/response/HandshakeRespPayload.java
new file mode 100644
index 0000000..3593a9b
--- /dev/null
+++ b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/response/HandshakeRespPayload.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2023 Ant Group
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alipay.antchain.bridge.relayer.core.types.network.response;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.annotation.JSONField;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor
+public class HandshakeRespPayload implements IResponsePayload {
+ public static HandshakeRespPayload decodeFromJson(String json) {
+ return JSON.parseObject(json, HandshakeRespPayload.class);
+ }
+
+ @JSONField(name = "network_id")
+ private String remoteNetworkId;
+
+ @JSONField(name = "remote_node_info")
+ private String remoteNodeInfo;
+
+ @Override
+ public String encode() {
+ return JSON.toJSONString(this);
+ }
+}
diff --git a/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/response/IResponsePayload.java b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/response/IResponsePayload.java
new file mode 100644
index 0000000..94bab61
--- /dev/null
+++ b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/response/IResponsePayload.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2023 Ant Group
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alipay.antchain.bridge.relayer.core.types.network.response;
+
+public interface IResponsePayload {
+
+ String encode();
+}
diff --git a/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/response/RelayerResponse.java b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/response/RelayerResponse.java
new file mode 100644
index 0000000..a551cef
--- /dev/null
+++ b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/response/RelayerResponse.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2023 Ant Group
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alipay.antchain.bridge.relayer.core.types.network.response;
+
+import java.security.PublicKey;
+import java.security.Signature;
+
+import cn.hutool.core.util.ObjectUtil;
+import com.alipay.antchain.bridge.commons.bcdns.AbstractCrossChainCertificate;
+import com.alipay.antchain.bridge.commons.bcdns.RelayerCredentialSubject;
+import com.alipay.antchain.bridge.commons.bcdns.utils.ObjectIdentityUtil;
+import com.alipay.antchain.bridge.commons.utils.codec.tlv.TLVTypeEnum;
+import com.alipay.antchain.bridge.commons.utils.codec.tlv.TLVUtils;
+import com.alipay.antchain.bridge.commons.utils.codec.tlv.annotation.TLVField;
+import com.alipay.antchain.bridge.relayer.commons.model.RelayerNodeInfo;
+import com.alipay.antchain.bridge.relayer.core.manager.network.IRelayerCredentialManager;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Relaye请求响应
+ */
+@Getter
+@Setter
+@Slf4j
+public class RelayerResponse {
+
+ public static final int SUCCESS = 0;
+
+ public static final int FAILED = -1;
+
+ public static final short TLV_TYPE_RELAYER_RESPONSE_CODE = 0;
+
+ public static final short TLV_TYPE_RELAYER_RESPONSE_MSG = 1;
+
+ public static final short TLV_TYPE_RELAYER_RESPONSE_PAYLOAD = 2;
+
+ public static final short TLV_TYPE_RELAYER_RESPONSE_REMOTE_RELAYER_CERT = 3;
+
+ public static final short TLV_TYPE_RELAYER_RESPONSE_REMOTE_SIG_ALGO = 4;
+
+ public static final short TLV_TYPE_RELAYER_RESPONSE_SIG = 5;
+
+ public static RelayerResponse createSuccessResponse(
+ IResponsePayload payload,
+ IRelayerCredentialManager relayerCredentialManager
+ ) {
+ return createResponse(
+ SUCCESS,
+ "",
+ payload,
+ relayerCredentialManager
+ );
+ }
+
+ public static RelayerResponse createFailureResponse(
+ String errorMsg,
+ IResponsePayload payload,
+ IRelayerCredentialManager relayerCredentialManager
+ ) {
+ return createResponse(
+ FAILED,
+ errorMsg,
+ payload,
+ relayerCredentialManager
+ );
+ }
+
+ public static RelayerResponse createFailureResponse(
+ String errorMsg,
+ IRelayerCredentialManager relayerCredentialManager
+ ) {
+ return createResponse(
+ FAILED,
+ errorMsg,
+ null,
+ relayerCredentialManager
+ );
+ }
+
+ public static RelayerResponse createResponse(
+ int errorCode,
+ String message,
+ IResponsePayload payload,
+ IRelayerCredentialManager relayerCredentialManager
+ ) {
+ RelayerResponse relayerResponse = new RelayerResponse();
+ relayerResponse.setResponseCode(errorCode);
+ relayerResponse.setResponseMessage(message);
+ relayerResponse.setResponsePayload(ObjectUtil.isNull(payload) ? "" : payload.encode());
+ relayerCredentialManager.signRelayerResponse(relayerResponse);
+
+ return relayerResponse;
+ }
+
+ public static RelayerResponse decode(byte[] rawData) {
+ return TLVUtils.decode(rawData, RelayerResponse.class);
+ }
+
+ @TLVField(tag = TLV_TYPE_RELAYER_RESPONSE_CODE, type = TLVTypeEnum.UINT8)
+ private int responseCode;
+
+ @TLVField(tag = TLV_TYPE_RELAYER_RESPONSE_MSG, type = TLVTypeEnum.STRING, order = TLV_TYPE_RELAYER_RESPONSE_MSG)
+ private String responseMessage;
+
+ @TLVField(tag = TLV_TYPE_RELAYER_RESPONSE_PAYLOAD, type = TLVTypeEnum.STRING, order = TLV_TYPE_RELAYER_RESPONSE_PAYLOAD)
+ private String responsePayload;
+
+ @TLVField(
+ tag = TLV_TYPE_RELAYER_RESPONSE_REMOTE_RELAYER_CERT,
+ type = TLVTypeEnum.BYTES,
+ order = TLV_TYPE_RELAYER_RESPONSE_REMOTE_RELAYER_CERT
+ )
+ private AbstractCrossChainCertificate remoteRelayerCertificate;
+
+ @TLVField(
+ tag = TLV_TYPE_RELAYER_RESPONSE_REMOTE_SIG_ALGO,
+ type = TLVTypeEnum.STRING,
+ order = TLV_TYPE_RELAYER_RESPONSE_REMOTE_SIG_ALGO
+ )
+ private String sigAlgo;
+
+ @TLVField(
+ tag = TLV_TYPE_RELAYER_RESPONSE_SIG,
+ type = TLVTypeEnum.BYTES,
+ order = TLV_TYPE_RELAYER_RESPONSE_SIG
+ )
+ private byte[] signature;
+
+ public byte[] rawEncode() {
+ return TLVUtils.encode(this, TLV_TYPE_RELAYER_RESPONSE_SIG);
+ }
+
+ public byte[] encode() {
+ return TLVUtils.encode(this);
+ }
+
+ /**
+ * 验签
+ *
+ * @return
+ */
+ public boolean verify() {
+
+ try {
+ RelayerCredentialSubject relayerCredentialSubject = RelayerCredentialSubject.decode(
+ remoteRelayerCertificate.getCredentialSubject()
+ );
+ PublicKey publicKey = ObjectIdentityUtil.getPublicKeyFromSubject(
+ relayerCredentialSubject.getApplicant(),
+ relayerCredentialSubject.getSubjectInfo()
+ );
+
+ Signature verifier = Signature.getInstance(sigAlgo);
+ verifier.initVerify(publicKey);
+ verifier.update(rawEncode());
+
+ return verifier.verify(signature);
+
+ } catch (Exception e) {
+ throw new RuntimeException("failed to verify response sig", e);
+ }
+ }
+
+ public boolean isSuccess() {
+ return responseCode == SUCCESS;
+ }
+
+ public String calcRelayerNodeId() {
+ return RelayerNodeInfo.calculateNodeId(remoteRelayerCertificate);
+ }
+}
diff --git a/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/ws/WsRelayerNodePeerEndpoint.java b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/ws/WsRelayerNodePeerEndpoint.java
new file mode 100644
index 0000000..df0fd3f
--- /dev/null
+++ b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/ws/WsRelayerNodePeerEndpoint.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2023 Ant Group
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alipay.antchain.bridge.relayer.core.types.network.ws;
+
+//@Endpoint
+//@Slf4j
+public class WsRelayerNodePeerEndpoint {
+
+// @PayloadRoot(localPart = "request", namespace = "http://ws.offchainapi.oracle.mychain.alipay.com/")
+// @ResponsePayload
+// public RequestResponse request(@RequestPayload Request request) {
+// log.info("request!!! {}", request.getRelayerRequest());
+// RequestResponse response = new RequestResponse();
+// response.setReturn("test");
+// return response;
+// }
+//
+// @PayloadRoot(localPart = "WSRelayerServerAPImplService", namespace = "http://ws.offchainapi.oracle.mychain.alipay.com/")
+// @ResponsePayload
+// public String request(@RequestPayload String relayerRequest) {
+// log.info("request!!! {}", relayerRequest);
+// return relayerRequest;
+// }
+}
diff --git a/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/ws/WsSslFactory.java b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/ws/WsSslFactory.java
new file mode 100644
index 0000000..37f7813
--- /dev/null
+++ b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/ws/WsSslFactory.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2023 Ant Group
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alipay.antchain.bridge.relayer.core.types.network.ws;
+
+import java.io.ByteArrayInputStream;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManagerFactory;
+
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.net.SSLUtil;
+import cn.hutool.crypto.PemUtil;
+import io.grpc.util.CertificateUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+@Component
+public class WsSslFactory {
+
+ @Value("${relayer.network.node.tls.private_key_path}")
+ private String privateKeyPath;
+
+ @Value("${relayer.network.node.tls.trust_ca_path}")
+ private String trustCaPath;
+
+ public SSLContext getSslContext() throws Exception {
+ PrivateKey privateKey = PemUtil.readPemPrivateKey(
+ new ByteArrayInputStream(FileUtil.readBytes(privateKeyPath))
+ );
+ Certificate[] trustCertificates = CertificateUtils.getX509Certificates(
+ new ByteArrayInputStream(FileUtil.readBytes(trustCaPath))
+ );
+
+ char[] keyStorePassword = new char[0];
+ KeyStore keyStore = KeyStore.getInstance("JKS");
+ keyStore.load(null, null);
+ int count = 0;
+ for (Certificate cert : trustCertificates) {
+ keyStore.setCertificateEntry("cert" + count, cert);
+ count++;
+ }
+ keyStore.setKeyEntry("key", privateKey, keyStorePassword, trustCertificates);
+
+ TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
+ TrustManagerFactory.getDefaultAlgorithm()
+ );
+ KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(
+ KeyManagerFactory.getDefaultAlgorithm()
+ );
+
+ trustManagerFactory.init(keyStore);
+ keyManagerFactory.init(keyStore, keyStorePassword);
+
+ return SSLUtil.createSSLContext(
+ "TLSv1.2",
+ keyManagerFactory.getKeyManagers(),
+ trustManagerFactory.getTrustManagers()
+ );
+ }
+}
diff --git a/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/ws/client/WSRelayerClient.java b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/ws/client/WSRelayerClient.java
new file mode 100644
index 0000000..6a04ae1
--- /dev/null
+++ b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/ws/client/WSRelayerClient.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2023 Ant Group
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alipay.antchain.bridge.relayer.core.types.network.ws.client;
+
+import java.util.concurrent.ExecutorService;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLSocketFactory;
+import javax.xml.ws.BindingProvider;
+
+import cn.hutool.core.codec.Base64;
+import cn.hutool.core.util.ObjectUtil;
+import com.alipay.antchain.bridge.relayer.commons.model.RelayerNodeInfo;
+import com.alipay.antchain.bridge.relayer.core.manager.network.IRelayerCredentialManager;
+import com.alipay.antchain.bridge.relayer.core.types.network.BaseRelayerClient;
+import com.alipay.antchain.bridge.relayer.core.types.network.request.RelayerRequest;
+import com.alipay.antchain.bridge.relayer.core.types.network.response.RelayerResponse;
+import com.alipay.antchain.bridge.relayer.core.types.network.ws.client.generated.WSRelayerServerAPImpl;
+
+public class WSRelayerClient extends BaseRelayerClient {
+
+ private static final String HOSTNAME_VERIFIER = "com.sun.xml.internal.ws.transport.https.client.hostname.verifier";
+
+ private static final String SSL_SOCKET_FACTORY = "com.sun.xml.internal.ws.transport.https.client.SSLSocketFactory";
+
+ private WSRelayerServerAPImpl wsEndpointServer;
+
+ private final ExecutorService workers;
+
+ private final SSLSocketFactory sslSocketFactory;
+
+ public WSRelayerClient(
+ RelayerNodeInfo remoteNodeInfo,
+ IRelayerCredentialManager relayerCredentialManager,
+ String defaultNetworkId,
+ ExecutorService workers,
+ SSLSocketFactory sslSocketFactory
+ ) {
+ super(remoteNodeInfo, relayerCredentialManager, defaultNetworkId);
+ this.workers = workers;
+ this.sslSocketFactory = sslSocketFactory;
+ }
+
+ @Override
+ public void startup() {
+
+ HostnameVerifier hostnameVerifier = (s, sslSession) -> true;
+
+ HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);
+ HttpsURLConnection.setDefaultSSLSocketFactory(sslSocketFactory);
+
+ if (getRemoteNodeInfo().getEndpoints().size() == 0) {
+ throw new RuntimeException(
+ String.format(
+ "failed to start WSRelayerServerAPImplService: zero size endpoints for relayer (node_id: %s)",
+ getRemoteNodeInfo().getNodeId()
+ )
+ );
+ }
+
+ boolean isRunningOnTLS = false;
+ WSRelayerServerAPImplServiceWithHost serverService = null;
+ for (int idx = 0; idx < getRemoteNodeInfo().getEndpoints().size(); ++idx) {
+ try {
+ String url = getRemoteNodeInfo().getEndpoints().get(idx);
+ if (!url.startsWith("http")) {
+ url += "https://";
+ }
+ serverService = new WSRelayerServerAPImplServiceWithHost(url);
+ isRunningOnTLS = url.startsWith("https");
+ break;
+ } catch (Exception e) {
+ if (idx == getRemoteNodeInfo().getEndpoints().size() - 1) {
+ throw new RuntimeException("failed to start WSRelayerServerAPImplService. ", e);
+ }
+ }
+ }
+ if (ObjectUtil.isNull(serverService)) {
+ throw new RuntimeException("null webservice client made for relayer " + getRemoteNodeInfo().getNodeId());
+ }
+
+ serverService.setExecutor(workers);
+ wsEndpointServer = serverService.getWSRelayerServerAPImplPort();
+
+ BindingProvider bindingProvider = (BindingProvider) wsEndpointServer;
+
+ if (isRunningOnTLS) {
+ bindingProvider.getRequestContext().put(HOSTNAME_VERIFIER, hostnameVerifier);
+ bindingProvider.getRequestContext().put(SSL_SOCKET_FACTORY, sslSocketFactory);
+ bindingProvider.getRequestContext().put(
+ BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
+ serverService.getWSDLDocumentLocation().toString()
+ );
+ }
+ }
+
+ @Override
+ public boolean shutdown() {
+ return true;
+ }
+
+ @Override
+ public RelayerResponse sendRequest(RelayerRequest relayerRequest) {
+ return RelayerResponse.decode(
+ Base64.decode(
+ wsEndpointServer.request(
+ Base64.encode(relayerRequest.encode())
+ )
+ )
+ );
+ }
+}
diff --git a/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/ws/client/WSRelayerServerAPImplServiceWithHost.java b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/ws/client/WSRelayerServerAPImplServiceWithHost.java
new file mode 100644
index 0000000..dc81c6c
--- /dev/null
+++ b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/ws/client/WSRelayerServerAPImplServiceWithHost.java
@@ -0,0 +1,111 @@
+
+package com.alipay.antchain.bridge.relayer.core.types.network.ws.client;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import javax.xml.namespace.QName;
+import javax.xml.ws.Service;
+import javax.xml.ws.WebEndpoint;
+import javax.xml.ws.WebServiceClient;
+import javax.xml.ws.WebServiceException;
+import javax.xml.ws.WebServiceFeature;
+
+import com.alipay.antchain.bridge.relayer.core.types.network.ws.client.generated.WSRelayerServerAPImpl;
+
+/**
+ * THIS IS NOT A GENERATED CLASS!!!
+ *
+ * Add some features to {@link com.alipay.antchain.bridge.relayer.core.types.network.ws.client.generated.WSRelayerServerAPImplService}
+ * for supporting define the host to connect,
+ * and we get this class {@code WSRelayerServerAPImplServiceWithHost}
+ *
+ */
+@WebServiceClient(name = "WSRelayerServerAPImplService",
+ targetNamespace = "http://ws.offchainapi.oracle.mychain.alipay.com/",
+ wsdlLocation = "http://127.0.0.1:8082/WSEndpointServer?wsdl")
+public class WSRelayerServerAPImplServiceWithHost
+ extends Service {
+
+ private final static URL WSRELAYERSERVERAPIMPLSERVICE_WSDL_LOCATION;
+ private final static WebServiceException WSRELAYERSERVERAPIMPLSERVICE_EXCEPTION;
+ private final static QName WSRELAYERSERVERAPIMPLSERVICE_QNAME = new QName(
+ "http://ws.offchainapi.oracle.mychain.alipay.com/", "WSRelayerServerAPImplService");
+
+ static {
+ URL url = null;
+ WebServiceException e = null;
+ try {
+ url = new URL("http://127.0.0.1:8082/WSEndpointServer?wsdl");
+ } catch (MalformedURLException ex) {
+ e = new WebServiceException(ex);
+ }
+ WSRELAYERSERVERAPIMPLSERVICE_WSDL_LOCATION = url;
+ WSRELAYERSERVERAPIMPLSERVICE_EXCEPTION = e;
+ }
+
+ public WSRelayerServerAPImplServiceWithHost() {
+ super(__getWsdlLocation(), WSRELAYERSERVERAPIMPLSERVICE_QNAME);
+ }
+
+ public WSRelayerServerAPImplServiceWithHost(WebServiceFeature... features) {
+ super(__getWsdlLocation(), WSRELAYERSERVERAPIMPLSERVICE_QNAME, features);
+ }
+
+ public WSRelayerServerAPImplServiceWithHost(String host) {
+
+ super(__getWsdlLocation(host), WSRELAYERSERVERAPIMPLSERVICE_QNAME);
+ }
+
+ public WSRelayerServerAPImplServiceWithHost(URL wsdlLocation) {
+ super(wsdlLocation, WSRELAYERSERVERAPIMPLSERVICE_QNAME);
+ }
+
+ public WSRelayerServerAPImplServiceWithHost(URL wsdlLocation, WebServiceFeature... features) {
+ super(wsdlLocation, WSRELAYERSERVERAPIMPLSERVICE_QNAME, features);
+ }
+
+ public WSRelayerServerAPImplServiceWithHost(URL wsdlLocation, QName serviceName) {
+ super(wsdlLocation, serviceName);
+ }
+
+ public WSRelayerServerAPImplServiceWithHost(URL wsdlLocation, QName serviceName, WebServiceFeature... features) {
+ super(wsdlLocation, serviceName, features);
+ }
+
+ /**
+ * @return returns WSRelayerServerAPImpl
+ */
+ @WebEndpoint(name = "WSRelayerServerAPImplPort")
+ public WSRelayerServerAPImpl getWSRelayerServerAPImplPort() {
+ return super.getPort(new QName("http://ws.offchainapi.oracle.mychain.alipay.com/", "WSRelayerServerAPImplPort"),
+ WSRelayerServerAPImpl.class);
+ }
+
+ /**
+ * @param features A list of {@link WebServiceFeature} to configure on the proxy. Supported features not in the
+ * features parameter will have their default values.
+ * @return returns WSRelayerServerAPImpl
+ */
+ @WebEndpoint(name = "WSRelayerServerAPImplPort")
+ public WSRelayerServerAPImpl getWSRelayerServerAPImplPort(WebServiceFeature... features) {
+ return super.getPort(new QName("http://ws.offchainapi.oracle.mychain.alipay.com/", "WSRelayerServerAPImplPort"),
+ WSRelayerServerAPImpl.class, features);
+ }
+
+ private static URL __getWsdlLocation() {
+ if (WSRELAYERSERVERAPIMPLSERVICE_EXCEPTION != null) {
+ throw WSRELAYERSERVERAPIMPLSERVICE_EXCEPTION;
+ }
+ return WSRELAYERSERVERAPIMPLSERVICE_WSDL_LOCATION;
+ }
+
+ private static URL __getWsdlLocation(String host) {
+ try {
+ return new URL(host + "/WSEndpointServer?wsdl");
+ } catch (MalformedURLException e) {
+ throw new WebServiceException(e);
+ }
+ }
+
+}
diff --git a/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/ws/client/generated/ObjectFactory.java b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/ws/client/generated/ObjectFactory.java
new file mode 100644
index 0000000..d211ef4
--- /dev/null
+++ b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/ws/client/generated/ObjectFactory.java
@@ -0,0 +1,79 @@
+
+package com.alipay.antchain.bridge.relayer.core.types.network.ws.client.generated;
+
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.annotation.XmlElementDecl;
+import javax.xml.bind.annotation.XmlRegistry;
+import javax.xml.namespace.QName;
+
+
+/**
+ * This object contains factory methods for each
+ * Java content interface and Java element interface
+ * generated in the com.alipay.antchain.bridge.relayer.core.types.network.ws.client.generated package.
+ *
An ObjectFactory allows you to programatically
+ * construct new instances of the Java representation
+ * for XML content. The Java representation of XML
+ * content can consist of schema derived interfaces
+ * and classes representing the binding of schema
+ * type definitions, element declarations and model
+ * groups. Factory methods for each of these are
+ * provided in this class.
+ *
+ */
+@XmlRegistry
+public class ObjectFactory {
+
+ private final static QName _Request_QNAME = new QName("http://ws.offchainapi.oracle.mychain.alipay.com/", "request");
+ private final static QName _RequestResponse_QNAME = new QName("http://ws.offchainapi.oracle.mychain.alipay.com/", "requestResponse");
+
+ /**
+ * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: com.alipay.antchain.bridge.relayer.core.types.network.ws.client.generated
+ *
+ */
+ public ObjectFactory() {
+ }
+
+ /**
+ * Create an instance of {@link Request }
+ *
+ */
+ public Request createRequest() {
+ return new Request();
+ }
+
+ /**
+ * Create an instance of {@link RequestResponse }
+ *
+ */
+ public RequestResponse createRequestResponse() {
+ return new RequestResponse();
+ }
+
+ /**
+ * Create an instance of {@link JAXBElement }{@code <}{@link Request }{@code >}
+ *
+ * @param value
+ * Java instance representing xml element's value.
+ * @return
+ * the new instance of {@link JAXBElement }{@code <}{@link Request }{@code >}
+ */
+ @XmlElementDecl(namespace = "http://ws.offchainapi.oracle.mychain.alipay.com/", name = "request")
+ public JAXBElement createRequest(Request value) {
+ return new JAXBElement(_Request_QNAME, Request.class, null, value);
+ }
+
+ /**
+ * Create an instance of {@link JAXBElement }{@code <}{@link RequestResponse }{@code >}
+ *
+ * @param value
+ * Java instance representing xml element's value.
+ * @return
+ * the new instance of {@link JAXBElement }{@code <}{@link RequestResponse }{@code >}
+ */
+ @XmlElementDecl(namespace = "http://ws.offchainapi.oracle.mychain.alipay.com/", name = "requestResponse")
+ public JAXBElement createRequestResponse(RequestResponse value) {
+ return new JAXBElement(_RequestResponse_QNAME, RequestResponse.class, null, value);
+ }
+
+}
diff --git a/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/ws/client/generated/Request.java b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/ws/client/generated/Request.java
new file mode 100644
index 0000000..e017cae
--- /dev/null
+++ b/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/ws/client/generated/Request.java
@@ -0,0 +1,60 @@
+
+package com.alipay.antchain.bridge.relayer.core.types.network.ws.client.generated;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlType;
+
+
+/**
+ *