一次跑通 fabric 小汽車例子


一次跑通 fabric 小汽車例子
 
Docker 部分
docker --version # 檢測docker安裝
sudo systemctl start docker # 啟動守護程序
sudo usermod -a -G docker $USER # 添加當前用戶到docker用戶組
docker-compose --version # 檢測docker-compose安裝
上面沒有出現問題Docker部分准備完成
Golang 部分
go env # 查看golang環境變量
go env -w GOPROXY=https://goproxy.cn,direct # 更換go代理,也就是換國內源
一定要設置好GOPATH,GOROOT等相關變量。
Java 部分
本次sdk采用fabric-gateway-java v1.4,這個庫是fabric-java-sdk的高級庫
sudo dnf install maven 
cd /etc/maven
code settings.xml # 把146行內容替換成下面的內容,更換為阿里源
mvn -v # 檢測是否安裝成功
更換阿里源
 
  <mirrors>
    <!-- mirror
     | Specifies a repository mirror site to use instead of a given repository. The repository that
     | this mirror serves has an ID that matches the mirrorOf element of this mirror. IDs are used
     | for inheritance and direct lookup purposes, and must be unique across the set of mirrors.
     |
    <mirror>
      <id>mirrorId</id>
      <mirrorOf>repositoryId</mirrorOf>
      <name>Human Readable Name for this Mirror.</name>
      <url>http://my.repository.com/repo/path</url>
    </mirror>
     -->
     <mirror>
      <id>alimaven</id>
      <name>aliyun maven</name>
      <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
       <mirrorOf>central</mirrorOf>
    </mirror>
  </mirrors>

 fabric 安裝部分

cd ~
export mypn=ska # 這里設置一個環境變量,ska換成你自己的項目名 
mkdir -p src/solo-dev-env/$mypn  # 創建項目目錄
cd ./src/solo-dev-env/$mypn # 進入目錄,
curl -sSL https://bit.ly/2ysbOFE | bash # 使用最新發布版本 
cd ../../ tree ./solo-dev-env -L 3 docker images
這個過程會很漫長主要是docker鏡像安裝預計有12G
docker一定要用國內源,否則等到死
如果執行 網絡安裝錯誤可以執行下面步驟
 
# 如果出錯,復制附錄內容,在文章目錄,
#執行 code bootstrap.sh 把內容粘貼進去,然后保存,
#然后執行 sudo chmod +x ./bootstrap.sh 執行腳本 ./bootstrap.sh
# 完整如下
cd ~
export mypn=ska # 這里設置一個環境變量,ska換成你自己的項目名 
mkdir -p src/solo-dev-env/$mypn  # 創建項目目錄
cd ./src/solo-dev-env/$mypn # 進入目錄
# 上面是之前操作的,不用執行了,直接執行下面部分
code bootstrap.sh  # 復制附錄內容保存。
sudo chmod +x ./bootstrap.sh 
./bootstrap.sh
cd ../../ 
tree ./solo-dev-env -L 3 
docker images

 

完成后目錄和Docker鏡像
目錄
solo-dev-env/
└── ska
    ├── fabric-samples ---------------------------------> clone 的倉庫
    │   ├── bin ---------------------------------> 下載的二進制文件都在里面
    │   ├── chaincode
    │   ├── chaincode-docker-devmode
    │   ├── ci
    │   ├── CODE_OF_CONDUCT.md
    │   ├── CODEOWNERS
    │   ├── commercial-paper
    │   ├── config
    │   ├── CONTRIBUTING.md
    │   ├── fabcar ---------------------------------> 要跑的例子使用的是測試網         
    │   ├── first-network -----------------------------> 被淘汰的例子
    │   ├── high-throughput
    │   ├── interest_rate_swaps
    │   ├── LICENSE
    │   ├── MAINTAINERS.md
    │   ├── off_chain_data
    │   ├── README.md
    │   ├── SECURITY.md
    │   └── test-network  -----------------------------> 新出的測試網

 

