社区能源共享
功能描述
本 合约 以纽约实验性的能源微电网为例,作为一个简单的案例进行实现。
/*
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
创建新用户,并加入到能源微网中 invokebuyByAddress
向某一位用户购买一定量的电力 invokegetTransactionById
通过id获取交易内容 querygetTransactions
获取交易(如果交易数大于10,获取前10个) querygetHomes
获取用户(如果用户数大于10,获取前10个) querygetHomeByAddress
通过地址获取用户 querychangeStatus
某一位用户修改自身的状态 invokewriteUser
将新用户写入到键值对中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表示