├── .circleci └── config.yml ├── pom.xml ├── readme.md ├── renovate.json └── src ├── main ├── java │ └── pl │ │ └── piomin │ │ └── service │ │ └── blockchain │ │ ├── BlockchainApp.java │ │ ├── config │ │ ├── Web3jAutoConfiguration.java │ │ └── Web3jProperties.java │ │ ├── controller │ │ └── BlockchainController.java │ │ ├── model │ │ └── BlockchainTransaction.java │ │ └── service │ │ └── BlockchainService.java └── resources │ └── application.yml └── test └── java └── pl └── piomin └── service └── blockchain └── BlockchainTest.java /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: '2.1' 2 | 3 | jobs: 4 | analyze: 5 | docker: 6 | - image: 'cimg/openjdk:21.0.6' 7 | steps: 8 | - checkout 9 | - run: 10 | name: Analyze on SonarCloud 11 | command: mvn verify sonar:sonar -DskipTests 12 | test: 13 | executor: machine_executor_amd64 14 | steps: 15 | - checkout 16 | - run: 17 | name: Install OpenJDK 21 18 | command: | 19 | java -version 20 | sudo apt-get update && sudo apt-get install openjdk-21-jdk 21 | sudo update-alternatives --set java /usr/lib/jvm/java-21-openjdk-amd64/bin/java 22 | sudo update-alternatives --set javac /usr/lib/jvm/java-21-openjdk-amd64/bin/javac 23 | java -version 24 | export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64 25 | - run: 26 | name: Maven Tests 27 | command: mvn test 28 | 29 | orbs: 30 | maven: circleci/maven@2.0.0 31 | 32 | executors: 33 | machine_executor_amd64: 34 | machine: 35 | image: ubuntu-2204:2023.10.1 36 | environment: 37 | architecture: "amd64" 38 | platform: "linux/amd64" 39 | 40 | workflows: 41 | maven_test: 42 | jobs: 43 | - test 44 | - analyze: 45 | context: SonarCloud 46 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | org.springframework.boot 9 | spring-boot-starter-parent 10 | 3.5.0 11 | 12 | 13 | pl.piomin.services 14 | sample-spring-blockchain 15 | 1.0-SNAPSHOT 16 | 17 | 18 | 21 19 | piomin_sample-spring-blockchain 20 | piomin 21 | https://sonarcloud.io 22 | 1.21.1 23 | 5.0.0 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | org.springdoc 34 | springdoc-openapi-starter-webmvc-ui 35 | 2.8.9 36 | 37 | 38 | org.web3j 39 | core 40 | ${web3j.version} 41 | 42 | 43 | org.web3j 44 | tuples 45 | ${web3j.version} 46 | 47 | 48 | org.web3j 49 | abi 50 | ${web3j.version} 51 | 52 | 53 | org.web3j 54 | crypto 55 | ${web3j.version} 56 | 57 | 58 | org.springframework.boot 59 | spring-boot-starter-web 60 | 61 | 62 | org.springframework.boot 63 | spring-boot-starter-test 64 | test 65 | 66 | 67 | org.testcontainers 68 | testcontainers 69 | ${testcontainers.version} 70 | test 71 | 72 | 73 | org.testcontainers 74 | junit-jupiter 75 | ${testcontainers.version} 76 | test 77 | 78 | 79 | org.springframework.boot 80 | spring-boot-testcontainers 81 | test 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ## Introduction to Blockchain with Java using Ethereum, web3j and Spring Boot [![Twitter](https://img.shields.io/twitter/follow/piotr_minkowski.svg?style=social&logo=twitter&label=Follow%20Me)](https://twitter.com/piotr_minkowski) 2 | 3 | [![CircleCI](https://circleci.com/gh/piomin/sample-spring-blockchain.svg?style=svg)](https://circleci.com/gh/piomin/sample-spring-blockchain) 4 | 5 | [![SonarCloud](https://sonarcloud.io/images/project_badges/sonarcloud-black.svg)](https://sonarcloud.io/dashboard?id=piomin_sample-spring-blockchain) 6 | [![Bugs](https://sonarcloud.io/api/project_badges/measure?project=piomin_sample-spring-blockchain&metric=bugs)](https://sonarcloud.io/dashboard?id=piomin_sample-spring-blockchain) 7 | [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=piomin_sample-spring-blockchain&metric=coverage)](https://sonarcloud.io/dashboard?id=piomin_sample-spring-blockchain) 8 | [![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=piomin_sample-spring-blockchain&metric=ncloc)](https://sonarcloud.io/dashboard?id=piomin_sample-spring-blockchain) 9 | 10 | Detailed description can be found here: [Introduction to Blockchain with Java using Ethereum, web3j and Spring Boot](https://piotrminkowski.com/2018/06/22/introduction-to-blockchain-with-java-using-ethereum-web3j-and-spring-boot/) 11 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:base",":dependencyDashboard" 5 | ], 6 | "packageRules": [ 7 | { 8 | "matchUpdateTypes": ["minor", "patch", "pin", "digest"], 9 | "automerge": true 10 | } 11 | ], 12 | "prCreation": "not-pending" 13 | } -------------------------------------------------------------------------------- /src/main/java/pl/piomin/service/blockchain/BlockchainApp.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.service.blockchain; 2 | 3 | import jakarta.annotation.PostConstruct; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.boot.SpringApplication; 7 | import org.springframework.boot.autoconfigure.SpringBootApplication; 8 | import org.web3j.protocol.Web3j; 9 | import org.web3j.protocol.core.DefaultBlockParameterName; 10 | import org.web3j.protocol.core.methods.request.Transaction; 11 | import org.web3j.protocol.core.methods.response.EthCoinbase; 12 | import org.web3j.protocol.core.methods.response.EthGetTransactionCount; 13 | import pl.piomin.service.blockchain.service.BlockchainService; 14 | 15 | import java.io.IOException; 16 | import java.math.BigInteger; 17 | 18 | @SpringBootApplication 19 | public class BlockchainApp { 20 | 21 | private static final Logger LOGGER = LoggerFactory.getLogger(BlockchainService.class); 22 | 23 | private final Web3j web3j; 24 | 25 | public BlockchainApp(Web3j web3j) { 26 | this.web3j = web3j; 27 | } 28 | 29 | public static void main(String[] args) { 30 | SpringApplication.run(BlockchainApp.class, args); 31 | } 32 | 33 | @PostConstruct 34 | public void listen() { 35 | 36 | web3j.transactionFlowable().subscribe(tx -> { 37 | 38 | LOGGER.info("New tx: id={}, block={}, from={}, to={}, value={}", tx.getHash(), tx.getBlockHash(), tx.getFrom(), tx.getTo(), tx.getValue().intValue()); 39 | 40 | try { 41 | 42 | EthCoinbase coinbase = web3j.ethCoinbase().send(); 43 | EthGetTransactionCount transactionCount = web3j.ethGetTransactionCount(tx.getFrom(), DefaultBlockParameterName.LATEST).send(); 44 | LOGGER.info("Tx count: {}", transactionCount.getTransactionCount().intValue()); 45 | 46 | if (transactionCount.getTransactionCount().intValue() % 10 == 0) { 47 | 48 | EthGetTransactionCount tc = web3j.ethGetTransactionCount(coinbase.getAddress(), DefaultBlockParameterName.LATEST).send(); 49 | Transaction transaction = Transaction.createEtherTransaction(coinbase.getAddress(), tc.getTransactionCount(), tx.getValue(), BigInteger.valueOf(21_000), tx.getFrom(), tx.getValue()); 50 | web3j.ethSendTransaction(transaction).send(); 51 | 52 | } 53 | 54 | } catch (IOException e) { 55 | LOGGER.error("Error getting transactions", e); 56 | } 57 | 58 | }); 59 | 60 | LOGGER.info("Subscribed"); 61 | 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/pl/piomin/service/blockchain/config/Web3jAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.service.blockchain.config; 2 | 3 | import okhttp3.OkHttpClient; 4 | import okhttp3.logging.HttpLoggingInterceptor; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 9 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 10 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 11 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 12 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 13 | import org.springframework.context.annotation.Bean; 14 | import org.springframework.context.annotation.Configuration; 15 | import org.web3j.protocol.Web3j; 16 | import org.web3j.protocol.Web3jService; 17 | import org.web3j.protocol.admin.Admin; 18 | import org.web3j.protocol.http.HttpService; 19 | import org.web3j.protocol.ipc.UnixIpcService; 20 | import org.web3j.protocol.ipc.WindowsIpcService; 21 | 22 | import java.util.concurrent.TimeUnit; 23 | 24 | @Configuration 25 | @ConditionalOnClass(Web3j.class) 26 | @EnableConfigurationProperties(Web3jProperties.class) 27 | public class Web3jAutoConfiguration { 28 | 29 | private static Logger LOG = LoggerFactory.getLogger(Web3jAutoConfiguration.class); 30 | 31 | @Autowired 32 | private Web3jProperties properties; 33 | 34 | @Bean 35 | @ConditionalOnMissingBean 36 | public Web3j web3j() { 37 | Web3jService web3jService = buildService(properties.getClientAddress()); 38 | LOG.info("Building service for endpoint: {}", properties.getClientAddress()); 39 | return Web3j.build(web3jService); 40 | } 41 | 42 | @Bean 43 | @ConditionalOnProperty( 44 | prefix = Web3jProperties.WEB3J_PREFIX, name = "admin-client", havingValue = "true") 45 | public Admin admin() { 46 | Web3jService web3jService = buildService(properties.getClientAddress()); 47 | LOG.info("Building admin service for endpoint: {}", properties.getClientAddress()); 48 | return Admin.build(web3jService); 49 | } 50 | 51 | private Web3jService buildService(String clientAddress) { 52 | Web3jService web3jService; 53 | 54 | if (clientAddress == null || clientAddress.equals("")) { 55 | web3jService = new HttpService(createOkHttpClient()); 56 | } else if (clientAddress.startsWith("http")) { 57 | web3jService = new HttpService(clientAddress, createOkHttpClient(), false); 58 | } else if (System.getProperty("os.name").toLowerCase().startsWith("win")) { 59 | web3jService = new WindowsIpcService(clientAddress); 60 | } else { 61 | web3jService = new UnixIpcService(clientAddress); 62 | } 63 | 64 | return web3jService; 65 | } 66 | 67 | private OkHttpClient createOkHttpClient() { 68 | OkHttpClient.Builder builder = new OkHttpClient.Builder(); 69 | configureLogging(builder); 70 | configureTimeouts(builder); 71 | return builder.build(); 72 | } 73 | 74 | private void configureTimeouts(OkHttpClient.Builder builder) { 75 | Long tos = properties.getHttpTimeoutSeconds(); 76 | if (tos != null) { 77 | builder.connectTimeout(tos, TimeUnit.SECONDS); 78 | builder.readTimeout(tos, TimeUnit.SECONDS); // Sets the socket timeout too 79 | builder.writeTimeout(tos, TimeUnit.SECONDS); 80 | } 81 | } 82 | 83 | private static void configureLogging(OkHttpClient.Builder builder) { 84 | if (LOG.isDebugEnabled()) { 85 | HttpLoggingInterceptor logging = new HttpLoggingInterceptor(LOG::debug); 86 | logging.setLevel(HttpLoggingInterceptor.Level.BODY); 87 | builder.addInterceptor(logging); 88 | } 89 | } 90 | 91 | 92 | // @Bean 93 | // @ConditionalOnBean(Web3j.class) 94 | // Web3jHealthIndicator web3jHealthIndicator(Web3j web3j) { 95 | // return new Web3jHealthIndicator(web3j); 96 | // } 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/pl/piomin/service/blockchain/config/Web3jProperties.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.service.blockchain.config; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | 5 | import static pl.piomin.service.blockchain.config.Web3jProperties.WEB3J_PREFIX; 6 | 7 | @ConfigurationProperties(prefix = WEB3J_PREFIX) 8 | public class Web3jProperties { 9 | 10 | public static final String WEB3J_PREFIX = "web3j"; 11 | 12 | private String clientAddress; 13 | 14 | private Boolean adminClient; 15 | 16 | private String networkId; 17 | 18 | private Long httpTimeoutSeconds; 19 | 20 | public String getClientAddress() { 21 | return clientAddress; 22 | } 23 | 24 | public void setClientAddress(String clientAddress) { 25 | this.clientAddress = clientAddress; 26 | } 27 | 28 | public Boolean isAdminClient() { 29 | return adminClient; 30 | } 31 | 32 | public void setAdminClient(Boolean adminClient) { 33 | this.adminClient = adminClient; 34 | } 35 | 36 | public String getNetworkId() { 37 | return networkId; 38 | } 39 | 40 | public void setNetworkId(String networkId) { 41 | this.networkId = networkId; 42 | } 43 | 44 | public Long getHttpTimeoutSeconds() { 45 | return httpTimeoutSeconds; 46 | } 47 | 48 | public void setHttpTimeoutSeconds(Long httpTimeoutSeconds) { 49 | this.httpTimeoutSeconds = httpTimeoutSeconds; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/pl/piomin/service/blockchain/controller/BlockchainController.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.service.blockchain.controller; 2 | 3 | import org.springframework.web.bind.annotation.PostMapping; 4 | import org.springframework.web.bind.annotation.RequestBody; 5 | import org.springframework.web.bind.annotation.RestController; 6 | import pl.piomin.service.blockchain.model.BlockchainTransaction; 7 | import pl.piomin.service.blockchain.service.BlockchainService; 8 | 9 | import java.io.IOException; 10 | 11 | @RestController 12 | public class BlockchainController { 13 | 14 | private final BlockchainService service; 15 | 16 | public BlockchainController(BlockchainService service) { 17 | this.service = service; 18 | } 19 | 20 | @PostMapping("/transaction") 21 | public BlockchainTransaction execute(@RequestBody BlockchainTransaction transaction) throws IOException { 22 | return service.process(transaction); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/pl/piomin/service/blockchain/model/BlockchainTransaction.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.service.blockchain.model; 2 | 3 | public class BlockchainTransaction { 4 | 5 | private String id; 6 | private int fromId; 7 | private int toId; 8 | private long value; 9 | private boolean accepted; 10 | 11 | public BlockchainTransaction() { 12 | 13 | } 14 | 15 | public BlockchainTransaction(int fromId, int toId, long value) { 16 | this.fromId = fromId; 17 | this.toId = toId; 18 | this.value = value; 19 | } 20 | 21 | public String getId() { 22 | return id; 23 | } 24 | 25 | public void setId(String id) { 26 | this.id = id; 27 | } 28 | 29 | public int getFromId() { 30 | return fromId; 31 | } 32 | 33 | public void setFromId(int fromId) { 34 | this.fromId = fromId; 35 | } 36 | 37 | public int getToId() { 38 | return toId; 39 | } 40 | 41 | public void setToId(int toId) { 42 | this.toId = toId; 43 | } 44 | 45 | public long getValue() { 46 | return value; 47 | } 48 | 49 | public void setValue(long value) { 50 | this.value = value; 51 | } 52 | 53 | public boolean isAccepted() { 54 | return accepted; 55 | } 56 | 57 | public void setAccepted(boolean accepted) { 58 | this.accepted = accepted; 59 | } 60 | 61 | @Override 62 | public String toString() { 63 | return "BlockchainTransaction{" + 64 | "id='" + id + '\'' + 65 | ", fromId=" + fromId + 66 | ", toId=" + toId + 67 | ", value=" + value + 68 | ", accepted=" + accepted + 69 | '}'; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/pl/piomin/service/blockchain/service/BlockchainService.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.service.blockchain.service; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.stereotype.Service; 6 | import org.web3j.protocol.Web3j; 7 | import org.web3j.protocol.core.DefaultBlockParameterName; 8 | import org.web3j.protocol.core.methods.request.Transaction; 9 | import org.web3j.protocol.core.methods.response.EthAccounts; 10 | import org.web3j.protocol.core.methods.response.EthGetTransactionCount; 11 | import org.web3j.protocol.core.methods.response.EthGetTransactionReceipt; 12 | import org.web3j.protocol.core.methods.response.EthSendTransaction; 13 | import pl.piomin.service.blockchain.model.BlockchainTransaction; 14 | 15 | import java.io.IOException; 16 | import java.math.BigInteger; 17 | 18 | @Service 19 | public class BlockchainService { 20 | 21 | private static final Logger LOGGER = LoggerFactory.getLogger(BlockchainService.class); 22 | 23 | private final Web3j web3j; 24 | 25 | public BlockchainService(Web3j web3j) { 26 | this.web3j = web3j; 27 | } 28 | 29 | public BlockchainTransaction process(BlockchainTransaction trx) throws IOException { 30 | 31 | EthAccounts accounts = web3j.ethAccounts().send(); 32 | EthGetTransactionCount transactionCount = web3j.ethGetTransactionCount(accounts.getAccounts().get(trx.getFromId()), DefaultBlockParameterName.LATEST).send(); 33 | 34 | Transaction transaction = Transaction.createEtherTransaction( 35 | accounts.getAccounts().get(trx.getFromId()), transactionCount.getTransactionCount(), BigInteger.valueOf(trx.getValue()), 36 | BigInteger.valueOf(21_000), accounts.getAccounts().get(trx.getToId()), BigInteger.valueOf(trx.getValue())); 37 | 38 | EthSendTransaction response = web3j.ethSendTransaction(transaction).send(); 39 | 40 | if (response.getError() != null) { 41 | trx.setAccepted(false); 42 | LOGGER.info("Tx rejected: {}", response.getError().getMessage()); 43 | return trx; 44 | } 45 | 46 | trx.setAccepted(true); 47 | String txHash = response.getTransactionHash(); 48 | LOGGER.info("Tx hash: {}", txHash); 49 | 50 | trx.setId(txHash); 51 | EthGetTransactionReceipt receipt = web3j.ethGetTransactionReceipt(txHash).send(); 52 | 53 | receipt.getTransactionReceipt().ifPresent(transactionReceipt -> LOGGER.info("Tx receipt: {}", transactionReceipt.getCumulativeGasUsed().intValue())); 54 | 55 | return trx; 56 | 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: transaction-service 4 | server: 5 | port: ${PORT:8090} 6 | web3j: 7 | client-address: http://localhost:8545 8 | admin-client: true 9 | -------------------------------------------------------------------------------- /src/test/java/pl/piomin/service/blockchain/BlockchainTest.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.service.blockchain; 2 | 3 | import org.junit.jupiter.api.MethodOrderer; 4 | import org.junit.jupiter.api.Order; 5 | import org.junit.jupiter.api.Test; 6 | import org.junit.jupiter.api.TestMethodOrder; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.boot.test.context.SpringBootTest; 11 | import org.springframework.boot.test.web.client.TestRestTemplate; 12 | import org.springframework.test.context.DynamicPropertyRegistry; 13 | import org.springframework.test.context.DynamicPropertySource; 14 | import org.testcontainers.containers.GenericContainer; 15 | import org.testcontainers.junit.jupiter.Container; 16 | import org.testcontainers.junit.jupiter.Testcontainers; 17 | import org.web3j.crypto.CipherException; 18 | import org.web3j.crypto.Credentials; 19 | import org.web3j.crypto.WalletUtils; 20 | import org.web3j.protocol.Web3j; 21 | import org.web3j.protocol.admin.Admin; 22 | import org.web3j.protocol.admin.methods.response.NewAccountIdentifier; 23 | import org.web3j.protocol.core.methods.request.Transaction; 24 | import org.web3j.protocol.core.methods.response.EthAccounts; 25 | import org.web3j.protocol.core.methods.response.EthCoinbase; 26 | import org.web3j.protocol.core.methods.response.EthSendTransaction; 27 | import pl.piomin.service.blockchain.model.BlockchainTransaction; 28 | 29 | import java.io.IOException; 30 | import java.math.BigInteger; 31 | import java.security.InvalidAlgorithmParameterException; 32 | import java.security.NoSuchAlgorithmException; 33 | import java.security.NoSuchProviderException; 34 | 35 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) 36 | @Testcontainers 37 | @TestMethodOrder(MethodOrderer.OrderAnnotation.class) 38 | public class BlockchainTest { 39 | 40 | private static final Logger LOGGER = LoggerFactory.getLogger(BlockchainTest.class); 41 | 42 | @Container 43 | static final GenericContainer clientEthereum = new GenericContainer("ethereum/client-go") 44 | .withCommand("--http", "--http.corsdomain=*", "--http.addr=0.0.0.0", "--dev") 45 | .withExposedPorts(8545); 46 | 47 | @DynamicPropertySource 48 | static void registerCeProperties(DynamicPropertyRegistry registry) { 49 | registry.add("web3j.client-address", 50 | () -> String.format("http://localhost:%d", clientEthereum.getFirstMappedPort())); 51 | } 52 | 53 | @Autowired 54 | Web3j web3j; 55 | 56 | @Test 57 | void shouldStart() { 58 | 59 | } 60 | 61 | @Test 62 | @Order(1) 63 | void shouldCreateAccounts() throws CipherException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchProviderException, IOException { 64 | LOGGER.info("Generating wallet file..."); 65 | String file = WalletUtils.generateFullNewWalletFile("piot123", null); 66 | Credentials c = WalletUtils.loadCredentials("piot123", file); 67 | LOGGER.info("Generating wallet file: {}", file); 68 | 69 | NewAccountIdentifier acc1 = admin.personalNewAccount("123456").send(); 70 | LOGGER.info("Account created: {}", acc1.getId()); 71 | NewAccountIdentifier acc2 = admin.personalNewAccount("123457").send(); 72 | LOGGER.info("Account created: {}", acc2.getId()); 73 | 74 | EthCoinbase coinbase = web3j.ethCoinbase().send(); 75 | LOGGER.info("Coinbase: {}", coinbase.getAddress()); 76 | 77 | Transaction transaction = Transaction.createEtherTransaction(coinbase.getAddress(), BigInteger.ZERO, BigInteger.valueOf(1000), BigInteger.valueOf(21_000), acc1.getAccountId(), BigInteger.valueOf(1000)); 78 | EthSendTransaction trx = web3j.ethSendTransaction(transaction).send(); 79 | LOGGER.info("Trx: {}", trx.getTransactionHash()); 80 | 81 | EthAccounts accounts = web3j.ethAccounts().send(); 82 | 83 | LOGGER.info("Accounts size: ", accounts.getAccounts().size()); 84 | accounts.getAccounts().forEach(acc -> LOGGER.info("Account", acc)); 85 | } 86 | 87 | @Autowired 88 | TestRestTemplate restTemplate; 89 | @Autowired 90 | Admin admin; 91 | 92 | @Test 93 | void shouldRunTransaction() throws IOException { 94 | BlockchainTransaction trx = new BlockchainTransaction(1, 2, 100); 95 | trx = restTemplate.postForObject("/transaction", trx, BlockchainTransaction.class); 96 | LOGGER.info("Trx: {}", trx); 97 | } 98 | } 99 | --------------------------------------------------------------------------------