example02

package example02;

import org.hyperledger.fabric.contract.Context;
import org.hyperledger.fabric.contract.ContractInterface;
import org.hyperledger.fabric.contract.annotation.*;
import org.hyperledger.fabric.shim.ChaincodeException;
import org.hyperledger.fabric.shim.ChaincodeStub;

/**
 * Class: MyContract
 */
@Contract(
        name = "example02.MyContract",
        info = @Info(
                title = "MyContract",
                description = "SmartContract Example 02 - Blockchain Workshop",
                version = "1.0.0",
                license = @License(
                        name = "Apache 2.0 License",
                        url = "http://www.apache.org/licenses/LICENSE-2.0.html"),
                contact = @Contact(
                        email = "23227732@qq.com",
                        name = "Bing"
                )
        )
)
@Default
public final class MyContract implements ContractInterface {
    enum Message {
        UNKNOWN_ERROR("chaincode failed with unknown reason."),
        FUNC_NOT_SUPPORT("function name '%s' is not support."),
        ARG_NUM_WRONG("Incorrect number of arguments. (Expecting %d)"),
        ACCOUNT_NOT_EXISTING("Account '%s' does not exist."),
        NO_ENOUGH_BALANCE("There is no enough balance to transfer in account '%s'."),
        BALANCE_INVALID("Account balance is invalid. ('%s': %s)");
    
        private String tmpl;
    
        private Message(String tmpl) {
            this.tmpl = tmpl;
        }
    
        public String template() {
            return this.tmpl;
        }
    }

    /**
     * Initialize Ledger
     * @param ctx context
     */
    @Transaction(name = "Init", intent = Transaction.TYPE.SUBMIT)
    public void init(final Context ctx, final String keyA, final String valueA, final String keyB, final String valueB) {
        ChaincodeStub stub = ctx.getStub();
        try {
            Integer.valueOf(valueA);
        } catch (Exception e) {
            String errorMessage = String.format(Message.BALANCE_INVALID.template(), keyA, valueA);
            System.out.println(errorMessage);
            throw new ChaincodeException(errorMessage, e);
        }

        try {
            Integer.valueOf(valueB);
        } catch (Exception e) {
            String errorMessage = String.format(Message.BALANCE_INVALID.template(), keyB, valueB);
            System.out.println(errorMessage);
            throw new ChaincodeException(errorMessage, e);
        }

        // init account A
        stub.putStringState(keyA, valueA);
        // init account B
        stub.putStringState(keyB, valueB);
    }

    /**
     * Query Account
     * @param ctx context
     * @return name state in ledger
     */
    @Transaction(name = "Query", intent = Transaction.TYPE.EVALUATE)
    public String query(final Context ctx, final String key) {
        ChaincodeStub stub = ctx.getStub();
        String valueA = stub.getStringState(key);

        // account not existing
        if (valueA.isEmpty()) {
            String errorMessage = String.format(Message.ACCOUNT_NOT_EXISTING.template(), key);
            System.out.println(errorMessage);
            throw new ChaincodeException(errorMessage);
        }

        return valueA;
    }

    /**
     * Transfer Amount
     * @param ctx context
     */
    @Transaction(name = "Transfer", intent = Transaction.TYPE.SUBMIT)
    public void transfer(final Context ctx, final String keyFrom, final String keyTo, final String valueTrans) {
        ChaincodeStub stub = ctx.getStub();
        String valueA = stub.getStringState(keyFrom);
        String valueB = stub.getStringState(keyTo);
        int intValueA = Integer.parseInt(valueA);
        int intValueB = Integer.parseInt(valueB);
        int intValueTrans = Integer.parseInt(valueTrans);
        if (intValueA < intValueTrans) {
            String errorMessage = String.format(Message.NO_ENOUGH_BALANCE.template(), keyFrom);
            throw new ChaincodeException(errorMessage);
        }
        intValueA = intValueA - intValueTrans;
        stub.putStringState(keyFrom, String.valueOf(intValueA));
        intValueB =  intValueB + intValueTrans;
        stub.putStringState(keyTo, String.valueOf(intValueB));
    }

    @Transaction(name = "Charge", intent = Transaction.TYPE.SUBMIT)
    public void charge(final Context ctx, final String key, final String chargeValue){
        ChaincodeStub stub = ctx.getStub();
        String value = stub.getStringState(key);

        // account not existing
        if (value.isEmpty()) {
            String errorMessage = String.format(Message.ACCOUNT_NOT_EXISTING.template(), key);
            System.out.println(errorMessage);
            throw new ChaincodeException(errorMessage);
        }

        int intValue = Integer.parseInt(value);

        // value not valid
        int intChargeValue = Integer.parseInt(chargeValue);
        if (intChargeValue<0){
            String errorMessage = String.format(Message.ARG_NUM_WRONG.template(), intChargeValue);
            throw new ChaincodeException(errorMessage);
        }

        intValue = intValue + intChargeValue;
        stub.putStringState(key, String.valueOf(intValue));
    }
}

test:

package example02;

import org.hyperledger.fabric.contract.Context;
import org.hyperledger.fabric.shim.ChaincodeException;
import org.hyperledger.fabric.shim.ChaincodeStub;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.ThrowableAssert.catchThrowable;

import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import org.mockito.InOrder;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