Docker 鏡像
REPOSITORY TAG IMAGE ID CREATED SIZE
hyperledger/fabric-tools 2.1 bf6ec47ac5fd 8 days ago 522MB
hyperledger/fabric-tools 2.1.0 bf6ec47ac5fd 8 days ago 522MB
hyperledger/fabric-tools latest bf6ec47ac5fd 8 days ago 522MB
hyperledger/fabric-peer 2.1 4136ba27e279 8 days ago 56.6MB
hyperledger/fabric-peer 2.1.0 4136ba27e279 8 days ago 56.6MB
hyperledger/fabric-peer latest 4136ba27e279 8 days ago 56.6MB
hyperledger/fabric-orderer 2.1 af4fdf2df06c 8 days ago 39.4MB
hyperledger/fabric-orderer 2.1.0 af4fdf2df06c 8 days ago 39.4MB
hyperledger/fabric-orderer latest af4fdf2df06c 8 days ago 39.4MB
hyperledger/fabric-ccenv 2.1 eefa22c7b7e7 8 days ago 554MB
hyperledger/fabric-ccenv 2.1.0 eefa22c7b7e7 8 days ago 554MB
hyperledger/fabric-ccenv latest eefa22c7b7e7 8 days ago 554MB
hyperledger/fabric-baseos 2.1 52bb8d969801 8 days ago 6.94MB
hyperledger/fabric-baseos 2.1.0 52bb8d969801 8 days ago 6.94MB
hyperledger/fabric-baseos latest 52bb8d969801 8 days ago 6.94MB
hyperledger/fabric-nodeenv 2.1 8f2b5101881b 9 days ago 292MB
hyperledger/fabric-nodeenv 2.1.0 8f2b5101881b 9 days ago 292MB
hyperledger/fabric-nodeenv latest 8f2b5101881b 9 days ago 292MB
hyperledger/fabric-javaenv 2.1 3e83cbc5b332 10 days ago 505MB
hyperledger/fabric-javaenv 2.1.0 3e83cbc5b332 10 days ago 505MB
hyperledger/fabric-javaenv latest 3e83cbc5b332 10 days ago 505MB
hyperledger/fabric-ca 1.4 3b96a893c1e4 8 weeks ago 150MB
hyperledger/fabric-ca 1.4.6 3b96a893c1e4 8 weeks ago 150MB
hyperledger/fabric-ca latest 3b96a893c1e4 8 weeks ago 150MB
hyperledger/fabric-tools 2.0.0 639ab50feac9 2 months ago 514MB
hyperledger/fabric-zookeeper 0.4 ede9389347db 5 months ago 276MB
hyperledger/fabric-zookeeper 0.4.18 ede9389347db 5 months ago 276MB
hyperledger/fabric-zookeeper latest ede9389347db 5 months ago 276MB
hyperledger/fabric-kafka 0.4 caaae0474ef2 5 months ago 270MB
hyperledger/fabric-kafka 0.4.18 caaae0474ef2 5 months ago 270MB
hyperledger/fabric-kafka latest caaae0474ef2 5 months ago 270MB
hyperledger/fabric-couchdb 0.4 d369d4eaa0fd 5 months ago 261MB
hyperledger/fabric-couchdb 0.4.18 d369d4eaa0fd 5 months ago 261MB
hyperledger/fabric-couchdb latest d369d4eaa0fd 5 months ago 261MB
hyperledger/fabric-couchdb 0.4.16 5a66d69b7d4d 6 months ago 261MB

啟動測試網

 

cd ~/src/solo-dev-env/$mypn/fabric-samples/test-network/
./network.sh up # 啟動測試網絡
./network.sh createChannel # 創建通道,默認名字mychannel ./network.sh deployCC # 部署合約 ./network.sh down #清理網絡

 

 

 

上面應該不會出現什么問題,出現問題大多數是Dokcer權限和golang代理。

這些只是檢測這個測試環境已經搭建好了,下面准備fabcar

啟動fabcar


cd ~/src/solo-dev-env/$mypn/fabric-samples/fabcar
./startFabric.sh 

 

創建Java項目 這里每一行都是一個命令
cd ~
mkdir -p src/java/MVN/project # 創建java項目目錄
cd src/java/MVN/project
mkdir github.com # 創建github庫目錄
cd github.com
git clone -b release-1.4 https://github.com/hyperledger/fabric-gateway-java.git # clone 1.4 sdk
cd fabric-gateway-java
export ORG_HYPERLEDGER_FABRIC_SDK_CONNECTIONS_SSL_SSLPROVIDER=JDK # 設置SSL
mvn install # 安裝到本地倉庫,可以在本地倉庫 ~/.m2 中找到,安裝好后就可以 Ctrl + C,不執行Test
cd ../../

