diff --git a/.circleci/config.yml b/.circleci/config.yml index 278485a..53fec70 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,27 +1,48 @@ -version: 2.1 +version: '2.1' jobs: analyze: docker: - - image: 'cimg/openjdk:11.0' + - image: 'cimg/openjdk:21.0.2' steps: - checkout - run: name: Analyze on SonarCloud - command: mvn verify sonar:sonar - -executors: - jdk: - docker: - - image: 'cimg/openjdk:11.0' + command: mvn web3j:generate-sources verify sonar:sonar -DskipTests + test: + executor: machine_executor_amd64 + steps: + - checkout + - run: + name: Install OpenJDK 21 + command: | + java -version + sudo apt-get update && sudo apt-get install openjdk-21-jdk + sudo update-alternatives --set java /usr/lib/jvm/java-21-openjdk-amd64/bin/java + sudo update-alternatives --set javac /usr/lib/jvm/java-21-openjdk-amd64/bin/javac + java -version + export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64 + - run: + name: Generate Sources + command: mvn web3j:generate-sources + - run: + name: Maven Tests + command: mvn test orbs: maven: circleci/maven@1.4.1 +executors: + machine_executor_amd64: + machine: + image: ubuntu-2204:2023.10.1 + environment: + architecture: "amd64" + platform: "linux/amd64" + workflows: maven_test: jobs: - - maven/test: - executor: jdk + - test - analyze: context: SonarCloud \ No newline at end of file diff --git a/contract-service/pom.xml b/contract-service/pom.xml index 8c877c4..2911a11 100644 --- a/contract-service/pom.xml +++ b/contract-service/pom.xml @@ -25,11 +25,67 @@ org.springframework.boot spring-boot-starter-web + + org.web3j + core + ${web3j.version} + + + org.web3j + tuples + ${web3j.version} + + + org.web3j + abi + ${web3j.version} + + + org.web3j + crypto + ${web3j.version} + org.springframework.boot spring-boot-starter-test test + + org.testcontainers + testcontainers + ${testcontainers.version} + test + + + org.testcontainers + junit-jupiter + ${testcontainers.version} + test + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + add-source + generate-sources + + add-source + + + + src/main/generated + + + + + + + + + \ No newline at end of file diff --git a/contract-service/src/main/java/pl/piomin/services/contract/ContractApp.java b/contract-service/src/main/java/pl/piomin/services/contract/ContractApp.java index 1f9c0a7..4824e54 100644 --- a/contract-service/src/main/java/pl/piomin/services/contract/ContractApp.java +++ b/contract-service/src/main/java/pl/piomin/services/contract/ContractApp.java @@ -14,27 +14,27 @@ @SpringBootApplication public class ContractApp { - private static final Logger LOGGER = LoggerFactory.getLogger(ContractApp.class); - + private static final Logger LOGGER = LoggerFactory.getLogger(ContractApp.class); + @Autowired Web3j web3j; @Autowired ContractService service; - + public static void main(String[] args) { SpringApplication.run(ContractApp.class, args); } @PostConstruct public void listen() { - web3j.transactionObservable().subscribe(tx -> { - if (tx.getTo() != null && tx.getTo().equals(service.getOwnerAccount())) { - LOGGER.info("New tx: id={}, block={}, from={}, to={}, value={}", tx.getHash(), tx.getBlockHash(), tx.getFrom(), tx.getTo(), tx.getValue().intValue()); - service.processContracts(tx.getValue().longValue()); - } else { - LOGGER.info("Not matched: id={}, to={}", tx.getHash(), tx.getTo()); - } - }); + web3j.transactionFlowable().subscribe(tx -> { + if (tx.getTo() != null && tx.getTo().equals(service.getOwnerAccount())) { + LOGGER.info("New tx: id={}, block={}, from={}, to={}, value={}", tx.getHash(), tx.getBlockHash(), tx.getFrom(), tx.getTo(), tx.getValue().intValue()); + service.processContracts(tx.getValue().longValue()); + } else { + LOGGER.info("Not matched: id={}, to={}", tx.getHash(), tx.getTo()); + } + }); } - + } diff --git a/contract-service/src/main/java/pl/piomin/services/contract/controller/ContractController.java b/contract-service/src/main/java/pl/piomin/services/contract/controller/ContractController.java index 2d8d103..456a44d 100644 --- a/contract-service/src/main/java/pl/piomin/services/contract/controller/ContractController.java +++ b/contract-service/src/main/java/pl/piomin/services/contract/controller/ContractController.java @@ -16,15 +16,15 @@ public class ContractController { @Autowired ContractService service; - + @GetMapping("/owner") public String getOwnerAccount() { - return service.getOwnerAccount(); + return service.getOwnerAccount(); } - + @PostMapping public Contract createContract(@RequestBody Contract newContract) throws Exception { - return service.createContract(newContract); + return service.createContract(newContract); } - + } diff --git a/contract-service/src/main/java/pl/piomin/services/contract/model/Contract.java b/contract-service/src/main/java/pl/piomin/services/contract/model/Contract.java index e23b43a..b9a57c5 100644 --- a/contract-service/src/main/java/pl/piomin/services/contract/model/Contract.java +++ b/contract-service/src/main/java/pl/piomin/services/contract/model/Contract.java @@ -2,32 +2,32 @@ public class Contract { - private int fee; - private String receiver; - private String address; + private int fee; + private String receiver; + private String address; - public int getFee() { - return fee; - } + public int getFee() { + return fee; + } - public void setFee(int fee) { - this.fee = fee; - } + public void setFee(int fee) { + this.fee = fee; + } - public String getReceiver() { - return receiver; - } + public String getReceiver() { + return receiver; + } - public void setReceiver(String receiver) { - this.receiver = receiver; - } + public void setReceiver(String receiver) { + this.receiver = receiver; + } - public String getAddress() { - return address; - } + public String getAddress() { + return address; + } - public void setAddress(String address) { - this.address = address; - } + public void setAddress(String address) { + this.address = address; + } } diff --git a/contract-service/src/main/java/pl/piomin/services/contract/model/Transaction.java b/contract-service/src/main/java/pl/piomin/services/contract/model/Transaction.java index a6caa9a..f9a8a77 100644 --- a/contract-service/src/main/java/pl/piomin/services/contract/model/Transaction.java +++ b/contract-service/src/main/java/pl/piomin/services/contract/model/Transaction.java @@ -2,23 +2,23 @@ public class Transaction { - private String contract; - private long amount; + private String contract; + private long amount; - public String getContract() { - return contract; - } + public String getContract() { + return contract; + } - public void setContract(String contract) { - this.contract = contract; - } + public void setContract(String contract) { + this.contract = contract; + } - public long getAmount() { - return amount; - } + public long getAmount() { + return amount; + } - public void setAmount(long amount) { - this.amount = amount; - } + public void setAmount(long amount) { + this.amount = amount; + } } diff --git a/contract-service/src/main/java/pl/piomin/services/contract/model/Transactionfee.java b/contract-service/src/main/java/pl/piomin/services/contract/model/Transactionfee.java deleted file mode 100644 index 8f5cfad..0000000 --- a/contract-service/src/main/java/pl/piomin/services/contract/model/Transactionfee.java +++ /dev/null @@ -1,168 +0,0 @@ -package pl.piomin.services.contract.model; - -import java.math.BigInteger; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import org.web3j.abi.EventEncoder; -import org.web3j.abi.FunctionEncoder; -import org.web3j.abi.TypeReference; -import org.web3j.abi.datatypes.Address; -import org.web3j.abi.datatypes.Bool; -import org.web3j.abi.datatypes.Event; -import org.web3j.abi.datatypes.Function; -import org.web3j.abi.datatypes.Type; -import org.web3j.abi.datatypes.generated.Uint256; -import org.web3j.crypto.Credentials; -import org.web3j.protocol.Web3j; -import org.web3j.protocol.core.DefaultBlockParameter; -import org.web3j.protocol.core.RemoteCall; -import org.web3j.protocol.core.methods.request.EthFilter; -import org.web3j.protocol.core.methods.response.Log; -import org.web3j.protocol.core.methods.response.TransactionReceipt; -import org.web3j.tx.Contract; -import org.web3j.tx.TransactionManager; -import rx.Observable; -import rx.functions.Func1; - -/** - *

Auto generated code. - *

Do not modify! - *

Please use the web3j command line tools, - * or the org.web3j.codegen.SolidityFunctionWrapperGenerator in the - * codegen module to update. - * - *

Generated with web3j version 3.4.0. - */ -public class Transactionfee extends Contract { - private static final String BINARY = "608060405234801561001057600080fd5b506040516040806102c783398101604052805160209091015160018054600160a060020a03909316600160a060020a03199093169290921790915560005561026a8061005d6000396000f30060806040526004361061006c5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166310fb7bc6811461007157806327e235e314610098578063d0f452dd146100c6578063ddca3f43146100d0578063f7260d3e146100e5575b600080fd5b34801561007d57600080fd5b50610086610123565b60408051918252519081900360200190f35b3480156100a457600080fd5b5061008673ffffffffffffffffffffffffffffffffffffffff60043516610140565b6100ce610152565b005b3480156100dc57600080fd5b5061008661021c565b3480156100f157600080fd5b506100fa610222565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b60015473ffffffffffffffffffffffffffffffffffffffff163190565b60026020526000908152604090205481565b600080548190606490340260015460405192909104935073ffffffffffffffffffffffffffffffffffffffff169083156108fc029084906000818181858888f16001805473ffffffffffffffffffffffffffffffffffffffff90811660009081526002602090815260409182902080548c01905592548151338152921692820192909252808201899052821515606082015290519196507f4a7ca93981c43b6021cfef8fa7764ad1a6abd748f97622b8fc50d887bf603c6495508190036080019350915050a15050565b60005481565b60015473ffffffffffffffffffffffffffffffffffffffff16815600a165627a7a7230582056902cd076d831b0bb2b8ffad0441a608f0fc2f58ca92718e460b34e34f24cad0029"; - - public static final String FUNC_GETRECEIVERBALANCE = "getReceiverBalance"; - - public static final String FUNC_BALANCES = "balances"; - - public static final String FUNC_SENDTRX = "sendTrx"; - - public static final String FUNC_FEE = "fee"; - - public static final String FUNC_RECEIVER = "receiver"; - - public static final Event SENT_EVENT = new Event("Sent", - Arrays.>asList(), - Arrays.>asList(new TypeReference

() {}, new TypeReference
() {}, new TypeReference() {}, new TypeReference() {})); - ; - - protected Transactionfee(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) { - super(BINARY, contractAddress, web3j, credentials, gasPrice, gasLimit); - } - - protected Transactionfee(String contractAddress, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) { - super(BINARY, contractAddress, web3j, transactionManager, gasPrice, gasLimit); - } - - public RemoteCall getReceiverBalance() { - final Function function = new Function(FUNC_GETRECEIVERBALANCE, - Arrays.asList(), - Arrays.>asList(new TypeReference() {})); - return executeRemoteCallSingleValueReturn(function, BigInteger.class); - } - - public RemoteCall balances(String param0) { - final Function function = new Function(FUNC_BALANCES, - Arrays.asList(new org.web3j.abi.datatypes.Address(param0)), - Arrays.>asList(new TypeReference() {})); - return executeRemoteCallSingleValueReturn(function, BigInteger.class); - } - - public RemoteCall sendTrx(BigInteger weiValue) { - final Function function = new Function( - FUNC_SENDTRX, - Arrays.asList(), - Collections.>emptyList()); - return executeRemoteCallTransaction(function, weiValue); - } - - public RemoteCall fee() { - final Function function = new Function(FUNC_FEE, - Arrays.asList(), - Arrays.>asList(new TypeReference() {})); - return executeRemoteCallSingleValueReturn(function, BigInteger.class); - } - - public RemoteCall receiver() { - final Function function = new Function(FUNC_RECEIVER, - Arrays.asList(), - Arrays.>asList(new TypeReference
() {})); - return executeRemoteCallSingleValueReturn(function, String.class); - } - - public static RemoteCall deploy(Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit, String _receiver, BigInteger _fee) { - String encodedConstructor = FunctionEncoder.encodeConstructor(Arrays.asList(new org.web3j.abi.datatypes.Address(_receiver), - new org.web3j.abi.datatypes.generated.Uint256(_fee))); - return deployRemoteCall(Transactionfee.class, web3j, credentials, gasPrice, gasLimit, BINARY, encodedConstructor); - } - - public static RemoteCall deploy(Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit, String _receiver, BigInteger _fee) { - String encodedConstructor = FunctionEncoder.encodeConstructor(Arrays.asList(new org.web3j.abi.datatypes.Address(_receiver), - new org.web3j.abi.datatypes.generated.Uint256(_fee))); - return deployRemoteCall(Transactionfee.class, web3j, transactionManager, gasPrice, gasLimit, BINARY, encodedConstructor); - } - - public List getSentEvents(TransactionReceipt transactionReceipt) { - List valueList = extractEventParametersWithLog(SENT_EVENT, transactionReceipt); - ArrayList responses = new ArrayList(valueList.size()); - for (Contract.EventValuesWithLog eventValues : valueList) { - SentEventResponse typedResponse = new SentEventResponse(); - typedResponse.log = eventValues.getLog(); - typedResponse.from = (String) eventValues.getNonIndexedValues().get(0).getValue(); - typedResponse.to = (String) eventValues.getNonIndexedValues().get(1).getValue(); - typedResponse.amount = (BigInteger) eventValues.getNonIndexedValues().get(2).getValue(); - typedResponse.sent = (Boolean) eventValues.getNonIndexedValues().get(3).getValue(); - responses.add(typedResponse); - } - return responses; - } - - public Observable sentEventObservable(EthFilter filter) { - return web3j.ethLogObservable(filter).map(new Func1() { - @Override - public SentEventResponse call(Log log) { - Contract.EventValuesWithLog eventValues = extractEventParametersWithLog(SENT_EVENT, log); - SentEventResponse typedResponse = new SentEventResponse(); - typedResponse.log = log; - typedResponse.from = (String) eventValues.getNonIndexedValues().get(0).getValue(); - typedResponse.to = (String) eventValues.getNonIndexedValues().get(1).getValue(); - typedResponse.amount = (BigInteger) eventValues.getNonIndexedValues().get(2).getValue(); - typedResponse.sent = (Boolean) eventValues.getNonIndexedValues().get(3).getValue(); - return typedResponse; - } - }); - } - - public Observable sentEventObservable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { - EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); - filter.addSingleTopic(EventEncoder.encode(SENT_EVENT)); - return sentEventObservable(filter); - } - - public static Transactionfee load(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) { - return new Transactionfee(contractAddress, web3j, credentials, gasPrice, gasLimit); - } - - public static Transactionfee load(String contractAddress, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) { - return new Transactionfee(contractAddress, web3j, transactionManager, gasPrice, gasLimit); - } - - public static class SentEventResponse { - public Log log; - - public String from; - - public String to; - - public BigInteger amount; - - public Boolean sent; - } -} diff --git a/contract-service/src/main/java/pl/piomin/services/contract/service/ContractService.java b/contract-service/src/main/java/pl/piomin/services/contract/service/ContractService.java index e71f451..72ebf3e 100644 --- a/contract-service/src/main/java/pl/piomin/services/contract/service/ContractService.java +++ b/contract-service/src/main/java/pl/piomin/services/contract/service/ContractService.java @@ -1,16 +1,5 @@ package pl.piomin.services.contract.service; -import java.io.IOException; -import java.math.BigInteger; -import java.security.InvalidAlgorithmParameterException; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - -import javax.annotation.PostConstruct; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -26,9 +15,18 @@ import org.web3j.protocol.core.methods.response.EthGetBalance; import org.web3j.protocol.core.methods.response.EthGetTransactionCount; import org.web3j.protocol.core.methods.response.TransactionReceipt; - import pl.piomin.services.contract.model.Contract; -import pl.piomin.services.contract.model.Transactionfee; +import pl.piomin.services.contract.model.TransactionFee; + +import javax.annotation.PostConstruct; +import java.io.IOException; +import java.math.BigInteger; +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; @Service public class ContractService { @@ -37,60 +35,60 @@ public class ContractService { private static final String PASSWORD = "piot123"; private static final BigInteger GAS_PRICE = BigInteger.valueOf(1L); private static final BigInteger GAS_LIMIT = BigInteger.valueOf(500_000L); - + @Autowired Web3j web3j; Credentials credentials; private List contracts = new ArrayList<>(); - + @PostConstruct - public void init() throws IOException, CipherException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException { - String file = WalletUtils.generateLightNewWalletFile(PASSWORD, null); - credentials = WalletUtils.loadCredentials(PASSWORD, file); - LOGGER.info("Credentials created: file={}, address={}", file, credentials.getAddress()); - EthCoinbase coinbase = web3j.ethCoinbase().send(); - EthGetTransactionCount transactionCount = web3j.ethGetTransactionCount(coinbase.getAddress(), DefaultBlockParameterName.LATEST).send(); - Transaction transaction = Transaction.createEtherTransaction(coinbase.getAddress(), transactionCount.getTransactionCount(), BigInteger.valueOf(20_000_000_000L), BigInteger.valueOf(21_000), credentials.getAddress(),BigInteger.valueOf(25_000_000_000_000_000L)); - web3j.ethSendTransaction(transaction).send(); - EthGetBalance balance = web3j.ethGetBalance(credentials.getAddress(), DefaultBlockParameterName.LATEST).send(); - LOGGER.info("Balance: {}", balance.getBalance().longValue()); + public void init() throws IOException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException, CipherException { + String file = WalletUtils.generateLightNewWalletFile(PASSWORD, null); + credentials = WalletUtils.loadCredentials(PASSWORD, file); + LOGGER.info("Credentials created: file={}, address={}", file, credentials.getAddress()); + EthCoinbase coinbase = web3j.ethCoinbase().send(); + EthGetTransactionCount transactionCount = web3j.ethGetTransactionCount(coinbase.getAddress(), DefaultBlockParameterName.LATEST).send(); + Transaction transaction = Transaction.createEtherTransaction(coinbase.getAddress(), transactionCount.getTransactionCount(), BigInteger.valueOf(20_000_000_000L), BigInteger.valueOf(21_000), credentials.getAddress(), BigInteger.valueOf(25_000_000_000_000_000L)); + web3j.ethSendTransaction(transaction).send(); + EthGetBalance balance = web3j.ethGetBalance(credentials.getAddress(), DefaultBlockParameterName.LATEST).send(); + LOGGER.info("Balance: {}", balance.getBalance().longValue()); } - + public String getOwnerAccount() { - return credentials.getAddress(); + return credentials.getAddress(); } - + public Contract createContract(Contract newContract) throws Exception { - String file = WalletUtils.generateLightNewWalletFile(PASSWORD, null); - Credentials receiverCredentials = WalletUtils.loadCredentials(PASSWORD, file); - LOGGER.info("Credentials created: file={}, address={}", file, credentials.getAddress()); - Transactionfee contract = Transactionfee.deploy(web3j, credentials, GAS_PRICE, GAS_LIMIT, receiverCredentials.getAddress(), BigInteger.valueOf(newContract.getFee())).send(); - newContract.setReceiver(receiverCredentials.getAddress()); - newContract.setAddress(contract.getContractAddress()); - contracts.add(contract.getContractAddress()); - LOGGER.info("New contract deployed: address={}", contract.getContractAddress()); - Optional tr = contract.getTransactionReceipt(); - if (tr.isPresent()) { - LOGGER.info("Transaction receipt: from={}, to={}, gas={}", tr.get().getFrom(), tr.get().getTo(), tr.get().getGasUsed().intValue()); - } - return newContract; + String file = WalletUtils.generateLightNewWalletFile(PASSWORD, null); + Credentials receiverCredentials = WalletUtils.loadCredentials(PASSWORD, file); + LOGGER.info("Credentials created: file={}, address={}", file, credentials.getAddress()); + TransactionFee contract = TransactionFee.deploy(web3j, credentials, GAS_PRICE, GAS_LIMIT, receiverCredentials.getAddress(), BigInteger.valueOf(newContract.getFee())).send(); + newContract.setReceiver(receiverCredentials.getAddress()); + newContract.setAddress(contract.getContractAddress()); + contracts.add(contract.getContractAddress()); + LOGGER.info("New contract deployed: address={}", contract.getContractAddress()); + Optional tr = contract.getTransactionReceipt(); + if (tr.isPresent()) { + LOGGER.info("Transaction receipt: from={}, to={}, gas={}", tr.get().getFrom(), tr.get().getTo(), tr.get().getGasUsed().intValue()); + } + return newContract; } - + public void processContracts(long transactionAmount) { - contracts.forEach(it -> { - Transactionfee contract = Transactionfee.load(it, web3j, credentials, GAS_PRICE, GAS_LIMIT); - try { - TransactionReceipt tr = contract.sendTrx(BigInteger.valueOf(transactionAmount)).send(); - LOGGER.info("Transaction receipt: from={}, to={}, gas={}", tr.getFrom(), tr.getTo(), tr.getGasUsed().intValue()); - LOGGER.info("Get receiver: {}", contract.getReceiverBalance().send().longValue()); - EthFilter filter = new EthFilter(DefaultBlockParameterName.EARLIEST, DefaultBlockParameterName.LATEST, contract.getContractAddress()); - web3j.ethLogObservable(filter).subscribe(log -> { - LOGGER.info("Log: {}", log.getData()); - }); - } catch (Exception e) { - LOGGER.error("Error during contract execution", e); - } - }); + contracts.forEach(it -> { + TransactionFee contract = TransactionFee.load(it, web3j, credentials, GAS_PRICE, GAS_LIMIT); + try { + TransactionReceipt tr = contract.sendTrx(BigInteger.valueOf(transactionAmount)).send(); + LOGGER.info("Transaction receipt: from={}, to={}, gas={}", tr.getFrom(), tr.getTo(), tr.getGasUsed().intValue()); + LOGGER.info("Get receiver: {}", contract.getReceiverBalance().send().longValue()); + EthFilter filter = new EthFilter(DefaultBlockParameterName.EARLIEST, DefaultBlockParameterName.LATEST, contract.getContractAddress()); + web3j.ethLogFlowable(filter).subscribe(log -> { + LOGGER.info("Log: {}", log.getData()); + }); + } catch (Exception e) { + LOGGER.error("Error during contract execution", e); + } + }); } - + } diff --git a/contract-service/src/main/resources/fee.sol b/contract-service/src/main/resources/fee.sol new file mode 100644 index 0000000..c139f2e --- /dev/null +++ b/contract-service/src/main/resources/fee.sol @@ -0,0 +1,33 @@ +pragma solidity ^0.4.21; + +contract TransactionFee { + + // (1) + uint public fee; + // (2) + address public receiver; + // (3) + mapping (address => uint) public balances; + // (4) + event Sent(address from, address to, uint amount, bool sent); + + // (5) + constructor(address _receiver, uint _fee) public { + receiver = _receiver; + fee = _fee; + } + + // (6) + function getReceiverBalance() public view returns(uint) { + return receiver.balance; + } + + // (7) + function sendTrx() public payable { + uint value = msg.value * fee / 100; + bool sent = receiver.send(value); + balances[receiver] += (value); + emit Sent(msg.sender, receiver, value, sent); + } + +} \ No newline at end of file diff --git a/contract-service/src/test/java/pl/piomin/services/contract/ContractTest.java b/contract-service/src/test/java/pl/piomin/services/contract/ContractTest.java index 8abf377..79d7cf0 100644 --- a/contract-service/src/test/java/pl/piomin/services/contract/ContractTest.java +++ b/contract-service/src/test/java/pl/piomin/services/contract/ContractTest.java @@ -1,15 +1,16 @@ package pl.piomin.services.contract; -import java.math.BigInteger; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; import org.web3j.crypto.Credentials; import org.web3j.crypto.WalletUtils; import org.web3j.protocol.Web3j; @@ -20,63 +21,79 @@ import org.web3j.protocol.core.methods.response.EthGetBalance; import org.web3j.protocol.core.methods.response.EthGetTransactionCount; import org.web3j.protocol.core.methods.response.TransactionReceipt; +import org.web3j.tx.RawTransactionManager; +import org.web3j.tx.TransactionManager; +import pl.piomin.services.contract.model.TransactionFee; -import pl.piomin.services.contract.model.Transactionfee; +import java.math.BigInteger; @SpringBootTest -@RunWith(SpringRunner.class) +@Testcontainers public class ContractTest { - private static final Logger LOGGER = LoggerFactory.getLogger(ContractTest.class); - public static final BigInteger GAS_PRICE = BigInteger.valueOf(1L); + private static final Logger LOGGER = LoggerFactory.getLogger(ContractTest.class); + public static final BigInteger GAS_PRICE = BigInteger.valueOf(1L); public static final BigInteger GAS_LIMIT = BigInteger.valueOf(500_000L); public static final BigInteger AMOUNT = BigInteger.valueOf(10_000L); public static final BigInteger FEE = BigInteger.valueOf(10L); - - @Autowired + + @Autowired Web3j web3j; - Credentials credentialsFrom; - Credentials credentialsTo; - - @Before - public void before() throws Exception { + Credentials credentialsFrom; + Credentials credentialsTo; + + @Container + static final GenericContainer clientEthereum = new GenericContainer("ethereum/client-go") + .withCommand("--http", "--http.corsdomain=*", "--http.addr=0.0.0.0", "--dev") + .withExposedPorts(8545); + + @DynamicPropertySource + static void registerCeProperties(DynamicPropertyRegistry registry) { + registry.add("web3j.client-address", + () -> String.format("http://localhost:%d", clientEthereum.getFirstMappedPort())); + } + + @BeforeEach + public void before() throws Exception { // String file = WalletUtils.generateLightNewWalletFile("piot123", null); // String file = "src/test/resources/john_smith.json"; - credentialsFrom = WalletUtils.loadCredentials("piot123", "src/test/resources/john_smith.json"); - credentialsTo = WalletUtils.loadCredentials("piot123", "src/test/resources/jason_sullivan.json"); - LOGGER.info("Credentials: address={}", credentialsFrom.getAddress()); - EthCoinbase coinbase = web3j.ethCoinbase().send(); - EthGetTransactionCount transactionCount = web3j.ethGetTransactionCount(coinbase.getAddress(), DefaultBlockParameterName.LATEST).send(); - Transaction transaction = Transaction.createEtherTransaction(coinbase.getAddress(), transactionCount.getTransactionCount(), BigInteger.valueOf(20_000_000_000L), BigInteger.valueOf(21_000), credentialsFrom.getAddress(),BigInteger.valueOf(25_000_000_000_000_000L)); - web3j.ethSendTransaction(transaction).send(); - EthGetBalance balance = web3j.ethGetBalance(credentialsFrom.getAddress(), DefaultBlockParameterName.LATEST).send(); - LOGGER.info("Balance: {}", balance.getBalance().longValue()); - } - - @Test - public void testTransactionFeeContract() throws Exception { - Transactionfee contract = Transactionfee.deploy(web3j, credentialsFrom, GAS_PRICE, GAS_LIMIT, "0xd7850bd94f189ce38ce5729052926094997de310", FEE).send(); - EthGetBalance balance = web3j.ethGetBalance(credentialsTo.getAddress(), DefaultBlockParameterName.LATEST).send(); - EthFilter filter = new EthFilter(DefaultBlockParameterName.EARLIEST, DefaultBlockParameterName.LATEST, contract.getContractAddress()); - LOGGER.info("Sending to: account={}, balance={}", "0xd7850bd94f189ce38ce5729052926094997de310", balance.getBalance().longValue()); - for (int i=0; i<3; i++) { - TransactionReceipt tr = contract.sendTrx(AMOUNT).send(); - LOGGER.info("Transaction receipt: from={}, to={}, gas={}", tr.getFrom(), tr.getTo(), tr.getGasUsed().intValue()); - balance = web3j.ethGetBalance(credentialsFrom.getAddress(), DefaultBlockParameterName.LATEST).send(); - LOGGER.info("From balance after: account={}, balance={}", "0xd7850bd94f189ce38ce5729052926094997de310", balance.getBalance().longValue()); - balance = web3j.ethGetBalance(credentialsTo.getAddress(), DefaultBlockParameterName.LATEST).send(); - LOGGER.info("To balance after: account={}, balance={}", "0xd7850bd94f189ce38ce5729052926094997de310", balance.getBalance().longValue()); - LOGGER.info("Contract: account={}, balance={}", "0xd7850bd94f189ce38ce5729052926094997de310", contract.balances(credentialsTo.getAddress()).send().longValue()); - LOGGER.info("Get receiver: {}", contract.getReceiverBalance().send().longValue()); + credentialsFrom = WalletUtils.loadCredentials("piot123", "src/test/resources/john_smith.json"); + credentialsTo = WalletUtils.loadCredentials("piot123", "src/test/resources/jason_sullivan.json"); + LOGGER.info("Credentials: address={}", credentialsFrom.getAddress()); + EthCoinbase coinbase = web3j.ethCoinbase().send(); + EthGetTransactionCount transactionCount = web3j.ethGetTransactionCount(coinbase.getAddress(), DefaultBlockParameterName.LATEST).send(); + Transaction transaction = Transaction.createEtherTransaction(coinbase.getAddress(), transactionCount.getTransactionCount(), BigInteger.valueOf(20_000_000_000L), BigInteger.valueOf(21_000), credentialsFrom.getAddress(), BigInteger.valueOf(25_000_000_000_000_000L)); + web3j.ethSendTransaction(transaction).send(); + EthGetBalance balance = web3j.ethGetBalance(credentialsFrom.getAddress(), DefaultBlockParameterName.LATEST).send(); + LOGGER.info("Balance: {}", balance.getBalance().longValue()); + } + + @Test + public void testTransactionFeeContract() throws Exception { + long chainId = 56; + TransactionManager txManager = new RawTransactionManager(web3j, credentialsFrom, chainId); + TransactionFee contract = TransactionFee.deploy(web3j, credentialsFrom, GAS_PRICE, GAS_LIMIT, "0xd7850bd94f189ce38ce5729052926094997de310", FEE).send(); + EthGetBalance balance = web3j.ethGetBalance(credentialsTo.getAddress(), DefaultBlockParameterName.LATEST).send(); + EthFilter filter = new EthFilter(DefaultBlockParameterName.EARLIEST, DefaultBlockParameterName.LATEST, contract.getContractAddress()); + LOGGER.info("Sending to: account={}, balance={}", "0xd7850bd94f189ce38ce5729052926094997de310", balance.getBalance().longValue()); + for (int i = 0; i < 3; i++) { + TransactionReceipt tr = contract.sendTrx(AMOUNT).send(); + LOGGER.info("Transaction receipt: from={}, to={}, gas={}", tr.getFrom(), tr.getTo(), tr.getGasUsed().intValue()); + balance = web3j.ethGetBalance(credentialsFrom.getAddress(), DefaultBlockParameterName.LATEST).send(); + LOGGER.info("From balance after: account={}, balance={}", "0xd7850bd94f189ce38ce5729052926094997de310", balance.getBalance().longValue()); + balance = web3j.ethGetBalance(credentialsTo.getAddress(), DefaultBlockParameterName.LATEST).send(); + LOGGER.info("To balance after: account={}, balance={}", "0xd7850bd94f189ce38ce5729052926094997de310", balance.getBalance().longValue()); + LOGGER.info("Contract: account={}, balance={}", "0xd7850bd94f189ce38ce5729052926094997de310", contract.balances(credentialsTo.getAddress()).send().longValue()); + LOGGER.info("Get receiver: {}", contract.getReceiverBalance().send().longValue()); // LOGGER.info("Contract To: account={}, balance={}", tr.getTo(), contract.balances(tr.getTo()).send().longValue()); // balance = web3j.ethGetBalance(tr.getTo(), DefaultBlockParameterName.LATEST).send(); // LOGGER.info("Contract To 2: account={}, balance={}", credentialsTo.getAddress(), balance.getBalance().longValue()); - } - - web3j.ethLogObservable(filter).subscribe(log -> { - LOGGER.info("Log: {}", log.getData()); - }); - Thread.sleep(2000); - } - + } + + web3j.ethLogFlowable(filter).subscribe(log -> { + LOGGER.info("Log: {}", log.getData()); + }); + Thread.sleep(2000); + } + } diff --git a/pom.xml b/pom.xml index 1b497e2..37ce1b0 100644 --- a/pom.xml +++ b/pom.xml @@ -1,27 +1,49 @@ - - 4.0.0 + + 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 2.0.3.RELEASE - + + org.springframework.boot + spring-boot-starter-parent + 2.7.18 + - pl.piomin.services - sample-spring-blockchain-contract - 1.0-SNAPSHOT - pom + pl.piomin.services + sample-spring-blockchain-contract + 1.0-SNAPSHOT + pom - - 11 - piomin_sample-spring-blockchain-contract - piomin - https://sonarcloud.io - + + 11 + piomin_sample-spring-blockchain-contract + piomin + https://sonarcloud.io + 1.19.6 + 4.10.0 + - - contract-service - transaction-service - + + contract-service + transaction-service + + + + + org.web3j + web3j-maven-plugin + 4.11.0 + + pl.piomin.services.contract.model + contract-service/src/main/generated + + contract-service/src/main/resources + + **/*.sol + + + + + + \ No newline at end of file diff --git a/transaction-service/pom.xml b/transaction-service/pom.xml index 7367cec..c286d07 100644 --- a/transaction-service/pom.xml +++ b/transaction-service/pom.xml @@ -1,33 +1,34 @@ - - 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 2.0.3.RELEASE - - transaction-service - pl.piomin.services - 1.0-SNAPSHOT + + 4.0.0 + + pl.piomin.services + sample-spring-blockchain-contract + 1.0-SNAPSHOT + + transaction-service + pl.piomin.services + 1.0-SNAPSHOT - - ${project.artifactId} - + + ${project.artifactId} + + + + + org.web3j + web3j-spring-boot-starter + 1.6.0 + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-test + test + + - - - org.web3j - web3j-spring-boot-starter - 1.6.0 - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-test - test - - - \ No newline at end of file diff --git a/transaction-service/src/main/resources/application.yml b/transaction-service/src/main/resources/application.yml index 67bb24b..c20d461 100644 --- a/transaction-service/src/main/resources/application.yml +++ b/transaction-service/src/main/resources/application.yml @@ -4,7 +4,7 @@ spring: server: port: ${PORT:8091} web3j: - client-address: http://192.168.99.100:8545 + client-address: http://localhost:8545 contract-service: url: http://localhost:8090/contract/owner \ No newline at end of file