diff --git a/build.gradle b/build.gradle index 6c8d5dde3..58db590ca 100644 --- a/build.gradle +++ b/build.gradle @@ -28,6 +28,7 @@ ext { slf4jApiVerison = '1.7.36' mockitoVersion = '4.8.0' gsonVersion = '2.10.1' + tarsSDKVersion = '3.5.0-SNAPSHOT' } // check.dependsOn integrationTest @@ -59,8 +60,8 @@ allprojects { repositories { mavenCentral() maven { url "https://maven.aliyun.com/nexus/content/groups/public/" } - maven { url "https://oss.sonatype.org/service/local/staging/deploy/maven2" } maven { url "https://oss.sonatype.org/content/repositories/snapshots" } + maven { url "https://oss.sonatype.org/service/local/staging/deploy/maven2" } } dependencies { @@ -124,6 +125,7 @@ googleJavaFormat { } dependencies { + api("org.fisco-bcos:fisco-bcos-tars-sdk:${tarsSDKVersion}") api("org.fisco-bcos:bcos-sdk-jni:${bcosSdkJniVersion}") { exclude group : "org.slf4j" exclude group : "com.fasterxml.jackson.core" @@ -181,6 +183,7 @@ javadoc { task sourcesJar(type: Jar) { from sourceSets.main.allJava archiveClassifier = 'sources' + duplicatesStrategy = 'warn' } task javadocJar(type: Jar) { @@ -207,7 +210,6 @@ tasks.withType(Test) { publishing { publications { mavenJava(MavenPublication) { - artifactId "fisco-bcos-" + project.name groupId project.group version project.version diff --git a/src/main/java/org/fisco/bcos/sdk/v3/BcosSDK.java b/src/main/java/org/fisco/bcos/sdk/v3/BcosSDK.java index 2322b9c45..0537aba6e 100644 --- a/src/main/java/org/fisco/bcos/sdk/v3/BcosSDK.java +++ b/src/main/java/org/fisco/bcos/sdk/v3/BcosSDK.java @@ -17,6 +17,7 @@ import org.fisco.bcos.sdk.jni.BlockNotifier; import org.fisco.bcos.sdk.v3.amop.Amop; import org.fisco.bcos.sdk.v3.client.Client; +import org.fisco.bcos.sdk.v3.client.TarsClient; import org.fisco.bcos.sdk.v3.config.Config; import org.fisco.bcos.sdk.v3.config.ConfigOption; import org.fisco.bcos.sdk.v3.config.exceptions.ConfigException; @@ -25,121 +26,128 @@ import org.slf4j.LoggerFactory; public class BcosSDK { - private static final Logger logger = LoggerFactory.getLogger(BcosSDK.class); + private static final Logger logger = LoggerFactory.getLogger(BcosSDK.class); - private final ConfigOption config; - private final BcosSDKJniObj bcosSDKJniObj; + private final ConfigOption config; + private final BcosSDKJniObj bcosSDKJniObj; - public ConfigOption getConfig() { - return config; - } + public ConfigOption getConfig() { + return config; + } - /** - * Build BcosSDK instance from toml config path - * - * @param tomlConfigFilePath the Toml type config file - * @return BcosSDK instance - * @throws BcosSDKException create sdk instance failed - */ - public static BcosSDK build(String tomlConfigFilePath) throws BcosSDKException { - try { - ConfigOption configOption = Config.load(tomlConfigFilePath); - logger.info("create BcosSDK, configPath: {}", tomlConfigFilePath); - return new BcosSDK(configOption); - } catch (ConfigException e) { - throw new BcosSDKException("create BcosSDK failed, error info: " + e.getMessage(), e); - } + /** + * Build BcosSDK instance from toml config path + * + * @param tomlConfigFilePath the Toml type config file + * @return BcosSDK instance + * @throws BcosSDKException create sdk instance failed + */ + public static BcosSDK build(String tomlConfigFilePath) throws BcosSDKException { + try { + ConfigOption configOption = Config.load(tomlConfigFilePath); + logger.info("create BcosSDK, configPath: {}", tomlConfigFilePath); + return new BcosSDK(configOption); + } catch (ConfigException e) { + throw new BcosSDKException("create BcosSDK failed, error info: " + e.getMessage(), e); } + } - /** - * Constructor, init by ConfigOption - * - * @param configOption the ConfigOption - * @throws BcosSDKException create sdk instance failed - */ - public BcosSDK(ConfigOption configOption) throws BcosSDKException { - try { - this.config = configOption; - this.bcosSDKJniObj = BcosSDKJniObj.build(this.config.getJniConfig()); - } catch (Exception e) { - throw new BcosSDKException("create BcosSDK failed, error: " + e.getMessage(), e); - } + /** + * Constructor, init by ConfigOption + * + * @param configOption the ConfigOption + * @throws BcosSDKException create sdk instance failed + */ + public BcosSDK(ConfigOption configOption) throws BcosSDKException { + try { + this.config = configOption; + this.bcosSDKJniObj = BcosSDKJniObj.build(this.config.getJniConfig()); + } catch (Exception e) { + throw new BcosSDKException("create BcosSDK failed, error: " + e.getMessage(), e); } + } + + /** + * @param groupID group id + * @param blockNotifier block notifier instance + */ + public void registerBlockNotifier(String groupID, BlockNotifier blockNotifier) { + this.bcosSDKJniObj.registerBlockNotifier(groupID, blockNotifier); + } - /** - * @param groupID group id - * @param blockNotifier block notifier instance - */ - public void registerBlockNotifier(String groupID, BlockNotifier blockNotifier) { - this.bcosSDKJniObj.registerBlockNotifier(groupID, blockNotifier); + /** + * Get a Client instance of a specific group + * + * @param groupId specific group id + * @return Client + */ + public Client getClient(String groupId) throws BcosSDKException { + try { + return Client.build(groupId, config, bcosSDKJniObj.getNativePointer()); + } catch (Exception e) { + logger.warn("create client for failed, error: ", e); + throw new BcosSDKException("get Client failed, e: " + e.getMessage(), e); } + } - /** - * Get a Client instance of a specific group - * - * @param groupId specific group id - * @return Client - */ - public Client getClient(String groupId) throws BcosSDKException { - try { - return Client.build(groupId, config, bcosSDKJniObj.getNativePointer()); - } catch (Exception e) { - logger.warn("create client for failed, error: ", e); - throw new BcosSDKException("get Client failed, e: " + e.getMessage(), e); - } + public TarsClient getTarsClient(String groupID) { + try { + return TarsClient.build(groupID, config, bcosSDKJniObj.getNativePointer()); + } catch (Exception e) { + logger.warn("create client for failed, error: ", e); + throw new BcosSDKException("get Client failed, e: " + e.getMessage(), e); } + } - /** - * Get a Client instance of default group in config - * - * @return Client - */ - public Client getClient() throws BcosSDKException { - try { - String groupId = config.getNetworkConfig().getDefaultGroup(); - if ((groupId == null) || groupId.isEmpty()) { - throw new BcosSDKException( - "The default group is not set, please set it in config.toml: defaultGroup field"); - } - return Client.build( - config.getNetworkConfig().getDefaultGroup(), - config, - bcosSDKJniObj.getNativePointer()); - } catch (Exception e) { - logger.warn("create client for failed, error: ", e); - throw new BcosSDKException("get Client failed, e: " + e.getMessage(), e); - } + /** + * Get a Client instance of default group in config + * + * @return Client + */ + public Client getClient() throws BcosSDKException { + try { + String groupId = config.getNetworkConfig().getDefaultGroup(); + if ((groupId == null) || groupId.isEmpty()) { + throw new BcosSDKException( + "The default group is not set, please set it in config.toml: defaultGroup field"); + } + return Client.build(config.getNetworkConfig().getDefaultGroup(), config, + bcosSDKJniObj.getNativePointer()); + } catch (Exception e) { + logger.warn("create client for failed, error: ", e); + throw new BcosSDKException("get Client failed, e: " + e.getMessage(), e); } + } - /** - * Get an amop instance of a specific group - * - * @return Client - */ - public Amop getAmop() throws BcosSDKException { - try { - return Amop.build(config); - } catch (Exception e) { - logger.error("create amop for failed, error: ", e); - throw new BcosSDKException("get amop failed, e: " + e.getMessage()); - } + /** + * Get an amop instance of a specific group + * + * @return Client + */ + public Amop getAmop() throws BcosSDKException { + try { + return Amop.build(config); + } catch (Exception e) { + logger.error("create amop for failed, error: ", e); + throw new BcosSDKException("get amop failed, e: " + e.getMessage()); } + } - /** - * Get an event subscribe instance of a specific group - * - * @param groupId specific group id - * @return Client - */ - public EventSubscribe getEventSubscribe(String groupId) throws BcosSDKException { - try { - return EventSubscribe.build(groupId, config); - } catch (Exception e) { - logger.warn("create event sub for failed, error: ", e); - throw new BcosSDKException("get event sub failed, e: " + e.getMessage()); - } + /** + * Get an event subscribe instance of a specific group + * + * @param groupId specific group id + * @return Client + */ + public EventSubscribe getEventSubscribe(String groupId) throws BcosSDKException { + try { + return EventSubscribe.build(groupId, config); + } catch (Exception e) { + logger.warn("create event sub for failed, error: ", e); + throw new BcosSDKException("get event sub failed, e: " + e.getMessage()); } + } - /** Stop all module of BcosSDK */ - public void stopAll() {} + /** Stop all module of BcosSDK */ + public void stopAll() {} } diff --git a/src/main/java/org/fisco/bcos/sdk/v3/client/TarsClient.java b/src/main/java/org/fisco/bcos/sdk/v3/client/TarsClient.java new file mode 100644 index 000000000..615986904 --- /dev/null +++ b/src/main/java/org/fisco/bcos/sdk/v3/client/TarsClient.java @@ -0,0 +1,145 @@ +package org.fisco.bcos.sdk.v3.client; + +import java.math.BigInteger; +import java.util.Objects; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import org.fisco.bcos.sdk.v3.client.protocol.response.BcosTransactionReceipt; +import org.fisco.bcos.sdk.v3.config.ConfigOption; +import org.fisco.bcos.sdk.v3.model.callback.TransactionCallback; +import org.fisco.bcos.sdk.v3.utils.Hex; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.fisco.bcos.sdk.tars.LogEntry; +import org.fisco.bcos.sdk.tars.TransactionReceipt; +import org.fisco.bcos.sdk.tars.bcos; +import org.fisco.bcos.sdk.tars.RPCClient; +import org.fisco.bcos.sdk.tars.SWIGTYPE_p_bcos__bytesConstRef; +import org.fisco.bcos.sdk.tars.SWIGTYPE_p_bcos__h256; +import org.fisco.bcos.sdk.tars.SWIGTYPE_p_std__vectorT_unsigned_char_t; +import org.fisco.bcos.sdk.tars.SendTransaction; +import org.fisco.bcos.sdk.tars.StringVector; +import org.fisco.bcos.sdk.tars.Transaction; +import org.fisco.bcos.sdk.tars.TransactionFactoryImpl; +import org.fisco.bcos.sdk.tars.Callback; +import org.fisco.bcos.sdk.tars.CryptoSuite; + +public class TarsClient extends ClientImpl implements Client { + private static Logger logger = LoggerFactory.getLogger(TarsClient.class); + private RPCClient tarsRPCClient; + private TransactionFactoryImpl transactionFactory; + private ThreadPoolExecutor asyncThreadPool; + + protected TarsClient(String groupID, ConfigOption configOption, long nativePointer) { + super(groupID, configOption, nativePointer); + String connectionString = RPCClient + .toConnectionString(new StringVector(configOption.getNetworkConfig().getTarsPeers())); + + logger.info("Tars connection: {}", connectionString); + tarsRPCClient = new RPCClient(connectionString); + + CryptoSuite cryptoSuite = + bcos.newCryptoSuite(configOption.getCryptoMaterialConfig().getUseSmCrypto()); + transactionFactory = new TransactionFactoryImpl(cryptoSuite); + asyncThreadPool = + new ThreadPoolExecutor(1, configOption.getThreadPoolConfig().getThreadPoolSize(), 0, + TimeUnit.SECONDS, new ArrayBlockingQueue(100 * 10000)); + } + + public static void loadLibrary() { + System.loadLibrary("bcos_swig_java"); + } + + public static TarsClient build(String groupId, ConfigOption configOption, long nativePointer) { + logger.info("build, groupID: {}, configOption: {}, nativePointer: {}", groupId, configOption, + nativePointer); + return new TarsClient(groupId, configOption, nativePointer); + } + + @Override + public BcosTransactionReceipt sendTransaction(String node, String signedTransactionData, + boolean withProof) { + if (withProof) { + return super.sendTransaction(node, signedTransactionData, withProof); + } + node = Objects.isNull(node) ? "" : node; + + Transaction transaction = toTransaction(signedTransactionData); + TransactionReceipt receipt = new SendTransaction(tarsRPCClient).send(transaction).get(); + BcosTransactionReceipt bcosReceipt = new BcosTransactionReceipt(); + bcosReceipt.setResult(toJSONTransactionReceipt(receipt, transaction)); + + return bcosReceipt; + } + + @Override + public void sendTransactionAsync(String node, String signedTransactionData, boolean withProof, + TransactionCallback callback) { + if (withProof) { + super.sendTransactionAsync(node, signedTransactionData, withProof, callback); + return; + } + node = Objects.isNull(node) ? "" : node; + Transaction transaction = toTransaction(signedTransactionData); + SendTransaction sendTransaction = new SendTransaction(tarsRPCClient); + + sendTransaction.setCallback(new Callback() { + public void onMessage() { + asyncThreadPool.submit(() -> { + TransactionReceipt receipt = sendTransaction.get(); + callback.onResponse(toJSONTransactionReceipt(receipt, transaction)); + }); + } + }); + sendTransaction.send(transaction); + } + + private Transaction toTransaction(String signedTransactionData) { + byte[] transactionBytes = Hex.decode(signedTransactionData); + + // Move data from java to jni + SWIGTYPE_p_std__vectorT_unsigned_char_t vectorTransactionBytes = bcos.toBytes(transactionBytes); + + SWIGTYPE_p_bcos__bytesConstRef ref = bcos.toBytesConstRef(vectorTransactionBytes); + Transaction transaction = transactionFactory.createTransaction(ref, false, false); + return transaction; + } + + private org.fisco.bcos.sdk.v3.model.TransactionReceipt toJSONTransactionReceipt( + TransactionReceipt receipt, Transaction transaction) { + org.fisco.bcos.sdk.v3.model.TransactionReceipt jsonReceipt = + new org.fisco.bcos.sdk.v3.model.TransactionReceipt(); + jsonReceipt.setTransactionHash("0x" + bcos.toHex(transaction.hash())); + jsonReceipt.setVersion(receipt.version()); + jsonReceipt.setReceiptHash("0x" + bcos.toHex(receipt.hash())); + jsonReceipt.setBlockNumber(BigInteger.valueOf(receipt.blockNumber())); + jsonReceipt.setFrom(bcos.toString(transaction.sender())); + jsonReceipt.setTo(bcos.toString(transaction.to())); + jsonReceipt.setGasUsed(bcos.toString(receipt.gasUsed())); + jsonReceipt.setContractAddress(bcos.toString(receipt.contractAddress())); + jsonReceipt.setChecksumContractAddress(jsonReceipt.getContractAddress()); // FIXME: how to? + jsonReceipt.setLogEntries( + bcos.logEntrySpanToVector(receipt.logEntries()).stream().map((LogEntry logEntry) -> { + org.fisco.bcos.sdk.v3.model.TransactionReceipt.Logs rawLogEntry = + new org.fisco.bcos.sdk.v3.model.TransactionReceipt.Logs(); + rawLogEntry.setAddress(bcos.toString(logEntry.address())); + rawLogEntry.setBlockNumber(String.valueOf(receipt.blockNumber())); + rawLogEntry.setData("0x" + bcos.toHex(logEntry.data())); + rawLogEntry.setTopics(bcos.h256SpanToVector(logEntry.topics()).stream() + .map((SWIGTYPE_p_bcos__h256 hash) -> { + return "0x" + bcos.toHex(hash); + }).collect(Collectors.toList())); + return rawLogEntry; + }).collect(Collectors.toList())); + jsonReceipt.setStatus(receipt.status()); + jsonReceipt.setInput("0x" + bcos.toHex(transaction.input())); + jsonReceipt.setOutput("0x" + bcos.toHex(receipt.output())); + jsonReceipt.setExtraData(bcos.toString(transaction.extraData())); + + return jsonReceipt; + } +} diff --git a/src/main/java/org/fisco/bcos/sdk/v3/config/model/NetworkConfig.java b/src/main/java/org/fisco/bcos/sdk/v3/config/model/NetworkConfig.java index 3c472b7c0..a5011046b 100644 --- a/src/main/java/org/fisco/bcos/sdk/v3/config/model/NetworkConfig.java +++ b/src/main/java/org/fisco/bcos/sdk/v3/config/model/NetworkConfig.java @@ -27,6 +27,7 @@ public class NetworkConfig { private static final Logger logger = LoggerFactory.getLogger(NetworkConfig.class); private List peers; + private List tarsPeers; private int timeout = -1; private String defaultGroup; private boolean sendRpcRequestToHighestBlockNode = true; @@ -37,6 +38,7 @@ public NetworkConfig(ConfigProperty configProperty) { Map networkProperty = configProperty.getNetwork(); if (networkProperty != null) { peers = (List) networkProperty.get("peers"); + tarsPeers = (List) networkProperty.get("tarsPeers"); defaultGroup = (String) networkProperty.get("defaultGroup"); Object value = networkProperty.get("messageTimeout"); if (Objects.nonNull(value)) { @@ -62,6 +64,14 @@ public List getPeers() { public void setPeers(List peers) { this.peers = peers; } + + public List getTarsPeers() { + return tarsPeers; + } + + public void setTarsPeers(List tarsPeers) { + this.tarsPeers = tarsPeers; + } public String getDefaultGroup() { return defaultGroup; @@ -92,6 +102,8 @@ public String toString() { return "NetworkConfig{" + "peers=" + peers + + ", tarsPeers=" + + tarsPeers + ", timeout=" + timeout + ", defaultGroup='"