社区能源共享

功能描述

合约 以纽约实验性的能源微电网为例,作为一个简单的案例进行实现。

/*
    author:swb
    emial:swbsin@163.com
    MIT License
*/

package main

import (
    "errors"
    "fmt"
    "strconv"
    "crypto/md5"
    "crypto/rand"
    "encoding/json"
    "encoding/base64"
    "encoding/hex"
    "io"
    "time"
    "github.com/hyperledger/fabric/core/chaincode/shim"
)

type SimpleChaincode struct {
}

var homeNo int = 0
var transactionNo int = 0

type Home struct {
    Address string
    Energy  int
    Money   int
    Id      int
    Status  int
    PriKey  string
    PubKey  string
}

type Transaction struct {
    BuyerAddress     string
    BuyerAddressSign string
    SellerAddress    string
    Energy           int
    Money            int
    Id               int
    Time             int64
}

func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
    if len(args) != 0 {
        return nil, errors.New("Incorrect number of arguments. Expecting 0")
    }

    fmt.Println("Init success!")

    return nil, nil
}

func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
    if function == "changeStatus" {
        if len(args) != 3 {
            return nil, errors.New("Incorrect number of arguments. Expecting 3")
        }
        return changeStatus(stub, args)
    } else if function == "buyByAddress" {
        if len(args) != 4 {
            return nil, errors.New("Incorrect number of arguments. Expecting 4")
        }
        return buyByAddress(stub, args)
    } else if function == "createUser" {
        if len(args) != 2 {
            return nil, errors.New("Incorrect number of arguments. Expecting 2")
        }
        return t.createUser(stub, args)
    }
    return nil, errors.New("Received unknown function invocation")
}

func (t *SimpleChaincode) Query(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
    if function == "getHomeByAddress" {
        if len(args) != 1 {
            return nil, errors.New("Incorrect number of arguments. Expecting 1")
        }
        _, homeBytes, err := getHomeByAddress(stub, args[0])
        if err != nil {
            fmt.Println("Error get home")
            return nil, err
        }
        return homeBytes, nil
    } else if function == "getHomes" {
        if len(args) != 0 {
            return nil, errors.New("Incorrect number of arguments. Expecting 0")
        }
        homes, err := getHomes(stub)
        if err != nil {
            fmt.Println("Error unmarshalling")
            return nil, err
        }
        homeBytes, err1 := json.Marshal(&homes)
        if err1 != nil {
            fmt.Println("Error marshalling banks")
        }
        return homeBytes, nil
    } else if function == "getTransactions" {
        if len(args) != 0 {
            return nil, errors.New("Incorrect number of arguments. Expecting 0")
        }
        transactions, err := getTransactions(stub)
        if err != nil {
            fmt.Println("Error unmarshalling")
            return nil, err
        }
        txBytes, err1 := json.Marshal(&transactions)
        if err1 != nil {
            fmt.Println("Error marshalling data")
        }
        return txBytes, nil
    } else if function == "getTransactionById" {
        if len(args) != 1 {
            return nil, errors.New("Incorrect number of arguments. Expecting 1")
        }
        _, txBytes, err := getTransactionById(stub, args[0])
        if err != nil {
            return nil, err
        }
        return txBytes, nil
    }
    return nil, errors.New("Received unknown function invocation")
}

func main() {
    err := shim.Start(new(SimpleChaincode))
    if err != nil {
        fmt.Printf("Error starting Simple chaincode: %s", err)
    }
}

// 生成 Address
func GetAddress() (string, string, string) {
    var address, priKey, pubKey string
    b := make([]byte, 48)

    if _, err := io.ReadFull(rand.Reader, b); err != nil {
        return "", "", ""
    }

    h := md5.New()
    h.Write([]byte(base64.URLEncoding.EncodeToString(b)))

    address = hex.EncodeToString(h.Sum(nil))
    priKey = address + "1"
    pubKey = address + "2"

    return address, priKey, pubKey
}

func (t *SimpleChaincode) createUser(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
    var energy, money int
    var err error
    var homeBytes []byte
    if len(args) != 2 {
        return nil, errors.New("Incorrect number of arguments. Expecting 2")
    }
    address, priKey, pubKey := GetAddress()
    energy, err = strconv.Atoi(args[0])
    if err != nil {
        return nil, errors.New("want Integer number")
    }
    money, err = strconv.Atoi(args[1])
    if err != nil {
        return nil, errors.New("want Integer number")
    }
    fmt.Printf("HomeInfo: address = %v, energy = %v, money = %v, homeNo = %v, priKey = %v, pubKey = %v\n", address, energy, money, homeNo, priKey, pubKey)
    home := Home{Address: address, Energy: energy, Money: money, Id: homeNo, Status: 1, PriKey: priKey, PubKey: pubKey}
    err = writeHome(stub, home)
    if err != nil {
        return nil, errors.New("write Error" + err.Error())
    }
    homeBytes, err = json.Marshal(&home)
    if err != nil {
        return nil, errors.New("Error retrieve")
    }
    homeNo = homeNo + 1
    fmt.Println("Create user success!")
    return homeBytes, nil
}