mvn archetype:generate # 創建mvn項目
7 # 模板版本
cn.edu.zzuli # 組ID
fabricIecTest # 項目名
1.0 # 版本號
cn.edu.zzuli # 包名

cd ./fabricIecTest
code pom.xml # 更改為下面內容
mvn clean
cd ./src/main/java/cn/edu/zzuli
code EnrollAdmin.java # 內容如下
code RegisterUser.java # 內容如下
code App.java # 內容如下
cd ../../../../../../../
code ./fabricIecTest # 執行這些代碼

 

pom.xml
 
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

<!-- 標明版本號 -->
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

<java.version>1.8</java.version>

<maven.compiler.source>1.8</maven.compiler.source>

<maven.compiler.target>1.8</maven.compiler.target>

</properties>

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!-- 添加依賴 -->
<dependency>
<groupId>org.hyperledger.fabric</groupId>
<artifactId>fabric-gateway-java</artifactId>
<version>1.4.2</version>
</dependency>

</dependencies>

 


 
EnrollAdmin.java

 

package cn.edu.zzuli;


import java.nio.file.Paths;
import java.util.Properties;

import org.hyperledger.fabric.gateway.Wallet;
import org.hyperledger.fabric.gateway.Wallet.Identity;
import org.hyperledger.fabric.sdk.Enrollment;
import org.hyperledger.fabric.sdk.security.CryptoSuite;
import org.hyperledger.fabric.sdk.security.CryptoSuiteFactory;
import org.hyperledger.fabric_ca.sdk.EnrollmentRequest;
import org.hyperledger.fabric_ca.sdk.HFCAClient;

public class EnrollAdmin {
static {
System.setProperty("org.hyperledger.fabric.sdk.service_discovery.as_localhost", "true");
}
public static void main(String[] args) throws Exception {

// Create a CA client for interacting with the CA.
Properties props = new Properties();
// 注意帶 $ 變量
props.put("pemFile",
"/home/$USER/src/solo-dev-env/$mypn/fabric-samples/test-network/organizations/peerOrganizations/org1.example.com/ca/ca.org1.example.com-cert.pem");
props.put("allowAllHostNames", "true");
HFCAClient caClient = HFCAClient.createNewInstance("https://localhost:7054", props);
CryptoSuite cryptoSuite = CryptoSuiteFactory.getDefault().getCryptoSuite();
caClient.setCryptoSuite(cryptoSuite);

// Create a wallet for managing identities
Wallet wallet = Wallet.createFileSystemWallet(Paths.get("wallet"));

// Check to see if we've already enrolled the admin user.
boolean adminExists = wallet.exists("admin");
if (adminExists) {
System.out.println("An identity for the admin user \"admin\" already exists in the wallet");
return;
}

// Enroll the admin user, and import the new identity into the wallet.
final EnrollmentRequest enrollmentRequestTLS = new EnrollmentRequest();
enrollmentRequestTLS.addHost("localhost");
enrollmentRequestTLS.setProfile("tls");
Enrollment enrollment = caClient.enroll("admin", "adminpw", enrollmentRequestTLS);
Identity user = Identity.createIdentity("Org1MSP", enrollment.getCert(), enrollment.getKey());
wallet.put("admin", user);
System.out.println("Successfully enrolled user \"admin\" and imported it into the wallet");
 
}
}

 

RegisterUser.java
package cn.edu.zzuli;

import java.nio.file.Paths;
import java.security.PrivateKey;
import java.util.Properties;
import java.util.Set;


