diff --git a/build.gradle b/build.gradle index be140b2..cf03a44 100644 --- a/build.gradle +++ b/build.gradle @@ -90,6 +90,9 @@ dependencies { exclude group: "io.netty" exclude group: 'org.fisco-bcos', module: 'tcnative' } + + implementation 'org.web3j:core:4.9.8' + // implementation('org.fisco-bcos.java-sdk:fisco-bcos-sdk-abi:2.10.0-SNAPSHOT') // Use JUnit test framework @@ -124,15 +127,8 @@ sourceSets { shadowJar { dependsOn(copyHooks) - dependencies { - //排除掉io.netty:.*,不打包进fat jar - exclude(dependency('io.netty:.*')) - } mergeServiceFiles() - minimize{ - //不优化处理io.grpc:.*,直接打进fat jar - exclude(dependency('io.grpc:.*')) - } + minimize() } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..f3d88b1 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..98d0a7f --- /dev/null +++ b/settings.gradle @@ -0,0 +1,9 @@ +pluginManagement { + repositories { + maven { + url 'https://maven.aliyun.com/repository/gradle-plugin' + } + gradlePluginPortal() + } +} +rootProject.name = 'WeCross-Web3-Stub' \ No newline at end of file diff --git a/src/main/java/com/webank/wecross/stub/web3/Web3BaseStubFactory.java b/src/main/java/com/webank/wecross/stub/web3/Web3BaseStubFactory.java new file mode 100644 index 0000000..945be0b --- /dev/null +++ b/src/main/java/com/webank/wecross/stub/web3/Web3BaseStubFactory.java @@ -0,0 +1,61 @@ +package com.webank.wecross.stub.web3; + +import com.webank.wecross.stub.Account; +import com.webank.wecross.stub.Connection; +import com.webank.wecross.stub.Driver; +import com.webank.wecross.stub.StubFactory; +import com.webank.wecross.stub.WeCrossContext; +import com.webank.wecross.stub.web3.account.Web3AccountFactory; +import com.webank.wecross.stub.web3.common.Web3Constant; +import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Web3BaseStubFactory implements StubFactory { + private static final Logger logger = LoggerFactory.getLogger(Web3BaseStubFactory.class); + + @Override + public void init(WeCrossContext weCrossContext) {} + + @Override + public Driver newDriver() { + return null; + } + + @Override + public Connection newConnection(String path) { + try { + logger.info("New connection: {}", path); + Web3Connection connection = Web3ConnectionFactory.build(path, Web3Constant.STUB_TOML_NAME); + + // check proxy contract + if (!connection.hasProxyDeployed()) { + String errorMsg = "WeCrossProxy error: WeCrossProxy contract has not been deployed!"; + System.out.println(errorMsg); + throw new Exception(errorMsg); + } + + // check hub contract + if (!connection.hasHubDeployed()) { + String errorMsg = "WeCrossHub error: WeCrossHub contract has not been deployed!"; + System.out.println(errorMsg); + throw new Exception(errorMsg); + } + return connection; + } catch (Exception e) { + logger.error("New connection fail, e: ", e); + return null; + } + } + + @Override + public Account newAccount(Map properties) { + return Web3AccountFactory.build(properties); + } + + @Override + public void generateAccount(String path, String[] args) {} + + @Override + public void generateConnection(String path, String[] args) {} +} diff --git a/src/main/java/com/webank/wecross/stub/web3/Web3Connection.java b/src/main/java/com/webank/wecross/stub/web3/Web3Connection.java new file mode 100644 index 0000000..f61145c --- /dev/null +++ b/src/main/java/com/webank/wecross/stub/web3/Web3Connection.java @@ -0,0 +1,393 @@ +package com.webank.wecross.stub.web3; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.moandjiezana.toml.Toml; +import com.webank.wecross.stub.Connection; +import com.webank.wecross.stub.Request; +import com.webank.wecross.stub.ResourceInfo; +import com.webank.wecross.stub.Response; +import com.webank.wecross.stub.web3.client.ClientWrapper; +import com.webank.wecross.stub.web3.client.ClientWrapperImpl; +import com.webank.wecross.stub.web3.common.ObjectMapperFactory; +import com.webank.wecross.stub.web3.common.Web3Constant; +import com.webank.wecross.stub.web3.common.Web3RequestType; +import com.webank.wecross.stub.web3.common.Web3StatusCode; +import com.webank.wecross.stub.web3.config.Web3StubConfig; +import com.webank.wecross.stub.web3.protocol.request.TransactionParams; +import com.webank.wecross.stub.web3.protocol.response.TransactionPair; +import java.io.IOException; +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.scheduling.concurrent.CustomizableThreadFactory; +import org.web3j.crypto.RawTransaction; +import org.web3j.crypto.TransactionDecoder; +import org.web3j.protocol.core.methods.response.EthBlock; +import org.web3j.protocol.core.methods.response.EthCall; +import org.web3j.protocol.core.methods.response.EthSendTransaction; +import org.web3j.protocol.core.methods.response.Transaction; +import org.web3j.protocol.core.methods.response.TransactionReceipt; + +public class Web3Connection implements Connection { + private static final Logger logger = LoggerFactory.getLogger(Web3Connection.class); + public static final String RECEIPT_SUCCESS = "0x1"; + + private final ObjectMapper objectMapper = ObjectMapperFactory.getObjectMapper(); + private List resourceInfoList = new ArrayList<>(); + private ConnectionEventHandler eventHandler; + private final Map properties = new HashMap<>(); + private final ClientWrapper clientWrapper; + private final BigInteger chainId; + private final String stubConfigFilePath; + private long stubConfigFileLastModified; + + public Web3Connection(ClientWrapper clientWrapper, Web3StubConfig web3StubConfig) + throws IOException { + this.objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); + this.clientWrapper = clientWrapper; + this.chainId = clientWrapper.ethChainId(); + this.stubConfigFilePath = web3StubConfig.getStubConfigPath(); + // init + refreshStubConfig(web3StubConfig); + + // refresh + ScheduledExecutorService executorService = + new ScheduledThreadPoolExecutor(1, new CustomizableThreadFactory("refreshStubConfig-")); + executorService.scheduleAtFixedRate( + () -> { + try { + PathMatchingResourcePatternResolver resolver = + new PathMatchingResourcePatternResolver(); + Resource resource = resolver.getResource(stubConfigFilePath); + long lastModified = resource.getFile().lastModified(); + if (Objects.equals(stubConfigFileLastModified, lastModified)) { + return; + } + Web3StubConfig newWeb3StubConfig = + new Toml().read(resource.getInputStream()).to(Web3StubConfig.class); + newWeb3StubConfig.setStubConfigPath(stubConfigFilePath); + // changed + refreshStubConfig(newWeb3StubConfig); + if (Objects.nonNull(eventHandler)) { + // refresh remote resource + eventHandler.onResourcesChange(resourceInfoList); + } + this.stubConfigFileLastModified = lastModified; + } catch (IOException e) { + logger.error("refreshStubConfig Exception:", e); + } + }, + 30000, + 30000, + TimeUnit.MILLISECONDS); + } + + @Override + public void asyncSend(Request request, Callback callback) { + // request type + int type = request.getType(); + if (type == Web3RequestType.CALL) { + // constantCall constantCallWithXa + handleAsyncCallRequest(request, callback); + } else if (type == Web3RequestType.SEND_TRANSACTION) { + // sendTransaction sendTransactionWithXa + handleAsyncTransactionRequest(request, callback); + } else if (type == Web3RequestType.GET_BLOCK_NUMBER) { + handleAsyncGetBlockNumberRequest(callback); + } else if (type == Web3RequestType.GET_BLOCK_BY_NUMBER) { + handleAsyncGetBlockRequest(request, callback); + } else if (type == Web3RequestType.GET_TRANSACTION) { + handleAsyncGetTransaction(request, callback); + } else { + logger.warn(" unrecognized request type, type: {}", request.getType()); + Response response = new Response(); + response.setErrorCode(Web3StatusCode.UnrecognizedRequestType); + response.setErrorMessage( + Web3StatusCode.getStatusMessage(Web3StatusCode.UnrecognizedRequestType) + + " ,type: " + + request.getType()); + callback.onResponse(response); + } + } + + @Override + public Map getProperties() { + return properties; + } + + @Override + public void setConnectionEventHandler(ConnectionEventHandler eventHandler) { + this.eventHandler = eventHandler; + } + + private void handleAsyncGetTransaction(Request request, Callback callback) { + Response response = new Response(); + response.setErrorCode(Web3StatusCode.Success); + response.setErrorMessage(Web3StatusCode.getStatusMessage(Web3StatusCode.Success)); + try { + String transactionHash = new String(request.getData(), StandardCharsets.UTF_8); + Transaction transaction = clientWrapper.ethGetTransactionByHash(transactionHash); + TransactionReceipt transactionReceipt = + clientWrapper.ethGetTransactionReceipt(transactionHash); + + // transaction or transactionReceipt is null + if (Objects.isNull(transaction) + || Objects.isNull(transaction.getHash()) + || Objects.isNull(transactionReceipt) + || Objects.isNull(transactionReceipt.getTransactionHash())) { + response.setErrorCode(Web3StatusCode.TransactionNotExist); + response.setErrorMessage("transaction not found, tx hash: " + transactionHash); + return; + } + + // transaction is revert + String receiptStatus = transactionReceipt.getStatus(); + String revertReason = transactionReceipt.getRevertReason(); + if (!Objects.equals(receiptStatus, RECEIPT_SUCCESS) && StringUtils.isBlank(revertReason)) { + if (clientWrapper instanceof ClientWrapperImpl) { + ClientWrapperImpl clientWrapperImpl = (ClientWrapperImpl) clientWrapper; + clientWrapperImpl.extractRevertReason(transactionReceipt, transaction.getInput()); + } + } + response.setData( + objectMapper.writeValueAsBytes(new TransactionPair(transaction, transactionReceipt))); + + if (logger.isDebugEnabled()) { + logger.debug( + "handleAsyncGetTransaction, tx hash: {}, transaction: {}, transactionReceipt: {}", + transactionHash, + transaction, + transactionReceipt); + } + } catch (Exception e) { + logger.error("handleAsyncGetTransaction Exception, e: ", e); + response.setErrorCode(Web3StatusCode.HandleGetTransactionFailed); + response.setErrorMessage(e.getMessage()); + } finally { + callback.onResponse(response); + } + } + + private void handleAsyncGetBlockRequest(Request request, Callback callback) { + Response response = new Response(); + response.setErrorCode(Web3StatusCode.Success); + response.setErrorMessage(Web3StatusCode.getStatusMessage(Web3StatusCode.Success)); + try { + BigInteger blockNumber = new BigInteger(request.getData()); + if (logger.isDebugEnabled()) { + logger.debug("handleAsyncGetBlockRequest,blockNumber: {}", blockNumber); + } + // from chain + EthBlock.Block block = clientWrapper.ethGetBlockByNumber(blockNumber); + + // block is null + if (Objects.isNull(block)) { + response.setErrorCode(Web3StatusCode.BlockNotExist); + response.setErrorMessage(Web3StatusCode.getStatusMessage(Web3StatusCode.BlockNotExist)); + return; + } + + response.setData(objectMapper.writeValueAsBytes(block)); + if (logger.isDebugEnabled()) { + logger.debug("handleAsyncGetBlockRequest,blockNumber: {}, block: {}", blockNumber, block); + } + } catch (Exception e) { + logger.error("handleAsyncGetBlockRequest Exception, e: ", e); + response.setErrorCode(Web3StatusCode.HandleGetBlockFailed); + response.setErrorMessage(e.getMessage()); + } finally { + callback.onResponse(response); + } + } + + private void handleAsyncGetBlockNumberRequest(Callback callback) { + Response response = new Response(); + response.setErrorCode(Web3StatusCode.Success); + response.setErrorMessage(Web3StatusCode.getStatusMessage(Web3StatusCode.Success)); + try { + BigInteger blockNumber = clientWrapper.ethBlockNumber(); + if (logger.isDebugEnabled()) { + logger.debug("handleAsyncGetBlockNumberRequest,blockNumber: {}", blockNumber); + } + + response.setData(blockNumber.toByteArray()); + } catch (Exception e) { + logger.error("handleGetBlockNumberRequest Exception, e: ", e); + response.setErrorCode(Web3StatusCode.HandleGetBlockNumberFailed); + response.setErrorMessage(e.getMessage()); + } finally { + callback.onResponse(response); + } + } + + private void handleAsyncCallRequest(Request request, Callback callback) { + Response response = new Response(); + response.setErrorCode(Web3StatusCode.Success); + response.setErrorMessage(Web3StatusCode.getStatusMessage(Web3StatusCode.Success)); + try { + TransactionParams transactionParams = + objectMapper.readValue(request.getData(), TransactionParams.class); + if (logger.isDebugEnabled()) { + logger.debug("handleAsyncCallRequest: {}", transactionParams); + } + String from = transactionParams.getFrom(); + String to = transactionParams.getTo(); + String data = transactionParams.getData(); + BigInteger nonce = clientWrapper.getNonce(from); + + // build Transaction + org.web3j.protocol.core.methods.request.Transaction transaction = + org.web3j.protocol.core.methods.request.Transaction.createEthCallTransaction( + from, to, data); + EthCall ethCall = clientWrapper.ethCall(transaction); + + // ethCall has error + if (ethCall.hasError()) { + response.setErrorCode(Web3StatusCode.CallNotSuccessStatus); + response.setErrorMessage(ethCall.getError().getMessage()); + return; + } + + response.setData(objectMapper.writeValueAsBytes(ethCall)); + } catch (Exception e) { + logger.error("handleAsyncCallRequest Exception:", e); + response.setErrorCode(Web3StatusCode.HandleCallRequestFailed); + response.setErrorMessage(e.getMessage()); + } finally { + callback.onResponse(response); + } + } + + private void handleAsyncTransactionRequest(Request request, Callback callback) { + Response response = new Response(); + response.setErrorCode(Web3StatusCode.Success); + response.setErrorMessage(Web3StatusCode.getStatusMessage(Web3StatusCode.Success)); + try { + TransactionParams transactionParams = + objectMapper.readValue(request.getData(), TransactionParams.class); + if (logger.isDebugEnabled()) { + logger.debug("handleAsyncTransactionRequest: {}", transactionParams); + } + + // send transaction + String signedTransactionData = transactionParams.getData(); + EthSendTransaction ethSendTransaction = + clientWrapper.ethSendRawTransaction(signedTransactionData); + + // ethSendTransaction has error + if (ethSendTransaction.hasError()) { + response.setErrorCode(Web3StatusCode.SendTransactionNotSuccessStatus); + response.setErrorMessage(ethSendTransaction.getError().getMessage()); + return; + } + + // get transactionReceipt + String transactionHash = ethSendTransaction.getTransactionHash(); + TransactionReceipt transactionReceipt = + clientWrapper.ethGetTransactionReceipt(transactionHash); + + // transactionReceipt is null + if (Objects.isNull(transactionReceipt)) { + response.setErrorCode(Web3StatusCode.TransactionReceiptNotExist); + response.setErrorMessage( + Web3StatusCode.getStatusMessage(Web3StatusCode.TransactionReceiptNotExist)); + return; + } + + // transaction is revert + String receiptStatus = transactionReceipt.getStatus(); + if (!Objects.equals(receiptStatus, RECEIPT_SUCCESS)) { + // decode revertReason + String revertReason = transactionReceipt.getRevertReason(); + if (StringUtils.isBlank(revertReason)) { + if (clientWrapper instanceof ClientWrapperImpl) { + ClientWrapperImpl clientWrapperImpl = (ClientWrapperImpl) clientWrapper; + RawTransaction originalRawTransaction = + TransactionDecoder.decode(signedTransactionData); + revertReason = + clientWrapperImpl.extractRevertReason( + transactionReceipt, originalRawTransaction.getData()); + } + } + response.setErrorCode(Web3StatusCode.SendTransactionNotSuccessStatus); + response.setErrorMessage(revertReason); + return; + } + + response.setData(objectMapper.writeValueAsBytes(transactionReceipt)); + } catch (Exception e) { + logger.error("handleAsyncTransactionRequest Exception:", e); + response.setErrorCode(Web3StatusCode.HandleSendTransactionFailed); + response.setErrorMessage(e.getMessage()); + } finally { + callback.onResponse(response); + } + } + + private synchronized void refreshStubConfig(Web3StubConfig web3StubConfig) throws IOException { + this.resourceInfoList = web3StubConfig.convertToResourceInfos(); + + addProperty(Web3Constant.WEB3_PROPERTY_CHAIN_ID, chainId.toString()); + addProperty(Web3Constant.WEB3_PROPERTY_STUB_TYPE, web3StubConfig.getCommon().getType()); + addProperty(Web3Constant.WEB3_PROPERTY_CHAIN_URL, web3StubConfig.getService().getUrl()); + List resources = web3StubConfig.getResources(); + if (!resources.isEmpty()) { + for (Web3StubConfig.Resource resource : resources) { + String name = resource.getName(); + // name->address + this.addProperty(name, resource.getAddress()); + // name+ABI->abi + this.addAbi(name, resource.getAbi()); + } + } + } + + public boolean hasProxyDeployed() { + return getProperties().containsKey(Web3Constant.WEB3_PROXY_NAME); + } + + public boolean hasHubDeployed() { + return getProperties().containsKey(Web3Constant.WEB3_HUB_NAME); + } + + public List getResourceInfoList() { + return resourceInfoList; + } + + public void addProperty(String key, String value) { + this.properties.put(key, value); + } + + public String getProperty(String key) { + return this.properties.get(key); + } + + public void addAbi(String key, String value) { + addProperty(key + Web3Constant.WEB3_PROPERTY_ABI_SUFFIX, value); + } + + public String getAbi(String key) { + return getProperty(key + Web3Constant.WEB3_PROPERTY_ABI_SUFFIX); + } + + public ClientWrapper getClientWrapper() { + return clientWrapper; + } + + public BigInteger getChainId() { + return chainId; + } +} diff --git a/src/main/java/com/webank/wecross/stub/web3/Web3ConnectionFactory.java b/src/main/java/com/webank/wecross/stub/web3/Web3ConnectionFactory.java new file mode 100644 index 0000000..64865fa --- /dev/null +++ b/src/main/java/com/webank/wecross/stub/web3/Web3ConnectionFactory.java @@ -0,0 +1,26 @@ +package com.webank.wecross.stub.web3; + +import com.webank.wecross.stub.web3.client.ClientWrapper; +import com.webank.wecross.stub.web3.client.ClientWrapperFactory; +import com.webank.wecross.stub.web3.config.Web3StubConfig; +import com.webank.wecross.stub.web3.config.Web3StubConfigParser; +import java.io.IOException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Web3ConnectionFactory { + private static final Logger logger = LoggerFactory.getLogger(Web3ConnectionFactory.class); + + public static Web3Connection build(String stubConfigPath, String configName) throws Exception { + Web3StubConfigParser web3StubConfigParser = + new Web3StubConfigParser(stubConfigPath, configName); + Web3StubConfig web3StubConfig = web3StubConfigParser.loadConfig(); + return build(web3StubConfig); + } + + public static Web3Connection build(Web3StubConfig web3StubConfig) throws IOException { + logger.info("web3StubConfig: {}", web3StubConfig); + ClientWrapper clientWrapper = ClientWrapperFactory.createClientWrapperInstance(web3StubConfig); + return new Web3Connection(clientWrapper, web3StubConfig); + } +} diff --git a/src/main/java/com/webank/wecross/stub/web3/Web3StubFactory.java b/src/main/java/com/webank/wecross/stub/web3/Web3StubFactory.java index 4e54558..b3b8972 100644 --- a/src/main/java/com/webank/wecross/stub/web3/Web3StubFactory.java +++ b/src/main/java/com/webank/wecross/stub/web3/Web3StubFactory.java @@ -1,9 +1,17 @@ package com.webank.wecross.stub.web3; +import static com.webank.wecross.stub.web3.common.Web3Constant.WEB3_STUB_TYPE; + +import com.webank.wecross.stub.Stub; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class Web3StubFactory { - private final Logger logger = LoggerFactory.getLogger(Web3StubFactory.class); +@Stub(WEB3_STUB_TYPE) +public class Web3StubFactory extends Web3BaseStubFactory { + private static final Logger logger = LoggerFactory.getLogger(Web3StubFactory.class); + public static void main(String[] args) { + System.out.printf( + "This is %s Stub Plugin. Please copy this file to router/plugin/%n", WEB3_STUB_TYPE); + } } diff --git a/src/main/java/com/webank/wecross/stub/web3/account/Web3Account.java b/src/main/java/com/webank/wecross/stub/web3/account/Web3Account.java new file mode 100644 index 0000000..17dd966 --- /dev/null +++ b/src/main/java/com/webank/wecross/stub/web3/account/Web3Account.java @@ -0,0 +1,70 @@ +package com.webank.wecross.stub.web3.account; + +import com.webank.wecross.stub.Account; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.web3j.crypto.Credentials; +import org.web3j.utils.Numeric; + +public class Web3Account implements Account { + private static final Logger logger = LoggerFactory.getLogger(Web3Account.class); + + private final String name; + private final String type; + private final String identity; + private int keyID; + private boolean isDefault; + + private final String publicKey; + private final Credentials credentials; + + public Web3Account(String name, String type, Credentials credentials) { + this.name = name; + this.type = type; + this.identity = credentials.getAddress(); + this.credentials = credentials; + this.publicKey = Numeric.toHexStringNoPrefix(credentials.getEcKeyPair().getPublicKey()); + logger.info(" name: {}, type: {}, publicKey: {}", name, type, publicKey); + } + + public Credentials getCredentials() { + return credentials; + } + + public String getPublicKey() { + return publicKey; + } + + public void setKeyID(int keyID) { + this.keyID = keyID; + } + + public void setDefault(boolean aDefault) { + isDefault = aDefault; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getType() { + return type; + } + + @Override + public String getIdentity() { + return identity; + } + + @Override + public int getKeyID() { + return keyID; + } + + @Override + public boolean isDefault() { + return isDefault; + } +} diff --git a/src/main/java/com/webank/wecross/stub/web3/account/Web3AccountFactory.java b/src/main/java/com/webank/wecross/stub/web3/account/Web3AccountFactory.java new file mode 100644 index 0000000..d836af6 --- /dev/null +++ b/src/main/java/com/webank/wecross/stub/web3/account/Web3AccountFactory.java @@ -0,0 +1,108 @@ +package com.webank.wecross.stub.web3.account; + +import com.webank.wecross.stub.web3.common.Web3Constant; +import com.webank.wecross.stub.web3.config.Web3AccountConfig; +import com.webank.wecross.stub.web3.config.Web3AccountConfigParser; +import java.io.IOException; +import java.util.Map; +import java.util.Objects; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.core.io.support.ResourcePatternResolver; +import org.web3j.crypto.CipherException; +import org.web3j.crypto.Credentials; +import org.web3j.crypto.WalletUtils; + +public class Web3AccountFactory { + private static final Logger logger = LoggerFactory.getLogger(Web3AccountFactory.class); + + public static Web3Account build(Map properties) { + String username = (String) properties.get("username"); + Integer keyID = (Integer) properties.get("keyID"); + String type = (String) properties.get("type"); + Boolean isDefault = (Boolean) properties.get("isDefault"); + String secKey = (String) properties.get("secKey"); + String pubKey = (String) properties.get("pubKey"); + String address = (String) properties.get("ext0"); + + if (StringUtils.isBlank(username)) { + logger.error("username has not given"); + return null; + } + + if (keyID == null) { + logger.error("keyID has not given"); + return null; + } + + if (StringUtils.isBlank(type)) { + logger.error("type has not given"); + return null; + } + + if (isDefault == null) { + logger.error("isDefault has not given"); + return null; + } + + if (StringUtils.isBlank(secKey)) { + logger.error("secKey has not given"); + return null; + } + + if (StringUtils.isBlank(pubKey)) { + logger.error("pubKey has not given"); + return null; + } + + if (StringUtils.isBlank(address)) { + logger.error("address has not given in ext0"); + return null; + } + + try { + // build credentials from privateKey + Credentials credentials = Credentials.create(secKey); + Web3Account web3Account = new Web3Account(username, type, credentials); + + // check publicKey + if (!Objects.equals(web3Account.getPublicKey(), pubKey)) { + throw new Exception("Given pubKey is not belongs to the secKey of " + username); + } + + // check address + if (!Objects.equals(web3Account.getIdentity(), address)) { + throw new Exception("Given address is not belongs to the secKey of " + username); + } + return web3Account; + } catch (Exception e) { + logger.error("build account exception: {}", e.getMessage()); + return null; + } + } + + public Web3Account build(String name, String accountPath) throws IOException, CipherException { + Web3AccountConfigParser parser = + new Web3AccountConfigParser(accountPath, Web3Constant.ACCOUNT_TOML_NAME); + Web3AccountConfig web3AccountConfig = parser.loadConfig(); + Web3AccountConfig.Account account = web3AccountConfig.getAccount(); + String type = account.getType(); + String accountFile = account.getAccountFile(); + String password = account.getPassword(); + + ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); + Resource accountFileResource = resolver.getResource(accountFile); + + Credentials credentials; + + if (StringUtils.endsWith(accountFile, ".json")) { + credentials = WalletUtils.loadCredentials(password, accountFileResource.getFile()); + } else { + return null; + } + return new Web3Account(name, type, credentials); + } +} diff --git a/src/main/java/com/webank/wecross/stub/web3/client/ClientWrapper.java b/src/main/java/com/webank/wecross/stub/web3/client/ClientWrapper.java new file mode 100644 index 0000000..45e3e59 --- /dev/null +++ b/src/main/java/com/webank/wecross/stub/web3/client/ClientWrapper.java @@ -0,0 +1,34 @@ +package com.webank.wecross.stub.web3.client; + +import java.io.IOException; +import java.math.BigInteger; +import org.web3j.protocol.core.methods.response.EthBlock; +import org.web3j.protocol.core.methods.response.EthCall; +import org.web3j.protocol.core.methods.response.EthSendTransaction; +import org.web3j.protocol.core.methods.response.Transaction; +import org.web3j.protocol.core.methods.response.TransactionReceipt; + +/** Wrapper interface for JavaSDK */ +public interface ClientWrapper { + + EthBlock.Block ethGetBlockByNumber(BigInteger blockNumber) throws IOException; + + BigInteger ethBlockNumber() throws IOException; + + EthSendTransaction ethSendRawTransaction(String signedTransactionData) throws IOException; + + TransactionReceipt ethGetTransactionReceipt(String transactionHash) throws Exception; + + Transaction ethGetTransactionByHash(String transactionHash) throws IOException; + + EthCall ethCall(org.web3j.protocol.core.methods.request.Transaction transaction) + throws IOException; + + BigInteger getNonce(String address) throws IOException; + + BigInteger ethChainId() throws IOException; + + BigInteger ethGasPrice() throws IOException; + + BigInteger ethGasLimit() throws IOException; +} diff --git a/src/main/java/com/webank/wecross/stub/web3/client/ClientWrapperFactory.java b/src/main/java/com/webank/wecross/stub/web3/client/ClientWrapperFactory.java new file mode 100644 index 0000000..e05dd16 --- /dev/null +++ b/src/main/java/com/webank/wecross/stub/web3/client/ClientWrapperFactory.java @@ -0,0 +1,14 @@ +package com.webank.wecross.stub.web3.client; + +import com.webank.wecross.stub.web3.config.Web3StubConfig; +import org.web3j.protocol.Web3j; +import org.web3j.protocol.http.HttpService; + +public class ClientWrapperFactory { + + public static ClientWrapper createClientWrapperInstance(Web3StubConfig web3StubConfig) { + String url = web3StubConfig.getService().getUrl(); + Web3j web3j = Web3j.build(new HttpService(url)); + return new ClientWrapperImpl(web3j); + } +} diff --git a/src/main/java/com/webank/wecross/stub/web3/client/ClientWrapperImpl.java b/src/main/java/com/webank/wecross/stub/web3/client/ClientWrapperImpl.java new file mode 100644 index 0000000..fc7cb72 --- /dev/null +++ b/src/main/java/com/webank/wecross/stub/web3/client/ClientWrapperImpl.java @@ -0,0 +1,165 @@ +package com.webank.wecross.stub.web3.client; + +import com.webank.wecross.stub.web3.common.LRUCache; +import java.io.IOException; +import java.math.BigInteger; +import java.util.Objects; +import java.util.Optional; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.web3j.protocol.Web3j; +import org.web3j.protocol.core.DefaultBlockParameter; +import org.web3j.protocol.core.DefaultBlockParameterName; +import org.web3j.protocol.core.methods.response.EthBlock; +import org.web3j.protocol.core.methods.response.EthCall; +import org.web3j.protocol.core.methods.response.EthGetTransactionCount; +import org.web3j.protocol.core.methods.response.EthGetTransactionReceipt; +import org.web3j.protocol.core.methods.response.EthSendTransaction; +import org.web3j.protocol.core.methods.response.Transaction; +import org.web3j.protocol.core.methods.response.TransactionReceipt; +import org.web3j.utils.RevertReasonExtractor; + +public class ClientWrapperImpl implements ClientWrapper { + private static final Logger logger = LoggerFactory.getLogger(ClientWrapperImpl.class); + + private static final int SLEEP_DURATION = 2000; + private static final int ATTEMPTS = 40; + + private final Web3j web3j; + + private final LRUCache blockLRUCache = new LRUCache<>(100); + private final LRUCache transactionReceiptLRUCache = + new LRUCache<>(100); + private final LRUCache transactionLRUCache = new LRUCache<>(100); + + public ClientWrapperImpl(Web3j web3j) { + this.web3j = web3j; + } + + @Override + public EthBlock.Block ethGetBlockByNumber(BigInteger blockNumber) throws IOException { + EthBlock.Block block = blockLRUCache.get(blockNumber); + if (Objects.nonNull(block)) { + return block; + } + block = + web3j + .ethGetBlockByNumber(DefaultBlockParameter.valueOf(blockNumber), true) + .send() + .getBlock(); + if (Objects.nonNull(block)) { + blockLRUCache.put(blockNumber, block); + } + return block; + } + + @Override + public BigInteger ethBlockNumber() throws IOException { + return web3j.ethBlockNumber().send().getBlockNumber(); + } + + @Override + public EthSendTransaction ethSendRawTransaction(String signedTransactionData) throws IOException { + return web3j.ethSendRawTransaction(signedTransactionData).send(); + } + + @Override + public TransactionReceipt ethGetTransactionReceipt(String transactionHash) throws Exception { + TransactionReceipt transactionReceipt = transactionReceiptLRUCache.get(transactionHash); + if (Objects.nonNull(transactionReceipt)) { + return transactionReceipt; + } + transactionReceipt = waitForTransactionReceipt(transactionHash, SLEEP_DURATION, ATTEMPTS); + if (Objects.nonNull(transactionReceipt)) { + transactionReceiptLRUCache.put(transactionHash, transactionReceipt); + } + return transactionReceipt; + } + + @Override + public Transaction ethGetTransactionByHash(String transactionHash) throws IOException { + Transaction transaction = transactionLRUCache.get(transactionHash); + if (Objects.nonNull(transaction)) { + return transaction; + } + Optional optionalTransaction = + web3j.ethGetTransactionByHash(transactionHash).send().getTransaction(); + if (optionalTransaction.isPresent()) { + transaction = optionalTransaction.get(); + transactionLRUCache.put(transactionHash, transaction); + } + return transaction; + } + + @Override + public EthCall ethCall(org.web3j.protocol.core.methods.request.Transaction transaction) + throws IOException { + return web3j.ethCall(transaction, DefaultBlockParameterName.LATEST).send(); + } + + @Override + public BigInteger getNonce(String address) throws IOException { + EthGetTransactionCount ethGetTransactionCount = + web3j.ethGetTransactionCount(address, DefaultBlockParameterName.LATEST).send(); + return ethGetTransactionCount.getTransactionCount(); + } + + @Override + public BigInteger ethChainId() throws IOException { + return web3j.ethChainId().send().getChainId(); + } + + @Override + public BigInteger ethGasPrice() throws IOException { + return web3j.ethGasPrice().send().getGasPrice(); + } + + @Override + public BigInteger ethGasLimit() throws IOException { + return web3j.ethMaxPriorityFeePerGas().send().getMaxPriorityFeePerGas(); + } + + public String extractRevertReason(TransactionReceipt transactionReceipt, String data) + throws IOException { + String revertReason = + RevertReasonExtractor.extractRevertReason( + transactionReceipt, data, web3j, true, BigInteger.ZERO); + // refresh receipt + transactionReceiptLRUCache.put(transactionReceipt.getTransactionHash(), transactionReceipt); + return revertReason; + } + + private TransactionReceipt waitForTransactionReceipt( + String transactionHash, int sleepDuration, int attempts) throws Exception { + Optional transactionReceiptOptional = + getTransactionReceipt(transactionHash, sleepDuration, attempts); + + if (!transactionReceiptOptional.isPresent()) { + logger.error( + "Transaction receipt not generated after {} , attempts {}", sleepDuration, attempts); + return null; + } + return transactionReceiptOptional.get(); + } + + private Optional getTransactionReceipt( + String transactionHash, int sleepDuration, int attempts) throws Exception { + Optional receiptOptional = sendTransactionReceiptRequest(transactionHash); + for (int i = 0; i < attempts; i++) { + if (!receiptOptional.isPresent()) { + Thread.sleep(sleepDuration); + receiptOptional = sendTransactionReceiptRequest(transactionHash); + } else { + break; + } + } + return receiptOptional; + } + + private Optional sendTransactionReceiptRequest(String transactionHash) + throws Exception { + EthGetTransactionReceipt transactionReceipt = + web3j.ethGetTransactionReceipt(transactionHash).sendAsync().get(); + return transactionReceipt.getTransactionReceipt(); + } +} diff --git a/src/main/java/com/webank/wecross/stub/web3/common/LRUCache.java b/src/main/java/com/webank/wecross/stub/web3/common/LRUCache.java new file mode 100644 index 0000000..0fae316 --- /dev/null +++ b/src/main/java/com/webank/wecross/stub/web3/common/LRUCache.java @@ -0,0 +1,18 @@ +package com.webank.wecross.stub.web3.common; + +import java.util.LinkedHashMap; +import java.util.Map; + +public class LRUCache extends LinkedHashMap { + private final int cacheSize; + + public LRUCache(int cacheSize) { + super(cacheSize, 0.75f, true); + this.cacheSize = cacheSize; + } + + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > cacheSize; + } +} diff --git a/src/main/java/com/webank/wecross/stub/web3/common/ObjectMapperFactory.java b/src/main/java/com/webank/wecross/stub/web3/common/ObjectMapperFactory.java new file mode 100644 index 0000000..56de2cc --- /dev/null +++ b/src/main/java/com/webank/wecross/stub/web3/common/ObjectMapperFactory.java @@ -0,0 +1,30 @@ +package com.webank.wecross.stub.web3.common; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectReader; + +public class ObjectMapperFactory { + private static final ObjectMapper DEFAULT_OBJECT_MAPPER = getObjectMapper(); + + static { + configureObjectMapper(DEFAULT_OBJECT_MAPPER); + } + + public static ObjectMapper getObjectMapper() { + return configureObjectMapper(new ObjectMapper()); + } + + public static ObjectReader getObjectReader() { + return DEFAULT_OBJECT_MAPPER.reader(); + } + + private static ObjectMapper configureObjectMapper(ObjectMapper objectMapper) { + + objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + + return objectMapper; + } +} diff --git a/src/main/java/com/webank/wecross/stub/web3/common/Web3Constant.java b/src/main/java/com/webank/wecross/stub/web3/common/Web3Constant.java new file mode 100644 index 0000000..3eed4a2 --- /dev/null +++ b/src/main/java/com/webank/wecross/stub/web3/common/Web3Constant.java @@ -0,0 +1,18 @@ +package com.webank.wecross.stub.web3.common; + +import com.webank.wecross.stub.StubConstant; + +public interface Web3Constant { + + String STUB_TOML_NAME = "stub.toml"; + String ACCOUNT_TOML_NAME = "account.toml"; + String WEB3_STUB_TYPE = "WEB3"; + + String WEB3_PROXY_NAME = StubConstant.PROXY_NAME; + String WEB3_HUB_NAME = StubConstant.HUB_NAME; + + String WEB3_PROPERTY_ABI_SUFFIX = "ABI"; + String WEB3_PROPERTY_CHAIN_ID = "WEB3_PROPERTY_CHAIN_ID"; + String WEB3_PROPERTY_STUB_TYPE = "WEB3_PROPERTY_STUB_TYPE"; + String WEB3_PROPERTY_CHAIN_URL = "WEB3_PROPERTY_CHAIN_URL"; +} diff --git a/src/main/java/com/webank/wecross/stub/web3/common/Web3RequestType.java b/src/main/java/com/webank/wecross/stub/web3/common/Web3RequestType.java new file mode 100644 index 0000000..6241c77 --- /dev/null +++ b/src/main/java/com/webank/wecross/stub/web3/common/Web3RequestType.java @@ -0,0 +1,9 @@ +package com.webank.wecross.stub.web3.common; + +public class Web3RequestType { + public static final int CALL = 1000; + public static final int SEND_TRANSACTION = 1001; + public static final int GET_BLOCK_NUMBER = 1002; + public static final int GET_BLOCK_BY_NUMBER = 1003; + public static final int GET_TRANSACTION = 1004; +} diff --git a/src/main/java/com/webank/wecross/stub/web3/common/Web3StatusCode.java b/src/main/java/com/webank/wecross/stub/web3/common/Web3StatusCode.java new file mode 100644 index 0000000..78ed486 --- /dev/null +++ b/src/main/java/com/webank/wecross/stub/web3/common/Web3StatusCode.java @@ -0,0 +1,83 @@ +package com.webank.wecross.stub.web3.common; + +public class Web3StatusCode { + public static final int Success = 0; + + public static final int InvalidParameter = 2000; + public static final int UnrecognizedRequestType = 2001; + public static final int FetchBlockHeaderFailed = 2002; + public static final int InvalidEncodedBlockHeader = 2003; + public static final int TransactionProofVerifyFailed = 2004; + public static final int TransactionReceiptProofVerifyFailed = 2005; + + public static final int TransactionReceiptNotExist = 2010; + public static final int TransactionNotExist = 2011; + public static final int BlockNotExist = 2012; + public static final int TransactionProofNotExist = 2013; + public static final int TransactionReceiptProofNotExist = 2014; + + public static final int HandleSendTransactionFailed = 2021; + public static final int HandleCallRequestFailed = 2022; + public static final int HandleGetBlockNumberFailed = 2023; + public static final int HandleGetBlockFailed = 2024; + public static final int HandleGetTransactionFailed = 2025; + public static final int ListResourcesFailed = 2026; + public static final int RegisterContractFailed = 2027; + + public static final int CallNotSuccessStatus = 2030; + public static final int SendTransactionNotSuccessStatus = 2031; + + public static final int ABINotExist = 2040; + public static final int EncodeAbiFailed = 2041; + public static final int MethodNotExist = 2042; + + public static final int UnsupportedRPC = 2050; + public static final int UnclassifiedError = 2100; + + public static String getStatusMessage(int status) { + String message = ""; + switch (status) { + case Success: + message = "success"; + break; + case InvalidParameter: + message = "invalid parameter"; + break; + case UnrecognizedRequestType: + message = "unrecognized request type"; + break; + case TransactionReceiptNotExist: + message = "transaction receipt not exist"; + break; + case FetchBlockHeaderFailed: + message = "fetch block header from block header manager failed"; + break; + case InvalidEncodedBlockHeader: + message = "invalid encoded block header"; + break; + case TransactionProofVerifyFailed: + message = " transaction verify failed"; + break; + case TransactionReceiptProofVerifyFailed: + message = " transaction receipt verify failed"; + break; + case TransactionNotExist: + message = "transaction not exist"; + break; + case TransactionProofNotExist: + message = "transaction proof not exist"; + break; + case TransactionReceiptProofNotExist: + message = "transaction receipt proof not exist"; + break; + case BlockNotExist: + message = "block not exist"; + break; + default: + message = "unrecognized status: " + status; + break; + } + + return message; + } +} diff --git a/src/main/java/com/webank/wecross/stub/web3/common/Web3StubException.java b/src/main/java/com/webank/wecross/stub/web3/common/Web3StubException.java new file mode 100644 index 0000000..9ca0778 --- /dev/null +++ b/src/main/java/com/webank/wecross/stub/web3/common/Web3StubException.java @@ -0,0 +1,9 @@ +package com.webank.wecross.stub.web3.common; + +import com.webank.wecross.exception.WeCrossException; + +public class Web3StubException extends WeCrossException { + public Web3StubException(Integer errorCode, String message) { + super(errorCode, message); + } +} diff --git a/src/main/java/com/webank/wecross/stub/web3/common/Web3Toml.java b/src/main/java/com/webank/wecross/stub/web3/common/Web3Toml.java new file mode 100644 index 0000000..0de9954 --- /dev/null +++ b/src/main/java/com/webank/wecross/stub/web3/common/Web3Toml.java @@ -0,0 +1,24 @@ +package com.webank.wecross.stub.web3.common; + +import com.moandjiezana.toml.Toml; +import java.io.IOException; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; + +public class Web3Toml { + private final String path; + + public Web3Toml(String path) { + this.path = path; + } + + public String getPath() { + return path; + } + + public Toml getToml() throws IOException { + PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); + Resource resource = resolver.getResource(getPath()); + return new Toml().read(resource.getInputStream()); + } +} diff --git a/src/main/java/com/webank/wecross/stub/web3/config/AbstractWeb3ConfigParser.java b/src/main/java/com/webank/wecross/stub/web3/config/AbstractWeb3ConfigParser.java new file mode 100644 index 0000000..71969f8 --- /dev/null +++ b/src/main/java/com/webank/wecross/stub/web3/config/AbstractWeb3ConfigParser.java @@ -0,0 +1,31 @@ +package com.webank.wecross.stub.web3.config; + +import java.security.InvalidParameterException; +import java.util.Objects; + +public abstract class AbstractWeb3ConfigParser { + + public AbstractWeb3ConfigParser(String configPath) { + this.configPath = configPath; + } + + private final String configPath; + + public String getConfigPath() { + return configPath; + } + + public void requireItemNotNull(Object object, String item, String configFile) { + if (Objects.isNull(object)) { + throw new InvalidParameterException( + item + " item not found, please check config file: " + configFile); + } + } + + public void requireFieldNotNull(Object object, String item, String field, String configFile) { + if (Objects.isNull(object)) { + throw new InvalidParameterException( + "[" + item + "]:" + field + " field not found, please check config file: " + configFile); + } + } +} diff --git a/src/main/java/com/webank/wecross/stub/web3/config/Web3AccountConfig.java b/src/main/java/com/webank/wecross/stub/web3/config/Web3AccountConfig.java new file mode 100644 index 0000000..28b81e9 --- /dev/null +++ b/src/main/java/com/webank/wecross/stub/web3/config/Web3AccountConfig.java @@ -0,0 +1,84 @@ +package com.webank.wecross.stub.web3.config; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Web3AccountConfig { + private static final Logger logger = LoggerFactory.getLogger(Web3AccountConfig.class); + + private Account account; + + private String accountConfigPath; + + public static class Account { + private String type; + private String accountFile; + private String password; + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getAccountFile() { + return accountFile; + } + + public void setAccountFile(String accountFile) { + this.accountFile = accountFile; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + @Override + public String toString() { + return "Account{" + + "type='" + + type + + '\'' + + ", accountFile='" + + accountFile + + '\'' + + ", password='" + + password + + '\'' + + '}'; + } + } + + public Account getAccount() { + return account; + } + + public void setAccount(Account account) { + this.account = account; + } + + public String getAccountConfigPath() { + return accountConfigPath; + } + + public void setAccountConfigPath(String accountConfigPath) { + this.accountConfigPath = accountConfigPath; + } + + @Override + public String toString() { + return "Web3AccountConfig{" + + "account=" + + account + + ", accountConfigPath='" + + accountConfigPath + + '\'' + + '}'; + } +} diff --git a/src/main/java/com/webank/wecross/stub/web3/config/Web3AccountConfigParser.java b/src/main/java/com/webank/wecross/stub/web3/config/Web3AccountConfigParser.java new file mode 100644 index 0000000..954555d --- /dev/null +++ b/src/main/java/com/webank/wecross/stub/web3/config/Web3AccountConfigParser.java @@ -0,0 +1,25 @@ +package com.webank.wecross.stub.web3.config; + +import com.moandjiezana.toml.Toml; +import com.webank.wecross.stub.web3.common.Web3Toml; +import java.io.File; +import java.io.IOException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Web3AccountConfigParser extends AbstractWeb3ConfigParser { + + private static final Logger logger = LoggerFactory.getLogger(Web3AccountConfigParser.class); + + public Web3AccountConfigParser(String configPath, String configName) { + super(configPath + File.separator + configName); + } + + public Web3AccountConfig loadConfig() throws IOException { + Web3Toml web3Toml = new Web3Toml(getConfigPath()); + Toml toml = web3Toml.getToml(); + Web3AccountConfig web3AccountConfig = toml.to(Web3AccountConfig.class); + web3AccountConfig.setAccountConfigPath(getConfigPath()); + return web3AccountConfig; + } +} diff --git a/src/main/java/com/webank/wecross/stub/web3/config/Web3StubConfig.java b/src/main/java/com/webank/wecross/stub/web3/config/Web3StubConfig.java new file mode 100644 index 0000000..4f4908f --- /dev/null +++ b/src/main/java/com/webank/wecross/stub/web3/config/Web3StubConfig.java @@ -0,0 +1,176 @@ +package com.webank.wecross.stub.web3.config; + +import com.webank.wecross.stub.ResourceInfo; +import com.webank.wecross.stub.web3.common.Web3Constant; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Web3StubConfig { + private static final Logger logger = LoggerFactory.getLogger(Web3StubConfig.class); + + private Common common; + private Service service; + private List resources; + + private String stubConfigPath; + + public List convertToResourceInfos() { + List resourceInfos = new ArrayList<>(); + for (Resource resource : this.getResources()) { + ResourceInfo resourceInfo = new ResourceInfo(); + resourceInfo.setName(resource.getName()); + resourceInfo.setStubType(this.getCommon().getType()); + + Map properties = resourceInfo.getProperties(); + properties.put(resource.getName(), resource.getAddress()); + properties.put(resource.getName() + Web3Constant.WEB3_PROPERTY_ABI_SUFFIX, resource.getAbi()); + resourceInfos.add(resourceInfo); + } + return resourceInfos; + } + + public static class Common { + private String name; + private String type; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + @Override + public String toString() { + return "Common{" + "name='" + name + '\'' + ", type='" + type + '\'' + '}'; + } + } + + public static class Service { + private String url; + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + } + + public static class Resource { + private String type; + private String name; + private String address; + private String abi; + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getAbi() { + return abi; + } + + public void setAbi(String abi) { + this.abi = abi; + } + + @Override + public String toString() { + return "Resource{" + + "type='" + + type + + '\'' + + ", name='" + + name + + '\'' + + ", address='" + + address + + '\'' + + ", abi='" + + abi + + '\'' + + '}'; + } + } + + public Common getCommon() { + return common; + } + + public void setCommon(Common common) { + this.common = common; + } + + public Service getService() { + return service; + } + + public void setService(Service service) { + this.service = service; + } + + public List getResources() { + return resources; + } + + public void setResources(List resources) { + this.resources = resources; + } + + public String getStubConfigPath() { + return stubConfigPath; + } + + public void setStubConfigPath(String stubConfigPath) { + this.stubConfigPath = stubConfigPath; + } + + @Override + public String toString() { + return "Web3StubConfig{" + + "common=" + + common + + ", service=" + + service + + ", resources=" + + resources + + ", stubConfigPath='" + + stubConfigPath + + '\'' + + '}'; + } +} diff --git a/src/main/java/com/webank/wecross/stub/web3/config/Web3StubConfigParser.java b/src/main/java/com/webank/wecross/stub/web3/config/Web3StubConfigParser.java new file mode 100644 index 0000000..fcac873 --- /dev/null +++ b/src/main/java/com/webank/wecross/stub/web3/config/Web3StubConfigParser.java @@ -0,0 +1,25 @@ +package com.webank.wecross.stub.web3.config; + +import com.moandjiezana.toml.Toml; +import com.webank.wecross.stub.web3.common.Web3Toml; +import java.io.File; +import java.io.IOException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Web3StubConfigParser extends AbstractWeb3ConfigParser { + + private static final Logger logger = LoggerFactory.getLogger(Web3StubConfigParser.class); + + public Web3StubConfigParser(String configPath, String configName) { + super(configPath + File.separator + configName); + } + + public Web3StubConfig loadConfig() throws IOException { + Web3Toml web3Toml = new Web3Toml(getConfigPath()); + Toml toml = web3Toml.getToml(); + Web3StubConfig web3StubConfig = toml.to(Web3StubConfig.class); + web3StubConfig.setStubConfigPath(getConfigPath()); + return web3StubConfig; + } +} diff --git a/src/main/java/com/webank/wecross/stub/web3/protocol/request/TransactionParams.java b/src/main/java/com/webank/wecross/stub/web3/protocol/request/TransactionParams.java new file mode 100644 index 0000000..8fce4bc --- /dev/null +++ b/src/main/java/com/webank/wecross/stub/web3/protocol/request/TransactionParams.java @@ -0,0 +1,116 @@ +package com.webank.wecross.stub.web3.protocol.request; + +import com.webank.wecross.stub.TransactionRequest; + +public class TransactionParams { + private TransactionRequest transactionRequest; + private SUB_TYPE subType; + private String from; + private String to; + private String data; + private String abi; + + public TransactionParams() {} + + // SendTransaction + public TransactionParams( + TransactionRequest transactionRequest, SUB_TYPE subType, String data, String abi) { + this.transactionRequest = transactionRequest; + this.subType = subType; + this.data = data; + this.abi = abi; + } + + // Call + public TransactionParams( + TransactionRequest transactionRequest, + SUB_TYPE subType, + String from, + String to, + String data, + String abi) { + this.transactionRequest = transactionRequest; + this.subType = subType; + this.from = from; + this.to = to; + this.data = data; + this.abi = abi; + } + + public enum SUB_TYPE { + SEND_TX_BY_PROXY, + CALL_BY_PROXY, + SEND_TX, + CALL + } + + public TransactionRequest getTransactionRequest() { + return transactionRequest; + } + + public void setTransactionRequest(TransactionRequest transactionRequest) { + this.transactionRequest = transactionRequest; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getFrom() { + return from; + } + + public void setFrom(String from) { + this.from = from; + } + + public String getTo() { + return to; + } + + public void setTo(String to) { + this.to = to; + } + + public String getAbi() { + return abi; + } + + public void setAbi(String abi) { + this.abi = abi; + } + + public SUB_TYPE getSubType() { + return subType; + } + + public void setSubType(SUB_TYPE subType) { + this.subType = subType; + } + + @Override + public String toString() { + return "TransactionParams{" + + "transactionRequest=" + + transactionRequest + + ", subType=" + + subType + + ", from='" + + from + + '\'' + + ", to='" + + to + + '\'' + + ", data='" + + data + + '\'' + + ", abi='" + + abi + + '\'' + + '}'; + } +} diff --git a/src/main/java/com/webank/wecross/stub/web3/protocol/response/TransactionPair.java b/src/main/java/com/webank/wecross/stub/web3/protocol/response/TransactionPair.java new file mode 100644 index 0000000..69ef734 --- /dev/null +++ b/src/main/java/com/webank/wecross/stub/web3/protocol/response/TransactionPair.java @@ -0,0 +1,42 @@ +package com.webank.wecross.stub.web3.protocol.response; + +import org.web3j.protocol.core.methods.response.Transaction; +import org.web3j.protocol.core.methods.response.TransactionReceipt; + +public class TransactionPair { + private Transaction transaction; + private TransactionReceipt transactionReceipt; + + public TransactionPair() {} + + public TransactionPair(Transaction transaction, TransactionReceipt transactionReceipt) { + this.transaction = transaction; + this.transactionReceipt = transactionReceipt; + } + + public TransactionReceipt getTransactionReceipt() { + return transactionReceipt; + } + + public void setTransactionReceipt(TransactionReceipt transactionReceipt) { + this.transactionReceipt = transactionReceipt; + } + + public Transaction getTransaction() { + return transaction; + } + + public void setTransaction(Transaction transaction) { + this.transaction = transaction; + } + + @Override + public String toString() { + return "TransactionPair{" + + "transactionReceipt=" + + transactionReceipt + + ", transaction=" + + transaction + + '}'; + } +} diff --git a/src/main/resources/stub.toml b/src/main/resources/stub.toml new file mode 100644 index 0000000..2ccebf0 --- /dev/null +++ b/src/main/resources/stub.toml @@ -0,0 +1,19 @@ +[common] + name = 'web3' # stub must be same with directory name + type = 'WEB3' # WEB3 + +[service] + url = 'http://localhost:8545' + +[[resources]] + name = 'WeCrossProxy' + type = 'WEB3_CONTRACT' + address = '0xdbF599778641083c9717Ec69e984D67d3309B811' + abi = '[{"constant":false,"inputs":[{"name":"n","type":"string"}],"name":"set","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]' + +[[resources]] + name = 'WeCrossHub' + type = 'WEB3_CONTRACT' + address = '0xdbF599778641083c9717Ec69e984D67d3309B811' + abi = '[{"constant":false,"inputs":[{"name":"n","type":"string"}],"name":"set","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]' +