func buyByAddress(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
    if len(args) != 4 {
        return nil, errors.New("Incorrect number of arguments. Expecting 4")
    }
    homeSeller, _, err := getHomeByAddress(stub, args[0])
    homeBuyer, _, err := getHomeByAddress(stub, args[2])

    if args[1] != args[2]+"11" {
        return nil, errors.New("Verify sign data failed!")
    }

    buyValue, erro := strconv.Atoi(args[3])
    if erro != nil {
        return nil, errors.New("want integer number")
    }
    if homeSeller.Energy < buyValue && homeBuyer.Money < buyValue {
        return nil, errors.New("not enough money or energy")
    }

    fmt.Println("Before transaction:")
    fmt.Printf("    homeSeller.Energy = %d, homeSeller.Money = %d\n", homeSeller.Energy, homeSeller.Money)
    fmt.Printf("    homeBuyer.Energy = %d, homeBuyer.Money = %d\n", homeBuyer.Energy, homeBuyer.Money)

    homeSeller.Energy = homeSeller.Energy - buyValue
    homeSeller.Money = homeSeller.Money + buyValue
    homeBuyer.Energy = homeBuyer.Energy + buyValue
    homeBuyer.Money = homeBuyer.Money - buyValue

    fmt.Println("After transaction:")
    fmt.Printf("    homeSeller.Energy = %d, homeSeller.Money = %d\n", homeSeller.Energy, homeSeller.Money)
    fmt.Printf("    homeBuyer.Energy = %d, homeBuyer.Money = %d\n", homeBuyer.Energy, homeBuyer.Money)

    err = writeHome(stub, homeSeller)
    if err != nil {
        return nil, err
    }

    err = writeHome(stub, homeBuyer)
    if err != nil {
        return nil, err
    }

    fmt.Println("TransactionInfo:")
    fmt.Println("    BuyerAddress:", args[2])
    fmt.Println("    BuyerAddressSign:", args[1])
    fmt.Println("    SellerAddress:", args[0])
    fmt.Println("    Energy:", buyValue)
    fmt.Println("    Money:", buyValue)
    fmt.Println("    Id:", transactionNo)

    transaction := Transaction{BuyerAddress: args[2], BuyerAddressSign: args[1], SellerAddress: args[0], Energy: buyValue, Money: buyValue, Id: transactionNo, Time: time.Now().Unix()}
    err = writeTransaction(stub, transaction)
    if err != nil {
        return nil, err
    }
    transactionNo = transactionNo + 1
    txBytes, err := json.Marshal(&transaction)

    if err != nil {
        return nil, errors.New("Error retrieving schoolBytes")
    }

    return txBytes, nil
}

func changeStatus(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
    if len(args) != 3 {
        return nil, errors.New("Incorrect number of arguments. Expecting 3")
    }
    home, homeBytes, err := getHomeByAddress(stub, args[0])
    if err != nil {
        return nil, err
    }

    if args[1] == args[0]+"11" {
        status, _ := strconv.Atoi(args[2])
        home.Status = status
        err = writeHome(stub, home)
        if err != nil {
            return homeBytes, nil
        }
    }
    return nil, err
}

func getHomeByAddress(stub shim.ChaincodeStubInterface, address string) (Home, []byte, error) {
    var home Home
    homeBytes, err := stub.GetState(address)
    if err != nil {
        fmt.Println("Error retrieving home")
    }
    err = json.Unmarshal(homeBytes, &home)
    if err != nil {
        fmt.Println("Error unmarshalling home")
    }
    return home, homeBytes, nil
}

func getHomes(stub shim.ChaincodeStubInterface) ([]Home, error) {
    var homes []Home
    var number string
    var err error
    var home Home
    if homeNo <= 10 {
        i := 0
        for i < homeNo {
            number = strconv.Itoa(i)
            home, _, err = getHomeById(stub, number)
            if err != nil {
                return nil, errors.New("Error get detail")
            }
            homes = append(homes, home)
            i = i + 1
        }
    } else {
        i := 0
        for i < 10 {
            number = strconv.Itoa(i)
            home, _, err = getHomeById(stub, number)
            if err != nil {
                return nil, errors.New("Error get detail")
            }
            homes = append(homes, home)
            i = i + 1
        }
        return homes, nil
    }
    return nil, nil
}

func getHomeById(stub shim.ChaincodeStubInterface, id string) (Home, []byte, error) {
    var home Home
    // Need to be completed

    return home, nil, nil
}

func getTransactionById(stub shim.ChaincodeStubInterface, id string) (Transaction, []byte, error) {
    var transaction Transaction
    txBytes, err := stub.GetState("transaction" + id)
    if err != nil {
        fmt.Println("Error retrieving home")
    }

    err = json.Unmarshal(txBytes, &transaction)
    if err != nil {
        fmt.Println("Error unmarshalling home")
    }

    return transaction, txBytes, nil
}