import org.hyperledger.fabric.gateway.Wallet;
import org.hyperledger.fabric.gateway.Wallet.Identity;
import org.hyperledger.fabric.sdk.Enrollment;
import org.hyperledger.fabric.sdk.User;
import org.hyperledger.fabric.sdk.security.CryptoSuite;
import org.hyperledger.fabric.sdk.security.CryptoSuiteFactory;
import org.hyperledger.fabric_ca.sdk.HFCAClient;
import org.hyperledger.fabric_ca.sdk.RegistrationRequest;
public class RegisterUser {
static {
System.setProperty("org.hyperledger.fabric.sdk.service_discovery.as_localhost", "true");
}
 
public static void main(String[] args)throws Exception {
//創建用於與CA進行交互的CA客戶端。
Properties props = new Properties();
// 注意帶 $ 變量
props.put("pemFile",
"/home/$USER/src/solo-dev-env/$mypn/fabric-samples/test-network/organizations/peerOrganizations/org1.example.com/ca/ca.org1.example.com-cert.pem");
props.put("allowAllHostNames", "true");
HFCAClient caClient = HFCAClient.createNewInstance("https://localhost:7054", props);
CryptoSuite cryptoSuite = CryptoSuiteFactory.getDefault().getCryptoSuite();
caClient.setCryptoSuite(cryptoSuite);
// 創建一個用於管理身份的錢包
Wallet wallet = Wallet.createFileSystemWallet(Paths.get("wallet"));
// 檢查我們是否已經注冊了該用戶。
boolean userExists = wallet.exists("appUser");
if (userExists) {
System.out.println("An identity for the user \"appUser\" already exists in the wallet");
return;
}
userExists = wallet.exists("admin");
if (!userExists) {
System.out.println("\"admin\" needs to be enrolled and added to the wallet first");
return;
}
Identity adminIdentity = wallet.get("admin");
User admin = new User() {

@Override
public String getName() {
return "admin";
}

@Override
public Set<String> getRoles() {
return null;
}

@Override
public String getAccount() {
return null;
}

@Override
public String getAffiliation() {
return "org1.department1";
}

@Override
public Enrollment getEnrollment() {
return new Enrollment() {

@Override
public PrivateKey getKey() {
return adminIdentity.getPrivateKey();
}

@Override
public String getCert() {
return adminIdentity.getCertificate();
}
};
}

@Override
public String getMspId() {
return "Org1MSP";
}

};
// 注冊用戶,加入用戶,並將新身份導入錢包。
RegistrationRequest registrationRequest = new RegistrationRequest("appUser");
registrationRequest.setAffiliation("org1.department1");
registrationRequest.setEnrollmentID("appUser");
String enrollmentSecret = caClient.register(registrationRequest, admin);
Enrollment enrollment = caClient.enroll("appUser", enrollmentSecret);
Identity user = Identity.createIdentity("Org1MSP", enrollment.getCert(), enrollment.getKey());
wallet.put("appUser", user);
System.out.println("Successfully enrolled user \"appUser\" and imported it into the wallet");

}
}

 


 
App.java
package cn.edu.zzuli;


import java.nio.file.Path;
import java.nio.file.Paths;


import org.hyperledger.fabric.gateway.Contract;
import org.hyperledger.fabric.gateway.Gateway;
import org.hyperledger.fabric.gateway.Network;
import org.hyperledger.fabric.gateway.Wallet;
/**
* Hello world!
*
*/
public class App
{
static {
System.setProperty("org.hyperledger.fabric.sdk.service_discovery.as_localhost", "true");
}
public static void main( String[] args ) throws Exception
{
 
// Load a file system based wallet for managing identities.
Path walletPath = Paths.get("wallet");
Wallet wallet = Wallet.createFileSystemWallet(walletPath);
// 注意帶 $ 的變量
String yamlPath = "/home/$USER/src/solo-dev-env/$mypn/fabric-samples/test-network/organizations/peerOrganizations/org1.example.com/connection-org1.yaml";
Path networkConfigPath = Paths.get(yamlPath);
Gateway.Builder builder = Gateway.createBuilder();
builder.identity(wallet, "appUser").networkConfig(networkConfigPath).discovery(true);

// create a gateway connection
try (Gateway gateway = builder.connect()) {

// get the network and contract
Network network = gateway.getNetwork("mychannel");
Contract contract = network.getContract("fabcar");

byte[] result;

result = contract.evaluateTransaction("queryAllCars");
System.out.println(new String(result));

contract.submitTransaction("createCar", "CAR10", "VW", "Polo", "Grey", "Mary");

result = contract.evaluateTransaction("queryCar", "CAR10");
System.out.println(new String(result));

contract.submitTransaction("changeCarOwner", "CAR10", "Archie");

result = contract.evaluateTransaction("queryCar", "CAR10");
System.out.println(new String(result));
}

}
 
}

 附錄