public class MyContractTest {
    @Nested
    class InvokeInitTransaction {
        @Test
        public void initLedger() {
            MyContract contract = new MyContract();
            Context ctx = mock(Context.class);
            ChaincodeStub stub = mock(ChaincodeStub.class);
            when(ctx.getStub()).thenReturn(stub);

            contract.init(ctx, "A", "100", "B", "200");

            InOrder inOrder = inOrder(stub);
            inOrder.verify(stub).putStringState("A", "100");
            inOrder.verify(stub).putStringState("B", "200");
        }

        @Test
        public void whenBalanceANotValid() {
            MyContract contract = new MyContract();
            Context ctx = mock(Context.class);
            ChaincodeStub stub = mock(ChaincodeStub.class);
            when(ctx.getStub()).thenReturn(stub);

            Throwable thrown = catchThrowable(() -> {
                contract.init(ctx, "A", "100A", "B", "200");
            });

            assertThat(thrown)
                .isInstanceOf(ChaincodeException.class)
                .hasMessage(String.format(MyContract.Message.BALANCE_INVALID.template(), "A", "100A"));
        }
    }

    @Nested
    class InvokeTransferTransaction {
        @Test
        public void whenBalanceEnough() {
            MyContract contract = new MyContract();
            Context ctx = mock(Context.class);
            ChaincodeStub stub = mock(ChaincodeStub.class);

            when(ctx.getStub()).thenReturn(stub);
            when(stub.getStringState("A")).thenReturn("100");
            when(stub.getStringState("B")).thenReturn("100");

            Throwable thrown = catchThrowable(() -> {
                contract.transfer(ctx, "A", "B", "15");
            });

            assertThat(thrown).isNull();
        }

        @Test
        public void whenBalanceNotEnough() {
            MyContract contract = new MyContract();
            Context ctx = mock(Context.class);
            ChaincodeStub stub = mock(ChaincodeStub.class);

            when(ctx.getStub()).thenReturn(stub);
            when(stub.getStringState("A")).thenReturn("100");
            when(stub.getStringState("B")).thenReturn("100");

            Throwable thrown = catchThrowable(() -> {
                contract.transfer(ctx, "A", "B", "150");
            });

            assertThat(thrown)
                .isInstanceOf(ChaincodeException.class)
                .hasNoCause()
                .hasMessage(String.format(MyContract.Message.NO_ENOUGH_BALANCE.template(), "A"));
        }
    }

    @Nested
    class InvokeQueryTransaction {
        @Test
        public void whenAccountNotExists() {
            MyContract contract = new MyContract();
            Context ctx = mock(Context.class);
            ChaincodeStub stub = mock(ChaincodeStub.class);
            when(ctx.getStub()).thenReturn(stub);
            when(stub.getStringState("A")).thenReturn("");

            Throwable thrown = catchThrowable(() -> {
                contract.query(ctx, "A");
            });

            assertThat(thrown)
                .isInstanceOf(ChaincodeException.class)
                .hasNoCause()
                .hasMessage(String.format(MyContract.Message.ACCOUNT_NOT_EXISTING.template(), "A"));
        }

        @Test
        public void whenAccountExists() {
            MyContract contract = new MyContract();
            Context ctx = mock(Context.class);
            ChaincodeStub stub = mock(ChaincodeStub.class);
            when(ctx.getStub()).thenReturn(stub);
            when(stub.getStringState("A")).thenReturn("100");
            assertThat(contract.query(ctx, "A")).isEqualTo("100");
        }
    }

    @Nested
    class InvokeChargeTransaction {
        @Test
        public void whenAccountNotExists() {
            MyContract contract = new MyContract();
            Context ctx = mock(Context.class);
            ChaincodeStub stub = mock(ChaincodeStub.class);
            when(ctx.getStub()).thenReturn(stub);

            when(stub.getStringState("A")).thenReturn("");

            Throwable thrown = catchThrowable(() -> {
                contract.charge(ctx,"A","10");
            });

            assertThat(thrown)
                    .isInstanceOf(ChaincodeException.class)
                    .hasNoCause()
                    .hasMessage(String.format(MyContract.Message.ACCOUNT_NOT_EXISTING.template(), "A"));
        }

        @Test
        public void whenValueNotValid() {
            MyContract contract = new MyContract();
            Context ctx = mock(Context.class);
            ChaincodeStub stub = mock(ChaincodeStub.class);
            when(ctx.getStub()).thenReturn(stub);

            when(stub.getStringState("A")).thenReturn("100");

            Throwable thrown = catchThrowable(() -> {
                contract.charge(ctx,"A","-10");
            });

            assertThat(thrown)
                    .isInstanceOf(ChaincodeException.class)
                    .hasNoCause()
                    .hasMessage(String.format(MyContract.Message.ARG_NUM_WRONG.template(), -10));
        }

        @Test
        public void whenAccountExistsAndValueValid() {
            MyContract contract = new MyContract();
            Context ctx = mock(Context.class);
            ChaincodeStub stub = mock(ChaincodeStub.class);
            when(ctx.getStub()).thenReturn(stub);

            when(stub.getStringState("A")).thenReturn("100");

            Throwable thrown = catchThrowable(() -> {
                contract.charge(ctx,"A","10");
            });

            assertThat(thrown).isNull();
        }
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值