func getTransactions(stub shim.ChaincodeStubInterface) ([]Transaction, error) {
    var transactions []Transaction
    var number string
    var err error
    var transaction Transaction
    if transactionNo <= 10 {
        i := 0
        for i < transactionNo {
            number = strconv.Itoa(i)
            transaction, _, err = getTransactionById(stub, number)
            if err != nil {
                return nil, errors.New("Error get detail")
            }
            transactions = append(transactions, transaction)
            i = i + 1
        }
        return transactions, nil
    } else {
        i := 0
        for i < 10 {
            number = strconv.Itoa(i)
            transaction, _, err = getTransactionById(stub, number)
            if err != nil {
                return nil, errors.New("Error get detail")
            }
            transactions = append(transactions, transaction)
            i = i + 1
        }
        return transactions, nil
    }
    return nil, nil
}

func writeHome(stub shim.ChaincodeStubInterface, home Home) (error) {
    homeBytes, err := json.Marshal(&home)
    if err != nil {
        return errors.New("Marshalling Error" + err.Error())
    }
    err = stub.PutState(home.Address, homeBytes)
    if err != nil {
        return errors.New("PutState Error" + err.Error())
    }
    return nil
}

func writeTransaction(stub shim.ChaincodeStubInterface, transaction Transaction) (error) {
    txBytes, err := json.Marshal(&transaction)
    if err != nil {
        return errors.New("Marshalling Error" + err.Error())
    }
    id := strconv.Itoa(transaction.Id)
    err = stub.PutState("transaction"+id, txBytes)
    if err != nil {
        return errors.New("PutState Error" + err.Error())
    }
    return nil
}

“在总统大道的一边,五户家庭通过太阳能板发电;在街道的另一边的五户家庭可以购买对面家庭不需要的电力。而连接这项交易的就是区块链网络,几乎不需要人员参与就可以管理记录交易。”但是这个想法是非常有潜力的,能够代表未来社区管理能源系统。”

布鲁克林微电网开发商 LO3 创始人 Lawrence Orsini 说:

“我们正在这条街道上建立一个可再生电力市场,来测试人们对于购买彼此手中的电力是否感兴趣。如果你在很远的地方生产能源,运输途中会有很多损耗,你也得不到这电力价值。但是如果你就在街对面,你就能高效的利用能源。”

在某一块区域内存在一个能源微电网,每一户家庭可能为生产者也可能为消费者。部分家庭拥有太阳能电池板,太阳能电池板的剩余电量为可以售出的电力的值,为了简化,单位为1.需要电力的家庭可以向有足够余额的电力的家庭购买电力。

账户私钥应该由安装在本地的客户端生成,本例中为了简便,使用模拟私钥和公钥。每位用户的私钥为guid+“1”,公钥为guid+“2”。签名方式简化为私钥+”1”

数据结构设计

在该智能合约中暂时只有一种角色,为每一户家庭用户。

  • 家庭用户
    • 账户地址
    • 剩余能量 //部分家庭没有太阳能电池板,值为0
    • 账户余额(电子货币)
    • 编号
    • 状态 //0:不可购买, 1:可以购买
    • 账户公钥
    • 账户私钥
  • 交易(一笔交易必须同时具有卖方和买方的公钥签名,方能承认这笔交易。公钥签名生成规则,公钥+待创建交易的ID号,在本交易类型中,只要买家有足够的货币,卖家自动会对交易进行签名)
    • 购买方地址
    • 销售方地址
    • 电量销售量
    • 电量交易金额
    • 编号
    • 交易时间

function及各自实现的功能

  • init 初始化操作

  • invoke 调用合约内部的函数

  • query 查询相关的信息

  • createUser 创建新用户,并加入到能源微网中 invoke

  • buyByAddress 向某一位用户购买一定量的电力 invoke

  • getTransactionById 通过id获取交易内容 query

  • getTransactions 获取交易(如果交易数大于10,获取前10个) query

  • getHomes 获取用户(如果用户数大于10,获取前10个) query

  • getHomeByAddress 通过地址获取用户 query

  • changeStatus 某一位用户修改自身的状态 invoke

  • writeUser 将新用户写入到键值对中

  • writeTransaction 记录交易

    接口设计

    createUser

request参数:

args[0] 剩余能量值 
args[1] 剩余金额

response参数:

新建家庭用户的json表示

buyByAddress

request参数:

args[0] 卖家的账户地址
args[1] 买家签名
args[2] 买家的账户地址
args[3] 想要购买的电量数值

response参数:

购买成功的话返回该transaction的json串。
购买失败返回error

getTransactionById

request参数:

args[0] 交易编号

response参数:

查询结果的transaction 交易表示

getTransactions

request参数:

none

response参数:

获取所有的交易列表(如果交易大于10,则返回前10个)

getHomeByAddress

request参数

args[0] address

response参数

用户信息的json表示

getHomes

response参数

获取所有的用户列表(如果用户个数大于10,则返回前10个)

changeStatus

request参数:

args[0] 账户地址
args[1] 账户签名
args[2] 对自己的账户进行的操作,0:设置为不可购买  1:设置状态为可购买

response参数:

修改后的用户信息json表示

测试

最后编辑: kuteng  文档更新时间: 2023-05-04 16:28   作者:kuteng