bootstrap.sh

#!/bin/bash
#
# Copyright IBM Corp. All Rights Reserved.
#
# SPDX-License-Identifier: Apache-2.0
#

# if version not passed in, default to latest released version
VERSION=2.1.0
# if ca version not passed in, default to latest released version
CA_VERSION=1.4.6
# current version of thirdparty images (couchdb, kafka and zookeeper) released
THIRDPARTY_IMAGE_VERSION=0.4.18
ARCH=$(echo "$(uname -s|tr '[:upper:]' '[:lower:]'|sed 's/mingw64_nt.*/windows/')-$(uname -m | sed 's/x86_64/amd64/g')")
MARCH=$(uname -m)

printHelp() {
    echo "Usage: bootstrap.sh [version [ca_version [thirdparty_version]]] [options]"
    echo
    echo "options:"
    echo "-h : this help"
    echo "-d : bypass docker image download"
    echo "-s : bypass fabric-samples repo clone"
    echo "-b : bypass download of platform-specific binaries"
    echo
    echo "e.g. bootstrap.sh 2.1.0 1.4.6 0.4.18 -s"
    echo "would download docker images and binaries for Fabric v2.1.0 and Fabric CA v1.4.6"
}

# dockerPull() pulls docker images from fabric and chaincode repositories
# note, if a docker image doesn't exist for a requested release, it will simply
# be skipped, since this script doesn't terminate upon errors.

dockerPull() {
    #three_digit_image_tag is passed in, e.g. "1.4.6"
    three_digit_image_tag=$1
    shift
    #two_digit_image_tag is derived, e.g. "1.4", especially useful as a local tag for two digit references to most recent baseos, ccenv, javaenv, nodeenv patch releases
    two_digit_image_tag=$(echo $three_digit_image_tag | cut -d'.' -f1,2)
    while [[ $# -gt 0 ]]
    do
        image_name="$1"
        echo "====> hyperledger/fabric-$image_name:$three_digit_image_tag"
        docker pull "hyperledger/fabric-$image_name:$three_digit_image_tag"
        docker tag "hyperledger/fabric-$image_name:$three_digit_image_tag" "hyperledger/fabric-$image_name"
        docker tag "hyperledger/fabric-$image_name:$three_digit_image_tag" "hyperledger/fabric-$image_name:$two_digit_image_tag"
        shift
    done
}

cloneSamplesRepo() {
    # clone (if needed) hyperledger/fabric-samples and checkout corresponding
    # version to the binaries and docker images to be downloaded
    if [ -d first-network ]; then
        # if we are in the fabric-samples repo, checkout corresponding version
        echo "===> Checking out v${VERSION} of hyperledger/fabric-samples"
        git checkout v${VERSION}
    elif [ -d fabric-samples ]; then
        # if fabric-samples repo already cloned and in current directory,
        # cd fabric-samples and checkout corresponding version
        echo "===> Checking out v${VERSION} of hyperledger/fabric-samples"
        cd fabric-samples && git checkout v${VERSION}
    else
        echo "===> Cloning hyperledger/fabric-samples repo and checkout v${VERSION}"
        git clone -b master https://github.com/hyperledger/fabric-samples.git && cd fabric-samples && git checkout v${VERSION}
    fi
}

# This will download the .tar.gz
download() {
    local BINARY_FILE=$1
    local URL=$2
    echo "===> Downloading: " "${URL}"
    curl -L --retry 5 --retry-delay 3 "${URL}" | tar xz || rc=$?
    if [ -n "$rc" ]; then
        echo "==> There was an error downloading the binary file."
        return 22
    else
        echo "==> Done."
    fi
}

pullBinaries() {
    echo "===> Downloading version ${FABRIC_TAG} platform specific fabric binaries"
    download "${BINARY_FILE}" "https://github.com/hyperledger/fabric/releases/download/v${VERSION}/${BINARY_FILE}"
    if [ $? -eq 22 ]; then
        echo
        echo "------> ${FABRIC_TAG} platform specific fabric binary is not available to download <----"
        echo
        exit
    fi

    echo "===> Downloading version ${CA_TAG} platform specific fabric-ca-client binary"
    download "${CA_BINARY_FILE}" "https://github.com/hyperledger/fabric-ca/releases/download/v${CA_VERSION}/${CA_BINARY_FILE}"
    if [ $? -eq 22 ]; then
        echo
        echo "------> ${CA_TAG} fabric-ca-client binary is not available to download  (Available from 1.1.0-rc1) <----"
        echo
        exit
    fi
}

pullDockerImages() {
    command -v docker >& /dev/null
    NODOCKER=$?
    if [ "${NODOCKER}" == 0 ]; then
        FABRIC_IMAGES=(peer orderer ccenv tools)
        case "$VERSION" in
        1.*)
            FABRIC_IMAGES+=(javaenv)
            shift
            ;;
        2.*)
            FABRIC_IMAGES+=(nodeenv baseos javaenv)
            shift
            ;;
        esac
        echo "FABRIC_IMAGES:" "${FABRIC_IMAGES[@]}"
        echo "===> Pulling fabric Images"
        dockerPull "${FABRIC_TAG}" "${FABRIC_IMAGES[@]}"
        echo "===> Pulling fabric ca Image"
        CA_IMAGE=(ca)
        dockerPull "${CA_TAG}" "${CA_IMAGE[@]}"
        echo "===> Pulling thirdparty docker images"
        THIRDPARTY_IMAGES=(zookeeper kafka couchdb)
        dockerPull "${THIRDPARTY_TAG}" "${THIRDPARTY_IMAGES[@]}"
        echo
        echo "===> List out hyperledger docker images"
        docker images | grep hyperledger
    else
        echo "========================================================="
        echo "Docker not installed, bypassing download of Fabric images"
        echo "========================================================="
    fi
}

