diff --git a/.ci/check_rare_string.sh b/.ci/check_rare_string.sh new file mode 100644 index 000000000..fa2555b20 --- /dev/null +++ b/.ci/check_rare_string.sh @@ -0,0 +1,54 @@ +#!/bin/bash + + + +function LOG_ERROR() +{ + local content=${1} + echo -e "\033[31m"${content}"\033[0m" +} + +function LOG_INFO() +{ + local content=${1} + echo -e "\033[32m"${content}"\033[0m" +} + +get_md5sum_cmd() { + local md5sum_cmd="md5sum" + if [ "$(uname)" == "Darwin" ]; then + md5sum_cmd="md5" + fi + echo "$md5sum_cmd" +} + +function checkConcatenatedRareString() { + + concatenatedString=${1} + log_file=${2} + md5sum_cmd=$(get_md5sum_cmd) + + md5_concatenatedString=$(echo -n "$concatenatedString" | $md5sum_cmd | awk '{print $1}') + + # compare rare string and stringFromGet + get_output=$(cat ${log_file}| grep "result=" | awk -F '[][]' '{print $4}' | awk NF | tail -n 1) + md5_stringFromGet=$(echo -n "$get_output" | $md5sum_cmd | awk '{print $1}') + if [ "$md5_concatenatedString" != "$md5_stringFromGet" ]; then + LOG_ERROR "error: check failed, the md5 values of rareString and stringFromGet are not equal, fail concatenatedString: ${concatenatedString}, get_output: ${get_output}" + exit 1 + else + LOG_INFO "check success, concatenatedString: ${concatenatedString}" + fi +} + +main() { + concatenatedString=${1} + log_file=${2} + LOG_INFO "check rare string start, concatenatedString: ${concatenatedString}" + + checkConcatenatedRareString ${concatenatedString} ${log_file} + LOG_INFO "check rare string finished!" +} + +main "$@" + diff --git a/.ci/ci_cross_all_check.sh b/.ci/ci_cross_all_check.sh index 937bdc7c9..1a8390345 100644 --- a/.ci/ci_cross_all_check.sh +++ b/.ci/ci_cross_all_check.sh @@ -2,7 +2,9 @@ set -e ROOT=$(pwd)/demo/ +CI_PWD=$(pwd) PLUGIN_BRANCH=master +final_rare_input="" LOG_INFO() { local content=${1} @@ -14,6 +16,14 @@ LOG_ERROR() { echo -e "\033[31m[ERROR] ${content}\033[0m" } +prepare_rare_string() { + final_rare_input=$(bash ${CI_PWD}/.ci/gen_rare_string.sh) +} + +check_rare_string() { + bash ${CI_PWD}/.ci/check_rare_string.sh ${final_rare_input} ${ROOT}/WeCross-Console/logs/debug.log +} + check_log() { cd ${ROOT} error_log=routers-payment/127.0.0.1-8250-25500/logs/error.log @@ -62,6 +72,8 @@ demo_test() { ensure_bcos_nodes_running + prepare_rare_string + cd WeCross-Console/ bash start.sh < : certain tag or branch to download e.g - bash $0 v1.3.1 + bash $0 v1.4.0 EOF exit 0 } diff --git a/scripts/download_plugin.sh b/scripts/download_plugin.sh index 75c0f2b39..e8d0319f0 100644 --- a/scripts/download_plugin.sh +++ b/scripts/download_plugin.sh @@ -50,10 +50,10 @@ Usage: : certain tag or branch to download e.g - bash $0 BCOS2 v1.3.1 - bash $0 BCOS3 v1.3.1 - bash $0 Fabric1 v1.3.1 - bash $0 Fabric2 v1.3.1 + bash $0 BCOS2 v1.4.0 + bash $0 BCOS3 v1.4.0 + bash $0 Fabric1 v1.4.0 + bash $0 Fabric2 v1.4.0 EOF exit 0 } diff --git a/scripts/download_wecross.sh b/scripts/download_wecross.sh index e91169c31..ffe1ac226 100755 --- a/scripts/download_wecross.sh +++ b/scripts/download_wecross.sh @@ -6,7 +6,7 @@ LANG=en_US.UTF-8 enable_build_from_resource=0 compatibility_version= -default_compatibility_version=v1.3.1 # update this every release +default_compatibility_version=v1.4.0 # update this every release deps_dir=$(pwd)'/WeCross/plugin/' pages_dir=$(pwd)'/WeCross/pages/' src_dir=$(pwd)'/src/' diff --git a/src/main/java/com/webank/wecross/common/WeCrossDefault.java b/src/main/java/com/webank/wecross/common/WeCrossDefault.java index d826fd5fb..ef69b6e0a 100644 --- a/src/main/java/com/webank/wecross/common/WeCrossDefault.java +++ b/src/main/java/com/webank/wecross/common/WeCrossDefault.java @@ -4,7 +4,7 @@ import java.util.List; public class WeCrossDefault { - public static final String VERSION = "v1.3.1"; + public static final String VERSION = "v1.4.0"; public static final String TEMPLATE_URL = "http://127.0.0.1:8080/"; diff --git a/src/main/java/com/webank/wecross/interchain/InterchainDefault.java b/src/main/java/com/webank/wecross/interchain/InterchainDefault.java index 9324cd9e2..106d3b621 100644 --- a/src/main/java/com/webank/wecross/interchain/InterchainDefault.java +++ b/src/main/java/com/webank/wecross/interchain/InterchainDefault.java @@ -3,6 +3,7 @@ public class InterchainDefault { public static final int CALL_TYPE_QUERY = 0; public static final int CALL_TYPE_INVOKE = 1; + public static final int CALL_TYPE_GET_BLOCK = 2; public static final long TIMEOUT_DELAY = 10; public static final String SPLIT_REGEX = " "; diff --git a/src/main/java/com/webank/wecross/interchain/InterchainErrorCode.java b/src/main/java/com/webank/wecross/interchain/InterchainErrorCode.java index cf31c95a4..e569efe5f 100644 --- a/src/main/java/com/webank/wecross/interchain/InterchainErrorCode.java +++ b/src/main/java/com/webank/wecross/interchain/InterchainErrorCode.java @@ -6,4 +6,5 @@ public class InterchainErrorCode { public static final int CALL_TARGET_CHAIN_ERROR = 1003; public static final int CALL_CALLBACK_ERROR = 1004; public static final int REGISTER_CALLBACK_RESULT_ERROR = 1005; + public static final int GET_TARGET_CHAIN_BLOCK_ERROR = 1006; } diff --git a/src/main/java/com/webank/wecross/interchain/InterchainScheduler.java b/src/main/java/com/webank/wecross/interchain/InterchainScheduler.java index 097c06329..b1e05b8bb 100644 --- a/src/main/java/com/webank/wecross/interchain/InterchainScheduler.java +++ b/src/main/java/com/webank/wecross/interchain/InterchainScheduler.java @@ -2,6 +2,7 @@ import static com.webank.wecross.interchain.InterchainDefault.TIMEOUT_DELAY; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.webank.wecross.account.UniversalAccount; import com.webank.wecross.exception.WeCrossException; @@ -33,131 +34,199 @@ public interface InterchainCallback { } public void start(InterchainCallback callback) { - getXATransactionState( - (getTransactionStateException, xaTransactionID, xaTransactionSeq) -> { - if (Objects.nonNull(getTransactionStateException)) { - callback.onReturn(getTransactionStateException); - return; - } + if (interchainRequest.getCallType() == InterchainDefault.CALL_TYPE_GET_BLOCK) { + String realUid = + Sha256Utils.sha256String( + (systemResource.getHubResource() + interchainRequest.getUid()) + .getBytes(StandardCharsets.UTF_8)); + getTargetChainBlock( + ((callTargetChainException, block) -> { + boolean state = true; + String result; + try { + result = objectMapper.writeValueAsString(block); + } catch (JsonProcessingException ignored) { + result = "[]"; + } - if (logger.isDebugEnabled()) { - logger.debug( - "Transaction state, xaTransactionID: {}, xaTransactionSeq: {}, inter chain request: {}", - xaTransactionID, - xaTransactionSeq, - interchainRequest); - } + if (Objects.nonNull(callTargetChainException)) { + logger.error( + "Get target chain block failed, error code: {}, message: {}", + callTargetChainException.getInternalErrorCode(), + callTargetChainException.getInternalMessage()); - String realUid = - Sha256Utils.sha256String( - (systemResource.getHubResource() + interchainRequest.getUid()) - .getBytes(StandardCharsets.UTF_8)); - - long timestamp = System.currentTimeMillis(); - long callTargetChainSeq = - timestamp > xaTransactionSeq ? timestamp : (xaTransactionSeq + 1L); - callTargetChain( - realUid, - xaTransactionID, - callTargetChainSeq, - (callTargetChainException, callTargetChainResult) -> { - boolean state = true; - String result = callTargetChainResult; - - if (Objects.nonNull(callTargetChainException)) { - logger.error( - "Call target chain failed, error code: {}, message: {}", - callTargetChainException.getInternalErrorCode(), - callTargetChainException.getInternalMessage()); - - state = false; - result = "[]"; - } + state = false; + result = "[]"; + } + String finalResult = result; + callCallback( + realUid, + null, + 0, + state, + result, + (callCallbackException, errorCode, message, callCallbackResult) -> { + if (Objects.nonNull(callCallbackException)) { + /* exception occurred, no need to register result */ + callback.onReturn(callCallbackException); + return; + } + + if (logger.isDebugEnabled()) { + logger.debug( + "GetBlock call callback, result: {}, inter chain request: {}", + callCallbackResult, + interchainRequest); + } + + registerCallbackResult( + null, + 0, + errorCode, + message, + finalResult, + registerCallbackResultException -> { + if (logger.isDebugEnabled()) { + logger.debug( + "Register getBlock callback result, errorCode: {}, message: {}, result: {}, inter chain request: {}", + errorCode, + message, + callCallbackResult, + interchainRequest); + } + callback.onReturn(registerCallbackResultException); + }); + }); + })); + } else { + getXATransactionState( + (getTransactionStateException, xaTransactionID, xaTransactionSeq) -> { + if (Objects.nonNull(getTransactionStateException)) { + callback.onReturn(getTransactionStateException); + return; + } - if (logger.isDebugEnabled()) { - logger.debug( - "Call target chain, xaTransactionID: {}, xaTransactionSeq: {}, state: {}, result: {}, inter chain request: {}", - xaTransactionID, - callTargetChainSeq, - state, - result, - interchainRequest); - } + if (logger.isDebugEnabled()) { + logger.debug( + "Transaction state, xaTransactionID: {}, xaTransactionSeq: {}, inter chain request: {}", + xaTransactionID, + xaTransactionSeq, + interchainRequest); + } - boolean finalState = state; - String finalResult = result; - getXATransactionState( - (getCallbackTransactionStateException, - callbackXATransactionID, - callbackXATransactionSeq) -> { - if (Objects.nonNull( - getCallbackTransactionStateException)) { - callback.onReturn( - getCallbackTransactionStateException); - return; - } - - long newTimestamp = System.currentTimeMillis(); - long callCallbackSeq = - newTimestamp > callbackXATransactionSeq - ? newTimestamp - : (callbackXATransactionSeq + 1L); - - callCallback( - Sha256Utils.sha256String( - realUid.getBytes( - StandardCharsets.UTF_8)), + String realUid = + Sha256Utils.sha256String( + (systemResource.getHubResource() + + interchainRequest.getUid()) + .getBytes(StandardCharsets.UTF_8)); + + long timestamp = System.currentTimeMillis(); + long callTargetChainSeq = + timestamp > xaTransactionSeq ? timestamp : (xaTransactionSeq + 1L); + callTargetChain( + realUid, + xaTransactionID, + callTargetChainSeq, + (callTargetChainException, callTargetChainResult) -> { + boolean state = true; + String result = callTargetChainResult; + + if (Objects.nonNull(callTargetChainException)) { + logger.error( + "Call target chain failed, error code: {}, message: {}", + callTargetChainException.getInternalErrorCode(), + callTargetChainException.getInternalMessage()); + + state = false; + result = "[]"; + } + + if (logger.isDebugEnabled()) { + logger.debug( + "Call target chain, xaTransactionID: {}, xaTransactionSeq: {}, state: {}, result: {}, inter chain request: {}", + xaTransactionID, + callTargetChainSeq, + state, + result, + interchainRequest); + } + + boolean finalState = state; + String finalResult = result; + getXATransactionState( + (getCallbackTransactionStateException, callbackXATransactionID, - callCallbackSeq, - finalState, - finalResult, - (callCallbackException, - errorCode, - message, - callCallbackResult) -> { - if (Objects.nonNull( - callCallbackException)) { - /* exception occurred, no need to register result */ - callback.onReturn( - callCallbackException); - return; - } - - if (logger.isDebugEnabled()) { - logger.debug( - "Call callback, xaTransactionID: {}, xaTransactionSeq: {}, result: {}, inter chain request: {}", + callbackXATransactionSeq) -> { + if (Objects.nonNull( + getCallbackTransactionStateException)) { + callback.onReturn( + getCallbackTransactionStateException); + return; + } + + long newTimestamp = System.currentTimeMillis(); + long callCallbackSeq = + newTimestamp > callbackXATransactionSeq + ? newTimestamp + : (callbackXATransactionSeq + 1L); + + callCallback( + Sha256Utils.sha256String( + realUid.getBytes( + StandardCharsets.UTF_8)), + callbackXATransactionID, + callCallbackSeq, + finalState, + finalResult, + (callCallbackException, + errorCode, + message, + callCallbackResult) -> { + if (Objects.nonNull( + callCallbackException)) { + /* exception occurred, no need to register result */ + callback.onReturn( + callCallbackException); + return; + } + + if (logger.isDebugEnabled()) { + logger.debug( + "Call callback, xaTransactionID: {}, xaTransactionSeq: {}, result: {}, inter chain request: {}", + callbackXATransactionID, + callCallbackSeq, + callCallbackResult, + interchainRequest); + } + + registerCallbackResult( callbackXATransactionID, callCallbackSeq, + errorCode, + message, callCallbackResult, - interchainRequest); - } - - registerCallbackResult( - callbackXATransactionID, - callCallbackSeq, - errorCode, - message, - callCallbackResult, - registerCallbackResultException -> { - if (logger.isDebugEnabled()) { - logger.debug( - "Register callback result, xaTransactionID: {}, xaTransactionSeq: {}, errorCode: {}, message: {}, result: {}, inter chain request: {}", - callbackXATransactionID, - callCallbackSeq, - errorCode, - message, - callCallbackResult, - interchainRequest); - } - callback.onReturn( - registerCallbackResultException); - }); - }); - }, - interchainRequest.getCallbackPath()); - }); - }, - interchainRequest.getPath()); + registerCallbackResultException -> { + if (logger + .isDebugEnabled()) { + logger.debug( + "Register callback result, xaTransactionID: {}, xaTransactionSeq: {}, errorCode: {}, message: {}, result: {}, inter chain request: {}", + callbackXATransactionID, + callCallbackSeq, + errorCode, + message, + callCallbackResult, + interchainRequest); + } + callback.onReturn( + registerCallbackResultException); + }); + }); + }, + interchainRequest.getCallbackPath()); + }); + }, + interchainRequest.getPath()); + } } public interface GetTransactionStateCallback { @@ -333,6 +402,54 @@ public void callTargetChain( } } + public interface GetBlockCallback { + void onReturn(WeCrossException exception, Block block); + } + + public void getTargetChainBlock(GetBlockCallback callback) { + try { + Path path = Path.decode(interchainRequest.getPath()); + Resource resource = systemResource.getZoneManager().fetchResource(path); + if (Objects.isNull(resource)) { + callback.onReturn( + new WeCrossException( + WeCrossException.ErrorCode.INTER_CHAIN_ERROR, + "GET_TARGET_CHAIN_BLOCK_ERROR", + InterchainErrorCode.GET_TARGET_CHAIN_BLOCK_ERROR, + "Target path '" + path + "' not found"), + null); + return; + } + long blockNumber = Long.parseLong(interchainRequest.getArgs()[0]); + resource.getBlockManager() + .asyncGetBlock( + blockNumber, + (blockHeaderException, block) -> { + if (Objects.nonNull(blockHeaderException)) { + callback.onReturn( + new WeCrossException( + WeCrossException.ErrorCode.INTER_CHAIN_ERROR, + "GET_TARGET_CHAIN_BLOCK_ERROR", + InterchainErrorCode + .GET_TARGET_CHAIN_BLOCK_ERROR, + blockHeaderException.getMessage()), + null); + } else { + callback.onReturn(null, block); + } + }); + } catch (Exception e) { + logger.error("Get target chain block exception, ", e); + callback.onReturn( + new WeCrossException( + WeCrossException.ErrorCode.INTER_CHAIN_ERROR, + "GET_TARGET_CHAIN_BLOCK_ERROR", + InterchainErrorCode.GET_TARGET_CHAIN_BLOCK_ERROR, + "Exception occurred"), + null); + } + } + public interface CallCallbackCallback { // result is json form of string array void onReturn(WeCrossException exception, int errorCode, String message, String result); diff --git a/src/main/java/com/webank/wecross/network/p2p/netty/channel/handler/ChannelHandler.java b/src/main/java/com/webank/wecross/network/p2p/netty/channel/handler/ChannelHandler.java index 3b5393898..63d24a3a8 100644 --- a/src/main/java/com/webank/wecross/network/p2p/netty/channel/handler/ChannelHandler.java +++ b/src/main/java/com/webank/wecross/network/p2p/netty/channel/handler/ChannelHandler.java @@ -8,7 +8,6 @@ import io.netty.handler.ssl.SslHandshakeCompletionEvent; import io.netty.handler.timeout.IdleStateEvent; import io.netty.util.AttributeKey; -import javax.net.ssl.SSLPeerUnverifiedException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -59,7 +58,7 @@ public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { logger.info(" handshake success, host: {}, ctx: {}", node, hashCode); try { getChannelHandlerCallBack().onConnect(ctx, getConnectToServer()); - } catch (SSLPeerUnverifiedException e1) { + } catch (Exception e1) { logger.warn( " handshake on connect exception, disconnect, host: {}, ctx: {}, cause: {}", node, @@ -67,6 +66,8 @@ public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { e1.getCause()); ctx.disconnect(); ctx.close(); + throw new RuntimeException( + "SSLPeerUnverifiedException, handshake on connect exception", e1); } } else { logger.warn( diff --git a/src/main/java/com/webank/wecross/network/p2p/netty/channel/handler/ChannelHandlerCallBack.java b/src/main/java/com/webank/wecross/network/p2p/netty/channel/handler/ChannelHandlerCallBack.java index 8a01439fd..7839a0c23 100644 --- a/src/main/java/com/webank/wecross/network/p2p/netty/channel/handler/ChannelHandlerCallBack.java +++ b/src/main/java/com/webank/wecross/network/p2p/netty/channel/handler/ChannelHandlerCallBack.java @@ -61,16 +61,20 @@ private String bytesToHex(byte[] hashInBytes) { return sb.toString(); } - private PublicKey fetchCertificate(ChannelHandlerContext ctx) - throws SSLPeerUnverifiedException { + private PublicKey fetchCertificate(ChannelHandlerContext ctx) throws Exception { SslHandler sslhandler = ctx.channel().pipeline().get(SslHandler.class); - - Certificate[] certs = sslhandler.engine().getSession().getPeerCertificates(); + Certificate[] certs; + try { + certs = sslhandler.engine().getSession().getPeerCertificates(); + } catch (SSLPeerUnverifiedException e) { + logger.error("fetchCertificate error", e); + throw new Exception("fetchCertificate error", e); + } logger.info( " ctx: {}, Certificate length: {}, pipeline sslHandlers: {}", Objects.hashCode(ctx), certs.length, - String.valueOf(ctx.channel().pipeline().names())); + ctx.channel().pipeline().names()); Certificate cert = certs[0]; PublicKey publicKey = cert.getPublicKey(); @@ -91,8 +95,7 @@ private PublicKey fetchCertificate(ChannelHandlerContext ctx) * @return * @throws SSLPeerUnverifiedException */ - public Node channelContext2Node(ChannelHandlerContext context) - throws SSLPeerUnverifiedException { + public Node channelContext2Node(ChannelHandlerContext context) throws Exception { if (null == context) { return null; } @@ -105,8 +108,7 @@ public Node channelContext2Node(ChannelHandlerContext context) return new Node(nodeID, host, port); } - public void onConnect(ChannelHandlerContext ctx, boolean connectToServer) - throws SSLPeerUnverifiedException { + public void onConnect(ChannelHandlerContext ctx, boolean connectToServer) throws Exception { Node node = channelContext2Node(ctx); int hashCode = System.identityHashCode(ctx); @@ -127,15 +129,9 @@ public void onConnect(ChannelHandlerContext ctx, boolean connectToServer) callBack.onConnect(ctx, node); } else { try { - threadPool.execute( - new Runnable() { - @Override - public void run() { - callBack.onConnect(ctx, node); - } - }); + threadPool.execute(() -> callBack.onConnect(ctx, node)); } catch (TaskRejectedException e) { - logger.warn(" TaskRejectedException: {} ", e); + logger.warn(" TaskRejectedException: ", e); callBack.onConnect(ctx, node); } } diff --git a/src/main/java/com/webank/wecross/network/rpc/handler/XATransactionHandler.java b/src/main/java/com/webank/wecross/network/rpc/handler/XATransactionHandler.java index 4e5ddb560..f5c0f2078 100644 --- a/src/main/java/com/webank/wecross/network/rpc/handler/XATransactionHandler.java +++ b/src/main/java/com/webank/wecross/network/rpc/handler/XATransactionHandler.java @@ -58,6 +58,7 @@ public void setPaths(Set paths) { public static class ListXATransactionsRequest { private int size; + private String path; private Map offsets = Collections.synchronizedMap(new HashMap<>()); public int getSize() { @@ -75,6 +76,14 @@ public Map getOffsets() { public void setOffsets(Map offsets) { this.offsets = offsets; } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } } @Override @@ -229,6 +238,7 @@ public void handle( host.getAccountManager().getAdminUA(), xaRequest.getData().getOffsets(), xaRequest.getData().getSize(), + xaRequest.getData().getPath(), (exception, xaTransactionListResponse) -> { if (logger.isDebugEnabled()) { logger.debug( diff --git a/src/main/java/com/webank/wecross/restserver/fetcher/TransactionFetcher.java b/src/main/java/com/webank/wecross/restserver/fetcher/TransactionFetcher.java index 6ed12f6b1..2a2d47e15 100644 --- a/src/main/java/com/webank/wecross/restserver/fetcher/TransactionFetcher.java +++ b/src/main/java/com/webank/wecross/restserver/fetcher/TransactionFetcher.java @@ -9,9 +9,11 @@ import com.webank.wecross.stub.Driver; import com.webank.wecross.stub.Path; import com.webank.wecross.stub.StubConstant; +import com.webank.wecross.stub.Transaction; import com.webank.wecross.zone.Chain; import com.webank.wecross.zone.ZoneManager; import java.util.Objects; +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -71,6 +73,8 @@ public void asyncFetchTransaction( completeTransactionResponse.setReceiptBytes(transaction.getReceiptBytes()); completeTransactionResponse.setBlockNumber(blockNumber); completeTransactionResponse.setTxHash(txHash); + completeTransactionResponse.setTimestamp( + transaction.getTransactionResponse().getTimestamp()); if (transaction.isTransactionByProxy()) { completeTransactionResponse.setByProxy(true); @@ -242,53 +246,107 @@ private void recursiveFetchTransactionList( response); } - if (block.transactionsHashes.isEmpty()) { + if (block.transactionsHashes.isEmpty() + && block.transactionsWithDetail.isEmpty()) { // blank block response.setNextBlockNumber(blockNumber - 1); response.setNextOffset(0); recursiveFetchTransactionList(chain, driver, size, response, mainCallback); return; } - - int offset = response.getNextOffset(); - if (offset >= block.transactionsHashes.size()) { - logger.warn( - "Wrong offset, total txHash: {}, offset: {}, response: {}", - block.transactionsHashes.size(), - offset, - response); - mainCallback.onResponse( - new WeCrossException( - WeCrossException.ErrorCode.GET_BLOCK_ERROR, "Wrong offset"), - null); - return; - } - int index; int count = size; - for (index = offset; - index < block.transactionsHashes.size() && count > 0; - index++) { - // hash is blank - if (!"".equals(block.transactionsHashes.get(index).trim())) { - TransactionListResponse.Transaction transaction = - new TransactionListResponse.Transaction(); - transaction.setBlockNumber(blockNumber); - transaction.setTxHash(block.transactionsHashes.get(index)); - response.addTransaction(transaction); - count--; + if (!block.transactionsWithDetail.isEmpty()) { + int offset = response.getNextOffset(); + if (offset >= block.transactionsWithDetail.size()) { + logger.warn( + "Wrong offset, total txHash: {}, offset: {}, response: {}", + block.transactionsWithDetail.size(), + offset, + response); + mainCallback.onResponse( + new WeCrossException( + WeCrossException.ErrorCode.GET_BLOCK_ERROR, + "Wrong offset"), + null); + return; + } + for (index = offset; + index < block.transactionsWithDetail.size() && count > 0; + index++) { + Transaction transaction = block.transactionsWithDetail.get(index); + if (Objects.nonNull(transaction) + && StringUtils.isNotBlank( + transaction.getTransactionResponse().getHash())) { + TransactionListResponse.TransactionWithDetail transactionDetail = + new TransactionListResponse.TransactionWithDetail(); + transactionDetail.setBlockNumber(blockNumber); + transactionDetail.setTxHash( + transaction.getTransactionResponse().getHash()); + if (transaction.isTransactionByProxy()) { + transactionDetail.setPath(transaction.getResource()); + transactionDetail.setAccountIdentity( + transaction.getAccountIdentity()); + transactionDetail.setMethod( + transaction.getTransactionRequest().getMethod()); + transactionDetail.setXaTransactionID( + (String) + transaction + .getTransactionRequest() + .getOptions() + .get(StubConstant.XA_TRANSACTION_ID)); + } + response.addTransactionWithDetail(transactionDetail); + count--; + } } - } - long nextBlockNumber = - index == block.transactionsHashes.size() - ? blockNumber - 1 - : blockNumber; - int nextOffset = index == block.transactionsHashes.size() ? 0 : index; - response.setNextBlockNumber(nextBlockNumber); - response.setNextOffset(nextOffset); + long nextBlockNumber = + index == block.transactionsWithDetail.size() + ? blockNumber - 1 + : blockNumber; + int nextOffset = index == block.transactionsWithDetail.size() ? 0 : index; + response.setNextBlockNumber(nextBlockNumber); + response.setNextOffset(nextOffset); + recursiveFetchTransactionList(chain, driver, count, response, mainCallback); + } else { + int offset = response.getNextOffset(); + if (offset >= block.transactionsHashes.size()) { + logger.warn( + "Wrong offset, total txHash: {}, offset: {}, response: {}", + block.transactionsHashes.size(), + offset, + response); + mainCallback.onResponse( + new WeCrossException( + WeCrossException.ErrorCode.GET_BLOCK_ERROR, + "Wrong offset"), + null); + return; + } + for (index = offset; + index < block.transactionsHashes.size() && count > 0; + index++) { + // hash is blank + if (!"".equals(block.transactionsHashes.get(index).trim())) { + TransactionListResponse.Transaction transaction = + new TransactionListResponse.Transaction(); + transaction.setBlockNumber(blockNumber); + transaction.setTxHash(block.transactionsHashes.get(index)); + response.addTransaction(transaction); + count--; + } + } - recursiveFetchTransactionList(chain, driver, count, response, mainCallback); + long nextBlockNumber = + index == block.transactionsHashes.size() + ? blockNumber - 1 + : blockNumber; + int nextOffset = index == block.transactionsHashes.size() ? 0 : index; + response.setNextBlockNumber(nextBlockNumber); + response.setNextOffset(nextOffset); + recursiveFetchTransactionList(chain, driver, count, response, mainCallback); + } }); } diff --git a/src/main/java/com/webank/wecross/restserver/response/CompleteTransactionResponse.java b/src/main/java/com/webank/wecross/restserver/response/CompleteTransactionResponse.java index 634778f4d..030049d76 100644 --- a/src/main/java/com/webank/wecross/restserver/response/CompleteTransactionResponse.java +++ b/src/main/java/com/webank/wecross/restserver/response/CompleteTransactionResponse.java @@ -22,6 +22,7 @@ public class CompleteTransactionResponse { private int errorCode; // transaction rolled back private String message; + private long timestamp; public String getPath() { return path; @@ -135,6 +136,14 @@ public void setMessage(String message) { this.message = message; } + public long getTimestamp() { + return timestamp; + } + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } + @Override public String toString() { return "CompleteTransactionResponse{" @@ -168,6 +177,9 @@ public String toString() { + ", message='" + message + '\'' + + ", timestamp='" + + timestamp + + '\'' + '}'; } } diff --git a/src/main/java/com/webank/wecross/restserver/response/TransactionListResponse.java b/src/main/java/com/webank/wecross/restserver/response/TransactionListResponse.java index c22de05b4..ede0150a0 100644 --- a/src/main/java/com/webank/wecross/restserver/response/TransactionListResponse.java +++ b/src/main/java/com/webank/wecross/restserver/response/TransactionListResponse.java @@ -10,8 +10,19 @@ public class TransactionListResponse { private int nextOffset; private List transactions = Collections.synchronizedList(new LinkedList<>()); + private List transactionWithDetails = + Collections.synchronizedList(new LinkedList<>()); + public TransactionListResponse() {} + public void addTransactionWithDetail(TransactionWithDetail transactionWithDetail) { + this.transactionWithDetails.add(transactionWithDetail); + } + + public void addTransactionWithDetails(List transactionWithDetails) { + this.transactionWithDetails.addAll(transactionWithDetails); + } + public void addTransaction(Transaction transaction) { this.transactions.add(transaction); } @@ -44,6 +55,14 @@ public void setTransactions(List transactions) { this.transactions = transactions; } + public List getTransactionWithDetails() { + return transactionWithDetails; + } + + public void setTransactionWithDetails(List transactionWithDetails) { + this.transactionWithDetails = transactionWithDetails; + } + @Override public String toString() { return "TransactionListResponse{" @@ -87,4 +106,84 @@ public String toString() { + '}'; } } + + public static class TransactionWithDetail { + private String txHash; + private long blockNumber; + private String accountIdentity; + private String path; + private String method; + private String xaTransactionID; + + public String getTxHash() { + return txHash; + } + + public void setTxHash(String txHash) { + this.txHash = txHash; + } + + public long getBlockNumber() { + return blockNumber; + } + + public void setBlockNumber(long blockNumber) { + this.blockNumber = blockNumber; + } + + public String getAccountIdentity() { + return accountIdentity; + } + + public void setAccountIdentity(String accountIdentity) { + this.accountIdentity = accountIdentity; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public String getMethod() { + return method; + } + + public void setMethod(String method) { + this.method = method; + } + + public String getXaTransactionID() { + return xaTransactionID; + } + + public void setXaTransactionID(String xaTransactionID) { + this.xaTransactionID = xaTransactionID; + } + + @Override + public String toString() { + return "TransactionWithDetail{" + + "txHash='" + + txHash + + '\'' + + ", blockNumber=" + + blockNumber + + ", accountIdentity='" + + accountIdentity + + '\'' + + ", path='" + + path + + '\'' + + ", method='" + + method + + '\'' + + ", xaTransactionID='" + + xaTransactionID + + '\'' + + '}'; + } + } } diff --git a/src/main/java/com/webank/wecross/routine/xa/XATransactionManager.java b/src/main/java/com/webank/wecross/routine/xa/XATransactionManager.java index 875fffd11..30db13765 100644 --- a/src/main/java/com/webank/wecross/routine/xa/XATransactionManager.java +++ b/src/main/java/com/webank/wecross/routine/xa/XATransactionManager.java @@ -576,6 +576,44 @@ public void asyncGetXATransaction( } } + private ListXAReduceCallback getListXACallback( + int count, Map offsets, int size, ListXATransactionsCallback callback) { + List errors = + Collections.synchronizedList(new LinkedList<>()); + Map nextOffsets = new ConcurrentHashMap<>(offsets); + + return (chain, listXAResponse) -> { + + // first time to list, reset offsets + if (nextOffsets.get(chain) == -1) { + nextOffsets.put(chain, listXAResponse.getTotal() - 1); + } + + XATransactionListResponse response = new XATransactionListResponse(); + if (Objects.nonNull(listXAResponse.getChainErrorMessage())) { + errors.add(listXAResponse.getChainErrorMessage()); + if (!errors.isEmpty()) { + XAResponse xaResponse = new XAResponse(); + xaResponse.setStatus(-1); + xaResponse.setChainErrorMessages(errors); + response.setXaResponse(xaResponse); + } + } else { + response.setXaList(listXAResponse.getXaTransactions()); + // update offsets + Long nextOffset = + nextOffsets.get(chain) - listXAResponse.getXaTransactions().size(); + nextOffsets.put(chain, nextOffset); + response.setNextOffsets(nextOffsets); + if (nextOffsets.get(chain) == -1) { + response.setFinished(true); + } + response.recoverUsername(accountManager); + } + callback.onResponse(null, response); + }; + } + public interface ListXATransactionsCallback { void onResponse(WeCrossException e, XATransactionListResponse xaTransactionListResponse); } @@ -728,6 +766,7 @@ public void asyncListXATransactions( UniversalAccount ua, Map offsets, int size, + String chainPath, ListXATransactionsCallback callback) { try { @@ -742,7 +781,12 @@ public void asyncListXATransactions( XATransactionListResponse response = new XATransactionListResponse(); - List chainPaths = setToList(zoneManager.getAllChainsInfo(false).keySet()); + List chainPaths = new ArrayList<>(); + if (Objects.isNull(chainPath) || chainPath.isEmpty()) { + chainPaths = setToList(zoneManager.getAllChainsInfo(false).keySet()); + } else { + chainPaths.add(Path.decode(chainPath)); + } int chainNum = chainPaths.size(); if (chainNum == 0) { @@ -761,8 +805,15 @@ public void asyncListXATransactions( } } - ListXAReduceCallback reduceCallback = - getListXAReduceCallback(offsets.size(), offsets, size, callback); + ListXAReduceCallback reduceCallback = null; + if (Objects.isNull(chainPath) || chainPath.isEmpty()) { + // has sort operation callback + reduceCallback = getListXAReduceCallback(offsets.size(), offsets, size, callback); + } else { + // Remove sort operation callback + reduceCallback = getListXACallback(offsets.size(), offsets, size, callback); + } + for (String chain : offsets.keySet()) { if (!requireIgnore || offsets.get(chain) != -1L) { asyncListXATransactions( diff --git a/src/main/java/com/webank/wecross/stub/Block.java b/src/main/java/com/webank/wecross/stub/Block.java index c00ff8ace..d1a9f35d6 100644 --- a/src/main/java/com/webank/wecross/stub/Block.java +++ b/src/main/java/com/webank/wecross/stub/Block.java @@ -9,6 +9,7 @@ public class Block { @JsonIgnore public byte[] rawBytes; public BlockHeader blockHeader; public List transactionsHashes = new LinkedList<>(); + public List transactionsWithDetail = new LinkedList<>(); public BlockHeader getBlockHeader() { return blockHeader; @@ -34,6 +35,14 @@ public void setRawBytes(byte[] rawBytes) { this.rawBytes = rawBytes; } + public List getTransactionsWithDetail() { + return transactionsWithDetail; + } + + public void setTransactionsWithDetail(List transactionsWithDetail) { + this.transactionsWithDetail = transactionsWithDetail; + } + @Override public String toString() { return "Block{" @@ -43,6 +52,8 @@ public String toString() { + blockHeader + ", transactionsHashes=" + Arrays.toString(transactionsHashes.toArray()) + + ", transactionsWithDetail=" + + Arrays.toString(transactionsWithDetail.toArray()) + '}'; } } diff --git a/src/main/java/com/webank/wecross/stub/BlockHeader.java b/src/main/java/com/webank/wecross/stub/BlockHeader.java index bd5cc0018..ae65dd773 100644 --- a/src/main/java/com/webank/wecross/stub/BlockHeader.java +++ b/src/main/java/com/webank/wecross/stub/BlockHeader.java @@ -7,6 +7,7 @@ public class BlockHeader { private String stateRoot; private String transactionRoot; private String receiptRoot; + private long timestamp; public long getNumber() { return number; @@ -56,6 +57,14 @@ public void setReceiptRoot(String receiptRoot) { this.receiptRoot = receiptRoot; } + public long getTimestamp() { + return timestamp; + } + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } + @Override public String toString() { return "BlockHeader{" @@ -76,6 +85,9 @@ public String toString() { + ", receiptRoot='" + receiptRoot + '\'' + + ", timestamp='" + + timestamp + + '\'' + '}'; } } diff --git a/src/main/java/com/webank/wecross/stub/TransactionResponse.java b/src/main/java/com/webank/wecross/stub/TransactionResponse.java index e8479a96d..a773cd56e 100644 --- a/src/main/java/com/webank/wecross/stub/TransactionResponse.java +++ b/src/main/java/com/webank/wecross/stub/TransactionResponse.java @@ -11,6 +11,7 @@ public class TransactionResponse { private List extraHashes; private long blockNumber; private String[] result; + private long timestamp; public Integer getErrorCode() { return errorCode; @@ -60,6 +61,14 @@ public List getExtraHashes() { return extraHashes; } + public long getTimestamp() { + return timestamp; + } + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } + public void addExtraHash(String hash) { if (this.extraHashes == null) { this.extraHashes = new ArrayList<>(); @@ -82,6 +91,8 @@ public String toString() { + extraHashes + ", blockNumber=" + blockNumber + + ", timestamp=" + + timestamp + ", result=" + Arrays.toString(result) + '}';