diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 00000000..a53d9de4
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,170 @@
+# [2025-11-06] 本PR所有改动总结
+
+## acb-committeeptc
+### monitor-node
+复制node模块并进行修改,改动如下:
+- monitor.node.client
+ - 与pluginserver和监管系统进行GPRC通信的客户端
+- monitor.node.commons.core 与 monitor.node.commons.enums
+ - 增加监管功能所需的一些配置
+- monitor.node.server
+ - 在MonitorNodeServiceImpl.verifyCrossChainMessage服务中增加事中监管功能
+ - 新增MonitorOrderServiceImpl服务,完成接收监管指令功能
+- monitor.node.service
+ - 完成上述功能的内部实现
+
+### monitor-node-cli
+复制node-cli模块,并只修改了配置文件以适配新模块名。
+
+## acb-relayer
+- 优化r-cli的命令**setup-contract**
+ - 能够一键部署AM SDP PTC Monitor相关的所有合约并完成初始化
+- 优化r-cli的命令**get-blockchain-contracts**
+ - 能够查询AM SDP 以及Monitor合约的地址,其中Monitor合约地址将代替SDP合约地址,在DAPP合约中进行初始化
+
+## acb-pluginserver
+增加了如下接口功能(BBC插件的新增功能与之对应)
+- **SetupMonitorMessage**
+ - 部署Monitor合约和MonitorVerifer合约,并在Monitor合约中初始化MonitorVerifer合约的地址
+- **SetMonitorContract**
+ - 在SDP合约中初始化Monitor合约地址
+- **SetProtocolInMonitor**
+ - 在Monitor合约中初始化SDP合约地址
+- **SetMonitorControl**
+ - 在Monitor合约中初始化变量monitorControl,控制监管开关(默认开)
+- **SetPtcHubInMonitorVerifier**
+ - 在MonitorVerifer合约中初始化PtcHub合约的地址
+- **RelayMonitorOrder**
+ - 将监管指令发送到Monitor合约
+
+## acb-sdk
+- antchain.bridge.commons
+ - 在BBCConext增加了监管合约地址和状态
+ - 增加了对监管合约的序列化和反序列化
+- antchain.bridge.spi
+ - 增加了监管合约所需接口
+- pluginset.ethereum2
+ - offchain
+ - 增加了支持监管合约部署、相关初始化、发送监管指令等接口
+ - onchain
+ - 新增monitor合约:在dapp和sdp合约层之间,完成事前监管,并能接收和存储监管指令,与monitorVerifer合约交互完成监管节点签名的验证
+ - 新增monitorVerifer合约,与monitor交互;接收跨链消息时从ptcHub合约获取监管节点签名
+ - 删除了sys/lib/ptc下的CommitteePtcVerifier.sol,原因如下:
+ - 并没有其他合约import该合约,
+ - 该合约的功能在同目录下的CommitteeLib.sol中已经实现
+ - 在添加功能代码时,如果不删除会导致编译失败
+
+
+## 注意事项
+**如果需要对一条链chain-B下达监管指令,该链必须先接收一条跨链消息。** 原因如下:
+- 监管指令上链时,是由committeeptc中的监管节点直接调用pluginserver的BBC服务来发送交易,不会经过relayer
+- 监管指令在链上需要验证监管节点签名来保证指令的真实性,所以链上需事先存储监管节点的公钥
+- 监管节点的公钥和其他节点一样,存储在ptchub合约接收的的tpbta中
+- 按照antchain的设计,当relayer接收到一条目的链为chain-B的跨链消息时,会检查是否已经上传chain-B最新的tpbta到链上,如果没有则上传
+- 所以如果chain-B没有接收过跨链消息,ptchub合约上就不会存储tpbta,从而无法获取监管节点公钥,无法完成监管指令的签名验证,导致无法成功接收监管指令
+
+
+## 含监管的流程图示
+- 下图为含监管的跨链流程图:
+
+
+
+
+## 其他说明
+
+### 跨链消息结构设计变更说明
+监管合约作为SDP上层合约,封装DApp消息的同时增加监管字段monitor_type和监管信息monitor_msg。
+
+
+
+| monitor_type值(uint32类型) | monitor_type含义 | monitor_msg含义(string类型) |
+| ---------------------- | ------------------------------------------------------------ | --------------------------- |
+| 1 | 发送方发出的不要求监管的跨链消息 | 可选 |
+| 2 | 对于发送方:发出要求监管的跨链消息对于接收方:成功接收到带监管的跨链消息 | 可选 |
+| 3 | 监管未通过,回滚到发送方的监管回滚消息 | 可选,如监管未通过的原因 |
+
+
+### 背书策略配置说明
+为加入antchain的区块链配置背书策略时,监管节点需要设置为true,举例说明如下。
+- 当监管开启时,监管节点会向监管系统请求跨链消息的合法性,**合法则返回一个正确签名,不合法则返回一个空签名**。
+- 当监管关闭时,监管节点的运行逻辑和其他节点完全相同,只是不会在PtcHub合约与monitorVerifier合约进行签名验证。
+```
+{
+ "committee_id": "default",
+ "endorsers": [
+ {
+ "node_id": "node1",
+ "node_public_key": {
+ "key_id": "default",
+ "public_key": ""
+ },
+ "required": true
+ },
+ {
+ "node_id": "monitor-node",
+ "node_public_key": {
+ "key_id": "default",
+ "public_key": ""
+ },
+ "required": true
+ }
+ ],
+ "policy": {
+ "threshold": ">=0"
+ }
+}
+```
+
+
+### 监管指令结构说明
+acb-committeeptc/monitor-node/src/main/proto/monitorSystemgrpc.proto中的监管指令结构如下:
+```
+message MonitorOrder {
+ string product = 1;
+ string domain = 2;
+ uint64 monitorOrderType = 3;
+ string senderDomain = 4;
+ string fromAddress = 5;
+ string receiverDomain = 6;
+ string toAddress = 7;
+ string transactionContent = 8;
+ string extra = 9;
+}
+```
+监管节点会解析出监管指令各字段,并构造包含监管指令的交易发送到指定区块链的监管合约,最终由监管合约更新监管规则。该结构各字段含义如下:
+- product
+ - 监管指令要下发到的区块链的类型,例如etherum2,fiscobcos等
+- domain
+ - 监管指令要下发到的区块链的域名
+- monitorOrderType
+ - 监管指令的类型。该字段长度为32bit,采用了分层编码的设计方式(如下图),分为主类型和子类型,每种主类型标识一种监管维度,每种主类型下分多种子类型
+ - 在当前设计中,每个主类型占1bit,每个子类型占3bit,即每种主类型共有8种子类型
+ - 主类型的具体含义由监管系统定义。以“黑名单”作为主类型来举例,该主类型的子类型可以包含:
+ - 禁止本区块链的应用合约a发送跨链交易;
+ - 禁止本区块链向区块链B的应用合约b发送跨链交易;
+ - 禁止本区块链向区块链B发送跨链交易等。
+
+
+
+- senderDomain
+ - 跨链过程中源区块链域名,处理方式随monitorOrderType含义而变化
+ - 例如监管指令是“禁止某区块链域名发送跨链消息”,则监管合约会把senderDomain加入黑名单,最终效果为该区块链无法在跨链系统发送跨链消息。
+- fromAddress
+ - 跨链过程中源区块链的应用合约地址,处理方式随monitorOrderType含义而变化
+ - 例如监管指令是“禁止某区块链的某应用合约发送跨链消息”,则监管合约会把fromAddress加入黑名单,最终效果为区块链的该应用合约无法在该跨链系统发送跨链消息。
+- receiverDomain
+ - 跨链过程中目的区块链域名,处理方式随monitorOrderType含义而变化
+- toAddress
+ - 跨链过程中目的区块链的应用合约地址,处理方式随monitorOrderType含义而变化。
+- transactionContent
+ - 针对可能要对跨链过程中的原始跨链消息内容本身进行审查而设计了该字段,用于在链上审查跨链消息内容的合规性。
+- extra
+ - 额外信息。用于存放监管系统希望在链上存储的一些监管指令描述,或者上述字段未充分考虑的情况等,也可为空。
+
+目前监管合约中对监管指令的支持,只完成了**合约黑名单**和**控制监管开关**两种功能。
+- 合约黑名单功能对应的监管指令,用二进制表示如下:
+ - **1000** 0000 0000 0000 0000 0000 0000 0000
+ - 即32bit中第一对4bit组表示“黑名单”及其子类型。子类型"000"表示加入黑名单,"001"表示移除出黑名单。
+- 控制监管开关的监管指令,用二进制表示如下:
+ - 0000 **1000** 0000 0000 0000 0000 0000 0000
+ - 即32bit中第二对4bit组表示“监管控制”及其子类型。子类型"000"表示关闭监管,"001"表示开启监管。
\ No newline at end of file
diff --git a/acb-committeeptc/monitor-node-cli/README.md b/acb-committeeptc/monitor-node-cli/README.md
new file mode 100755
index 00000000..663037e4
--- /dev/null
+++ b/acb-committeeptc/monitor-node-cli/README.md
@@ -0,0 +1,232 @@
+
+ request complex type的 Java 类。 + *
Java class for request complex type. * - *
以下模式片段指定包含在此类中的预期内容。 + *
The following schema fragment specifies the expected content contained within this class. * *
* <complexType name="request">
@@ -34,7 +34,7 @@ public class Request {
protected String relayerRequest;
/**
- * 获取relayerRequest属性的值。
+ * Gets the value of the relayerRequest property.
*
* @return
* possible object is
@@ -46,7 +46,7 @@ public String getRelayerRequest() {
}
/**
- * 设置relayerRequest属性的值。
+ * Sets the value of the relayerRequest property.
*
* @param value
* allowed object is
diff --git a/acb-relayer/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/ws/client/generated/RequestResponse.java b/acb-relayer/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/ws/client/generated/RequestResponse.java
index f1e53e0a..7d271b0b 100644
--- a/acb-relayer/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/ws/client/generated/RequestResponse.java
+++ b/acb-relayer/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/network/ws/client/generated/RequestResponse.java
@@ -8,9 +8,9 @@
/**
- * requestResponse complex type的 Java 类。
+ *
Java class for requestResponse complex type.
*
- *
以下模式片段指定包含在此类中的预期内容。
+ *
The following schema fragment specifies the expected content contained within this class.
*
*
* <complexType name="requestResponse">
@@ -36,7 +36,7 @@ public class RequestResponse {
protected String _return;
/**
- * 获取return属性的值。
+ * Gets the value of the return property.
*
* @return
* possible object is
@@ -48,7 +48,7 @@ public String getReturn() {
}
/**
- * 设置return属性的值。
+ * Sets the value of the return property.
*
* @param value
* allowed object is
diff --git a/acb-relayer/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/pluginserver/GRpcBBCServiceClient.java b/acb-relayer/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/pluginserver/GRpcBBCServiceClient.java
index 83f0d71f..c5da0443 100644
--- a/acb-relayer/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/pluginserver/GRpcBBCServiceClient.java
+++ b/acb-relayer/r-core/src/main/java/com/alipay/antchain/bridge/relayer/core/types/pluginserver/GRpcBBCServiceClient.java
@@ -210,6 +210,91 @@ public void setupSDPMessageContract() {
}
}
+ @Override
+ public void setupMonitorContract() {
+ Response response = this.blockingStub.bbcCall(
+ CallBBCRequest.newBuilder()
+ .setProduct(this.getProduct())
+ .setDomain(this.getDomain())
+ .setSetupMonitorMessageContractReq(SetupMonitorMessageContractRequest.getDefaultInstance())
+ .build()
+ );
+ if (response.getCode() != 0) {
+ throw new RuntimeException(
+ String.format("[GRpcBBCServiceClient (domain: %s, product: %s)] setupMonitorMessageContract request failed for plugin server %s: %s",
+ this.domain, this.product, this.psId, response.getErrorMsg())
+ );
+ }
+ }
+
+ @Override
+ public void setMonitorContract(String monitorContract) {
+ Response response = this.blockingStub.bbcCall(
+ CallBBCRequest.newBuilder()
+ .setProduct(this.getProduct())
+ .setDomain(this.getDomain())
+ .setSetMonitorContractReq(SetMonitorContractRequest.newBuilder().setContractAddress(monitorContract))
+ .build()
+ );
+ if (response.getCode() != 0) {
+ throw new RuntimeException(
+ String.format("[GRpcBBCServiceClient (domain: %s, product: %s)] setMonitorContract request failed for plugin server %s: %s",
+ this.domain, this.product, this.psId, response.getErrorMsg())
+ );
+ }
+ }
+
+ @Override
+ public void setProtocolInMonitor(String protocolContract) {
+ Response response = this.blockingStub.bbcCall(
+ CallBBCRequest.newBuilder()
+ .setProduct(this.getProduct())
+ .setDomain(this.getDomain())
+ .setSetProtocolInMonitorReq(SetProtocolInMonitorRequest.newBuilder().setProtocolAddress(protocolContract))
+ .build()
+ );
+ if (response.getCode() != 0) {
+ throw new RuntimeException(
+ String.format("[GRpcBBCServiceClient (domain: %s, product: %s)] setProtocolInMonitor request failed for plugin server %s: %s",
+ this.domain, this.product, this.psId, response.getErrorMsg())
+ );
+ }
+ }
+
+ @Override
+ public void setMonitorControl(int monitorType) {
+ Response response = this.blockingStub.bbcCall(
+ CallBBCRequest.newBuilder()
+ .setProduct(this.getProduct())
+ .setDomain(this.getDomain())
+ .setSetMonitorControlReq(SetMonitorControlRequest.newBuilder().setMonitorType(monitorType))
+ .build()
+ );
+ if (response.getCode() != 0) {
+ throw new RuntimeException(
+ String.format("[GRpcBBCServiceClient (domain: %s, product: %s)] setMonitorControl request failed for plugin server %s: %s",
+ this.domain, this.product, this.psId, response.getErrorMsg())
+ );
+ }
+ }
+
+ @Override
+ public void setPtcHubInMonitorVerifier(String contractAddress) {
+ Response response = this.blockingStub.bbcCall(
+ CallBBCRequest.newBuilder()
+ .setProduct(this.getProduct())
+ .setDomain(this.getDomain())
+ .setSetPtcHubInMonitorVerifierReq(SetPtcHubInMonitorVerifierRequest.newBuilder().setContractAddress(contractAddress))
+ .build()
+ );
+ if (response.getCode() != 0) {
+ throw new RuntimeException(
+ String.format("[GRpcBBCServiceClient (domain: %s, product: %s)] setPtcHubInMonitorVerifier request failed for plugin server %s: %s",
+ this.domain, this.product, this.psId, response.getErrorMsg())
+ );
+ }
+ }
+
@Override
public void setProtocol(String protocolAddress, String protocolType) {
Response response = this.blockingStub.bbcCall(
@@ -270,6 +355,11 @@ public CrossChainMessageReceipt relayAuthMessage(byte[] rawMessage) {
return PluginServerUtils.convertFromGRpcCrossChainMessageReceipt(response.getBbcResp().getRelayAuthMessageResponse().getReceipt());
}
+ @Override
+ public CrossChainMessageReceipt relayMonitorOrder(String committeeId, String signAlgo, byte[] rawProof, byte[] rawMonitorOrder) {
+ return new CrossChainMessageReceipt();
+ }
+
@Override
public void setAmContract(String contractAddress) {
Response response = this.blockingStub.bbcCall(
diff --git a/acb-relayer/r-core/src/main/proto/pluginserver.proto b/acb-relayer/r-core/src/main/proto/pluginserver.proto
index 706f88c5..a7f48eda 100644
--- a/acb-relayer/r-core/src/main/proto/pluginserver.proto
+++ b/acb-relayer/r-core/src/main/proto/pluginserver.proto
@@ -100,6 +100,12 @@ message CallBBCRequest {
QueryValidatedBlockStateRequest queryValidatedBlockStateRequest = 28;
RecvOffChainExceptionRequest recvOffChainExceptionRequest = 29;
ReliableRetryRequest reliableRetryRequest = 30;
+ SetupMonitorMessageContractRequest setupMonitorMessageContractReq=31;
+ SetMonitorContractRequest setMonitorContractReq = 32;
+ SetProtocolInMonitorRequest setProtocolInMonitorReq = 33;
+ SetMonitorControlRequest setMonitorControlReq = 34;
+ SetPtcHubInMonitorVerifierRequest setPtcHubInMonitorVerifierReq = 35;
+ RelayMonitorOrderRequest relayMonitorOrderReq =36;
}
}
@@ -123,6 +129,26 @@ message SetupSDPMessageContractRequest {
// stay empty body for now, maybe fill some stuff in future
}
+message SetupMonitorMessageContractRequest {
+ // stay empty body for now, maybe fill some stuff in future
+}
+
+message SetMonitorContractRequest {
+ string contractAddress = 1;
+}
+
+message SetProtocolInMonitorRequest {
+ string protocolAddress = 1;
+}
+
+message SetMonitorControlRequest {
+ uint32 monitorType = 1;
+}
+
+message SetPtcHubInMonitorVerifierRequest {
+ string contractAddress = 1;
+}
+
message SetProtocolRequest {
string protocolAddress = 1;
string protocolType = 2;
@@ -223,6 +249,13 @@ message ReliableRetryRequest {
bytes reliableCrossChainMessageData = 1;
}
+message RelayMonitorOrderRequest {
+ string committeeId = 1;
+ string signAlgo = 2;
+ bytes rawProof = 3;
+ bytes rawMonitorOrder = 4;
+}
+
// basic messages.
// same as project `antchain-bridge-commons`
message CrossChainMessageReceipt {
@@ -277,6 +310,8 @@ message CallBBCResponse {
QueryValidatedBlockStateResponse queryValidatedBlockStateResponse = 18;
RecvOffChainExceptionResponse recvOffChainExceptionResponse = 19;
ReliableRetryResponse reliableRetryResponse = 20;
+ SetupMonitorMessageContractResponse setupMonitorResp = 21;
+ RelayMonitorOrderResponse relayMonitorOrderResp = 22;
}
}
@@ -309,6 +344,16 @@ message SetupSDPMessageContractResponse {
SDPMessageContract sdpContract = 1;
}
+message MonitorMessageContract {
+ string contractAddress = 1;
+ ContractStatusEnum status = 2;
+}
+
+
+message SetupMonitorMessageContractResponse {
+ MonitorMessageContract monitorContract = 1;
+}
+
message PtcContract {
string contractAddress = 1;
ContractStatusEnum status = 2;
@@ -380,4 +425,8 @@ message RecvOffChainExceptionResponse {
message ReliableRetryResponse {
CrossChainMessageReceipt receipt = 1;
+}
+
+message RelayMonitorOrderResponse {
+ CrossChainMessageReceipt receipt = 1;
}
\ No newline at end of file
diff --git a/acb-relayer/r-facade/src/main/java/com/alipay/antchain/bridge/relayer/facade/admin/types/SysContractsInfo.java b/acb-relayer/r-facade/src/main/java/com/alipay/antchain/bridge/relayer/facade/admin/types/SysContractsInfo.java
index 4e0b030a..c7d33a80 100644
--- a/acb-relayer/r-facade/src/main/java/com/alipay/antchain/bridge/relayer/facade/admin/types/SysContractsInfo.java
+++ b/acb-relayer/r-facade/src/main/java/com/alipay/antchain/bridge/relayer/facade/admin/types/SysContractsInfo.java
@@ -17,6 +17,9 @@ public class SysContractsInfo {
@JSONField(name = "ptc_contract")
private String ptcContract;
+ @JSONField(name = "monitor_contract")
+ private String monitorContract;
+
@JSONField(name = "state")
private String state;
}
diff --git a/acb-relayer/r-server/src/main/java/com/alipay/antchain/bridge/relayer/server/admin/impl/BlockchainNamespace.java b/acb-relayer/r-server/src/main/java/com/alipay/antchain/bridge/relayer/server/admin/impl/BlockchainNamespace.java
index c16320b8..ade3f462 100644
--- a/acb-relayer/r-server/src/main/java/com/alipay/antchain/bridge/relayer/server/admin/impl/BlockchainNamespace.java
+++ b/acb-relayer/r-server/src/main/java/com/alipay/antchain/bridge/relayer/server/admin/impl/BlockchainNamespace.java
@@ -189,6 +189,12 @@ Object getBlockchainContracts(String... args) {
)
);
+ sysContractsInfo.setMonitorContract(
+ ObjectUtil.defaultIfNull(
+ blockchainMeta.getProperties().getMonitorContractAddress(), "empty"
+ )
+ );
+
OnChainServiceStatusEnum amStatus = blockchainMeta.getProperties().getAmServiceStatus();
sysContractsInfo.setState(
ObjectUtil.isNull(amStatus)? "" : amStatus.name()
diff --git a/acb-sdk/antchain-bridge-commons/src/main/java/com/alipay/antchain/bridge/commons/bbc/AbstractBBCContext.java b/acb-sdk/antchain-bridge-commons/src/main/java/com/alipay/antchain/bridge/commons/bbc/AbstractBBCContext.java
index c2a9d88e..53ebcd4c 100644
--- a/acb-sdk/antchain-bridge-commons/src/main/java/com/alipay/antchain/bridge/commons/bbc/AbstractBBCContext.java
+++ b/acb-sdk/antchain-bridge-commons/src/main/java/com/alipay/antchain/bridge/commons/bbc/AbstractBBCContext.java
@@ -19,6 +19,7 @@
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.annotation.JSONField;
import com.alipay.antchain.bridge.commons.bbc.syscontract.AuthMessageContract;
+import com.alipay.antchain.bridge.commons.bbc.syscontract.MonitorContract;
import com.alipay.antchain.bridge.commons.bbc.syscontract.SDPContract;
import com.alipay.antchain.bridge.commons.bbc.syscontract.PTCContract;
import lombok.Getter;
@@ -37,6 +38,9 @@ public abstract class AbstractBBCContext implements IBBCContext {
@JSONField(name = "sdp_contract")
private SDPContract sdpContract;
+ @JSONField(name = "monitor_contract")
+ private MonitorContract monitorContract;
+
@JSONField(name = "is_reliable")
private boolean isReliable;
@@ -47,6 +51,7 @@ public abstract class AbstractBBCContext implements IBBCContext {
public void decodeFromBytes(byte[] raw) {
AbstractBBCContext state = JSON.parseObject(raw, this.getClass());
this.setSdpContract(state.getSdpContract());
+ this.setMonitorContract(state.getMonitorContract());
this.setPtcContract(state.getPtcContract());
this.setAuthMessageContract(state.getAuthMessageContract());
this.setReliable(state.isReliable());
diff --git a/acb-sdk/antchain-bridge-commons/src/main/java/com/alipay/antchain/bridge/commons/bbc/syscontract/MonitorContract.java b/acb-sdk/antchain-bridge-commons/src/main/java/com/alipay/antchain/bridge/commons/bbc/syscontract/MonitorContract.java
new file mode 100755
index 00000000..6934c5f5
--- /dev/null
+++ b/acb-sdk/antchain-bridge-commons/src/main/java/com/alipay/antchain/bridge/commons/bbc/syscontract/MonitorContract.java
@@ -0,0 +1,13 @@
+package com.alipay.antchain.bridge.commons.bbc.syscontract;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class MonitorContract {
+
+ private String contractAddress;
+
+ private ContractStatusEnum status;
+}
diff --git a/acb-sdk/antchain-bridge-commons/src/main/java/com/alipay/antchain/bridge/commons/core/monitor/AbstractMonitorMessage.java b/acb-sdk/antchain-bridge-commons/src/main/java/com/alipay/antchain/bridge/commons/core/monitor/AbstractMonitorMessage.java
new file mode 100755
index 00000000..4c00c90b
--- /dev/null
+++ b/acb-sdk/antchain-bridge-commons/src/main/java/com/alipay/antchain/bridge/commons/core/monitor/AbstractMonitorMessage.java
@@ -0,0 +1,25 @@
+package com.alipay.antchain.bridge.commons.core.monitor;
+
+import lombok.Getter;
+import lombok.Setter;
+
+
+@Getter
+@Setter
+public abstract class AbstractMonitorMessage implements IMonitorMessage {
+
+ public static int MONITOR_CLOSE = 1;
+
+ public static int MONITOR_OPEN = 2;
+
+ public static int MONITOR_ROLLBACK = 3;
+
+ public static int MONITOR_ORDER = 4;
+
+ private int monitorType;
+
+ private String monitorMsg;
+
+ private byte[] payload;
+
+}
diff --git a/acb-sdk/antchain-bridge-commons/src/main/java/com/alipay/antchain/bridge/commons/core/monitor/AbstractMonitorOrder.java b/acb-sdk/antchain-bridge-commons/src/main/java/com/alipay/antchain/bridge/commons/core/monitor/AbstractMonitorOrder.java
new file mode 100644
index 00000000..b31d85cf
--- /dev/null
+++ b/acb-sdk/antchain-bridge-commons/src/main/java/com/alipay/antchain/bridge/commons/core/monitor/AbstractMonitorOrder.java
@@ -0,0 +1,74 @@
+package com.alipay.antchain.bridge.commons.core.monitor;
+
+import cn.hutool.core.util.HexUtil;
+import com.alipay.antchain.bridge.commons.exception.AntChainBridgeCommonsException;
+import com.alipay.antchain.bridge.commons.exception.CommonsErrorCodeEnum;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public abstract class AbstractMonitorOrder implements IMonitorOrder {
+
+ public static final int IDENTITY_LENGTH = 32;
+
+ private String product;
+
+ private String domain;
+
+ private long monitorOrderType;
+
+ private String senderDomain;
+
+ private String fromAddress;
+
+ private String receiverDomain;
+
+ private String toAddress;
+
+ private String transactionContent;
+
+ private String extra;
+
+ public byte[] getRawFromAddress() {
+ byte[] rawID = HexUtil.decodeHex(this.fromAddress);
+ if (rawID.length != IDENTITY_LENGTH) {
+ throw new AntChainBridgeCommonsException(
+ CommonsErrorCodeEnum.CROSS_CHAIN_IDENTITY_DECODE_ERROR,
+ String.format("expected id with length %d but got %d", IDENTITY_LENGTH, rawID.length)
+ );
+ }
+ return rawID;
+ }
+
+ public byte[] getRawToAddress() {
+ byte[] rawID = HexUtil.decodeHex(this.toAddress);
+ if (rawID.length != IDENTITY_LENGTH) {
+ throw new AntChainBridgeCommonsException(
+ CommonsErrorCodeEnum.CROSS_CHAIN_IDENTITY_DECODE_ERROR,
+ String.format("expected id with length %d but got %d", IDENTITY_LENGTH, rawID.length)
+ );
+ }
+ return rawID;
+ }
+
+ public void setFromAddressFromBytes(byte[] fromAddress) {
+ if (fromAddress.length != IDENTITY_LENGTH) {
+ throw new AntChainBridgeCommonsException(
+ CommonsErrorCodeEnum.CROSS_CHAIN_IDENTITY_DECODE_ERROR,
+ String.format("expected id with length %d but got %d", IDENTITY_LENGTH, fromAddress.length)
+ );
+ }
+ this.fromAddress = HexUtil.encodeHexStr(fromAddress);
+ }
+
+ public void setToAddressFromBytes(byte[] toAddress) {
+ if (toAddress.length != IDENTITY_LENGTH) {
+ throw new AntChainBridgeCommonsException(
+ CommonsErrorCodeEnum.CROSS_CHAIN_IDENTITY_DECODE_ERROR,
+ String.format("expected id with length %d but got %d", IDENTITY_LENGTH, toAddress.length)
+ );
+ }
+ this.toAddress = HexUtil.encodeHexStr(toAddress);
+ }
+}
diff --git a/acb-sdk/antchain-bridge-commons/src/main/java/com/alipay/antchain/bridge/commons/core/monitor/IMonitorMessage.java b/acb-sdk/antchain-bridge-commons/src/main/java/com/alipay/antchain/bridge/commons/core/monitor/IMonitorMessage.java
new file mode 100755
index 00000000..abff5991
--- /dev/null
+++ b/acb-sdk/antchain-bridge-commons/src/main/java/com/alipay/antchain/bridge/commons/core/monitor/IMonitorMessage.java
@@ -0,0 +1,12 @@
+package com.alipay.antchain.bridge.commons.core.monitor;
+
+import com.alipay.antchain.bridge.commons.core.base.IMessage;
+
+public interface IMonitorMessage extends IMessage {
+
+ int getMonitorType();
+
+ String getMonitorMsg();
+
+ byte[] getPayload();
+}
diff --git a/acb-sdk/antchain-bridge-commons/src/main/java/com/alipay/antchain/bridge/commons/core/monitor/IMonitorOrder.java b/acb-sdk/antchain-bridge-commons/src/main/java/com/alipay/antchain/bridge/commons/core/monitor/IMonitorOrder.java
new file mode 100644
index 00000000..2483ef0d
--- /dev/null
+++ b/acb-sdk/antchain-bridge-commons/src/main/java/com/alipay/antchain/bridge/commons/core/monitor/IMonitorOrder.java
@@ -0,0 +1,25 @@
+package com.alipay.antchain.bridge.commons.core.monitor;
+
+import com.alipay.antchain.bridge.commons.core.base.IMessage;
+
+public interface IMonitorOrder extends IMessage {
+
+ String getProduct();
+
+ String getDomain();
+
+ long getMonitorOrderType();
+
+ String getSenderDomain();
+
+ String getFromAddress();
+
+ String getReceiverDomain();
+
+ String getToAddress();
+
+ String getTransactionContent();
+
+ String getExtra();
+
+}
diff --git a/acb-sdk/antchain-bridge-commons/src/main/java/com/alipay/antchain/bridge/commons/core/monitor/MonitorMessageFactory.java b/acb-sdk/antchain-bridge-commons/src/main/java/com/alipay/antchain/bridge/commons/core/monitor/MonitorMessageFactory.java
new file mode 100755
index 00000000..3451e7cc
--- /dev/null
+++ b/acb-sdk/antchain-bridge-commons/src/main/java/com/alipay/antchain/bridge/commons/core/monitor/MonitorMessageFactory.java
@@ -0,0 +1,39 @@
+package com.alipay.antchain.bridge.commons.core.monitor;
+
+import com.alipay.antchain.bridge.commons.exception.AntChainBridgeCommonsException;
+import com.alipay.antchain.bridge.commons.exception.CommonsErrorCodeEnum;
+
+public class MonitorMessageFactory {
+
+ public static IMonitorMessage createMonitorMessage(byte[] rawMessage) {
+ IMonitorMessage monitorMessage = createAbstractMonitorMessage(MonitorMessageV1.MY_VERSION);
+ monitorMessage.decode(rawMessage);
+ return monitorMessage;
+ }
+
+ public static IMonitorMessage createMonitorMessage(int version, int monitorType, String monitorMsg, byte[] payload) {
+ AbstractMonitorMessage monitorMessage = createAbstractMonitorMessage(version);
+
+ monitorMessage.setMonitorType(monitorType);
+ monitorMessage.setMonitorMsg(monitorMsg);
+ monitorMessage.setPayload(payload);
+
+ return monitorMessage;
+ }
+
+ public static AbstractMonitorMessage createAbstractMonitorMessage(int version) {
+ AbstractMonitorMessage monitorMessage;
+ switch (version) {
+ case MonitorMessageV1.MY_VERSION:
+ monitorMessage = new MonitorMessageV1();
+ break;
+ default:
+ throw new AntChainBridgeCommonsException(
+ CommonsErrorCodeEnum.INCORRECT_MONITOR_MESSAGE_ERROR,
+ String.format("wrong version: %d", version)
+ );
+ }
+ return monitorMessage;
+ }
+
+}
diff --git a/acb-sdk/antchain-bridge-commons/src/main/java/com/alipay/antchain/bridge/commons/core/monitor/MonitorMessageV1.java b/acb-sdk/antchain-bridge-commons/src/main/java/com/alipay/antchain/bridge/commons/core/monitor/MonitorMessageV1.java
new file mode 100755
index 00000000..dfdfde20
--- /dev/null
+++ b/acb-sdk/antchain-bridge-commons/src/main/java/com/alipay/antchain/bridge/commons/core/monitor/MonitorMessageV1.java
@@ -0,0 +1,109 @@
+package com.alipay.antchain.bridge.commons.core.monitor;
+
+import cn.hutool.core.util.ByteUtil;
+import com.alipay.antchain.bridge.commons.exception.AntChainBridgeCommonsException;
+import com.alipay.antchain.bridge.commons.exception.CommonsErrorCodeEnum;
+
+import java.nio.ByteOrder;
+import java.nio.charset.StandardCharsets;
+
+public class MonitorMessageV1 extends AbstractMonitorMessage {
+
+ public static final int MY_VERSION = 1;
+
+ @Override
+ public void decode(byte[] rawMessage) {
+ int offset = rawMessage.length;
+
+ offset = extractMonitorType(rawMessage, offset);
+ offset = extractMonitorMsg(rawMessage, offset);
+ extractPayload(rawMessage, offset);
+ }
+
+ public int extractMonitorType(byte[] rawMessage, int offset) {
+ offset -= 4;
+ byte[] rawSeq = new byte[4];
+ System.arraycopy(rawMessage, offset, rawSeq, 0, 4);
+ this.setMonitorType(ByteUtil.bytesToInt(rawSeq, ByteOrder.BIG_ENDIAN));
+
+ return offset;
+ }
+
+ public int extractMonitorMsg(byte[] rawMessage, int offset) {
+ offset -= 4;
+ byte[] rawMonitorMsgLen = new byte[4];
+ System.arraycopy(rawMessage, offset, rawMonitorMsgLen, 0, 4);
+
+ byte[] monitorMsg = new byte[ByteUtil.bytesToInt(rawMonitorMsgLen, ByteOrder.BIG_ENDIAN)];
+ offset -= monitorMsg.length;
+ if (offset < 0) {
+ throw new AntChainBridgeCommonsException(
+ CommonsErrorCodeEnum.MONITOR_MESSAGE_DECODE_ERROR,
+ "length of error message in MonitorV1 is incorrect"
+ );
+ }
+ System.arraycopy(rawMessage, offset, monitorMsg, 0, monitorMsg.length);
+ this.setMonitorMsg(new String(monitorMsg));
+
+ return offset;
+ }
+
+ public int extractPayload(byte[] rawMessage, int offset) {
+ offset -= 4;
+ byte[] rawPayloadLen = new byte[4];
+ System.arraycopy(rawMessage, offset, rawPayloadLen, 0, 4);
+
+ byte[] payload = new byte[ByteUtil.bytesToInt(rawPayloadLen, ByteOrder.BIG_ENDIAN)];
+ offset -= payload.length;
+ if (offset < 0) {
+ throw new AntChainBridgeCommonsException(
+ CommonsErrorCodeEnum.MONITOR_MESSAGE_DECODE_ERROR,
+ "wrong payload or length of payload in MonitorV1"
+ );
+ }
+ System.arraycopy(rawMessage, offset, payload, 0, payload.length);
+ this.setPayload(payload);
+
+ return offset;
+ }
+
+ @Override
+ public byte[] encode() {
+ int rawMessageLen = 12 + this.getMonitorMsg().getBytes(StandardCharsets.UTF_8).length + this.getPayload().length;
+ byte[] rawMessage = new byte[rawMessageLen];
+
+ int offset = putMonitorType(rawMessage, rawMessageLen);
+ offset = putMonitorMsg(rawMessage, offset);
+ putPayload(rawMessage, offset);
+
+ return rawMessage;
+ }
+
+ private int putMonitorType(byte[] rawMessage, int offset) {
+ offset -= 4;
+ System.arraycopy(ByteUtil.intToBytes(this.getMonitorType(), ByteOrder.BIG_ENDIAN), 0, rawMessage, offset, 4);
+
+ return offset;
+ }
+
+ private int putMonitorMsg(byte[] rawMessage, int offset) {
+ offset -= 4;
+ System.arraycopy(ByteUtil.intToBytes(this.getMonitorMsg().getBytes(StandardCharsets.UTF_8).length, ByteOrder.BIG_ENDIAN), 0, rawMessage, offset, 4);
+
+ offset -= this.getMonitorMsg().length();
+ System.arraycopy(this.getMonitorMsg().getBytes(), 0, rawMessage, offset, this.getMonitorMsg().length());
+
+ return offset;
+ }
+
+ private int putPayload(byte[] rawMessage, int offset) {
+ offset -= 4;
+ System.arraycopy(ByteUtil.intToBytes(this.getPayload().length, ByteOrder.BIG_ENDIAN), 0, rawMessage, offset, 4);
+
+ offset -= this.getPayload().length;
+ System.arraycopy(this.getPayload(), 0, rawMessage, offset, this.getPayload().length);
+
+ return offset;
+ }
+
+}
diff --git a/acb-sdk/antchain-bridge-commons/src/main/java/com/alipay/antchain/bridge/commons/core/monitor/MonitorOrderFactory.java b/acb-sdk/antchain-bridge-commons/src/main/java/com/alipay/antchain/bridge/commons/core/monitor/MonitorOrderFactory.java
new file mode 100644
index 00000000..913874d8
--- /dev/null
+++ b/acb-sdk/antchain-bridge-commons/src/main/java/com/alipay/antchain/bridge/commons/core/monitor/MonitorOrderFactory.java
@@ -0,0 +1,41 @@
+package com.alipay.antchain.bridge.commons.core.monitor;
+
+import com.alipay.antchain.bridge.commons.exception.AntChainBridgeCommonsException;
+import com.alipay.antchain.bridge.commons.exception.CommonsErrorCodeEnum;
+
+public class MonitorOrderFactory {
+
+ public static IMonitorOrder createMonitorOrder(int version, String product, String domain, long monitorOrderType,
+ String senderDomain, String fromAddress,
+ String receiverDomain, String toAddress,
+ String transactionContent, String extra) {
+ AbstractMonitorOrder monitorOrder = createAbstractMonitorOrder(version);
+
+ monitorOrder.setProduct(product);
+ monitorOrder.setDomain(domain);
+ monitorOrder.setMonitorOrderType(monitorOrderType);
+ monitorOrder.setSenderDomain(senderDomain);
+ monitorOrder.setFromAddress(fromAddress);
+ monitorOrder.setReceiverDomain(receiverDomain);
+ monitorOrder.setToAddress(toAddress);
+ monitorOrder.setTransactionContent(transactionContent);
+ monitorOrder.setExtra(extra);
+
+ return monitorOrder;
+ }
+
+ private static AbstractMonitorOrder createAbstractMonitorOrder(int version) {
+ AbstractMonitorOrder monitorOrder;
+ switch (version) {
+ case MonitorOrderV1.MY_VERSION:
+ monitorOrder = new MonitorOrderV1();
+ break;
+ default:
+ throw new AntChainBridgeCommonsException(
+ CommonsErrorCodeEnum.INCORRECT_MONITOR_ORDER,
+ String.format("wrong version: %d", version)
+ );
+ }
+ return monitorOrder;
+ }
+}
diff --git a/acb-sdk/antchain-bridge-commons/src/main/java/com/alipay/antchain/bridge/commons/core/monitor/MonitorOrderV1.java b/acb-sdk/antchain-bridge-commons/src/main/java/com/alipay/antchain/bridge/commons/core/monitor/MonitorOrderV1.java
new file mode 100644
index 00000000..f07fc509
--- /dev/null
+++ b/acb-sdk/antchain-bridge-commons/src/main/java/com/alipay/antchain/bridge/commons/core/monitor/MonitorOrderV1.java
@@ -0,0 +1,123 @@
+package com.alipay.antchain.bridge.commons.core.monitor;
+
+import cn.hutool.core.util.ByteUtil;
+
+import java.nio.ByteOrder;
+import java.nio.charset.StandardCharsets;
+
+public class MonitorOrderV1 extends AbstractMonitorOrder {
+
+ public static final int MY_VERSION = 1;
+
+ // no need to do this
+ @Override
+ public void decode(byte[] rawMessage) { }
+
+ @Override
+ public byte[] encode() {
+ // monitorOrderType only needs 4 bytes
+ int rawMessageLen = 28 + this.getProduct().getBytes(StandardCharsets.UTF_8).length +
+ this.getDomain().getBytes(StandardCharsets.UTF_8).length +
+ this.getSenderDomain().getBytes(StandardCharsets.UTF_8).length +
+ this.getRawFromAddress().length +
+ this.getReceiverDomain().getBytes(StandardCharsets.UTF_8).length +
+ this.getRawToAddress().length +
+ this.getTransactionContent().getBytes(StandardCharsets.UTF_8).length +
+ this.getExtra().getBytes(StandardCharsets.UTF_8).length;
+ byte[] rawMessage = new byte[rawMessageLen];
+
+ int offset = putProduct(rawMessage, rawMessageLen);
+ offset = putDomain(rawMessage, offset);
+ offset = putMonitorOrderType(rawMessage, offset);
+ offset = putSenderDomain(rawMessage, offset);
+ offset = putRawFromAddress(rawMessage, offset);
+ offset = putReceiverDomain(rawMessage, offset);
+ offset = putRawToAddress(rawMessage, offset);
+ offset = putTransactionContent(rawMessage, offset);
+ putExtra(rawMessage, offset);
+
+ return rawMessage;
+ }
+
+ private int putProduct(byte[] rawMessage, int offset) {
+ offset -= 4;
+ System.arraycopy(ByteUtil.intToBytes(this.getProduct().getBytes(StandardCharsets.UTF_8).length, ByteOrder.BIG_ENDIAN), 0, rawMessage, offset, 4);
+
+ offset -= this.getProduct().length();
+ System.arraycopy(this.getProduct().getBytes(), 0, rawMessage, offset, this.getProduct().length());
+
+ return offset;
+ }
+
+ private int putDomain(byte[] rawMessage, int offset) {
+ offset -= 4;
+ System.arraycopy(ByteUtil.intToBytes(this.getDomain().getBytes(StandardCharsets.UTF_8).length, ByteOrder.BIG_ENDIAN), 0, rawMessage, offset, 4);
+
+ offset -= this.getDomain().length();
+ System.arraycopy(this.getDomain().getBytes(), 0, rawMessage, offset, this.getDomain().length());
+
+ return offset;
+ }
+
+ private int putMonitorOrderType(byte[] rawMessage, int offset) {
+ offset -= 4;
+ System.arraycopy(ByteUtil.intToBytes((int)this.getMonitorOrderType(), ByteOrder.BIG_ENDIAN), 0, rawMessage, offset, 4);
+
+ return offset;
+ }
+
+ private int putSenderDomain(byte[] rawMessage, int offset) {
+ offset -= 4;
+ System.arraycopy(ByteUtil.intToBytes(this.getSenderDomain().getBytes(StandardCharsets.UTF_8).length, ByteOrder.BIG_ENDIAN), 0, rawMessage, offset, 4);
+
+ offset -= this.getSenderDomain().length();
+ System.arraycopy(this.getSenderDomain().getBytes(), 0, rawMessage, offset, this.getSenderDomain().length());
+
+ return offset;
+ }
+
+ private int putRawFromAddress(byte[] rawMessage, int offset) {
+ offset -= 32;
+ System.arraycopy(this.getRawFromAddress(), 0, rawMessage, offset, 32);
+
+ return offset;
+ }
+
+ private int putReceiverDomain(byte[] rawMessage, int offset) {
+ offset -= 4;
+ System.arraycopy(ByteUtil.intToBytes(this.getReceiverDomain().getBytes(StandardCharsets.UTF_8).length, ByteOrder.BIG_ENDIAN), 0, rawMessage, offset, 4);
+
+ offset -= this.getReceiverDomain().length();
+ System.arraycopy(this.getReceiverDomain().getBytes(), 0, rawMessage, offset, this.getReceiverDomain().length());
+
+ return offset;
+ }
+
+ private int putRawToAddress(byte[] rawMessage, int offset) {
+ offset -= 32;
+ System.arraycopy(this.getRawToAddress(), 0, rawMessage, offset, 32);
+
+ return offset;
+ }
+
+ private int putTransactionContent(byte[] rawMessage, int offset) {
+ offset -= 4;
+ System.arraycopy(ByteUtil.intToBytes(this.getTransactionContent().getBytes(StandardCharsets.UTF_8).length, ByteOrder.BIG_ENDIAN), 0, rawMessage, offset, 4);
+
+ offset -= this.getTransactionContent().length();
+ System.arraycopy(this.getTransactionContent().getBytes(), 0, rawMessage, offset, this.getTransactionContent().length());
+
+ return offset;
+ }
+
+ private int putExtra(byte[] rawMessage, int offset) {
+ offset -= 4;
+ System.arraycopy(ByteUtil.intToBytes(this.getExtra().getBytes(StandardCharsets.UTF_8).length, ByteOrder.BIG_ENDIAN), 0, rawMessage, offset, 4);
+
+ offset -= this.getExtra().length();
+ System.arraycopy(this.getExtra().getBytes(), 0, rawMessage, offset, this.getExtra().length());
+
+ return offset;
+ }
+
+}
diff --git a/acb-sdk/antchain-bridge-commons/src/main/java/com/alipay/antchain/bridge/commons/exception/CommonsErrorCodeEnum.java b/acb-sdk/antchain-bridge-commons/src/main/java/com/alipay/antchain/bridge/commons/exception/CommonsErrorCodeEnum.java
index 19690cd3..1a0fdc4c 100644
--- a/acb-sdk/antchain-bridge-commons/src/main/java/com/alipay/antchain/bridge/commons/exception/CommonsErrorCodeEnum.java
+++ b/acb-sdk/antchain-bridge-commons/src/main/java/com/alipay/antchain/bridge/commons/exception/CommonsErrorCodeEnum.java
@@ -140,7 +140,22 @@ public enum CommonsErrorCodeEnum {
/**
*
*/
- BCDNS_BID_PUBLIC_KEY_ALGO_NOT_SUPPORT("0706", "BID pubkey algo not support");
+ BCDNS_BID_PUBLIC_KEY_ALGO_NOT_SUPPORT("0706", "BID pubkey algo not support"),
+
+ /**
+ * Something wrong about {@code MonitorMessage}, like version, etc.
+ */
+ INCORRECT_MONITOR_MESSAGE_ERROR("0801", "wrong monitor"),
+
+ /**
+ * Code shows where decode {@code MonitorMessage} failed
+ */
+ MONITOR_MESSAGE_DECODE_ERROR("0802", "monitor decode failed"),
+
+ /**
+ * Something wrong about {@code MonitorOrder}, like version, etc.
+ */
+ INCORRECT_MONITOR_ORDER("0803", "wrong monitor order");
/**
* Error code for errors happened in project {@code antchain-bridge-commons}
diff --git a/acb-sdk/antchain-bridge-spi/src/main/java/com/alipay/antchain/bridge/plugins/spi/bbc/core/write/IAntChainBridgeDataWriter.java b/acb-sdk/antchain-bridge-spi/src/main/java/com/alipay/antchain/bridge/plugins/spi/bbc/core/write/IAntChainBridgeDataWriter.java
index 77a4196b..c4af44ce 100644
--- a/acb-sdk/antchain-bridge-spi/src/main/java/com/alipay/antchain/bridge/plugins/spi/bbc/core/write/IAntChainBridgeDataWriter.java
+++ b/acb-sdk/antchain-bridge-spi/src/main/java/com/alipay/antchain/bridge/plugins/spi/bbc/core/write/IAntChainBridgeDataWriter.java
@@ -40,7 +40,7 @@
* interfaces like {@link IAMWriter}, {@link ISDPWriter}.
*
*/
-public interface IAntChainBridgeDataWriter extends IAMWriter, ISDPWriter {
+public interface IAntChainBridgeDataWriter extends IAMWriter, ISDPWriter, IMonitorWriter {
/**
* Set up the AuthMessage contract.
@@ -65,6 +65,10 @@ public interface IAntChainBridgeDataWriter extends IAMWriter, ISDPWriter {
*/
void setupSDPMessageContract();
+ /**
+ * Set up the monitor contracts, including monitor and monitorVerifier contract
+ */
+ void setupMonitorContract();
/**
* Set up the PTC contracts. For example PTCHub and its verify contracts
*/
diff --git a/acb-sdk/antchain-bridge-spi/src/main/java/com/alipay/antchain/bridge/plugins/spi/bbc/core/write/IMonitorWriter.java b/acb-sdk/antchain-bridge-spi/src/main/java/com/alipay/antchain/bridge/plugins/spi/bbc/core/write/IMonitorWriter.java
new file mode 100755
index 00000000..7d011b87
--- /dev/null
+++ b/acb-sdk/antchain-bridge-spi/src/main/java/com/alipay/antchain/bridge/plugins/spi/bbc/core/write/IMonitorWriter.java
@@ -0,0 +1,18 @@
+package com.alipay.antchain.bridge.plugins.spi.bbc.core.write;
+
+import com.alipay.antchain.bridge.commons.core.base.CrossChainMessageReceipt;
+
+/**
+ * Through {@code IMonitorWriter}, you can write data
+ * to the storage of the MonitorContract.
+ */
+public interface IMonitorWriter {
+
+ void setProtocolInMonitor(String contractAddress);
+
+ void setMonitorControl(int monitorType);
+
+ void setPtcHubInMonitorVerifier(String contractAddress);
+
+ CrossChainMessageReceipt relayMonitorOrder(String committeeId, String signAlgo, byte[] rawProof, byte[] rawMonitorOrder);
+}
\ No newline at end of file
diff --git a/acb-sdk/antchain-bridge-spi/src/main/java/com/alipay/antchain/bridge/plugins/spi/bbc/core/write/ISDPWriter.java b/acb-sdk/antchain-bridge-spi/src/main/java/com/alipay/antchain/bridge/plugins/spi/bbc/core/write/ISDPWriter.java
index 798c54a7..534bba44 100644
--- a/acb-sdk/antchain-bridge-spi/src/main/java/com/alipay/antchain/bridge/plugins/spi/bbc/core/write/ISDPWriter.java
+++ b/acb-sdk/antchain-bridge-spi/src/main/java/com/alipay/antchain/bridge/plugins/spi/bbc/core/write/ISDPWriter.java
@@ -41,4 +41,6 @@ public interface ISDPWriter {
* @param domain the domain value
*/
void setLocalDomain(String domain);
+
+ void setMonitorContract(String contractAddress);
}
diff --git a/acb-sdk/pluginset/ethereum2/offchain-plugin/pom.xml b/acb-sdk/pluginset/ethereum2/offchain-plugin/pom.xml
index 13847e02..1a7c0aa8 100644
--- a/acb-sdk/pluginset/ethereum2/offchain-plugin/pom.xml
+++ b/acb-sdk/pluginset/ethereum2/offchain-plugin/pom.xml
@@ -236,10 +236,12 @@
+ Monitor
AuthMsg
SDPMsg
PtcHub
CommitteePtcVerifier
+ MonitorVerifier
AppContract
ProxyAdmin
TransparentUpgradeableProxy
diff --git a/acb-sdk/pluginset/ethereum2/offchain-plugin/src/main/java/com/alipay/antchain/bridge/plugins/ethereum2/EthereumBBCService.java b/acb-sdk/pluginset/ethereum2/offchain-plugin/src/main/java/com/alipay/antchain/bridge/plugins/ethereum2/EthereumBBCService.java
index 12df3c6a..92991dfa 100644
--- a/acb-sdk/pluginset/ethereum2/offchain-plugin/src/main/java/com/alipay/antchain/bridge/plugins/ethereum2/EthereumBBCService.java
+++ b/acb-sdk/pluginset/ethereum2/offchain-plugin/src/main/java/com/alipay/antchain/bridge/plugins/ethereum2/EthereumBBCService.java
@@ -26,10 +26,7 @@
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alipay.antchain.bridge.commons.bbc.AbstractBBCContext;
-import com.alipay.antchain.bridge.commons.bbc.syscontract.AuthMessageContract;
-import com.alipay.antchain.bridge.commons.bbc.syscontract.ContractStatusEnum;
-import com.alipay.antchain.bridge.commons.bbc.syscontract.PTCContract;
-import com.alipay.antchain.bridge.commons.bbc.syscontract.SDPContract;
+import com.alipay.antchain.bridge.commons.bbc.syscontract.*;
import com.alipay.antchain.bridge.commons.bcdns.AbstractCrossChainCertificate;
import com.alipay.antchain.bridge.commons.bcdns.CrossChainCertificateTypeEnum;
import com.alipay.antchain.bridge.commons.bcdns.utils.CrossChainCertificateUtil;
@@ -102,6 +99,7 @@ public void startup(AbstractBBCContext abstractBBCContext) {
authMessageContract.setContractAddress(this.config.getAmContractAddressDeployed());
authMessageContract.setStatus(ContractStatusEnum.CONTRACT_DEPLOYED);
this.bbcContext.setAuthMessageContract(authMessageContract);
+ getBBCLogger().info("set the pre-deployed am contracts into context: {}: ", this.config.getAmContractAddressDeployed());
}
if (ObjectUtil.isNull(abstractBBCContext.getSdpContract())
@@ -110,6 +108,16 @@ public void startup(AbstractBBCContext abstractBBCContext) {
sdpContract.setContractAddress(this.config.getSdpContractAddressDeployed());
sdpContract.setStatus(ContractStatusEnum.CONTRACT_DEPLOYED);
this.bbcContext.setSdpContract(sdpContract);
+ getBBCLogger().info("set the pre-deployed sdp contracts into context: {}: ", this.config.getSdpContractAddressDeployed());
+ }
+
+ if (ObjectUtil.isNull(abstractBBCContext.getMonitorContract())
+ && StrUtil.isNotEmpty(this.config.getMonitorContractAddressDeployed())) {
+ MonitorContract monitorContract = new MonitorContract();
+ monitorContract.setContractAddress(this.config.getMonitorContractAddressDeployed());
+ monitorContract.setStatus(ContractStatusEnum.CONTRACT_DEPLOYED);
+ this.bbcContext.setMonitorContract(monitorContract);
+ getBBCLogger().info("set the pre-deployed monitor contracts into context: {}: ", this.config.getMonitorContractAddressDeployed());
}
if (ObjectUtil.isNull(abstractBBCContext.getPtcContract())
@@ -118,6 +126,7 @@ public void startup(AbstractBBCContext abstractBBCContext) {
ptcContract.setContractAddress(this.config.getPtcHubContractAddressDeployed());
ptcContract.setStatus(ContractStatusEnum.CONTRACT_READY);
this.bbcContext.setPtcContract(ptcContract);
+ getBBCLogger().info("set the pre-deployed ptc contracts into context: {}: ", this.config.getPtcHubContractAddressDeployed());
}
if (ObjectUtil.isEmpty(this.config.getProxyAdmin()) && this.config.isUpgradableContracts()) {
@@ -140,11 +149,13 @@ public AbstractBBCContext getContext() {
throw new RuntimeException("empty bbc context");
}
- getBBCLogger().debug("ETH BBCService context (amAddr: {}, amStatus: {}, sdpAddr: {}, sdpStatus: {})",
+ getBBCLogger().debug("ETH BBCService context (amAddr: {}, amStatus: {}, sdpAddr: {}, sdpStatus: {}, monitorAddr: {}, monitorStatus: {})",
this.bbcContext.getAuthMessageContract() != null ? this.bbcContext.getAuthMessageContract().getContractAddress() : "",
this.bbcContext.getAuthMessageContract() != null ? this.bbcContext.getAuthMessageContract().getStatus() : "",
this.bbcContext.getSdpContract() != null ? this.bbcContext.getSdpContract().getContractAddress() : "",
- this.bbcContext.getSdpContract() != null ? this.bbcContext.getSdpContract().getStatus() : ""
+ this.bbcContext.getSdpContract() != null ? this.bbcContext.getSdpContract().getStatus() : "",
+ this.bbcContext.getMonitorContract() != null ? this.bbcContext.getMonitorContract().getContractAddress() : "",
+ this.bbcContext.getMonitorContract() != null ? this.bbcContext.getMonitorContract().getStatus() : ""
);
this.bbcContext.setConfForBlockchainClient(this.config.toJsonString().getBytes());
@@ -293,6 +304,32 @@ public void setupSDPMessageContract() {
getBBCLogger().info("setup sdp contract successful: {}", sdpContractAddr);
}
+ @Override
+ public void setupMonitorContract() {
+ // 1. check context
+ if (ObjectUtil.isNull(this.bbcContext)) {
+ throw new RuntimeException("empty bbc context");
+ }
+ if (this.config.isUpgradableContracts() && StrUtil.isEmpty(this.config.getProxyAdmin())) {
+ throw new RuntimeException("empty proxy admin");
+ }
+ if (ObjectUtil.isNotNull(this.bbcContext.getMonitorContract())
+ && StrUtil.isNotEmpty(this.bbcContext.getMonitorContract().getContractAddress())) {
+ // If the contract has been pre-deployed and the contract address is configured in the configuration file,
+ // there is no need to redeploy.
+ return;
+ }
+
+ // 2. deploy contract
+ var monitorContractAddr = acbEthClient.deployMonitorContract(acbEthClient.deployMonitorVerifierContract());
+
+ MonitorContract monitorContract = new MonitorContract();
+ monitorContract.setContractAddress(monitorContractAddr);
+ monitorContract.setStatus(ContractStatusEnum.CONTRACT_DEPLOYED);
+ bbcContext.setMonitorContract(monitorContract);
+ getBBCLogger().info("setup monitor contract successful: {}", monitorContractAddr);
+ }
+
@Override
public long querySDPMessageSeq(String senderDomain, String senderID, String receiverDomain, String receiverID) {
// 1. check context
@@ -347,7 +384,38 @@ public void setAmContract(String contractAddress) {
// 4. update sdp contract status
try {
if (!StrUtil.isEmpty(acbEthClient.getAmContractFromSdp(this.bbcContext.getSdpContract().getContractAddress()))
- && !isByteArrayZero(acbEthClient.getLocalDomainFromSdp(this.bbcContext.getSdpContract().getContractAddress()))) {
+ && !isByteArrayZero(acbEthClient.getLocalDomainFromSdp(this.bbcContext.getSdpContract().getContractAddress()))
+ && !"0x0000000000000000000000000000000000000000".equalsIgnoreCase(acbEthClient.getMonitorContractFromSdp(this.bbcContext.getSdpContract().getContractAddress()))
+ ) {
+ this.bbcContext.getSdpContract().setStatus(ContractStatusEnum.CONTRACT_READY);
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(
+ String.format(
+ "failed to update sdp contract status (address: %s)",
+ this.bbcContext.getSdpContract().getContractAddress()
+ ), e);
+ }
+ }
+
+ @Override
+ public void setMonitorContract(String contractAddress) {
+ // 1. check context
+ if (ObjectUtil.isNull(this.bbcContext)) {
+ throw new RuntimeException("empty bbc context");
+ }
+ if (ObjectUtil.isNull(this.bbcContext.getSdpContract())) {
+ throw new RuntimeException("empty sdp contract in bbc context");
+ }
+
+ acbEthClient.setMonitorContractToSdp(this.bbcContext.getSdpContract().getContractAddress(), contractAddress);
+
+ // 4. update sdp contract status
+ try {
+ if (!StrUtil.isEmpty(acbEthClient.getAmContractFromSdp(this.bbcContext.getSdpContract().getContractAddress()))
+ && !isByteArrayZero(acbEthClient.getLocalDomainFromSdp(this.bbcContext.getSdpContract().getContractAddress()))
+ && !"0x0000000000000000000000000000000000000000".equalsIgnoreCase(acbEthClient.getMonitorContractFromSdp(this.bbcContext.getSdpContract().getContractAddress()))
+ ) {
this.bbcContext.getSdpContract().setStatus(ContractStatusEnum.CONTRACT_READY);
}
} catch (Exception e) {
@@ -374,7 +442,9 @@ public void setLocalDomain(String domain) {
// 4. update sdp contract status
try {
if (!StrUtil.isEmpty(acbEthClient.getAmContractFromSdp(this.bbcContext.getSdpContract().getContractAddress()))
- && !isByteArrayZero(acbEthClient.getLocalDomainFromSdp(this.bbcContext.getSdpContract().getContractAddress()))) {
+ && !isByteArrayZero(acbEthClient.getLocalDomainFromSdp(this.bbcContext.getSdpContract().getContractAddress()))
+ && !"0x0000000000000000000000000000000000000000".equalsIgnoreCase(acbEthClient.getMonitorContractFromSdp(this.bbcContext.getSdpContract().getContractAddress()))
+ ) {
this.bbcContext.getSdpContract().setStatus(ContractStatusEnum.CONTRACT_READY);
}
} catch (Exception e) {
@@ -386,6 +456,120 @@ public void setLocalDomain(String domain) {
}
}
+ @Override
+ public void setProtocolInMonitor(String protocolAddress) {
+ // 1. check context
+ if (ObjectUtil.isNull(this.bbcContext)) {
+ throw new RuntimeException("empty bbc context");
+ }
+ if (ObjectUtil.isNull(this.bbcContext.getMonitorContract())) {
+ throw new RuntimeException("empty monitor contract in bbc context");
+ }
+
+ acbEthClient.setProtocolToMonitor(this.bbcContext.getMonitorContract().getContractAddress(), protocolAddress);
+
+ // 4. update monitor contract status
+ try {
+ String monitorVerifier = acbEthClient.getMonitorVerifierFromMonitor(this.bbcContext.getMonitorContract().getContractAddress());
+ if (!"0x0000000000000000000000000000000000000000".equalsIgnoreCase(monitorVerifier)
+ && !"0x0000000000000000000000000000000000000000".equalsIgnoreCase(acbEthClient.getPtcHubFromMonitorVerifier(monitorVerifier))) {
+ if (!"0x0000000000000000000000000000000000000000".equalsIgnoreCase(acbEthClient.getProtocolFromMonitor(this.bbcContext.getMonitorContract().getContractAddress()))
+ && acbEthClient.getMonitorControlFromMonitor(this.bbcContext.getMonitorContract().getContractAddress()) != 0) {
+ this.bbcContext.getMonitorContract().setStatus(ContractStatusEnum.CONTRACT_READY);
+ }
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(
+ String.format(
+ "failed to update monitor contract status (address: %s)",
+ this.bbcContext.getMonitorContract().getContractAddress()
+ ), e);
+ }
+ }
+
+ @Override
+ public void setMonitorControl(int monitorType) {
+ // 1. check context
+ if (ObjectUtil.isNull(this.bbcContext)) {
+ throw new RuntimeException("empty bbc context");
+ }
+ if (ObjectUtil.isNull(this.bbcContext.getMonitorContract())) {
+ throw new RuntimeException("empty monitor contract in bbc context");
+ }
+
+ acbEthClient.setMonitorControl(this.bbcContext.getMonitorContract().getContractAddress(), monitorType);
+
+ // 4. update monitor contract status
+ try {
+ String monitorVerifier = acbEthClient.getMonitorVerifierFromMonitor(this.bbcContext.getMonitorContract().getContractAddress());
+ if (!"0x0000000000000000000000000000000000000000".equalsIgnoreCase(monitorVerifier)
+ && !"0x0000000000000000000000000000000000000000".equalsIgnoreCase(acbEthClient.getPtcHubFromMonitorVerifier(monitorVerifier))) {
+ if (!"0x0000000000000000000000000000000000000000".equalsIgnoreCase(acbEthClient.getProtocolFromMonitor(this.bbcContext.getMonitorContract().getContractAddress()))
+ && acbEthClient.getMonitorControlFromMonitor(this.bbcContext.getMonitorContract().getContractAddress()) != 0) {
+ this.bbcContext.getMonitorContract().setStatus(ContractStatusEnum.CONTRACT_READY);
+ }
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(
+ String.format(
+ "failed to update monitor contract status (address: %s)",
+ this.bbcContext.getMonitorContract().getContractAddress()
+ ), e);
+ }
+ }
+
+ @Override
+ public void setPtcHubInMonitorVerifier(String contractAddress) {
+ // 1. check context
+ if (ObjectUtil.isNull(this.bbcContext)) {
+ throw new RuntimeException("empty bbc context");
+ }
+ if (ObjectUtil.isNull(this.bbcContext.getMonitorContract())) {
+ throw new RuntimeException("empty monitor contract in bbc context");
+ }
+ if (ObjectUtil.isNull(this.bbcContext.getPtcContract())) {
+ throw new RuntimeException("empty PtcHub contract in bbc context");
+ }
+ String monitorVerifier = acbEthClient.getMonitorVerifierFromMonitor(this.bbcContext.getMonitorContract().getContractAddress());
+ if ("0x0000000000000000000000000000000000000000".equalsIgnoreCase(monitorVerifier)) {
+ throw new RuntimeException("not set monitor verifier contract in monitor contract yet");
+ }
+
+ acbEthClient.setPtcHubToMonitorVerifier(monitorVerifier, contractAddress);
+
+ // 4. update monitor contract status
+ try {
+ if (!"0x0000000000000000000000000000000000000000".equalsIgnoreCase(acbEthClient.getPtcHubFromMonitorVerifier(monitorVerifier)) &&
+ !"0x0000000000000000000000000000000000000000".equalsIgnoreCase(acbEthClient.getProtocolFromMonitor(this.bbcContext.getMonitorContract().getContractAddress()))
+ && acbEthClient.getMonitorControlFromMonitor(this.bbcContext.getMonitorContract().getContractAddress()) != 0 ) {
+ this.bbcContext.getMonitorContract().setStatus(ContractStatusEnum.CONTRACT_READY);
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(
+ String.format(
+ "failed to update monitor contract status (address: %s)",
+ this.bbcContext.getMonitorContract().getContractAddress()
+ ), e);
+ }
+
+ }
+
+ @Override
+ public CrossChainMessageReceipt relayMonitorOrder(String committeeId, String signAlgo, byte[] rawProof, byte[] rawMonitorOrder) {
+ // 1. check context
+ if (ObjectUtil.isNull(this.bbcContext)) {
+ throw new RuntimeException("empty bbc context");
+ }
+ if (ObjectUtil.isNull(this.bbcContext.getMonitorContract())) {
+ throw new RuntimeException("empty monitor contract in bbc context");
+ }
+
+ getBBCLogger().debug("relay MonitorOrder to {} ", this.bbcContext.getMonitorContract().getContractAddress());
+
+ return acbEthClient.relayMonitorOrderToMonitor(this.bbcContext.getMonitorContract().getContractAddress(), committeeId, signAlgo, rawProof, rawMonitorOrder);
+ }
+
+
@Override
public CrossChainMessageReceipt relayAuthMessage(byte[] rawMessage) {
// 1. check context
@@ -499,6 +683,7 @@ public boolean hasPTCVerifyAnchor(ObjectIdentity ptcOwnerOid, BigInteger version
return acbEthClient.hasPTCVerifyAnchor(this.bbcContext.getPtcContract().getContractAddress(), ptcOwnerOid, version);
}
+ // in this version, ptc contract needs to be deployed after monitor contract, since monitor verifier is necessary for the init
@Override
public void setupPTCContract() {
// 1. check context
@@ -525,7 +710,8 @@ public void setupPTCContract() {
}
// 2. deploy contract
- String ptcHubContractAddr = acbEthClient.deployPtcHubContract(bcdnsRootCert, acbEthClient.deployCommitteeVerifierContract());
+ String ptcHubContractAddr = acbEthClient.deployPtcHubContract(bcdnsRootCert, acbEthClient.deployCommitteeVerifierContract(),
+ acbEthClient.getMonitorVerifierFromMonitor(this.bbcContext.getMonitorContract().getContractAddress()));
PTCContract ptcContract = new PTCContract();
ptcContract.setContractAddress(ptcHubContractAddr);
diff --git a/acb-sdk/pluginset/ethereum2/offchain-plugin/src/main/java/com/alipay/antchain/bridge/plugins/ethereum2/conf/EthereumConfig.java b/acb-sdk/pluginset/ethereum2/offchain-plugin/src/main/java/com/alipay/antchain/bridge/plugins/ethereum2/conf/EthereumConfig.java
index 41a482d1..5814323d 100644
--- a/acb-sdk/pluginset/ethereum2/offchain-plugin/src/main/java/com/alipay/antchain/bridge/plugins/ethereum2/conf/EthereumConfig.java
+++ b/acb-sdk/pluginset/ethereum2/offchain-plugin/src/main/java/com/alipay/antchain/bridge/plugins/ethereum2/conf/EthereumConfig.java
@@ -87,6 +87,9 @@ public enum CrossChainMessageScanPolicyEnum {
@JSONField
private String sdpContractAddressDeployed;
+ @JSONField
+ private String monitorContractAddressDeployed;
+
@JSONField
private BlockHeightPolicyEnum blockHeightPolicy = BlockHeightPolicyEnum.FINALIZED;
diff --git a/acb-sdk/pluginset/ethereum2/offchain-plugin/src/main/java/com/alipay/antchain/bridge/plugins/ethereum2/core/AcbEthClient.java b/acb-sdk/pluginset/ethereum2/offchain-plugin/src/main/java/com/alipay/antchain/bridge/plugins/ethereum2/core/AcbEthClient.java
index 532adb5a..1de0c219 100644
--- a/acb-sdk/pluginset/ethereum2/offchain-plugin/src/main/java/com/alipay/antchain/bridge/plugins/ethereum2/core/AcbEthClient.java
+++ b/acb-sdk/pluginset/ethereum2/offchain-plugin/src/main/java/com/alipay/antchain/bridge/plugins/ethereum2/core/AcbEthClient.java
@@ -256,6 +256,190 @@ public String deploySdpContract() {
return sdpMsg.getContractAddress();
}
+ public String deployMonitorContract(String monitorVerifierContractAddress) {
+ Monitor monitor;
+ try {
+ monitor = Monitor.deploy(
+ web3j,
+ rawTransactionManager,
+ new AcbGasProvider(
+ this.contractGasPriceProvider,
+ createDeployGasLimitProvider(Monitor.BINARY)
+ )
+ ).send();
+ } catch (Exception e) {
+ throw new RuntimeException("failed to deploy monitor", e);
+ }
+
+ String monitorContractAddress = monitor.getContractAddress();
+
+ if (this.config.isUpgradableContracts()) {
+ TransparentUpgradeableProxy proxy;
+ try {
+ proxy = TransparentUpgradeableProxy.deploy(
+ web3j,
+ rawTransactionManager,
+ new AcbGasProvider(
+ this.contractGasPriceProvider,
+ createDeployGasLimitProvider(
+ TransparentUpgradeableProxy.BINARY +
+ FunctionEncoder.encodeConstructor(ListUtil.toList(
+ new org.web3j.abi.datatypes.Address(monitor.getContractAddress()),
+ new org.web3j.abi.datatypes.Address(this.config.getProxyAdmin()),
+ new DynamicBytes(HexUtil.decodeHex("e1c7392a"))
+ ))
+ )
+ ),
+ BigInteger.ZERO,
+ monitor.getContractAddress(),
+ this.config.getProxyAdmin(),
+ HexUtil.decodeHex("e1c7392a")
+ ).send();
+ } catch (Exception e) {
+ throw new RuntimeException("failed to deploy monitor contract", e);
+ }
+ getBbcLogger().info("deploy proxy contract for monitor: {}", proxy.getContractAddress());
+ monitorContractAddress = proxy.getContractAddress();
+ }
+
+ // set monitor verifier to monitor
+ monitor = Monitor.load(
+ monitorContractAddress,
+ web3j,
+ rawTransactionManager,
+ new AcbGasProvider(
+ this.contractGasPriceProvider,
+ createEthCallGasLimitProvider(
+ monitorContractAddress,
+ new Function(
+ Monitor.FUNC_SETMONITORVERIFIER,
+ ListUtil.toList(new org.web3j.abi.datatypes.Address(monitorVerifierContractAddress)),
+ ListUtil.empty()
+ )
+ )
+ )
+ );
+
+ try {
+ TransactionReceipt receipt = monitor.setMonitorVerifier(monitorVerifierContractAddress).send();
+ if (!receipt.isStatusOK()) {
+ throw new RuntimeException(
+ StrUtil.format(
+ "transaction {} shows failed when set monitor verifier {} to monitor {}: {}",
+ receipt.getTransactionHash(), monitorVerifierContractAddress, monitorContractAddress, receipt.getRevertReason()
+ )
+ );
+ }
+ getBbcLogger().info(
+ "set monitor verifier {} to monitor {} by tx {} ", monitorVerifierContractAddress, monitorContractAddress, receipt.getTransactionHash()
+ );
+ } catch (Exception e) {
+ throw new RuntimeException(
+ String.format(
+ "unexpected failure when setting monitor verifier %s to monitor %s",
+ monitorVerifierContractAddress, monitorContractAddress
+ ), e
+ );
+ }
+
+ return monitorContractAddress;
+ }
+
+ public String deployMonitorVerifierContract() {
+ MonitorVerifier monitorVerifier;
+ try {
+ monitorVerifier = MonitorVerifier.deploy(
+ web3j,
+ rawTransactionManager,
+ new AcbGasProvider(
+ this.contractGasPriceProvider,
+ createDeployGasLimitProvider(MonitorVerifier.BINARY)
+ )
+ ).send();
+ } catch (Exception e) {
+ throw new RuntimeException("failed to deploy MonitorVerifier", e);
+ }
+
+ if (this.config.isUpgradableContracts()) {
+ TransparentUpgradeableProxy proxy;
+ try {
+ proxy = TransparentUpgradeableProxy.deploy(
+ web3j,
+ rawTransactionManager,
+ new AcbGasProvider(
+ this.contractGasPriceProvider,
+ createDeployGasLimitProvider(
+ TransparentUpgradeableProxy.BINARY +
+ FunctionEncoder.encodeConstructor(ListUtil.toList(
+ new org.web3j.abi.datatypes.Address(monitorVerifier.getContractAddress()),
+ new org.web3j.abi.datatypes.Address(this.config.getProxyAdmin()),
+ new DynamicBytes(HexUtil.decodeHex("e1c7392a"))
+ ))
+ )
+ ),
+ BigInteger.ZERO,
+ monitorVerifier.getContractAddress(),
+ this.config.getProxyAdmin(),
+ HexUtil.decodeHex("e1c7392a")
+ ).send();
+ } catch (Exception e) {
+ throw new RuntimeException("failed to deploy monitorVerifier contract", e);
+ }
+ getBbcLogger().info("deploy proxy contract for monitorVerifier: {}", proxy.getContractAddress());
+ return proxy.getContractAddress();
+ }
+
+ return monitorVerifier.getContractAddress();
+ }
+
+ @SneakyThrows
+ public String getMonitorVerifierFromMonitor(String monitorContractAddress) {
+ Monitor monitor = Monitor.load(monitorContractAddress, web3j, rawTransactionManager, new DefaultGasProvider());
+ return monitor.getMonitorVerifier().send();
+ }
+
+ public void setPtcHubToMonitorVerifier(String monitorVerifierContractAddress, String ptcHubContractAddress) {
+ // 2. load monitor verifier contract
+ MonitorVerifier monitorVerifier = MonitorVerifier.load(
+ monitorVerifierContractAddress,
+ this.web3j,
+ this.rawTransactionManager,
+ new AcbGasProvider(
+ this.contractGasPriceProvider,
+ createEthCallGasLimitProvider(
+ monitorVerifierContractAddress,
+ new Function(
+ MonitorVerifier.FUNC_SETPTCHUBADDRESS,
+ ListUtil.toList(new org.web3j.abi.datatypes.Address(ptcHubContractAddress)), // inputs
+ Collections.emptyList()
+ )
+ )
+ )
+ );
+
+ // 3. set ptc hub to monitor verifier
+ try {
+ var receipt = monitorVerifier.setPtcHubAddress(ptcHubContractAddress).send();
+ getBbcLogger().info(
+ "set ptc hub {} to monitor verifier (address: {}) by tx {} ",
+ ptcHubContractAddress, monitorVerifierContractAddress, receipt.getTransactionHash()
+ );
+ } catch (Exception e) {
+ throw new RuntimeException(
+ String.format(
+ "failed to set ptc hub (address: %s) to monitor verifier %s",
+ ptcHubContractAddress, monitorVerifierContractAddress
+ ), e
+ );
+ }
+ }
+
+ @SneakyThrows
+ public String getPtcHubFromMonitorVerifier(String monitorVerifierContractAddress) {
+ MonitorVerifier monitorVerifier = MonitorVerifier.load(monitorVerifierContractAddress, web3j, rawTransactionManager, new DefaultGasProvider());
+ return monitorVerifier.getPtcHubAddress().send();
+ }
+
public long querySdpSeq(String sdpContractAddress, String senderDomain, String senderID, String receiverDomain, String receiverID) {
// 2. load sdpMsg
SDPMsg sdpMsg = SDPMsg.load(sdpContractAddress, web3j, rawTransactionManager, new DefaultGasProvider());
@@ -401,6 +585,198 @@ public void setLocalDomainToSdp(String sdpContractAddress, String localDomain) {
}
}
+ public void setMonitorContractToSdp(String sdpContractAddress, String monitorContractAddress) {
+ // 2. load sdp contract
+ SDPMsg sdp = SDPMsg.load(
+ sdpContractAddress,
+ this.web3j,
+ this.rawTransactionManager,
+ new AcbGasProvider(
+ this.contractGasPriceProvider,
+ createEthCallGasLimitProvider(
+ sdpContractAddress,
+ new Function(
+ SDPMsg.FUNC_SETMONITORCONTRACT,
+ ListUtil.toList(new org.web3j.abi.datatypes.Address(monitorContractAddress)), // inputs
+ Collections.emptyList()
+ )
+ )
+ )
+ );
+
+ // 3. set monitor to sdp
+ try {
+ var receipt = sdp.setMonitorContract(monitorContractAddress).send();
+ getBbcLogger().info(
+ "set monitor contract (address: {}) to SDP {} by tx {}",
+ monitorContractAddress, sdpContractAddress, receipt.getTransactionHash()
+ );
+ } catch (Exception e) {
+ throw new RuntimeException(
+ String.format("failed to set monitor contract (address: %s) to SDP %s", monitorContractAddress, sdpContractAddress), e
+ );
+ }
+ }
+
+ @SneakyThrows
+ public String getMonitorContractFromSdp(String sdpContractAddress) {
+ var sdp = SDPMsg.load(sdpContractAddress, web3j, rawTransactionManager, new DefaultGasProvider());
+ return sdp.getMonitorAddress().send();
+ }
+
+ public void setProtocolToMonitor(String monitorContractAddress, String protocolAddress) {
+ // 2. load am contract
+ Monitor monitor = Monitor.load(
+ monitorContractAddress,
+ this.web3j,
+ this.rawTransactionManager,
+ new AcbGasProvider(
+ this.contractGasPriceProvider,
+ createEthCallGasLimitProvider(
+ monitorContractAddress,
+ new Function(
+ Monitor.FUNC_SETPROTOCOL,
+ ListUtil.toList(new org.web3j.abi.datatypes.Address(protocolAddress)), // inputs
+ Collections.emptyList()
+ )
+ )
+ )
+ );
+
+ // 3. set protocol to monitor
+ try {
+ var receipt = monitor.setProtocol(protocolAddress).send();
+ getBbcLogger().info(
+ "set protocol (address: {}) to monitor {} by tx {} ",
+ protocolAddress, monitorContractAddress, receipt.getTransactionHash()
+ );
+ } catch (Exception e) {
+ throw new RuntimeException(
+ String.format(
+ "failed to set protocol (address: %s) to monitor %s",
+ protocolAddress, monitorContractAddress
+ ), e
+ );
+ }
+ }
+
+ @SneakyThrows
+ public String getProtocolFromMonitor(String monitorContractAddress) {
+ var monitor = Monitor.load(monitorContractAddress, web3j, rawTransactionManager, new DefaultGasProvider());
+ return monitor.getProtocol().send();
+ }
+
+ public void setMonitorControl(String monitorContractAddress, int monitorType) {
+ // load monitor contract
+ Monitor monitor = Monitor.load(
+ monitorContractAddress,
+ this.web3j,
+ this.rawTransactionManager,
+ new AcbGasProvider(
+ this.contractGasPriceProvider,
+ createEthCallGasLimitProvider(
+ monitorContractAddress,
+ new Function(
+ Monitor.FUNC_SETMONITORCONTROL,
+ ListUtil.toList(new org.web3j.abi.datatypes.generated.Uint32(monitorType)), // inputs
+ Collections.emptyList()
+ )
+ )
+ )
+ );
+
+ // set monitorType to monitor
+ try {
+ var receipt = monitor.setMonitorControl(BigInteger.valueOf(monitorType)).send();
+ getBbcLogger().info(
+ "set monitorType (value: {}) to monitor {} by tx {} ",
+ monitorType, monitorContractAddress, receipt.getTransactionHash()
+ );
+ } catch (Exception e) {
+ throw new RuntimeException(
+ String.format(
+ "failed to set monitorType (value: %s) to monitor %s",
+ monitorType, monitorContractAddress
+ ), e
+ );
+ }
+ }
+
+ @SneakyThrows
+ public int getMonitorControlFromMonitor(String monitorContractAddress) {
+ var monitor = Monitor.load(monitorContractAddress, web3j, rawTransactionManager, new DefaultGasProvider());
+ return monitor.getMonitorControl().send().intValue();
+ }
+
+ public CrossChainMessageReceipt relayMonitorOrderToMonitor(String monitorContractAddress, String committeeId, String signAlgo, byte[] rawProof, byte[] rawMonitorOrder) {
+ // 2. create Transaction
+ try {
+ // 2.1 create function
+ Function function = new Function(
+ Monitor.FUNC_RECVMONITORORDER, // function name
+ ListUtil.toList(new Utf8String(committeeId),
+ new Utf8String(signAlgo),
+ new DynamicBytes(rawProof),
+ new DynamicBytes(rawMonitorOrder)), // inputs
+ Collections.emptyList() // outputs
+ );
+ String encodedFunc = FunctionEncoder.encode(function);
+
+ // 2.2 pre-execute before commit tx
+ EthCall call = this.web3j.ethCall(
+ org.web3j.protocol.core.methods.request.Transaction.createEthCallTransaction(
+ config.isKmsService() ? this.txKMSSignService.getAddress() : this.credentials.getAddress(),
+ monitorContractAddress,
+ encodedFunc
+ ),
+ DefaultBlockParameterName.LATEST
+ ).send();
+
+ // 2.3 set `confirmed` and `successful` to false if reverted
+ CrossChainMessageReceipt crossChainMessageReceipt = new CrossChainMessageReceipt();
+ if (call.isReverted()) {
+ getBbcLogger().error("call monitor contract [recvMonitorOrder] {} reverted, reason: {}", monitorContractAddress, call.getRevertReason());
+ crossChainMessageReceipt.setSuccessful(false);
+ crossChainMessageReceipt.setConfirmed(false);
+ crossChainMessageReceipt.setErrorMsg(call.getRevertReason());
+ return crossChainMessageReceipt;
+ }
+
+ // 2.4 async send tx
+ EthSendTransaction ethSendTransaction = rawTransactionManager.sendTransaction(
+ this.contractGasPriceProvider.getGasPrice(encodedFunc),
+ createEthCallGasLimitProvider(monitorContractAddress, function).getGasLimit(encodedFunc),
+ monitorContractAddress,
+ encodedFunc,
+ BigInteger.ZERO
+ );
+ if (ObjectUtil.isNull(ethSendTransaction)) {
+ throw new RuntimeException("send tx with null result");
+ }
+ if (ethSendTransaction.hasError()) {
+ throw new RuntimeException(StrUtil.format("tx error: {} - {}",
+ ethSendTransaction.getError().getCode(), ethSendTransaction.getError().getMessage()));
+ }
+ if (StrUtil.isEmpty(ethSendTransaction.getTransactionHash())) {
+ throw new RuntimeException("tx hash is empty");
+ }
+
+ // 2.5 return crossChainMessageReceipt
+ crossChainMessageReceipt.setConfirmed(false);
+ crossChainMessageReceipt.setSuccessful(true);
+ crossChainMessageReceipt.setTxhash(ethSendTransaction.getTransactionHash());
+ crossChainMessageReceipt.setErrorMsg("");
+
+ getBbcLogger().info("relay monitor order by tx {}", ethSendTransaction.getTransactionHash());
+
+ return crossChainMessageReceipt;
+ } catch (Exception e) {
+ throw new RuntimeException(
+ String.format("failed to relay monitorOrder to %s", monitorContractAddress), e
+ );
+ }
+ }
+
public CrossChainMessageReceipt relayMsgToAuthMsg(String amContractAddress, byte[] rawMessage) {
// 2. create Transaction
try {
@@ -674,7 +1050,7 @@ public boolean hasPTCVerifyAnchor(String ptcHubAddress, ObjectIdentity ptcOwnerO
}
}
- public String deployPtcHubContract(AbstractCrossChainCertificate bcdnsRootCert, String committeePtcVerifier) {
+ public String deployPtcHubContract(AbstractCrossChainCertificate bcdnsRootCert, String committeePtcVerifier, String monitorVerifier) {
PtcHub ptcHub;
try {
var rawBcdnsRootCert = bcdnsRootCert.encode();
@@ -731,6 +1107,7 @@ public String deployPtcHubContract(AbstractCrossChainCertificate bcdnsRootCert,
getBbcLogger().info("deploy proxy contract for ptc hub: {}", proxy.getContractAddress());
}
+ // set committeePtcVerifier to ptc hub
ptcHub = PtcHub.load(
ptcHubContractAddr,
web3j,
@@ -770,6 +1147,46 @@ public String deployPtcHubContract(AbstractCrossChainCertificate bcdnsRootCert,
);
}
+ // set monitorVerifier to ptc hub
+ ptcHub = PtcHub.load(
+ ptcHubContractAddr,
+ web3j,
+ rawTransactionManager,
+ new AcbGasProvider(
+ this.contractGasPriceProvider,
+ createEthCallGasLimitProvider(
+ ptcHubContractAddr,
+ new Function(
+ PtcHub.FUNC_SETMONITORVERIFIER,
+ ListUtil.toList(new org.web3j.abi.datatypes.Address(monitorVerifier)),
+ ListUtil.empty()
+ )
+ )
+ )
+ );
+
+ try {
+ TransactionReceipt receipt = ptcHub.setMonitorVerifier(monitorVerifier).send();
+ if (!receipt.isStatusOK()) {
+ throw new RuntimeException(
+ StrUtil.format(
+ "transaction {} shows failed when set monitor verifier {} to ptc hub {}: {}",
+ receipt.getTransactionHash(), monitorVerifier, ptcHubContractAddr, receipt.getRevertReason()
+ )
+ );
+ }
+ getBbcLogger().info(
+ "set monitor verifier {} to ptc hub {} by tx {} ", monitorVerifier, ptcHubContractAddr, receipt.getTransactionHash()
+ );
+ } catch (Exception e) {
+ throw new RuntimeException(
+ String.format(
+ "unexpected failure when setting monitor verifier %s to ptc hub %s",
+ monitorVerifier, ptcHubContractAddr
+ ), e
+ );
+ }
+
return ptcHubContractAddr;
}
diff --git a/acb-sdk/pluginset/ethereum2/offchain-plugin/src/test/java/com/alipay/antchain/bridge/plugins/ethereum2/EthereumBBCServiceTest.java b/acb-sdk/pluginset/ethereum2/offchain-plugin/src/test/java/com/alipay/antchain/bridge/plugins/ethereum2/EthereumBBCServiceTest.java
index 10dc7cef..282b432f 100644
--- a/acb-sdk/pluginset/ethereum2/offchain-plugin/src/test/java/com/alipay/antchain/bridge/plugins/ethereum2/EthereumBBCServiceTest.java
+++ b/acb-sdk/pluginset/ethereum2/offchain-plugin/src/test/java/com/alipay/antchain/bridge/plugins/ethereum2/EthereumBBCServiceTest.java
@@ -43,6 +43,7 @@
import com.alipay.antchain.bridge.commons.bbc.DefaultBBCContext;
import com.alipay.antchain.bridge.commons.bbc.syscontract.AuthMessageContract;
import com.alipay.antchain.bridge.commons.bbc.syscontract.ContractStatusEnum;
+import com.alipay.antchain.bridge.commons.bbc.syscontract.MonitorContract;
import com.alipay.antchain.bridge.commons.bbc.syscontract.SDPContract;
import com.alipay.antchain.bridge.commons.bcdns.AbstractCrossChainCertificate;
import com.alipay.antchain.bridge.commons.bcdns.PTCCredentialSubject;
@@ -50,6 +51,7 @@
import com.alipay.antchain.bridge.commons.core.am.AuthMessageFactory;
import com.alipay.antchain.bridge.commons.core.am.IAuthMessage;
import com.alipay.antchain.bridge.commons.core.base.*;
+import com.alipay.antchain.bridge.commons.core.monitor.*;
import com.alipay.antchain.bridge.commons.core.ptc.*;
import com.alipay.antchain.bridge.commons.core.sdp.ISDPMessage;
import com.alipay.antchain.bridge.commons.core.sdp.SDPMessageFactory;
@@ -60,6 +62,7 @@
import com.alipay.antchain.bridge.commons.utils.crypto.SignAlgoEnum;
import com.alipay.antchain.bridge.plugins.ethereum2.abi.AppContract;
import com.alipay.antchain.bridge.plugins.ethereum2.abi.AuthMsg;
+import com.alipay.antchain.bridge.plugins.ethereum2.abi.Monitor;
import com.alipay.antchain.bridge.plugins.ethereum2.abi.SDPMsg;
import com.alipay.antchain.bridge.plugins.ethereum2.conf.Eth2NetworkEnum;
import com.alipay.antchain.bridge.plugins.ethereum2.conf.EthereumConfig;
@@ -220,7 +223,7 @@ public class EthereumBBCServiceTest {
nodeEndorseInfo.setPublicKey(nodePubkeyEntry);
NodeEndorseInfo nodeEndorseInfo2 = new NodeEndorseInfo();
- nodeEndorseInfo2.setNodeId("node2");
+ nodeEndorseInfo2.setNodeId("monitor-node");
nodeEndorseInfo2.setRequired(false);
nodeEndorseInfo2.setPublicKey(nodePubkeyEntry);
@@ -260,7 +263,7 @@ public class EthereumBBCServiceTest {
.sign(NODE_PTC_PRIVATE_KEY, tpbta.getEncodedToSign())
),
new CommitteeNodeProof(
- "node2",
+ "monitor-node",
SignAlgoEnum.KECCAK256_WITH_SECP256K1,
SignAlgoEnum.KECCAK256_WITH_SECP256K1.getSigner()
.sign(NODE_PTC_PRIVATE_KEY, tpbta.getEncodedToSign())
@@ -277,14 +280,14 @@ public class EthereumBBCServiceTest {
CommitteeVerifyAnchor verifyAnchor = new CommitteeVerifyAnchor("committee");
verifyAnchor.addNode("node1", "default", ((X509PubkeyInfoObjectIdentity) oid).getPublicKey());
- verifyAnchor.addNode("node2", "default", ((X509PubkeyInfoObjectIdentity) oid).getPublicKey());
+ verifyAnchor.addNode("monitor-node", "default", ((X509PubkeyInfoObjectIdentity) oid).getPublicKey());
verifyAnchor.addNode("node3", "default", ((X509PubkeyInfoObjectIdentity) oid).getPublicKey());
verifyAnchor.addNode("node4", "default", ((X509PubkeyInfoObjectIdentity) oid).getPublicKey());
// prepare the network stuff
CommitteeNetworkInfo committeeNetworkInfo = new CommitteeNetworkInfo("committee");
committeeNetworkInfo.addEndpoint("node1", "grpcs://0.0.0.0:8080", "");
- committeeNetworkInfo.addEndpoint("node2", "grpcs://0.0.0.0:8080", "");
+ committeeNetworkInfo.addEndpoint("monitor-node", "grpcs://0.0.0.0:8080", "");
committeeNetworkInfo.addEndpoint("node3", "grpcs://0.0.0.0:8080", "");
committeeNetworkInfo.addEndpoint("node4", "grpcs://0.0.0.0:8080", "");
@@ -371,16 +374,18 @@ public void testStartupWithDeployedContract() {
EthereumBBCService bbcServiceTmp = new EthereumBBCService();
bbcServiceTmp.startup(mockValidCtx);
- // set up am and sdp
+ // set up am and sdp and monitor
bbcServiceTmp.setupAuthMessageContract();
bbcServiceTmp.setupSDPMessageContract();
+ bbcServiceTmp.setupMonitorContract();
bbcServiceTmp.setupPTCContract();
String amAddr = bbcServiceTmp.getContext().getAuthMessageContract().getContractAddress();
String sdpAddr = bbcServiceTmp.getContext().getSdpContract().getContractAddress();
+ String monitorAddr = bbcServiceTmp.getContext().getMonitorContract().getContractAddress();
String ptcAddr = bbcServiceTmp.getContext().getPtcContract().getContractAddress();
// start up success
- AbstractBBCContext ctx = mockValidCtxWithPreDeployedContracts(amAddr, sdpAddr, ptcAddr);
+ AbstractBBCContext ctx = mockValidCtxWithPreDeployedContracts(amAddr, sdpAddr, monitorAddr, ptcAddr);
EthereumBBCService ethereumBBCService = new EthereumBBCService();
ethereumBBCService.startup(ctx);
@@ -388,6 +393,8 @@ public void testStartupWithDeployedContract() {
Assert.assertEquals(ContractStatusEnum.CONTRACT_DEPLOYED, ethereumBBCService.getBbcContext().getAuthMessageContract().getStatus());
Assert.assertEquals(sdpAddr, ethereumBBCService.getBbcContext().getSdpContract().getContractAddress());
Assert.assertEquals(ContractStatusEnum.CONTRACT_DEPLOYED, ethereumBBCService.getBbcContext().getSdpContract().getStatus());
+ Assert.assertEquals(monitorAddr, ethereumBBCService.getBbcContext().getMonitorContract().getContractAddress());
+ Assert.assertEquals(ContractStatusEnum.CONTRACT_DEPLOYED, ethereumBBCService.getBbcContext().getMonitorContract().getStatus());
Assert.assertEquals(ptcAddr, ethereumBBCService.getBbcContext().getPtcContract().getContractAddress());
Assert.assertEquals(ContractStatusEnum.CONTRACT_READY, ethereumBBCService.getBbcContext().getPtcContract().getStatus());
}
@@ -399,22 +406,26 @@ public void testStartupWithReadyContract() {
EthereumBBCService bbcServiceTmp = new EthereumBBCService();
bbcServiceTmp.startup(mockValidCtx);
- // set up am and sdp
+ // set up am and sdp and monitor
bbcServiceTmp.setupAuthMessageContract();
bbcServiceTmp.setupSDPMessageContract();
+ bbcServiceTmp.setupMonitorContract();
bbcServiceTmp.setupPTCContract();
String amAddr = bbcServiceTmp.getContext().getAuthMessageContract().getContractAddress();
String sdpAddr = bbcServiceTmp.getContext().getSdpContract().getContractAddress();
+ String monitorAddr = bbcServiceTmp.getContext().getMonitorContract().getContractAddress();
String ptcAddr = bbcServiceTmp.getContext().getPtcContract().getContractAddress();
// start up success
EthereumBBCService ethereumBBCService = new EthereumBBCService();
- AbstractBBCContext ctx = mockValidCtxWithPreReadyContracts(amAddr, sdpAddr, ptcAddr);
+ AbstractBBCContext ctx = mockValidCtxWithPreReadyContracts(amAddr, sdpAddr, monitorAddr, ptcAddr);
ethereumBBCService.startup(ctx);
Assert.assertEquals(amAddr, ethereumBBCService.getBbcContext().getAuthMessageContract().getContractAddress());
Assert.assertEquals(ContractStatusEnum.CONTRACT_READY, ethereumBBCService.getBbcContext().getAuthMessageContract().getStatus());
Assert.assertEquals(sdpAddr, ethereumBBCService.getBbcContext().getSdpContract().getContractAddress());
Assert.assertEquals(ContractStatusEnum.CONTRACT_READY, ethereumBBCService.getBbcContext().getSdpContract().getStatus());
+ Assert.assertEquals(monitorAddr, ethereumBBCService.getBbcContext().getMonitorContract().getContractAddress());
+ Assert.assertEquals(ContractStatusEnum.CONTRACT_READY, ethereumBBCService.getBbcContext().getMonitorContract().getStatus());
Assert.assertEquals(ptcAddr, ethereumBBCService.getBbcContext().getPtcContract().getContractAddress());
Assert.assertEquals(ContractStatusEnum.CONTRACT_READY, ethereumBBCService.getBbcContext().getPtcContract().getStatus());
}
@@ -468,18 +479,65 @@ public void testSetupSDPMessageContract() {
}
@Test
- public void testPtcContractAll() {
+ public void testSetupMonitorContract() {
EthereumBBCService ethereumBBCService = new EthereumBBCService();
// start up
AbstractBBCContext mockValidCtx = mockValidCtx();
ethereumBBCService.startup(mockValidCtx);
- // set up sdp
+ // set up monitor
+ ethereumBBCService.setupMonitorContract();
+
+ // get context
+ AbstractBBCContext ctx = ethereumBBCService.getContext();
+ Assert.assertEquals(ContractStatusEnum.CONTRACT_DEPLOYED, ctx.getMonitorContract().getStatus());
+ }
+
+ @Test
+ public void testMonitorAndPtcContractAll() throws Exception {
+ EthereumBBCService ethereumBBCService = new EthereumBBCService();
+ // start up
+ AbstractBBCContext mockValidCtx = mockValidCtx();
+ ethereumBBCService.startup(mockValidCtx);
+
+ // set up sdp monitor and ptc
+ ethereumBBCService.setupSDPMessageContract();
+ ethereumBBCService.setupMonitorContract();
ethereumBBCService.setupPTCContract();
var ctx = ethereumBBCService.getContext();
Assert.assertEquals(ContractStatusEnum.CONTRACT_READY, ctx.getPtcContract().getStatus());
+ Assert.assertEquals(ContractStatusEnum.CONTRACT_DEPLOYED, ctx.getSdpContract().getStatus());
+ Assert.assertEquals(ContractStatusEnum.CONTRACT_DEPLOYED, ctx.getMonitorContract().getStatus());
+
+ // init monitor
+ ethereumBBCService.setPtcHubInMonitorVerifier(ethereumBBCService.getBbcContext().getPtcContract().getContractAddress());
+
+ ethereumBBCService.setProtocolInMonitor(ctx.getSdpContract().getContractAddress());
+ String addr = Monitor.load(
+ ethereumBBCService.getBbcContext().getMonitorContract().getContractAddress(),
+ ethereumBBCService.getAcbEthClient().getWeb3j(),
+ ethereumBBCService.getAcbEthClient().getCredentials(),
+ new DefaultGasProvider()
+ ).getProtocol().send();
+ log.info("protocol in monitor: {}", addr);
+
+ Assert.assertEquals(ContractStatusEnum.CONTRACT_DEPLOYED, ctx.getMonitorContract().getStatus());
+
+ int monitorType = 2; // 2 denotes MONITOR_OPEN
+ ethereumBBCService.setMonitorControl(monitorType);
+ int monitorControl = Monitor.load(
+ ethereumBBCService.getBbcContext().getMonitorContract().getContractAddress(),
+ ethereumBBCService.getAcbEthClient().getWeb3j(),
+ ethereumBBCService.getAcbEthClient().getCredentials(),
+ new DefaultGasProvider()
+ ).getMonitorControl().send().intValue();
+ log.info("monitor control in monitor: {}", monitorControl);
+
+ Assert.assertEquals(ContractStatusEnum.CONTRACT_READY, ctx.getMonitorContract().getStatus());
+
+ // init ptc
ethereumBBCService.updatePTCTrustRoot(ptcTrustRoot);
var root = ethereumBBCService.getPTCTrustRoot(oid);
@@ -556,7 +614,8 @@ public void testSetProtocol() throws Exception {
}
@Test
- public void testSetAmContractAndLocalDomain() throws Exception {
+// public void testSetAmContractAndLocalDomain() throws Exception {
+ public void testSetSDPContractAll() throws Exception {
EthereumBBCService ethereumBBCService = new EthereumBBCService();
// start up
AbstractBBCContext mockValidCtx = mockValidCtx();
@@ -568,20 +627,19 @@ public void testSetAmContractAndLocalDomain() throws Exception {
// set up sdp
ethereumBBCService.setupSDPMessageContract();
+ // set up monitor
+ ethereumBBCService.setupMonitorContract();
+
// get context
AbstractBBCContext ctx = ethereumBBCService.getContext();
Assert.assertEquals(ContractStatusEnum.CONTRACT_DEPLOYED, ctx.getAuthMessageContract().getStatus());
Assert.assertEquals(ContractStatusEnum.CONTRACT_DEPLOYED, ctx.getSdpContract().getStatus());
+ Assert.assertEquals(ContractStatusEnum.CONTRACT_DEPLOYED, ctx.getMonitorContract().getStatus());
// set am to sdp
ethereumBBCService.setAmContract(ctx.getAuthMessageContract().getContractAddress());
- String amAddr = SDPMsg.load(
- ethereumBBCService.getBbcContext().getSdpContract().getContractAddress(),
- ethereumBBCService.getAcbEthClient().getWeb3j(),
- ethereumBBCService.getAcbEthClient().getCredentials(),
- new DefaultGasProvider()
- ).getAmAddress().send();
+ String amAddr = ethereumBBCService.getAcbEthClient().getAmContractFromSdp(ctx.getSdpContract().getContractAddress());
log.info("amAddr: {}", amAddr);
// check contract status
@@ -591,13 +649,31 @@ public void testSetAmContractAndLocalDomain() throws Exception {
// set the domain
ethereumBBCService.setLocalDomain(CHAIN_DOMAIN);
- byte[] rawDomain = SDPMsg.load(
+// byte[] rawDomain = SDPMsg.load(
+// ethereumBBCService.getBbcContext().getSdpContract().getContractAddress(),
+// ethereumBBCService.getAcbEthClient().getWeb3j(),
+// ethereumBBCService.getAcbEthClient().getCredentials(),
+// new DefaultGasProvider()
+// ).getLocalDomain().send();
+ byte[] rawDomain = ethereumBBCService.getAcbEthClient().getLocalDomainFromSdp(ctx.getSdpContract().getContractAddress());
+ log.info("domain: {}", HexUtil.encodeHexStr(rawDomain));
+
+// String monitorAddr0 = ethereumBBCService.getAcbEthClient().getMonitorContractFromSdp(ctx.getSdpContract().getContractAddress());
+// log.info("monitorAddr: {}", monitorAddr0);
+
+ // check contract status
+ ctx = ethereumBBCService.getContext();
+ Assert.assertEquals(ContractStatusEnum.CONTRACT_DEPLOYED, ctx.getSdpContract().getStatus());
+
+ // set monitor to sdp
+ ethereumBBCService.setMonitorContract(ctx.getMonitorContract().getContractAddress());
+ String monitorAddr = SDPMsg.load(
ethereumBBCService.getBbcContext().getSdpContract().getContractAddress(),
ethereumBBCService.getAcbEthClient().getWeb3j(),
ethereumBBCService.getAcbEthClient().getCredentials(),
new DefaultGasProvider()
- ).getLocalDomain().send();
- log.info("domain: {}", HexUtil.encodeHexStr(rawDomain));
+ ).getMonitorAddress().send();
+ log.info("monitorAddr: {}", monitorAddr);
// check contract status
ctx = ethereumBBCService.getContext();
@@ -635,6 +711,124 @@ public void testReadCrossChainMessageReceipt() throws IOException, InterruptedEx
Assert.assertEquals(crossChainMessageReceipt.isSuccessful(), crossChainMessageReceipt1.isSuccessful());
}
+ @Test
+ public void testRelayMonitorOrder() throws Exception {
+ setupBbc();
+
+ // relay monitor order: add app contract to blacklist
+ log.info("[TEST-1]: relay monitor order to ADD app contract to BLACKLIST");
+
+ IMonitorOrder monitorOrder = MonitorOrderFactory.createMonitorOrder(
+ 1,
+ "ethereum",
+ "testDomain1",
+ Long.parseLong("1000"+"0000000000000000000000000000", 2),
+ "testDomain1",
+ StrUtil.replace(appContract.getContractAddress(), "0x", "000000000000000000000000"),
+ "testDomain2",
+ StrUtil.replace(REMOTE_APP_CONTRACT, "0x", "000000000000000000000000"),
+ "this is a test monitor order",
+ "nothing in extra"
+ );
+
+ byte[] rawMonitorOrder = monitorOrder.encode();
+ byte[] rawProof = SignAlgoEnum.KECCAK256_WITH_SECP256K1.getSigner().sign(
+ NODE_PTC_PRIVATE_KEY,
+ rawMonitorOrder
+ );
+
+ CrossChainMessageReceipt crossChainMessageReceipt = ethereumBBCService.relayMonitorOrder(
+ COMMITTEE_ID,
+ SignAlgoEnum.KECCAK256_WITH_SECP256K1.getName(),
+ rawProof,
+ rawMonitorOrder
+ );
+
+// CrossChainMessageReceipt crossChainMessageReceipt = ethereumBBCService.relayMonitorOrder(
+// Long.parseLong("1000"+"0000000000000000000000000000", 2),
+// "testDomain1",
+// StrUtil.replace(appContract.getContractAddress(), "0x", "000000000000000000000000"),
+// "testDomain2",
+// StrUtil.replace(REMOTE_APP_CONTRACT, "0x", "000000000000000000000000"),
+// "this is a test monitor order",
+// "nothing in extra"
+// );
+ Assert.assertTrue(crossChainMessageReceipt.isSuccessful());
+ waitForTxConfirmed(crossChainMessageReceipt.getTxhash(), ethereumBBCService.getAcbEthClient().getWeb3j());
+ CrossChainMessageReceipt crossChainMessageReceipt1 = ethereumBBCService.readCrossChainMessageReceipt(crossChainMessageReceipt.getTxhash());
+ Assert.assertEquals(crossChainMessageReceipt.isSuccessful(), crossChainMessageReceipt1.isSuccessful());
+
+ // test app contract: it should be unable to send message
+ try {
+ appContract.sendUnorderedMessage("remoteDomain", DigestUtil.sha256(REMOTE_APP_CONTRACT), "UnorderedCrossChainMessage".getBytes()).send();
+ Assert.fail("[appContract.sendUnorderedMessage]: expected transaction to revert, but it succeeded.");
+ } catch (Exception e) {
+ log.info("[appContract.sendUnorderedMessage]: reverted as expected: {}", e.getMessage());
+ }
+
+ // relay monitor order: remove app contract from blacklist
+ log.info("[TEST-2]: relay monitor order to REMOVE app contract to BLACKLIST");
+ IMonitorOrder monitorOrder2 = MonitorOrderFactory.createMonitorOrder(
+ 1,
+ "ethereum",
+ "testDomain1",
+ Long.parseLong("1001"+"0000000000000000000000000000", 2),
+ "testDomain1",
+ StrUtil.replace(appContract.getContractAddress(), "0x", "000000000000000000000000"),
+ "testDomain2",
+ StrUtil.replace(REMOTE_APP_CONTRACT, "0x", "000000000000000000000000"),
+ "this is a test monitor order",
+ "nothing in extra"
+ );
+
+ byte[] rawMonitorOrder2 = monitorOrder2.encode();
+ byte[] rawProof2 = SignAlgoEnum.KECCAK256_WITH_SECP256K1.getSigner().sign(
+ NODE_PTC_PRIVATE_KEY,
+ rawMonitorOrder2
+ );
+
+ CrossChainMessageReceipt crossChainMessageReceipt2 = ethereumBBCService.relayMonitorOrder(
+ COMMITTEE_ID,
+ SignAlgoEnum.KECCAK256_WITH_SECP256K1.getName(),
+ rawProof2,
+ rawMonitorOrder2
+ );
+// CrossChainMessageReceipt crossChainMessageReceipt2 = ethereumBBCService.relayMonitorOrder(
+// Long.parseLong("1001"+"0000000000000000000000000000", 2),
+// "testDomain1",
+// StrUtil.replace(appContract.getContractAddress(), "0x", "000000000000000000000000"),
+// "testDomain2",
+// StrUtil.replace(REMOTE_APP_CONTRACT, "0x", "000000000000000000000000"),
+// "this is a test monitor order",
+// "nothing in extra"
+// );
+ Assert.assertTrue(crossChainMessageReceipt2.isSuccessful());
+ waitForTxConfirmed(crossChainMessageReceipt2.getTxhash(), ethereumBBCService.getAcbEthClient().getWeb3j());
+ CrossChainMessageReceipt crossChainMessageReceipt3 = ethereumBBCService.readCrossChainMessageReceipt(crossChainMessageReceipt2.getTxhash());
+ Assert.assertEquals(crossChainMessageReceipt2.isSuccessful(), crossChainMessageReceipt3.isSuccessful());
+
+ // test app contract: it should be able to send message
+ try {
+ var receipt = appContract.sendUnorderedMessage("remoteDomain", DigestUtil.sha256(REMOTE_APP_CONTRACT), "UnorderedCrossChainMessage".getBytes()).send();
+ Assert.assertTrue(receipt.isStatusOK());
+ } catch (Exception e) {
+ log.error("[appContract.sendUnorderedMessage]: unexpected revert: {}", e.getMessage());
+ Assert.fail("[appContract.sendUnorderedMessage]: expected transaction to execute successfully, but it failed");
+ }
+ }
+
+
+ @Test
+ public void testQueryLatestBlockNumber() throws Exception {
+ EthereumBBCService ethereumBBCService = new EthereumBBCService();
+ // start up
+ AbstractBBCContext mockValidCtx = mockValidCtx();
+ ethereumBBCService.startup(mockValidCtx);
+
+ BigInteger l = ethereumBBCService.getAcbEthClient().queryLatestBlockNumber();
+ Assert.assertTrue(l.compareTo(BigInteger.ZERO) > 0);
+ }
+
@Test
public void testReadCrossChainMessagesByHeight_sendUnordered() throws Exception {
setupBbc();
@@ -684,7 +878,7 @@ public void testReadCrossChainMessagesByHeight_sendOrdered() throws Exception {
Assert.assertNotNull(provableData);
Assert.assertEquals(msgOnHeight, provableData.getHeightVal());
Assert.assertEquals(receipt.getTransactionHash(), Numeric.toHexString(provableData.getTxHash()));
- Assert.assertEquals(receipt.getBlockHash(), Numeric.toHexString(provableData.getBlockHash()));
+ Assert.assertEquals(receipt.getBlockHash(), Numeric.toHexString(provableData.getBlockHash()));
var proofObj = EthReceiptProof.decodeFromJson(new String(provableData.getProof()));
Assert.assertNotNull(proofObj);
Assert.assertNotNull(proofObj.getEthTransactionReceipt());
@@ -784,6 +978,9 @@ private void setupBbc() {
// set up sdp
ethereumBBCService.setupSDPMessageContract();
+ // set up monitor
+ ethereumBBCService.setupMonitorContract();
+
ethereumBBCService.setupPTCContract();
// set protocol to am (sdp type: 0)
@@ -797,6 +994,19 @@ private void setupBbc() {
// set local domain to sdp
ethereumBBCService.setLocalDomain(CHAIN_DOMAIN);
+ // set monitor to sdp
+ ethereumBBCService.setMonitorContract(mockValidCtx.getMonitorContract().getContractAddress());
+
+ // set sdp to monitor
+ ethereumBBCService.setProtocolInMonitor(mockValidCtx.getSdpContract().getContractAddress());
+
+ // set monitorControl to monitor
+ ethereumBBCService.setMonitorControl(2);
+
+ // set ptc hub to monitor verifier
+ ethereumBBCService.setPtcHubInMonitorVerifier(mockValidCtx.getPtcContract().getContractAddress());
+
+ // set ptc tp am
ethereumBBCService.setPtcContract(mockValidCtx.getPtcContract().getContractAddress());
ethereumBBCService.updatePTCTrustRoot(ptcTrustRoot);
@@ -807,6 +1017,7 @@ private void setupBbc() {
AbstractBBCContext ctxCheck = ethereumBBCService.getContext();
Assert.assertEquals(ContractStatusEnum.CONTRACT_READY, ctxCheck.getAuthMessageContract().getStatus());
Assert.assertEquals(ContractStatusEnum.CONTRACT_READY, ctxCheck.getSdpContract().getStatus());
+ Assert.assertEquals(ContractStatusEnum.CONTRACT_READY, ctxCheck.getMonitorContract().getStatus());
TransactionReceipt receipt = appContract.setProtocol(ethereumBBCService.getBbcContext().getSdpContract().getContractAddress()).send();
if (receipt.isStatusOK()) {
@@ -819,6 +1030,17 @@ private void setupBbc() {
ethereumBBCService.getBbcContext().getSdpContract().getContractAddress()));
}
+ TransactionReceipt receipt1 = appContract.setMonitorContract(ethereumBBCService.getBbcContext().getMonitorContract().getContractAddress()).send();
+ if (receipt1.isStatusOK()) {
+ log.info("set monitor contract({}) to app contract({})",
+ ethereumBBCService.getBbcContext().getMonitorContract().getContractAddress(),
+ appContract.getContractAddress());
+ } else {
+ throw new Exception(String.format("failed to set monitor contract(%s) to app contract(%s)",
+ ethereumBBCService.getBbcContext().getMonitorContract().getContractAddress(),
+ appContract.getContractAddress()));
+ }
+
setupBBC = true;
}
@@ -860,13 +1082,14 @@ private AbstractBBCContext mockValidCtx() {
return mockCtx;
}
- private AbstractBBCContext mockValidCtxWithPreDeployedContracts(String amAddr, String sdpAddr, String ptcAddr) {
+ private AbstractBBCContext mockValidCtxWithPreDeployedContracts(String amAddr, String sdpAddr, String monitorAddr, String ptcAddr) {
EthereumConfig mockConf = new EthereumConfig();
mockConf.setUrl(VALID_URL);
mockConf.setBeaconApiUrl(VALID_BEACON_URL);
mockConf.setPrivateKey(BBC_ETH_PRIVATE_KEY_2);
mockConf.setAmContractAddressDeployed(amAddr);
mockConf.setSdpContractAddressDeployed(sdpAddr);
+ mockConf.setMonitorContractAddressDeployed(monitorAddr);
mockConf.setPtcHubContractAddressDeployed(ptcAddr);
mockConf.setMsgScanPolicy(scanPolicy);
mockConf.setGasLimitPolicy(gasLimitPolicy);
@@ -888,13 +1111,14 @@ private AbstractBBCContext mockValidCtxWithPreDeployedContracts(String amAddr, S
return mockCtx;
}
- private AbstractBBCContext mockValidCtxWithPreReadyContracts(String amAddr, String sdpAddr, String ptcAddr) {
+ private AbstractBBCContext mockValidCtxWithPreReadyContracts(String amAddr, String sdpAddr, String monitorAddr, String ptcAddr) {
EthereumConfig mockConf = new EthereumConfig();
mockConf.setUrl(VALID_URL);
mockConf.setBeaconApiUrl(VALID_BEACON_URL);
mockConf.setPrivateKey(BBC_ETH_PRIVATE_KEY_3);
mockConf.setAmContractAddressDeployed(amAddr);
mockConf.setSdpContractAddressDeployed(sdpAddr);
+ mockConf.setMonitorContractAddressDeployed(monitorAddr);
mockConf.setPtcHubContractAddressDeployed(ptcAddr);
mockConf.setMsgScanPolicy(scanPolicy);
mockConf.setGasLimitPolicy(gasLimitPolicy);
@@ -924,6 +1148,11 @@ private AbstractBBCContext mockValidCtxWithPreReadyContracts(String amAddr, Stri
sdpContract.setStatus(ContractStatusEnum.CONTRACT_READY);
mockCtx.setSdpContract(sdpContract);
+ MonitorContract monitorContract = new MonitorContract();
+ monitorContract.setContractAddress(monitorAddr);
+ monitorContract.setStatus(ContractStatusEnum.CONTRACT_READY);
+ mockCtx.setMonitorContract(monitorContract);
+
return mockCtx;
}
@@ -953,13 +1182,20 @@ private void waitForTxConfirmed(String txhash, Web3j web3j) {
}
private byte[] getRawMsgFromRelayer(String receiverAddr) throws IOException {
+ // need to replace "awesome antchain-bridge" with monitor message
+ IMonitorMessage monitorMessage = MonitorMessageFactory.createMonitorMessage(
+ 1,
+ 2,
+ "this is a monitorMsg",
+ "awesome antchain-bridge".getBytes()
+ );
ISDPMessage sdpMessage = SDPMessageFactory.createSDPMessage(
1,
new byte[32],
crossChainLane.getReceiverDomain().getDomain(),
Numeric.hexStringToByteArray(StrUtil.replace(receiverAddr, "0x", "000000000000000000000000")),
-1,
- "awesome antchain-bridge".getBytes()
+ monitorMessage.encode()
);
IAuthMessage am = AuthMessageFactory.createAuthMessage(
1,
@@ -982,7 +1218,14 @@ private byte[] getRawMsgFromRelayer(String receiverAddr) throws IOException {
thirdPartyProof.getEncodedToSign()
)).build();
CommitteeNodeProof node2Proof = CommitteeNodeProof.builder()
- .nodeId("node2")
+ .nodeId("monitor-node")
+ .signAlgo(SignAlgoEnum.KECCAK256_WITH_SECP256K1)
+ .signature(SignAlgoEnum.KECCAK256_WITH_SECP256K1.getSigner().sign(
+ NODE_PTC_PRIVATE_KEY,
+ thirdPartyProof.getEncodedToSign()
+ )).build();
+ CommitteeNodeProof node3Proof = CommitteeNodeProof.builder()
+ .nodeId("node3")
.signAlgo(SignAlgoEnum.KECCAK256_WITH_SECP256K1)
.signature(SignAlgoEnum.KECCAK256_WITH_SECP256K1.getSigner().sign(
NODE_PTC_PRIVATE_KEY,
@@ -991,7 +1234,7 @@ private byte[] getRawMsgFromRelayer(String receiverAddr) throws IOException {
CommitteeEndorseProof endorseProof = new CommitteeEndorseProof();
endorseProof.setCommitteeId(COMMITTEE_ID);
- endorseProof.setSigs(ListUtil.toList(node1Proof, node2Proof));
+ endorseProof.setSigs(ListUtil.toList(node1Proof, node2Proof, node3Proof));
thirdPartyProof.setRawProof(endorseProof.encode());
diff --git a/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/AppContract.sol b/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/AppContract.sol
index 5e60e4d2..bc996119 100644
--- a/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/AppContract.sol
+++ b/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/AppContract.sol
@@ -3,6 +3,7 @@ pragma solidity ^0.8.0;
import "./interfaces/IContractUsingSDP.sol";
import "./interfaces/ISDPMessage.sol";
+import "./interfaces/IMonitor.sol";
import "./lib/utils/Ownable.sol";
contract AppContract is IContractUsingSDP, Ownable {
@@ -15,6 +16,8 @@ contract AppContract is IContractUsingSDP, Ownable {
mapping(bytes32 => bytes[]) public sendMsg;
+ address public monitorAddress;
+
address public sdpAddress;
bytes32 public latest_msg_id_sent_order;
@@ -31,7 +34,7 @@ contract AppContract is IContractUsingSDP, Ownable {
event sendCrosschainMsg(string receiverDomain, bytes32 receiver, bytes message, bool isOrdered);
modifier onlySdpMsg() {
- require(msg.sender == sdpAddress, "INVALID_PERMISSION");
+ require(msg.sender == sdpAddress, "INVALID_PERMISSION: only sdp message");
_;
}
@@ -39,20 +42,30 @@ contract AppContract is IContractUsingSDP, Ownable {
sdpAddress = protocolAddress;
}
- function recvUnorderedMessage(string memory senderDomain, bytes32 author, bytes memory message) external override onlySdpMsg {
+ modifier onlyMonitorMsg() {
+ require(monitorAddress == msg.sender, "INVALID_PERMISSION: only monitor message");
+ _;
+ }
+
+ function setMonitorContract(address newMonitorAddress) external onlyOwner {
+ monitorAddress = newMonitorAddress;
+ }
+
+ function recvUnorderedMessage(string memory senderDomain, bytes32 author, bytes memory message) external override onlyMonitorMsg {
recvMsg[author].push(message);
last_uo_msg = message;
emit recvCrosschainMsg(senderDomain, author, message, false);
}
- function recvMessage(string memory senderDomain, bytes32 author, bytes memory message) external override onlySdpMsg {
+ function recvMessage(string memory senderDomain, bytes32 author, bytes memory message) external override onlyMonitorMsg {
recvMsg[author].push(message);
last_msg = message;
emit recvCrosschainMsg(senderDomain, author, message, true);
}
+ // notice: monitor only support the sendV1 version message
function sendUnorderedMessage(string memory receiverDomain, bytes32 receiver, bytes memory message) external {
- ISDPMessage(sdpAddress).sendUnorderedMessage(receiverDomain, receiver, message);
+ IMonitor(monitorAddress).sendUnorderedMonitorMessage(receiverDomain, receiver, message);
sendMsg[receiver].push(message);
emit sendCrosschainMsg(receiverDomain, receiver, message, false);
@@ -60,7 +73,7 @@ contract AppContract is IContractUsingSDP, Ownable {
function sendMessage(string memory receiverDomain, bytes32 receiver, bytes memory message) external{
- ISDPMessage(sdpAddress).sendMessage(receiverDomain, receiver, message);
+ IMonitor(monitorAddress).sendMonitorMessage(receiverDomain, receiver, message);
sendMsg[receiver].push(message);
emit sendCrosschainMsg(receiverDomain, receiver, message, true);
diff --git a/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/CommitteePtcVerifier.sol b/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/CommitteePtcVerifier.sol
index 2b783a42..24ba4198 100644
--- a/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/CommitteePtcVerifier.sol
+++ b/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/CommitteePtcVerifier.sol
@@ -11,8 +11,8 @@ contract CommitteePtcVerifier is IPtcVerifier {
return CommitteeLib.verifyTpBta(va, tpBta);
}
- function verifyTpProof(TpBta memory tpBta, ThirdPartyProof memory tpProof) external override returns (bool) {
- return CommitteeLib.verifyTpProof(tpBta, tpProof);
+ function verifyTpProof(TpBta memory tpBta, ThirdPartyProof memory tpProof, address monitorPtcAddr) external override returns (bool) {
+ return CommitteeLib.verifyTpProof(tpBta, tpProof, monitorPtcAddr);
}
function myPtcType() external pure override returns (PTCTypeEnum) {
diff --git a/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/Monitor.sol b/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/Monitor.sol
new file mode 100755
index 00000000..29e6a4c5
--- /dev/null
+++ b/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/Monitor.sol
@@ -0,0 +1,298 @@
+// SPDX-License-Identifier: Apache-2.0
+pragma solidity ^0.8.0;
+
+import "./interfaces/IContractUsingSDP.sol";
+import "./interfaces/IContractUsingMonitor.sol";
+import "./interfaces/ISDPMessage.sol";
+import "./interfaces/IMonitor.sol";
+import "./interfaces/IMonitorVerifier.sol";
+import "./lib/monitor/MonitorLib.sol";
+import "./lib/utils/Ownable.sol";
+import "./@openzeppelin/contracts/proxy/utils/Initializable.sol";
+
+// 监管节点部署该合约
+// 接受监管指令; 只在发送方进行事前监管
+contract Monitor is IMonitor, IContractUsingMonitor, Ownable, Initializable {
+ using MonitorLib for MonitorOrder;
+ using MonitorLib for MonitorMessage;
+
+ // SDP合约地址
+ address public sdpAddress;
+
+ // MonitorVerifier合约地址
+ address public monitorVerifierAddress;
+
+ // 控制监管的字段
+ uint32 public monitorControl;
+
+ // 黑名单
+ mapping(bytes32 => bool) public senderBlacklist;
+ mapping(bytes32 => bool) public receiverBlacklist;
+
+ // uint32 public successfulCallInMonitorOPEN = 0;
+ // uint32 public successfulCallInMonitorCLOSE = 0;
+ // uint32 public successfulCallInMonitorROLLBACK = 0;
+
+ modifier onlySubProtocols {
+ require(
+ msg.sender == sdpAddress,
+ "MonitorMsg: sender not valid sub-protocol"
+ );
+ _;
+ }
+
+ constructor() {
+ _disableInitializers();
+ }
+
+ function init() external initializer() {
+ _transferOwnership(_msgSender());
+ }
+
+ function setProtocol(address protocolAddress) override external onlyOwner {
+ require(protocolAddress != address(0), "MonitorMsg: invalid sdp contract");
+ sdpAddress = protocolAddress;
+ }
+
+ function setMonitorVerifier(address newMonitorVerifierAddress) override external {
+ require(newMonitorVerifierAddress != address(0), "MonitorMsg: invalid MonitorVerifier contract");
+ monitorVerifierAddress = newMonitorVerifierAddress;
+ }
+
+ function setMonitorControl(uint32 monitorType) override external onlyOwner {
+ monitorControl = monitorType == 0 ? MonitorLib.MONITOR_CLOSE : MonitorLib.MONITOR_OPEN;
+ }
+
+ function getProtocol() external view returns (address) {
+ return sdpAddress;
+ }
+
+ function getMonitorControl() external view returns (uint32) {
+ return monitorControl;
+ }
+
+ function getMonitorVerifier() external view returns (address) {
+ return monitorVerifierAddress;
+ }
+
+ // function getSuccessfulCallInMonitorOPEN() external view returns (uint32) {
+ // return successfulCallInMonitorOPEN;
+ // }
+
+ // function getSuccessfulCallInMonitorCLOSE() external view returns (uint32) {
+ // return successfulCallInMonitorCLOSE;
+ // }
+
+ // function getSuccessfulCallInMonitorROLLBACK() external view returns (uint32) {
+ // return successfulCallInMonitorROLLBACK;
+ // }
+
+ function sendMonitorMessage(string calldata receiverDomain, bytes32 receiverID, bytes calldata message) override external {
+ _beforeSend(receiverDomain, receiverID, message);
+
+ // 执行事前监管的检查
+ if (monitorControl == MonitorLib.MONITOR_OPEN) {
+ bool monitorResult = false;
+ monitorResult = PreMonitoring(receiverDomain, receiverID, message);
+ if(monitorResult == false) {
+ revert("beforehand Monitor disapproval");
+ }
+ }
+
+ MonitorMessage memory monitorMessage = MonitorMessage(
+ {
+ monitorType: monitorControl,
+ monitorMsg: "",
+ message: message
+ }
+ );
+
+ bytes memory rawMsg = monitorMessage.encode();
+
+ ISDPMessage(sdpAddress).sendMessage(receiverDomain, receiverID, msg.sender, rawMsg);
+
+ _afterSend();
+ }
+
+ function sendUnorderedMonitorMessage(string calldata receiverDomain, bytes32 receiverID, bytes calldata message) external {
+ _beforeSendUnordered(receiverDomain, receiverID, message);
+
+ // 执行事前监管的检查
+ if (monitorControl == MonitorLib.MONITOR_OPEN) {
+ bool monitorResult = false;
+ monitorResult = PreMonitoring(receiverDomain, receiverID, message);
+ if(monitorResult == false) {
+ revert("beforehand Monitor disapproval");
+ }
+ }
+
+ MonitorMessage memory monitorMessage = MonitorMessage(
+ {
+ monitorType: monitorControl,
+ monitorMsg: "",
+ message: message
+ }
+ );
+
+ bytes memory rawMsg = monitorMessage.encode();
+
+ ISDPMessage(sdpAddress).sendUnorderedMessage(receiverDomain, receiverID, msg.sender, rawMsg);
+
+ _afterSendUnordered();
+ }
+
+ // IContractUsingMonitor.sol里面的接口
+ function recvUnorderedMessageFromSDP(string memory senderDomain, bytes32 author, address receiverID, bytes memory message) external override onlySubProtocols {
+ // 去掉监管字段 再向上传给app合约
+ MonitorMessage memory monitorMessage;
+ monitorMessage.decode(message);
+
+ if (monitorMessage.monitorType == MonitorLib.MONITOR_OPEN) {
+ // 验证监管节点的签名
+ bool res = IMonitorVerifier(monitorVerifierAddress).verifyMonitorNodeProofMessage();
+ emit VerifyMonitorNodeProofMessage(senderDomain, author, receiverID, res);
+ // 监管不通过 构造回滚消息(仅修改监管字段 对原文不修改)
+ if (res == false) {
+ monitorMessage.monitorType = MonitorLib.MONITOR_ROLLBACK;
+ monitorMessage.monitorMsg = "MidMonitoring not pass";
+ ISDPMessage(sdpAddress).sendUnorderedMessage(senderDomain, author, receiverID, monitorMessage.encode());
+ emit sendMonitorRollbakMessage(monitorMessage.monitorType, MonitorLib.encodeAddressIntoCrossChainID(receiverID), senderDomain, author, monitorMessage.monitorMsg);
+ } else {
+ IContractUsingSDP(receiverID).recvUnorderedMessage(senderDomain, author, monitorMessage.message);
+ }
+ } else if (monitorMessage.monitorType == MonitorLib.MONITOR_ROLLBACK) {
+ IContractUsingSDP(receiverID).recvUnorderedMessage(senderDomain, author, monitorMessage.message);
+ emit receiveMonitorRollbackMessage(monitorMessage.monitorType, senderDomain, author, MonitorLib.encodeAddressIntoCrossChainID(receiverID), monitorMessage.monitorMsg);
+ } else if (monitorMessage.monitorType == MonitorLib.MONITOR_CLOSE) {
+ IContractUsingSDP(receiverID).recvUnorderedMessage(senderDomain, author, monitorMessage.message);
+ } else {
+ revert("Monitor_Msg: wrong monitor type");
+ }
+ }
+
+ // IContractUsingMonitor.sol里面的接口
+ function recvMessageFromSDP(string memory senderDomain, bytes32 author, address receiverID, bytes memory message) external override onlySubProtocols {
+ // 去掉监管字段 再向上传给app合约
+ MonitorMessage memory monitorMessage;
+ monitorMessage.decode(message);
+
+ if (monitorMessage.monitorType == MonitorLib.MONITOR_OPEN) {
+ // 验证监管节点的签名
+ bool res = IMonitorVerifier(monitorVerifierAddress).verifyMonitorNodeProofMessage();
+ emit VerifyMonitorNodeProofMessage(senderDomain, author, receiverID, res);
+ // 监管不通过 构造回滚消息(仅修改监管字段 对原文不修改)
+ if (res == false) {
+ // revert("Monitor_Msg: MidMonitoring not pass");
+ monitorMessage.monitorType = MonitorLib.MONITOR_ROLLBACK;
+ monitorMessage.monitorMsg = "MidMonitoring not pass";
+ ISDPMessage(sdpAddress).sendMessage(senderDomain, author, receiverID, monitorMessage.encode());
+ emit sendMonitorRollbakMessage(monitorMessage.monitorType, MonitorLib.encodeAddressIntoCrossChainID(receiverID), senderDomain, author, monitorMessage.monitorMsg);
+ } else {
+ IContractUsingSDP(receiverID).recvMessage(senderDomain, author, monitorMessage.message);
+ // successfulCallInMonitorOPEN += 1;
+ }
+ } else if (monitorMessage.monitorType == MonitorLib.MONITOR_ROLLBACK) {
+ IContractUsingSDP(receiverID).recvMessage(senderDomain, author, monitorMessage.message);
+ // successfulCallInMonitorROLLBACK += 1;
+ emit receiveMonitorRollbackMessage(monitorMessage.monitorType, senderDomain, author, MonitorLib.encodeAddressIntoCrossChainID(receiverID), monitorMessage.monitorMsg);
+ } else if (monitorMessage.monitorType == MonitorLib.MONITOR_CLOSE) {
+ IContractUsingSDP(receiverID).recvMessage(senderDomain, author, monitorMessage.message);
+ // successfulCallInMonitorCLOSE += 1;
+ } else {
+ revert("Monitor_Msg: wrong monitor type");
+ }
+ }
+
+ function PreMonitoring(string calldata receiverDomain, bytes32 receiverID, bytes calldata message) internal returns (bool) {
+ bool result = false;
+
+ // 事前监管逻辑
+ bytes32 senderID = MonitorLib.encodeAddressIntoCrossChainID(msg.sender);
+ if(senderBlacklist[senderID]) {
+ emit MonitorDisapproval(senderID,
+ receiverDomain,
+ receiverID,
+ MonitorLib.MAJOR_TYPE_CONTRACT_ADDRESS,
+ MonitorLib.SENDER_IN_BLACKLIST);
+ } else {
+ if (receiverBlacklist[receiverID]) {
+ emit MonitorDisapproval(senderID,
+ receiverDomain,
+ receiverID,
+ MonitorLib.MAJOR_TYPE_CONTRACT_ADDRESS,
+ MonitorLib.RECEIVER_IN_BLACKLIST);
+ } else {
+ emit MonitorApproval(senderID,
+ receiverDomain,
+ receiverID);
+ return true;
+ }
+ }
+
+ return result;
+ }
+
+
+ function recvMonitorOrder(string calldata committeeId, string calldata signAlgo, bytes memory rawProof, bytes memory rawMonitorOrder) external override {
+ // 对监管节点发送的监管指令 验签
+ bool res = IMonitorVerifier(monitorVerifierAddress).verifyMonitorOrder(committeeId, signAlgo, rawProof, rawMonitorOrder);
+ emit VerifyMonitorOrder(committeeId, res);
+ require(res == true, "Monitor_Msg: verify monitor node proof message failed");
+
+ MonitorOrder memory monitorOrder;
+ monitorOrder.decode(rawMonitorOrder);
+
+ uint8[8] memory flags;
+ uint8[8] memory values;
+ for (uint8 i = 0; i < 8; i++) {
+ uint8 chunk = uint8((monitorOrder.monitorOrderType >> (28 - i * 4)) & 0xF); // 取出 4-bit
+ flags[i] = (chunk >> 3) & 0x1; // 最高位为主类型
+ values[i] = chunk & 0x7; // 后3位为子类型
+ }
+
+ // 存储监管指令 目前只支持 加入和移除黑名单 控制监管开关 (合约部署时默认监管开启)
+ if(flags[0] == MonitorLib.MAJOR_TYPE_CONTRACT_ADDRESS) {
+ if(values[0] == MonitorLib.MINOR_TYPE_ADD_TO_BLACKLIST) {
+ senderBlacklist[monitorOrder.sender] = true;
+ receiverBlacklist[monitorOrder.receiver] = true;
+ } else if(values[0] == MonitorLib.MINOR_TYPE_REMOVE_FROM_BLACKLIST) {
+ senderBlacklist[monitorOrder.sender] = false;
+ receiverBlacklist[monitorOrder.receiver] = false;
+ } else {
+ revert("Monitor_Msg: not support monitor order containing this MINOR TYPE in MAJOR_TYPE_CONTRACT_ADDRESS yet");
+ }
+ }
+ if(flags[1] == MonitorLib.MAJOR_TYPE_CONTROL) {
+ if(values[1] == MonitorLib.MINOR_TYPE_MONITOR_CLOSE) {
+ monitorControl = MonitorLib.MONITOR_CLOSE;
+ } else if(values[1] == MonitorLib.MINOR_TYPE_MONITOR_OPEN) {
+ monitorControl = MonitorLib.MONITOR_OPEN;
+ } else {
+ revert("Monitor_Msg: not support monitor order containing this MINOR TYPE in MAJOR_TYPE_CONTROL yet");
+ }
+ }
+ if(flags[0] != MonitorLib.MAJOR_TYPE_CONTRACT_ADDRESS) {
+ if(flags[1] != MonitorLib.MAJOR_TYPE_CONTROL) {
+ revert("Monitor_Msg: not support monitor order containing this MAJOR TYPE yet");
+ }
+ }
+ emit receiveMonitorOrder(monitorOrder.monitorOrderType, monitorOrder.senderDomain, monitorOrder.sender,
+ monitorOrder.receiverDomain, monitorOrder.receiver,
+ monitorOrder.transactionContent, monitorOrder.extra);
+ }
+
+ function _beforeSend(string calldata receiverDomain, bytes32 receiverID, bytes calldata message) internal {}
+
+ function _afterSend() internal {}
+
+ function _beforeSendUnordered(string calldata receiverDomain, bytes32 receiverID, bytes calldata message) internal {}
+
+ function _afterSendUnordered() internal {}
+
+ /**
+ * @dev This empty reserved space is put in place to allow future versions to add new
+ * variables without shifting down storage in the inheritance chain.
+ * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
+ */
+ uint256[50] private __gap;
+}
\ No newline at end of file
diff --git a/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/MonitorVerifier.sol b/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/MonitorVerifier.sol
new file mode 100755
index 00000000..c416500d
--- /dev/null
+++ b/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/MonitorVerifier.sol
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: Apache-2.0
+pragma solidity ^0.8.0;
+pragma experimental ABIEncoderV2;
+
+import "./interfaces/IMonitorVerifier.sol";
+import "./lib/ptc/CommitteeLib.sol";
+import "./lib/monitor/MonitorLib.sol";
+import "./@openzeppelin/contracts/access/Ownable.sol";
+import "./@openzeppelin/contracts/proxy/utils/Initializable.sol";
+
+contract MonitorVerifier is Ownable, Initializable, IMonitorVerifier {
+
+ using CommitteeLib for NodeEndorseInfo;
+ using CommitteeLib for CommitteeNodeProof;
+ using CommitteeLib for CrossChainLane;
+
+ MonitorNodeProofMessage public monitorNodeProofMessage;
+
+ address public ptcHubAddress;
+
+ // key: committeeId, value: NodeEndorseInfo
+ mapping(string => NodeEndorseInfo) public monitorNodeEndorseInfoMap;
+
+ modifier onlyPtcHub() {
+ require(_msgSender() == ptcHubAddress, "MonitorVerifierMsg: caller is not the ptcHub");
+ _;
+ }
+
+ constructor() {
+ _disableInitializers();
+ }
+
+ function init() external initializer {
+ _transferOwnership(_msgSender());
+ }
+
+ function setPtcHubAddress(address newPtcHubAddress) external override onlyOwner {
+ ptcHubAddress = newPtcHubAddress;
+ }
+
+ function getPtcHubAddress() external view returns (address) {
+ return ptcHubAddress;
+ }
+
+ function updateMonitorNodeEndorseInfo(bytes memory rawEndorseRoot) external override onlyPtcHub {
+ CommitteeEndorseRoot memory cer = CommitteeLib.decodeCommitteeEndorseRootFrom(rawEndorseRoot);
+ for (uint i = 0; i < cer.endorsers.length; i++) {
+ NodeEndorseInfo memory info = cer.endorsers[i];
+ if (CommitteeLib.checkMonitorNode(info.nodeId)) {
+ monitorNodeEndorseInfoMap[cer.committeeId] = info;
+ emit UpdateMonitorNodeEndorseInfo(cer.committeeId, info.nodeId, info.publicKey.rawPublicKey);
+ break;
+ }
+ }
+ }
+
+ // AM-SDP-Monitor合约这样一条跨合约调用链是一个原子性操作, 属于同一笔交易的执行上下文,在交易结束之前不会切换到下一笔交易
+ // 所以此处不需要用modifier修饰符去保证来源的可靠性
+ function receiveMonitorNodeProofMessage(
+ CrossChainLane calldata newCrossChainLane,
+ string calldata newCommitteeId,
+ CommitteeNodeProof calldata newMonitorNodeProof,
+ bytes calldata newEncodedToSign) external override {
+ monitorNodeProofMessage.crossChainLane = newCrossChainLane;
+ monitorNodeProofMessage.committeeId = newCommitteeId;
+ monitorNodeProofMessage.monitorNodeProof = newMonitorNodeProof;
+ monitorNodeProofMessage.encodedToSign = newEncodedToSign;
+ }
+
+ function verifyMonitorNodeProofMessage() external override returns (bool) {
+ require(_hasMonitorNodeEndorseInfo(monitorNodeProofMessage.committeeId), "MonitorVerifierMsg: no monitor node endorse info");
+ return CommitteeLib.verifyTpProofFromMonitorNode(
+ monitorNodeProofMessage.crossChainLane,
+ monitorNodeProofMessage.committeeId,
+ monitorNodeEndorseInfoMap[monitorNodeProofMessage.committeeId],
+ monitorNodeProofMessage.monitorNodeProof,
+ monitorNodeProofMessage.encodedToSign);
+ }
+
+ function verifyMonitorOrder(string calldata committeeId, string calldata signAlgo, bytes calldata rawProof, bytes calldata rawMonitorOrder) external override returns (bool) {
+ require(_hasMonitorNodeEndorseInfo(committeeId), "MonitorVerifierMsg: no monitor node endorse info");
+ return CommitteeLib.verifyMonitorOrder(
+ committeeId,
+ monitorNodeEndorseInfoMap[committeeId],
+ signAlgo,
+ rawProof,
+ rawMonitorOrder);
+ }
+
+ function _hasMonitorNodeEndorseInfo(string memory committeeId) internal view returns (bool) {
+ return bytes(monitorNodeEndorseInfoMap[committeeId].nodeId).length > 0;
+ }
+
+}
diff --git a/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/PtcHub.sol b/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/PtcHub.sol
index a0995590..9ef2abd4 100644
--- a/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/PtcHub.sol
+++ b/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/PtcHub.sol
@@ -4,6 +4,7 @@ pragma experimental ABIEncoderV2;
import "./interfaces/IPtcHub.sol";
import "./interfaces/IPtcVerifier.sol";
+import "./interfaces/IMonitorVerifier.sol";
import "./lib/ptc/PtcLib.sol";
import "./@openzeppelin/contracts/access/Ownable.sol";
import "./@openzeppelin/contracts/proxy/utils/Initializable.sol";
@@ -43,6 +44,8 @@ contract PtcHub is IPtcHub, Ownable, Initializable {
PTCTypeEnum[] public ptcTypeSupported;
+ address public monitorVerifierAddr;
+
constructor(bytes memory rawRootBcdnsCert) {
_initBcdns(rawRootBcdnsCert);
_disableInitializers();
@@ -69,6 +72,15 @@ contract PtcHub is IPtcHub, Ownable, Initializable {
emit NewBcdnsCert(k);
}
+ function setMonitorVerifier(address newMonitorVerifierAddr) external override onlyOwner {
+ require(newMonitorVerifierAddr != address(0), "PtcHub: invalid monitorPtc contract");
+ monitorVerifierAddr = newMonitorVerifierAddr;
+ }
+
+ function getMonitorVerifier() external view returns (address) {
+ return monitorVerifierAddr;
+ }
+
function updatePTCTrustRoot(bytes calldata rawPtcTrustRoot)
external
override
@@ -190,6 +202,7 @@ contract PtcHub is IPtcHub, Ownable, Initializable {
}
s.mapByVersion[newTpBta.tpbtaVersion] = rawTpBta;
emit SaveTpBta(k, newTpBta.tpbtaVersion);
+ IMonitorVerifier(monitorVerifierAddr).updateMonitorNodeEndorseInfo(newTpBta.endorseRoot);
}
function getTpBta(bytes calldata tpbtaLane, uint32 tpBtaVersion) external override view returns (bytes memory) {
@@ -231,7 +244,7 @@ contract PtcHub is IPtcHub, Ownable, Initializable {
address verifier = verifierMap[AcbCommons.decodePTCCredentialSubjectFrom(ptr.ptcCrossChainCert.credentialSubject).ptcType];
require(verifier != address(0x0), "no ptc veifier set");
- bool result = IPtcVerifier(verifier).verifyTpProof(tpbta, tpProof);
+ bool result = IPtcVerifier(verifier).verifyTpProof(tpbta, tpProof, monitorVerifierAddr);
emit VerifyProof(tpbta.crossChainLane, result);
require(result, "verify not pass");
}
diff --git a/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/SDPMsg.sol b/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/SDPMsg.sol
index 898596c3..74a3f19c 100644
--- a/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/SDPMsg.sol
+++ b/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/SDPMsg.sol
@@ -3,6 +3,7 @@ pragma solidity ^0.8.0;
import "./interfaces/ISDPMessage.sol";
import "./interfaces/IAuthMessage.sol";
+import "./interfaces/IContractUsingMonitor.sol";
import "./interfaces/IContractUsingSDP.sol";
import "./interfaces/IContractWithAcks.sol";
import "./lib/sdp/SDPLib.sol";
@@ -17,6 +18,8 @@ contract SDPMsg is ISDPMessage, Ownable, Initializable {
using SDPLib for SDPMessageV3;
using SDPLib for BlockState;
+ address public monitorAddress;
+
address public amAddress;
bytes32 public localDomainHash;
@@ -37,6 +40,11 @@ contract SDPMsg is ISDPMessage, Ownable, Initializable {
*/
mapping(bytes32 => bool) sendSDPV3Msgs;
+ modifier onlyMonitorMsg() {
+ require(monitorAddress == msg.sender, "SDPMsg: not valid monitor contract");
+ _;
+ }
+
modifier onlyAM() {
require(
amAddress == msg.sender,
@@ -53,6 +61,15 @@ contract SDPMsg is ISDPMessage, Ownable, Initializable {
_transferOwnership(_msgSender());
}
+ function setMonitorContract(address newMonitorAddress) override external onlyOwner {
+ require(newMonitorAddress != address(0), "SDPMsg: invalid monitor contract");
+ monitorAddress = newMonitorAddress;
+ }
+
+ function getMonitorAddress() external view returns (address) {
+ return monitorAddress;
+ }
+
function setAmContract(address newAmContract) override external onlyOwner {
require(newAmContract != address(0), "SDPMsg: invalid am contract");
amAddress = newAmContract;
@@ -70,7 +87,7 @@ contract SDPMsg is ISDPMessage, Ownable, Initializable {
return localDomainHash;
}
- function sendMessage(string calldata receiverDomain, bytes32 receiverID, bytes calldata message) override external {
+ function sendMessage(string calldata receiverDomain, bytes32 receiverID, address senderID, bytes calldata message) override external onlyMonitorMsg {
_beforeSend(receiverDomain, receiverID, message);
SDPMessage memory sdpMessage = SDPMessage(
@@ -78,18 +95,18 @@ contract SDPMsg is ISDPMessage, Ownable, Initializable {
receiveDomain: receiverDomain,
receiver: receiverID,
message: message,
- sequence: _getAndUpdateSendSeq(receiverDomain, msg.sender, receiverID)
+ sequence: _getAndUpdateSendSeq(receiverDomain, senderID, receiverID)
}
);
bytes memory rawMsg = sdpMessage.encode();
- IAuthMessage(amAddress).recvFromProtocol(msg.sender, rawMsg);
+ IAuthMessage(amAddress).recvFromProtocol(senderID, rawMsg);
_afterSend();
}
- function sendUnorderedMessage(string calldata receiverDomain, bytes32 receiverID, bytes calldata message) override external {
+ function sendUnorderedMessage(string calldata receiverDomain, bytes32 receiverID, address senderID, bytes calldata message) override external onlyMonitorMsg {
_beforeSendUnordered(receiverDomain, receiverID, message);
SDPMessage memory sdpMessage = SDPMessage(
@@ -101,7 +118,7 @@ contract SDPMsg is ISDPMessage, Ownable, Initializable {
}
);
- IAuthMessage(amAddress).recvFromProtocol(msg.sender, sdpMessage.encode());
+ IAuthMessage(amAddress).recvFromProtocol(senderID, sdpMessage.encode());
_afterSendUnordered();
}
@@ -291,7 +308,7 @@ contract SDPMsg is ISDPMessage, Ownable, Initializable {
errMsg = "receiver has no code";
} else {
try
- IContractUsingSDP(receiver).recvMessage(senderDomain, senderID, sdpMessage.message)
+ IContractUsingMonitor(monitorAddress).recvMessageFromSDP(senderDomain, senderID, receiver, sdpMessage.message)
{
res = true;
} catch Error(
@@ -307,8 +324,8 @@ contract SDPMsg is ISDPMessage, Ownable, Initializable {
}
function _routeUnorderedMessage(string calldata senderDomain, bytes32 senderID, SDPMessage memory sdpMessage) internal {
- IContractUsingSDP(sdpMessage.getReceiverAddress())
- .recvUnorderedMessage(senderDomain, senderID, sdpMessage.message);
+ IContractUsingMonitor(monitorAddress)
+ .recvUnorderedMessageFromSDP(senderDomain, senderID, sdpMessage.getReceiverAddress(), sdpMessage.message);
}
function _processSDPv2(string calldata senderDomain, bytes32 senderID, bytes memory pkg) internal {
diff --git a/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/interfaces/IContractUsingMonitor.sol b/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/interfaces/IContractUsingMonitor.sol
new file mode 100755
index 00000000..621d055b
--- /dev/null
+++ b/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/interfaces/IContractUsingMonitor.sol
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: Apache-2.0
+pragma solidity ^0.8.0;
+
+// 供dapp合约编写的接口 由监管合约向上传递消息时调用
+interface IContractUsingMonitor {
+
+ function recvUnorderedMessageFromSDP(string memory senderDomain, bytes32 author, address receiverID, bytes memory message) external;
+
+ function recvMessageFromSDP(string memory senderDomain, bytes32 author, address receiverID, bytes memory message) external;
+}
\ No newline at end of file
diff --git a/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/interfaces/IMonitor.sol b/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/interfaces/IMonitor.sol
new file mode 100755
index 00000000..d3a44144
--- /dev/null
+++ b/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/interfaces/IMonitor.sol
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: Apache-2.0
+pragma solidity ^0.8.0;
+
+import "../lib/monitor/MonitorLib.sol";
+
+interface IMonitor {
+ // 发送方-监管通过 监管不通过
+ event MonitorApproval(
+ bytes32 senderID,
+ string receiverDomain,
+ bytes32 receiverID
+ );
+
+ event MonitorDisapproval(
+ bytes32 senderID,
+ string receiverDomain,
+ bytes32 receiverID,
+ uint8 monitorMainType,
+ uint8 monitorSubType
+ );
+
+ // 收到监管指令
+ event receiveMonitorOrder(
+ uint32 monitorType,
+ string senderDomain,
+ bytes32 sender,
+ string receiverDomain,
+ bytes32 receiver,
+ string transactionContent,
+ string extra
+ );
+
+ // 收到监管回滚消息
+ event receiveMonitorRollbackMessage(
+ uint32 monitorType,
+ string senderDomain,
+ bytes32 sender,
+ bytes32 receiver,
+ string monitorMsg
+ );
+
+ // 监管不通过 构造监管回滚消息
+ event sendMonitorRollbakMessage(
+ uint32 monitorType,
+ bytes32 sender,
+ string receiverDomain,
+ bytes32 receiver,
+ string monitorMsg
+ );
+
+ // 验证监管节点签名
+ event VerifyMonitorNodeProofMessage(
+ string senderDomain,
+ bytes32 author,
+ address receiverID,
+ bool result
+ );
+
+ event VerifyMonitorOrder(
+ string committeeId,
+ bool result
+ );
+
+ // send接口:供dapp合约调用
+ function sendMonitorMessage(string calldata receiverDomain, bytes32 receiverID, bytes calldata message) external;
+
+ function sendUnorderedMonitorMessage(string calldata receiverDomain, bytes32 receiverID, bytes calldata message) external;
+
+ function recvMonitorOrder(string calldata committeeId, string calldata signAlgo, bytes memory proof, bytes memory rawMonitorOrder) external;
+
+ // 其他
+ function setProtocol(address protocolAddress) external;
+
+ function setMonitorVerifier(address newMonitorVerifierAddress) external;
+
+ function setMonitorControl(uint32 monitorType) external;
+}
\ No newline at end of file
diff --git a/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/interfaces/IMonitorVerifier.sol b/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/interfaces/IMonitorVerifier.sol
new file mode 100755
index 00000000..7106fb5d
--- /dev/null
+++ b/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/interfaces/IMonitorVerifier.sol
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: Apache-2.0
+pragma solidity ^0.8.0;
+pragma experimental ABIEncoderV2;
+
+import "../lib/ptc/CommitteeLib.sol";
+import "../lib/commons/AcbCommons.sol";
+
+interface IMonitorVerifier {
+
+ struct MonitorNodeProofMessage {
+ CrossChainLane crossChainLane;
+ string committeeId;
+ CommitteeNodeProof monitorNodeProof;
+ bytes encodedToSign;
+ }
+
+ event UpdateMonitorNodeEndorseInfo(string committeeId, string nodeId, bytes rawPublicKey);
+
+ function setPtcHubAddress(address newPtcHubAddress) external;
+
+ function updateMonitorNodeEndorseInfo(bytes memory rawEndorseRoot) external;
+
+ function receiveMonitorNodeProofMessage(
+ CrossChainLane calldata newCrossChainLane,
+ string calldata newCommitteeId,
+ CommitteeNodeProof calldata newMonitorNodeProof,
+ bytes calldata encodedToSign) external;
+
+ function verifyMonitorNodeProofMessage() external returns (bool);
+
+ function verifyMonitorOrder(string calldata committeeId, string calldata signAlgo, bytes calldata rawProof, bytes calldata rawMonitorOrder) external returns (bool);
+}
\ No newline at end of file
diff --git a/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/interfaces/IPtcHub.sol b/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/interfaces/IPtcHub.sol
index ea01ca0b..6cedecc4 100644
--- a/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/interfaces/IPtcHub.sol
+++ b/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/interfaces/IPtcHub.sol
@@ -21,6 +21,8 @@ interface IPtcHub {
event VerifyProof(CrossChainLane tpbtaLane, bool result);
+ function setMonitorVerifier(address newMonitorVerifierAddr) external;
+
function updatePTCTrustRoot(bytes calldata rawPtcTrustRoot) external;
function getPTCTrustRoot(bytes calldata ptcOwnerOid) external view returns (bytes memory);
diff --git a/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/interfaces/IPtcVerifier.sol b/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/interfaces/IPtcVerifier.sol
index 7c0a8b78..558cdc53 100644
--- a/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/interfaces/IPtcVerifier.sol
+++ b/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/interfaces/IPtcVerifier.sol
@@ -8,7 +8,7 @@ interface IPtcVerifier {
function verifyTpBta(PTCVerifyAnchor calldata va, TpBta calldata tpBta) external returns (bool);
- function verifyTpProof(TpBta memory tpBta, ThirdPartyProof memory tpProof) external returns (bool);
+ function verifyTpProof(TpBta memory tpBta, ThirdPartyProof memory tpProof, address monitorPtcAddr) external returns (bool);
function myPtcType() external returns (PTCTypeEnum);
}
\ No newline at end of file
diff --git a/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/interfaces/ISDPMessage.sol b/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/interfaces/ISDPMessage.sol
index 915d9826..335c79b7 100644
--- a/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/interfaces/ISDPMessage.sol
+++ b/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/interfaces/ISDPMessage.sol
@@ -129,7 +129,7 @@ interface ISDPMessage is ISubProtocol {
* @param receiverID the address of the receiver.
* @param message the raw message from DApp contracts
*/
- function sendMessage(string calldata receiverDomain, bytes32 receiverID, bytes calldata message) external;
+ function sendMessage(string calldata receiverDomain, bytes32 receiverID, address senderID, bytes calldata message) external;
/**
* @dev Smart contracts call this method to send cross-chain messages out of order in SDPv2.
@@ -154,7 +154,7 @@ interface ISDPMessage is ISubProtocol {
* @param receiverID the address of the receiver.
* @param message the raw message from DApp contracts
*/
- function sendUnorderedMessage(string calldata receiverDomain, bytes32 receiverID, bytes calldata message) external;
+ function sendUnorderedMessage(string calldata receiverDomain, bytes32 receiverID, address senderID, bytes calldata message) external;
/**
* @dev Query the current sdp message sequence for the channel identited by `senderDomain`,
diff --git a/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/interfaces/ISubProtocol.sol b/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/interfaces/ISubProtocol.sol
index 4d25f4a1..53492ad9 100644
--- a/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/interfaces/ISubProtocol.sol
+++ b/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/interfaces/ISubProtocol.sol
@@ -18,4 +18,6 @@ interface ISubProtocol {
* @param newAmContract the address of the AuthMessage contract.
*/
function setAmContract(address newAmContract) external;
+
+ function setMonitorContract(address newMonitorAddress) external;
}
diff --git a/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/lib/monitor/MonitorLib.sol b/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/lib/monitor/MonitorLib.sol
new file mode 100755
index 00000000..a05714d4
--- /dev/null
+++ b/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/lib/monitor/MonitorLib.sol
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: Apache-2.0
+pragma solidity ^0.8.0;
+
+import "../utils/TypesToBytes.sol";
+import "../utils/BytesToTypes.sol";
+import "../utils/SizeOf.sol";
+import "../utils/TLVUtils.sol";
+import "../../@openzeppelin/contracts/utils/Strings.sol";
+
+struct MonitorOrder {
+ string product;
+ string domain;
+ uint32 monitorOrderType;
+ string senderDomain;
+ bytes32 sender;
+ string receiverDomain;
+ bytes32 receiver;
+ string transactionContent;
+ string extra;
+}
+
+struct MonitorMessage {
+ uint32 monitorType;
+ string monitorMsg;
+ bytes message;
+}
+
+library MonitorLib {
+ // 监管字段(monitorType值)
+ uint32 constant public MONITOR_CLOSE = 1;
+ uint32 constant public MONITOR_OPEN = 2;
+ uint32 constant public MONITOR_ROLLBACK = 3;
+
+ // 监管指令类型与子类型(32bit 每4bit为间隔 4bit中前1bit表示主类型 后3bit为子类型)
+ // 0000 0000 0000 0000 0000 0000 0000 0000: 从左到右共 8 个主类型
+ // 第一个主类型-地址相关
+ uint8 constant public MAJOR_TYPE_CONTRACT_ADDRESS = 1;
+ // 子类型-黑名单
+ uint8 constant public MINOR_TYPE_ADD_TO_BLACKLIST = 0;
+ uint8 constant public MINOR_TYPE_REMOVE_FROM_BLACKLIST = 1;
+
+ // 第二个主类型-控制相关
+ uint8 constant public MAJOR_TYPE_CONTROL = 1;
+ // 子类型-是否开启监管
+ uint8 constant public MINOR_TYPE_MONITOR_CLOSE = 0;
+ uint8 constant public MINOR_TYPE_MONITOR_OPEN = 1;
+
+ // 事前监管失败类型
+ uint8 constant public SENDER_IN_BLACKLIST = 0;
+ uint8 constant public RECEIVER_IN_BLACKLIST = 1;
+
+ function encodeAddressIntoCrossChainID(address _address) internal pure returns (bytes32) {
+ bytes32 id = TypesToBytes.addressToBytes32(_address);
+ return id;
+ }
+
+ function encodeCrossChainIDIntoAddress(bytes32 id) pure internal returns (address) {
+ bytes memory rawId = new bytes(32);
+ TypesToBytes.bytes32ToBytes(32, id, rawId);
+ return BytesToTypes.bytesToAddress(32, rawId);
+ }
+
+ function decode(MonitorOrder memory monitorOrder, bytes memory rawMessage) internal pure {
+ uint256 offset = rawMessage.length;
+
+ bytes memory raw_product = BytesToTypes.bytesToVarBytes(offset, rawMessage);
+ monitorOrder.product = string(raw_product);
+ offset -= 4 + raw_product.length;
+
+ bytes memory raw_domain = BytesToTypes.bytesToVarBytes(offset, rawMessage);
+ monitorOrder.domain = string(raw_domain);
+ offset -= 4 + raw_domain.length;
+
+ monitorOrder.monitorOrderType = BytesToTypes.bytesToUint32(offset, rawMessage);
+ offset -= 4;
+
+ bytes memory raw_send_domain = BytesToTypes.bytesToVarBytes(offset, rawMessage);
+ monitorOrder.senderDomain = string(raw_send_domain);
+ offset -= 4 + raw_send_domain.length;
+
+ monitorOrder.sender = BytesToTypes.bytesToBytes32(offset, rawMessage);
+ offset -= SizeOf.sizeOfBytes32();
+
+ bytes memory raw_recv_domain = BytesToTypes.bytesToVarBytes(offset, rawMessage);
+ monitorOrder.receiverDomain = string(raw_recv_domain);
+ offset -= 4 + raw_recv_domain.length;
+
+ monitorOrder.receiver = BytesToTypes.bytesToBytes32(offset, rawMessage);
+ offset -= SizeOf.sizeOfBytes32();
+
+ bytes memory raw_tran_cont = BytesToTypes.bytesToVarBytes(offset, rawMessage);
+ monitorOrder.transactionContent = string(raw_tran_cont);
+ offset -= 4 + raw_tran_cont.length;
+
+ bytes memory raw_extra = BytesToTypes.bytesToVarBytes(offset, rawMessage);
+ monitorOrder.extra = string(raw_extra);
+ offset -= 4 + raw_extra.length;
+ }
+
+ function encode(MonitorMessage memory monitorMessage) pure internal returns (bytes memory) {
+ require(
+ monitorMessage.message.length <= 0xFFFFFFFF,
+ "encodeSDPMessage: body length overlimit"
+ );
+ // 4 + (4 + monitorMsg) + (4 + payload)
+ uint total_size = 12 + bytes(monitorMessage.monitorMsg).length + monitorMessage.message.length;
+ bytes memory pkg = new bytes(total_size);
+ uint offset = total_size;
+
+ TypesToBytes.uint32ToBytes(offset, monitorMessage.monitorType, pkg);
+ offset -= SizeOf.sizeOfUint(32);
+
+ bytes memory raw_monitorMsg = bytes(monitorMessage.monitorMsg);
+ TypesToBytes.varBytesToBytes(offset, raw_monitorMsg, pkg);
+ offset -= 4 + raw_monitorMsg.length;
+
+ TypesToBytes.varBytesToBytes(offset, monitorMessage.message, pkg);
+ offset -= 4 + monitorMessage.message.length;
+
+ return pkg;
+ }
+
+ function decode(MonitorMessage memory monitorMessage, bytes memory rawMessage) internal pure {
+ uint256 offset = rawMessage.length;
+
+ monitorMessage.monitorType = BytesToTypes.bytesToUint32(offset, rawMessage);
+ offset -= SizeOf.sizeOfInt(32);
+
+ bytes memory raw_monitorMsg = BytesToTypes.bytesToVarBytes(offset, rawMessage);
+ monitorMessage.monitorMsg = string(raw_monitorMsg);
+ offset -= 4 + raw_monitorMsg.length;
+
+ monitorMessage.message = BytesToTypes.bytesToVarBytes(offset, rawMessage);
+ offset -= 4 + monitorMessage.message.length;
+ }
+}
\ No newline at end of file
diff --git a/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/lib/ptc/CommitteeLib.sol b/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/lib/ptc/CommitteeLib.sol
index 4212e9f9..34ed885c 100644
--- a/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/lib/ptc/CommitteeLib.sol
+++ b/acb-sdk/pluginset/ethereum2/onchain-plugin/solidity/sys/lib/ptc/CommitteeLib.sol
@@ -3,6 +3,7 @@ pragma solidity ^0.8.0;
pragma experimental ABIEncoderV2;
import "./PtcLib.sol";
+import "../../interfaces/IMonitorVerifier.sol";
import "../../@openzeppelin/contracts/utils/Strings.sol";
// tags for CommitteeEndorseRoot
@@ -94,6 +95,8 @@ library CommitteeLib {
event DoVeriyTpBta(CrossChainLane laneKey, string committeeId, string nodeId, bool result);
event DoVeriyTpProof(CrossChainLane laneKey, string committeeId, string nodeId, bool result);
+ event DoVerifyTpProofFromMonitorNode(CrossChainLane laneKey, string committeeId, string nodeId, bool result);
+ event DoVerifyMonitorOrder(string committeeId, string nodeId, bool res);
using TLVUtils for TLVPacket;
using TLVUtils for TLVItem;
@@ -162,7 +165,7 @@ library CommitteeLib {
return 3 * correct > 2 * cva.anchors.length;
}
- function verifyTpProof(TpBta memory tpBta, ThirdPartyProof memory tpProof) internal returns (bool) {
+ function verifyTpProof(TpBta memory tpBta, ThirdPartyProof memory tpProof , address monitorPtcAddr) internal returns (bool) {
CommitteeEndorseRoot memory cer = decodeCommitteeEndorseRootFrom(tpBta.endorseRoot);
CommitteeEndorseProof memory ceProof = decodeCommitteeEndorseProofFrom(tpProof.rawProof);
@@ -173,9 +176,17 @@ library CommitteeLib {
for (uint i = 0; i < cer.endorsers.length; i++)
{
NodeEndorseInfo memory info = cer.endorsers[i];
+ bool isMonitorNode = false;
bool res = false;
for (uint j = 0; j < ceProof.sigs.length; j++) {
if (info.nodeId.equal(ceProof.sigs[j].nodeId)) {
+ // if it is monitor node proof, trans it to MonitorPTC
+ if (checkMonitorNode(info.nodeId)) {
+ isMonitorNode = true;
+ IMonitorVerifier(monitorPtcAddr).receiveMonitorNodeProofMessage(tpBta.crossChainLane, cer.committeeId, ceProof.sigs[j], encodedToSign);
+ res = true;
+ break;
+ }
res = AcbCommons.verifySig(
ceProof.sigs[j].signAlgo,
info.publicKey.getRawPublicKey(),
@@ -188,8 +199,9 @@ library CommitteeLib {
}
}
}
-
- emit DoVeriyTpProof(tpBta.crossChainLane, cer.committeeId, info.nodeId, res);
+ if (!isMonitorNode) {
+ emit DoVeriyTpProof(tpBta.crossChainLane, cer.committeeId, info.nodeId, res);
+ }
if (!res && info.required) {
return false;
}
@@ -198,6 +210,59 @@ library CommitteeLib {
return cer.policy.threshold.check(optinalCorrect);
}
+ function checkMonitorNode(string memory nodeId) internal pure returns (bool) {
+ bytes memory prefix = bytes("monitor");
+ bytes memory nodeIdBytes = bytes(nodeId);
+
+ if (nodeIdBytes.length < prefix.length) {
+ return false;
+ }
+
+ for (uint i = 0; i < prefix.length; i++) {
+ if (nodeIdBytes[i] != prefix[i]) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ function verifyTpProofFromMonitorNode(
+ CrossChainLane memory crossChainLane,
+ string memory committeeId,
+ NodeEndorseInfo memory monitorNodeEndorseInfo,
+ CommitteeNodeProof memory monitorNodeProof,
+ bytes memory encodedToSign
+ ) internal returns (bool) {
+ bool res = false;
+ res = AcbCommons.verifySig(
+ monitorNodeProof.signAlgo,
+ monitorNodeEndorseInfo.publicKey.getRawPublicKey(),
+ encodedToSign,
+ monitorNodeProof.signature
+ );
+ emit DoVerifyTpProofFromMonitorNode(crossChainLane, committeeId, monitorNodeEndorseInfo.nodeId, res);
+ return res;
+ }
+
+ function verifyMonitorOrder(
+ string memory committeeId,
+ NodeEndorseInfo memory monitorNodeEndorseInfo,
+ string memory signAlgo,
+ bytes memory rawProof,
+ bytes memory rawMonitorOrder
+ ) internal returns (bool) {
+ bool res = false;
+ res = AcbCommons.verifySig(
+ signAlgo,
+ monitorNodeEndorseInfo.publicKey.getRawPublicKey(),
+ rawMonitorOrder,
+ rawProof
+ );
+ emit DoVerifyMonitorOrder(committeeId, monitorNodeEndorseInfo.nodeId, res);
+ return res;
+ }
+
function getRawPublicKey(
NodePublicKeyEntry memory entry
) internal pure returns (bytes memory) {
diff --git "a/docs/images/\345\220\253\347\233\221\347\256\241\347\232\204\350\267\250\351\223\276\346\265\201\347\250\213\345\233\276.png" "b/docs/images/\345\220\253\347\233\221\347\256\241\347\232\204\350\267\250\351\223\276\346\265\201\347\250\213\345\233\276.png"
new file mode 100644
index 00000000..66f09948
Binary files /dev/null and "b/docs/images/\345\220\253\347\233\221\347\256\241\347\232\204\350\267\250\351\223\276\346\265\201\347\250\213\345\233\276.png" differ
diff --git "a/docs/images/\345\220\253\347\233\221\347\256\241\347\232\204\350\267\250\351\223\276\346\266\210\346\201\257\347\273\223\346\236\204.png" "b/docs/images/\345\220\253\347\233\221\347\256\241\347\232\204\350\267\250\351\223\276\346\266\210\346\201\257\347\273\223\346\236\204.png"
new file mode 100644
index 00000000..d2b05531
Binary files /dev/null and "b/docs/images/\345\220\253\347\233\221\347\256\241\347\232\204\350\267\250\351\223\276\346\266\210\346\201\257\347\273\223\346\236\204.png" differ
diff --git "a/docs/images/\347\233\221\347\256\241\346\214\207\344\273\244\347\261\273\345\236\213monitorOrderType\347\232\204\347\273\223\346\236\204.png" "b/docs/images/\347\233\221\347\256\241\346\214\207\344\273\244\347\261\273\345\236\213monitorOrderType\347\232\204\347\273\223\346\236\204.png"
new file mode 100644
index 00000000..c8be7e8f
Binary files /dev/null and "b/docs/images/\347\233\221\347\256\241\346\214\207\344\273\244\347\261\273\345\236\213monitorOrderType\347\232\204\347\273\223\346\236\204.png" differ