DOCKER=true
SAMPLES=true
BINARIES=true

# Parse commandline args pull out
# version and/or ca-version strings first
if [ -n "$1" ] && [ "${1:0:1}" != "-" ]; then
    VERSION=$1;shift
    if [ -n "$1" ]  && [ "${1:0:1}" != "-" ]; then
        CA_VERSION=$1;shift
        if [ -n  "$1" ] && [ "${1:0:1}" != "-" ]; then
            THIRDPARTY_IMAGE_VERSION=$1;shift
        fi
    fi
fi

# prior to 1.2.0 architecture was determined by uname -m
if [[ $VERSION =~ ^1\.[0-1]\.* ]]; then
    export FABRIC_TAG=${MARCH}-${VERSION}
    export CA_TAG=${MARCH}-${CA_VERSION}
    export THIRDPARTY_TAG=${MARCH}-${THIRDPARTY_IMAGE_VERSION}
else
    # starting with 1.2.0, multi-arch images will be default
    : "${CA_TAG:="$CA_VERSION"}"
    : "${FABRIC_TAG:="$VERSION"}"
    : "${THIRDPARTY_TAG:="$THIRDPARTY_IMAGE_VERSION"}"
fi

BINARY_FILE=hyperledger-fabric-${ARCH}-${VERSION}.tar.gz
CA_BINARY_FILE=hyperledger-fabric-ca-${ARCH}-${CA_VERSION}.tar.gz

# then parse opts
while getopts "h?dsb" opt; do
    case "$opt" in
        h|\?)
            printHelp
            exit 0
            ;;
        d)  DOCKER=false
            ;;
        s)  SAMPLES=false
            ;;
        b)  BINARIES=false
            ;;
    esac
done

if [ "$SAMPLES" == "true" ]; then
    echo
    echo "Clone hyperledger/fabric-samples repo"
    echo
    cloneSamplesRepo
fi
if [ "$BINARIES" == "true" ]; then
    echo
    echo "Pull Hyperledger Fabric binaries"
    echo
    pullBinaries
fi
if [ "$DOCKER" == "true" ]; then
    echo
    echo "Pull Hyperledger Fabric docker images"
    echo
    pullDockerImages
fi

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM