package main
import (
"github.com/hyperledger/fabric/core/chaincode/shim"
"fmt"
pb "github.com/hyperledger/fabric/protos/peer"
"encoding/pem"
"crypto/x509"
"bytes"
"crypto/sha1"
"crypto/rand"
"crypto/rsa"
"errors"
"encoding/json"
"strconv"
"time"
"encoding/base64"
"crypto/aes"
"crypto/cipher"
"encoding/hex"
"strings"
)
type Trans struct { // 交易记录(交易主)
Name string `json:"name"` // account 使用 name + from + nonce 锁定一条交易历史记录
From string `json:"from"`
To string `json:"to"`
Value int `json:"value"`
Nonce int `json:"nonce"`
}
type Account struct {
Balance map[string]int `json:"tokens"` // 记录该账户下每个代币的余额
Allowance map[string]map[string]int `json:"allowance"` // 记录账户下每种代币中账户的代理额度
//Locked map[string]bool `json:"locked"` // 该账户在哪个代币被锁定
Nonce int `json:"nonce"` // 防止重放攻击,该nonce在trans和tokens只能锁定一条记录
Time time.Time `json:"time"` // 用于快速定位未花费的交易输出
}
type Data struct { // 最终存储的账户数据结构(主)
Account Account `json:"account"`
PreHash string `json:"preHash"`
Hash string `json:"hash"`
}
type Tokens struct { // 存储的代币信息(副)
Name string `json:"name"` // 代币名称
Supply int `json:"supply"` // 发行总量
Owner string `json:"owner"` // 所有人(受益人,发行人)
Lock bool `json:"lock"` // 代币状态
Nonce int `json:"nonce"` // 账户 nonce
AccountLock map[string]bool `json:"accountLock"`
}
type TokenChaincode struct {
}
var log *shim.ChaincodeLogger
func main() {
err := shim.Start(new(TokenChaincode))
if err != nil {
fmt.Errorf(err.Error())
}
}
func (t TokenChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
return shim.Success(SUCCESS)
}
func (t TokenChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
// 所有读的操作,按需提供参数
// 所有写的操作,因为携带秘钥,必须解析出来
function, args := stub.GetFunctionAndParameters()
log = getLogger(stub)
log.Infof("args: %s", args)
if function == "" {
return shim.Error("no function err")
} else if function == "balance" { // 读 获取余额 success
return t.Balance(stub, args)
} else if function == "allowance" { // 读 获取代理额度 success
return t.Allowance(stub, args)
} else if function == "getAccount" { // 读 获取账户信息 success
return t.GetAccount(stub, args)
} else if function == "getNonce" { // 读 获取账户nonce success
return t.GetNonce(stub, args)
} else if function == "getAccountWithSecret" { // 带秘钥读,可用于登录 获取账户信息 success
return t.GetAccountWithSecret(stub, args)
} else if function == "getTransRecord" { // 读 查询指定账户在合约内的交易记录 success
return t.GetTransRecord(stub, args)
} else if function == "newAccount" { // 写 新建账户 success
return t.NewAccount(stub, args)
} else if function == "newToken" { // 写 新建代币 success
return t.NewToken(stub, args)
} else if function == "transfer" { // 写 发起转账 success
return t.Transfer(stub, args)
} else if function == "approve" { // 写 授权 success
return t.Approve(stub, args)
} else if function == "transferFrom" { // 写 发起代理转账 success
return t.TransferFrom(stub, args)
} else if function == "lockToken" { // 写 锁定代币 success
return t.LockToken(stub, args)
} else if function == "unLockToken" { // 写 解除代币锁定 success
return t.UnLockToken(stub, args)
} else if function == "lockAccount" { // 写 锁定代币账户 success
return t.LockAccount(stub, args)
} else if function == "unLockAccount" { // 写 解锁代币账户 success
return t.UnlockAccount(stub, args)
} else if function == "mint" { // 写 增发代币 success
return t.Mint(stub, args)
} else if function == "burn" { // 写 回收代币 success
return t.Burn(stub, args)
}
// 总的账户不能锁定,没有自己的秘钥,无法操作自己的数据,如果用户锁定了自己的账户,那么说明他也可以解锁
// 如果秘钥泄露了,你锁住了,那肯定也能解锁
log.Errorf("%s was not found", function)
return shim.Error(fmt.Sprintf("%s was not found", function))
}
// 前端传递的数据
type Message struct { // 修改接受方式,不用对象接受
Key string `json:"key"` // 交易发起人的key(类似于普通用户的用户名,限制格式)
Data string `json:"data"` // 加密的交易参数
Sign string `json:"sign"` // 被加密的秘钥,防止被其它方式获取,链上数据不记录该秘钥,秘钥忘记则数据无法修改
}
// success
func (t TokenChaincode) NewAccount(stub shim.ChaincodeStubInterface, args []string) pb.Response {
if len(args) != 2 {
return shim.Error(parameterErr.Error())
}
// 解出秘钥
message := &Message{
Key: args[0],
Sign: args[1],
}
if message.Key == "" || message.Sign == "" {
return shim.Error(parameterErr.Error())
}
sign, err := base64.StdEncoding.DecodeString(message.Sign)
if err != nil {
return shim.Error(err.Error())
}
secret, err := PrivateDecrypt(sign, PRIVATE)
len := len(string(secret))
if len < 6 || len > 16 {
return shim.Error("secret length error")
}
if err != nil {
return shim.Error(err.Error())
}
// 判断该key是否有数据绑定(是否重复)
account, _ := getAccount(stub, message.Key)
if account != nil {
return shim.Error("key already exists")
}
// 当新增账户时,创建一条空的账户数据
account = &Account{
Balance: make(map[string]int),
Allowance: make(map[string]map[string]int),
//Locked: make(map[string]bool),
Nonce: 0,
Time: time.Now(),
}
err = setAccount(stub, message.Key, account, secret)
if err != nil {
return shim.Error(err.Error())
}
return shim.Success([]byte(message.Key))
}
// success
func (t TokenChaincode) NewToken(stub shim.ChaincodeStubInterface, args []string) pb.Response {
// 新建代币
// 解析请求信息
message, tokens, secret, err := messageToTokens(stub, args)
if err != nil {
return shim.Error(err.Error())
}
// 获取账户信息(连带UTXO一起统计)
account, err := getTransTo(stub, message.Key)
if err != nil {
return shim.Error(err.Error())
}
if account == nil {
return shim.Error("account is nil, please check your key")
}
// 判断nonce,防止重放攻击
if tokens.Nonce-account.Nonce != 1 {
return shim.Error(nonceError.Error())
}
// 如果下面的逻辑写入成功,递增nonce
account.Nonce += 1
// 拿到账户信息之后,新建一个token信息
// 查看token名称是否已存在
token, _ := getToken(stub, tokens.Name)
if token != nil {
return shim.Error("token name already exist")
}
token = &Tokens{
Name: tokens.Name,
Supply: tokens.Supply,
Owner: message.Key,
Lock: false,
Nonce: account.Nonce,
AccountLock: make(map[string]bool),
}
// 创建完成后,增加账户的拥有记录
account.Balance[token.Name] = token.Supply
// 改变时间戳
account.Time = time.Now()
// 写入数据
err = setToken(stub, token)
if err != nil {
return shim.Error(err.Error())
}
err = setAccount(stub, message.Key, account, secret)
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(SUCCESS)
}
// success
func (t TokenChaincode) Transfer(stub shim.ChaincodeStubInterface, args []string) pb.Response {
// 解析请求数据
message, trans, secret, err := messageToTrans(stub, args)
if err != nil {
return shim.Error(err.Error())
}
// 如果交易数额小于0,视为违规交易
if trans.Value < 0 {
return shim.Error(valueErr.Error())
}
// 当给自己转账时,会将UTXO数据写在历史的time中,然后修改自己的账户数据,会导致汇总UTXO时,汇总结果不正确
if trans.From == trans.To {
return shim.Error("you can't transfer tokens to yourself")
}
// 获取账户信息(UTXO)
accountFrom, err := getTransTo(stub, trans.From)
// 验证nonce
if trans.Nonce-accountFrom.Nonce != 1 {
return shim.Error(nonceError.Error())
}
// 验证余额
if accountFrom.Balance[trans.Name] < trans.Value {
return shim.Error(balanceErr.Error())
}
// 检查合约是否被锁定
err = checkTokenState(stub, trans.Name, message.Key, trans.From, trans.To)
if err != nil {
return shim.Error(err.Error())
}
// 递增nonce,只递增from的nonce
accountFrom.Nonce += 1
// 修改时间戳
accountFrom.Time = time.Now()
// 修改数据
accountFrom.Balance[trans.Name] -= trans.Value
// 写入数据(会自动写入UTXO中)
err = setTrans(stub, message.Key, trans)
if err != nil {
return shim.Error(err.Error())
}
// 此处由于无法读取用户的最新状态数据,所以当自己给自己转账时,会出现统计异常问题
err = setAccount(stub, message.Key, accountFrom, secret)
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(SUCCESS)
}
// success
func (t TokenChaincode) Approve(stub shim.ChaincodeStubInterface, args []string) pb.Response {
// 解析数据
message, trans, secret, err := messageToTrans(stub, args)
if err != nil {
return shim.Error(err.Error())
}
// 获取账户统计信息(所有)
account, err := getTransTo(stub, message.Key)
if err != nil {
return shim.Error(err.Error())
}
if trans.Value < 0 {
return shim.Error(valueErr.Error())
}
// 判断合约以及账户锁定
err = checkTokenState(stub, trans.Name, message.Key)
if err != nil {
return shim.Error(err.Error())
}
// 验证nonce
if trans.Nonce - account.Nonce != 1 {
return shim.Error(nonceError.Error())
}
// 验证完成后叠加nonce
account.Nonce += 1
// 修改时间
account.Time = time.Now()
// 授权操作(添加授权记录,当被授权用户下一次操作时,写入自己的账户信息中)
err = setApprove(stub, trans)
if err != nil {
return shim.Error(err.Error())
}
// 写数据
err = setAccount(stub, message.Key, account, secret)
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(SUCCESS)
}
func (t TokenChaincode) TransferFrom(stub shim.ChaincodeStubInterface, args []string) pb.Response {
// 解析参数
message, trans, secret, err := messageToTrans(stub, args)
if err != nil {
return shim.Error(err.Error())
}
// 获取账户信息
account, err := getTransTo(stub, message.Key)
if err != nil {
return shim.Error(err.Error())
}
// 判断合约以及账户锁定情况
err = checkTokenState(stub, trans.Name, message.Key, trans.From, trans.To)
if err != nil {
return shim.Error(err.Error())
}
if trans.Value < 0 {
return shim.Error(valueErr.Error())
}
if message.Key == trans.From {
return shim.Error(balanceErr.Error())
}
// 判断代理额度
if account.Allowance[trans.Name][trans.From] < trans.Value {
return shim.Error(approveBalanceErr.Error())
}
if trans.Nonce-account.Nonce != 1 {
return shim.Error(nonceError.Error())
}
// 代理额度扣减
account.Allowance[trans.Name][trans.From] -= trans.Value
account.Nonce += 1
// 设置转出方
err = setTransTo(stub, trans)
if err != nil {
return shim.Error(err.Error())
}
// 颠倒交易顺序再次记录
trans.From, trans.To, trans.Value = trans.To, trans.From, -trans.Value
err = setTransTo(stub, trans)
if err != nil {
return shim.Error(err.Error())
}
// 保存账户
err = setAccount(stub, message.Key, account, secret)
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(SUCCESS)
}
func (t TokenChaincode) LockToken(stub shim.ChaincodeStubInterface, args []string) pb.Response {
return tokenLock(stub, args, true)
}
func (t TokenChaincode) UnLockToken(stub shim.ChaincodeStubInterface, args []string) pb.Response {
return tokenLock(stub, args, false)
}
func tokenLock(stub shim.ChaincodeStubInterface, args []string, state bool) pb.Response {
message, trans, secret, err := messageToTrans(stub, args)
if err != nil {
return shim.Error(err.Error())
}
account, err := getTransTo(stub, message.Key)
if err != nil {
return shim.Error(err.Error())
}
token, err := getToken(stub, trans.Name)
if err != nil {
return shim.Error(err.Error())
}
// 验证nonce
if trans.Nonce-account.Nonce != 1 {
return shim.Error(nonceError.Error())
}
// 验证所有人
if token.Owner != message.Key {
return shim.Error(permissionErr.Error())
}
// 修改数据
account.Nonce += 1
// 将代币的nonce改成和账户一致
token.Lock = state
token.Nonce = account.Nonce
// 写数据
err = setToken(stub, token)
if err != nil {
return shim.Error(err.Error())
}
err = setAccount(stub, message.Key, account, secret)
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(SUCCESS)
}
func (t TokenChaincode) LockAccount(stub shim.ChaincodeStubInterface, args []string) pb.Response {
return accountLock(stub, args, true)
}
func (t TokenChaincode) UnlockAccount(stub shim.ChaincodeStubInterface, args []string) pb.Response {
return accountLock(stub, args, false)
}
func accountLock(stub shim.ChaincodeStubInterface, args []string, state bool) pb.Response {
// 解析数据
message, trans, secret, err := messageToTrans(stub, args)
if err != nil {
return shim.Error(err.Error())
}
token, err := getToken(stub, trans.Name)
if err != nil {
return shim.Error(err.Error())
}
// 判断权限
if token.Owner != message.Key {
return shim.Error(permissionErr.Error())
}
// 不能锁住自己
if trans.From == trans.To {
return shim.Error(valueErr.Error())
}
// 判断nonce
account, err := getTransTo(stub, message.Key)
if err != nil {
return shim.Error(err.Error())
}
if trans.Nonce-account.Nonce != 1 {
return shim.Error(nonceError.Error())
}
// 修改数据
if token.AccountLock == nil {
token.AccountLock = make(map[string]bool)
}
token.AccountLock[trans.To] = state
account.Nonce += 1
token.Nonce = account.Nonce
// 写入数据
err = setToken(stub, token)
if err != nil {
return shim.Error(err.Error())
}
err = setAccount(stub, message.Key, account, secret)
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(SUCCESS)
}
func (t TokenChaincode) Mint(stub shim.ChaincodeStubInterface, args []string) pb.Response {
// 获取相关数据
message, trans, secret, err := messageToTrans(stub, args)
if err != nil {
return shim.Error(err.Error())
}
account, err := getTransTo(stub, message.Key)
if err != nil {
return shim.Error(err.Error())
}
token, err := getToken(stub, trans.Name)
if err != nil {
return shim.Error(err.Error())
}
// 判断权限
if token.Owner != message.Key {
return shim.Error(permissionErr.Error())
}
// 判断数值
if trans.Value <= 0 {
return shim.Error(valueErr.Error())
}
// 判断nonce
if trans.Nonce-account.Nonce != 1 {
return shim.Error(nonceError.Error())
}
// 修改数据
token.Supply += trans.Value
account.Balance[token.Name] += trans.Value
account.Nonce += 1
token.Nonce = account.Nonce
// 写入数据
err = setToken(stub, token)
if err != nil {
return shim.Error(err.Error())
}
err = setAccount(stub, message.Key, account, secret)
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(SUCCESS)
}
/*
回收相当于增发的逆向操作
*/
func (t TokenChaincode) Burn(stub shim.ChaincodeStubInterface, args []string) pb.Response {
message, trans, secret, err := messageToTrans(stub, args)
if err != nil {
return shim.Error(err.Error())
}
account, err := getTransTo(stub, message.Key)
if err != nil {
return shim.Error(err.Error())
}
token, err := getToken(stub, trans.Name)
if err != nil {
return shim.Error(err.Error())
}
// 判断
if token.Owner != message.Key {
return shim.Error(permissionErr.Error())
}
if trans.Value <= 0 {
return shim.Error(valueErr.Error())
}
if trans.Nonce-account.Nonce != 1 {
return shim.Error(nonceError.Error())
}
// 因为回收的时候只能从本(管理员)账户进行回收操作,如果管理员的拥有额度不足,那么将回收失败
if account.Balance[trans.Name] < trans.Value {
return shim.Error(valueErr.Error())
}
// 如果回收数值超出发行总量
if token.Supply <= trans.Value {
return shim.Error(valueErr.Error())
}
//修改数据
token.Supply -= trans.Value
account.Balance[token.Name] -= trans.Value
account.Nonce += 1
token.Nonce = account.Nonce
// 写入数据
err = setToken(stub, token)
if err != nil {
return shim.Error(err.Error())
}
err = setAccount(stub, message.Key, account, secret)
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(SUCCESS)
}
// 获取当前账户的nonce,由于一个账号锁定一行数据,不会出现并发情况(多人操作同一账户导致nonce重放) success
func (t TokenChaincode) GetNonce(stub shim.ChaincodeStubInterface, args []string) pb.Response {
if len(args) != 1 {
return shim.Error(parameterErr.Error())
}
account, err := getAccount(stub, args[0])
if err != nil {
return shim.Error(err.Error())
}
return shim.Success([]byte(strconv.Itoa(account.Nonce + 1)))
}
// success
func (t TokenChaincode) Balance(stub shim.ChaincodeStubInterface, args []string) pb.Response {
if len(args) != 2 {
return shim.Error(parameterErr.Error())
}
name, key := args[0], args[1]
account, err := getTransTo(stub, key)
if err != nil {
return shim.Error(err.Error())
}
return shim.Success([]byte(strconv.Itoa(account.Balance[name])))
}
// success
func (t TokenChaincode) Allowance(stub shim.ChaincodeStubInterface, args []string) pb.Response {
if len(args) != 3 {
return shim.Error(parameterErr.Error())
}
name, key, from := args[0], args[1], args[2]
// 必须获取所有未花费的交易输出,才能保证数据真实
account, err := getTransTo(stub, key)
if err != nil {
return shim.Error(err.Error())
}
return shim.Success([]byte(strconv.Itoa(account.Allowance[name][from])))
}
// 测试成功
func (t TokenChaincode) GetAccount(stub shim.ChaincodeStubInterface, args []string) pb.Response {
if len(args) != 1 {
return shim.Error(parameterErr.Error())
}
key := args[0]
account, err := getTransTo(stub, key)
if err != nil {
return shim.Error(err.Error())
}
if account == nil {
return shim.Error("account is nil, please check your key")
}
ab, _ := json.Marshal(account)
return shim.Success(ab)
}
// 测试成功
func (t TokenChaincode) GetAccountWithSecret(stub shim.ChaincodeStubInterface, args []string) pb.Response {
if len(args) != 2 {
return shim.Error(parameterErr.Error())
}
// 解出秘钥
message := &Message{
Key: args[0],
Sign: args[1],
}
log.Infof("message: %s", message)
if message.Key == "" || message.Sign == "" {
return shim.Error(parameterErr.Error())
}
// 计算秘钥
sign, err := base64.StdEncoding.DecodeString(message.Sign)
if err != nil {
return shim.Error(err.Error())
}
secret, err := PrivateDecrypt(sign, PRIVATE)
log.Infof("计算出秘钥: %s", string(secret))
if err != nil {
return shim.Error(err.Error())
}
// 取数据
data, err := getData(stub, message.Key)
log.Infof("获取数据: %s", data)
if err != nil {
return shim.Error(err.Error())
}
// 验证秘钥
err = verifySecret(data, secret)
if err != nil {
return shim.Error(err.Error())
}
log.Info("验证通过")
// 验证完毕后,重新拉取数据,携带UTXO
account, err := getTransTo(stub, message.Key)
log.Info(account)
if err != nil {
return shim.Error(err.Error())
}
ab, err := json.Marshal(account)
log.Info(string(ab))
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(ab)
}
// success
func (t TokenChaincode) GetTransRecord(stub shim.ChaincodeStubInterface, args []string) pb.Response {
if len(args) != 2 {
return shim.Error(parameterErr.Error())
}
key, name := args[0], args[1]
if key == "" || name == "" {
return shim.Error("one of parameter is nil")
}
trans, err := getTransRecord(stub, key, name)
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(trans)
}
func checkTokenState(stub shim.ChaincodeStubInterface, name string, keys ...string) error {
token, err := getToken(stub, name)
if err != nil {
return err
}
if token.Lock {
return tokenLockErr
}
for _, key := range keys{
if token.AccountLock[key] {
return accountLockErr
}
}
return nil
}
// 使用现有的数据做计算,如果计算结果匹配,说明秘钥正确,如果不匹配,说明秘钥错误
func verifySecret(data *Data, secret []byte) error { // 该方法报空指针
log.Info("开始验证秘钥")
ab, err := json.Marshal(data.Account)
if err != nil {
return err
}
hashes := Hash(ab)
res, err := AesEncrypt(hashes, secret)
//log.Infof("进行正向加密: %s", string(res))
//log.Info(res)
//log.Info(hex.EncodeToString(res))
//log.Infof("状态数据的hash: %s", data.Hash)
//log.Info([]byte(data.Hash))
//log.Info(hex.EncodeToString([]byte(data.Hash)))
if err != nil {
return err
}
// bytes.Compare([]byte(data.Hash), res) == 0 该方法比较失败
// data.Hash == string(res) 比较失败
// bytes.Equal([]byte(data.Hash), res) 失败
// strings.Compare(data.Hash, string(res)) == 0 失败
if strings.EqualFold(data.Hash, string(res)) { // 成功
log.Info("秘钥验证通过")
return nil
} else {
log.Info("秘钥验证失败")
return verifyKeyErr
}
}
/*
封装读写数据
*/
// 获取state 已花费记录
func getTransRecord(stub shim.ChaincodeStubInterface, key string, name string) ([]byte, error) {
log.Info("查询交易记录")
transKey := fmt.Sprintf("trans.%s.%s", name, key)
res, err := stub.GetHistoryForKey(transKey)
if err != nil {
return nil, err
}
trans, err := getHisResult(res)
if err != nil {
return nil, err
}
return trans.Bytes(), nil
}
// 设置state 已花费
func setTrans(stub shim.ChaincodeStubInterface, key string, trans *Trans) error {
log.Info("保存已花费记录")
transKey := fmt.Sprintf("trans.%s.%s", trans.Name, key)
ab, err := json.Marshal(trans)
if err != nil {
return err
}
// 保存正常的交易流水
log.Info("记录流水")
err = stub.PutState(transKey, ab)
if err != nil {
return err
}
// 保存未花费的交易输出
// 此处操作的是to的账户时间戳,跟from无关,所以不用担心时间戳问题
err = setTransTo(stub, trans)
if err != nil {
return err
}
//err = setApprove(stub, trans)
//if err != nil {
// return err
//}
return nil
}
// 获取所有未花费的记录(与getAccount区别在于,此处统计了未花费的交易输出)
func getTransTo(stub shim.ChaincodeStubInterface, to string) (*Account, error) {
log.Info("获取账户未花费的交易输出")
// 记录当前时间点,所有在当前事件点后写进的数据不纳入统计
account, err := getAccount(stub, to)
if err != nil {
return nil, err
}
log.Infof("获取状态数据: %s", account)
if account.Balance == nil {
account.Balance = make(map[string]int)
}
// 这个key设计的不分币种,只要是未花费全部统计,怎么让每次获取utxo都不会全量循环?带上时间戳作为key,保证每次key都不同
transToKey := "trans.to." + to + account.Time.String() // 以账户的时间戳作为后缀,防止每次都读取重复的未花费记录
// 获取所有历史记录
tb, err := stub.GetHistoryForKey(transToKey)
if err != nil {
return nil, err
}
for tb.HasNext() {
tx, err := tb.Next()
if err != nil {
return nil, err
}
var trans Trans
json.Unmarshal(tx.Value, &trans)
// 叠加未花费输出
account.Balance[trans.Name] += trans.Value
}
log.Infof("account: %s", account)
// 获取代理额度
return getApprove(stub, to, account)
}
// 设置state 未花费的记录
func setTransTo(stub shim.ChaincodeStubInterface, trans *Trans) error {
log.Info("记录未花费交易输出")
account, err := getAccount(stub, trans.To)
if err != nil {
return err
}
transToKey := "trans.to." + trans.To + account.Time.String()
tb, err := json.Marshal(trans)
if err != nil {
return err
}
err = stub.PutState(transToKey, tb)
if err != nil {
return err
}
return nil
}
// 获取代理额度
func getApprove(stub shim.ChaincodeStubInterface, key string, account *Account) (*Account, error) {
log.Info("整理代理额度")
if account.Allowance == nil {
account.Allowance = make(map[string]map[string]int)
}
approveKey := "approve." + key + account.Time.String()
atb, err := stub.GetHistoryForKey(approveKey)
if err != nil {
return nil, err
}
for atb.HasNext() {
ab, err := atb.Next()
if err != nil {
return nil, err
}
var trans Trans
json.Unmarshal(ab.Value, &trans)
// 叠加代理额度
if account.Allowance[trans.Name] == nil {
account.Allowance[trans.Name] = make(map[string]int)
account.Allowance[trans.Name][trans.From] = trans.Value
} else {
account.Allowance[trans.Name][trans.From] += trans.Value
}
}
log.Infof("account: %s", account)
return account, nil
}
// 设置代理记录
func setApprove(stub shim.ChaincodeStubInterface, trans *Trans) error {
account, err := getAccount(stub, trans.To)
if err != nil {
return err
}
approveKey := "approve." + trans.To + account.Time.String()
ab, err := json.Marshal(trans)
if err != nil {
return err
}
err = stub.PutState(approveKey, ab)
return err
}
// 此处不统计UTXO数据
func getAccount(stub shim.ChaincodeStubInterface, key string) (*Account, error) {
data, err := getData(stub, key)
if err != nil {
return nil, err
}
return &data.Account, nil
}
func setAccount(stub shim.ChaincodeStubInterface, key string, account *Account, secret []byte) error {
log.Info("开始设置账户")
accountKey := "account." + key
data := &Data{
Account: *account,
PreHash: "",
Hash: "",
}
// 更新账户的操作时间
data.Account.Time = time.Now()
// 设置preHash
pd, err := stub.GetState(accountKey)
if err != nil {
return err
}
// 如果不存在,到底是数据不存在还是首条数据》?
if pd != nil {
var preData Data
err = json.Unmarshal(pd, &preData)
if err != nil {
return err
}
// 记录上一个数据的hash
data.PreHash = preData.Hash
}
// 设置hash
ab, err := json.Marshal(data.Account)
if err != nil {
return err
}
hashes := Hash(ab)
log.Infof("hash结果: %s", string(hashes))
res, err := AesEncrypt(hashes, secret)
if err != nil {
return err
}
data.Hash = string(res)
log.Infof("加密后的hash: %s", data.Hash)
db, err := json.Marshal(data)
if err != nil {
return err
}
err = stub.PutState(accountKey, db)
return nil
}
func getData(stub shim.ChaincodeStubInterface, key string) (*Data, error) {
accountKey := "account." + key
db, err := stub.GetState(accountKey)
if err != nil {
return nil, err
}
// 如果数据为空,直接返回出去
if db == nil {
return nil, errors.New("account data is nil")
}
var data Data
err = json.Unmarshal(db, &data)
if err != nil {
return nil, err
}
return &data, nil
}
func getToken(stub shim.ChaincodeStubInterface, name string) (*Tokens, error) {
tokenKey := "token." + name
tb, err := stub.GetState(tokenKey)
if err != nil {
return nil, err
}
var token Tokens
err = json.Unmarshal(tb, &token)
if err != nil {
return nil, err
}
return &token, nil
}
func setToken(stub shim.ChaincodeStubInterface, token *Tokens) error {
tokenKey := "token." + token.Name
tb, err := json.Marshal(token)
if err != nil {
return err
}
err = stub.PutState(tokenKey, tb)
if err != nil {
return err
}
return nil
}
func messageToTokens(stub shim.ChaincodeStubInterface, args []string) (*Message, *Tokens, []byte, error) {
log.Info("begin message to tokens")
if len(args) != 3 {
return nil, nil, nil, parameterErr
}
message := &Message{
Key: args[0],
Data: args[1],
Sign: args[2],
}
if message.Key == "" || message.Sign == "" || message.Data == "" {
return nil, nil, nil, parameterErr
}
log.Infof("message: %s", message)
secret, tb, err := parseMessage(stub, message)
if err != nil {
return nil, nil, nil, err
}
var tokens Tokens
err = json.Unmarshal(tb, &tokens)
if err != nil {
return nil, nil, nil, err
}
return message, &tokens, secret, nil
}
func messageToTrans(stub shim.ChaincodeStubInterface, args []string) (*Message, *Trans, []byte, error) {
log.Info("")
if len(args) != 3 {
return nil, nil, nil, parameterErr
}
message := &Message{
Key: args[0],
Data: args[1],
Sign: args[2],
}
if message.Key == "" || message.Sign == "" || message.Data == "" {
return nil, nil, nil, parameterErr
}
secret, tb, err := parseMessage(stub, message)
if err != nil {
return nil, nil, nil, err
}
var trans Trans
err = json.Unmarshal(tb, &trans)
if err != nil {
return nil, nil, nil, err
}
return message, &trans, secret, nil
}
// 解析请求消息 success
func parseMessage(stub shim.ChaincodeStubInterface, message *Message) ([]byte, []byte, error) {
log.Info("开始转换消息数据")
// 计算秘钥
sign, err := base64.StdEncoding.DecodeString(message.Sign)
if err != nil {
return nil, nil, err
}
secret, err := PrivateDecrypt(sign, PRIVATE)
if err != nil {
return nil, nil, err
}
log.Infof("获取秘钥: %s", string(secret))
// 获取当前记录数据,验证秘钥
data, err := getData(stub, message.Key)
if err != nil {
return nil, nil, err
}
err = verifySecret(data, secret)
if err != nil {
return nil, nil, err
}
// 秘钥正确,则解析交易信息
// 这里属于外来加密数据,需要进行16进制转换
content, err := hex.DecodeString(message.Data)
if err != nil {
return nil, nil, err
}
tb, err := AesDecrypt(content, secret)
if err != nil {
return nil, nil, err
}
return secret, tb, nil
}
func getLogger(stub shim.ChaincodeStubInterface) (c *shim.ChaincodeLogger) {
fcn, _ := stub.GetFunctionAndParameters()
c = shim.NewLogger(fmt.Sprintf("%s.%s.%s", stub.GetChannelID(), "token", fcn))
c.SetLevel(shim.LogDebug)
return
}
/*
常量以及无关数据读写的方法
*/
var SUCCESS = []byte("success")
var parameterErr = errors.New("parameter err")
var privateDecodeErr = errors.New("private decrypt error")
var verifyKeyErr = errors.New("verify key error")
var nonceError = errors.New("nonce error")
var balanceErr = errors.New("insufficient balance")
var tokenLockErr = errors.New("token was locked")
var accountLockErr = errors.New("account was locked")
var valueErr = errors.New("does not suppert this transaction")
var approveBalanceErr = errors.New("allowance approve balance error")
var permissionErr = errors.New("permission denied")
func Hash(text []byte) []byte {
shaInst := sha1.New()
shaInst.Write(text)
result := shaInst.Sum(nil)
return result
}
var PUBLIC = []byte(`
-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALAEZjmAtbVl5534yrDD4n5UUcH1NdNL
TQzEPojUvf4/o7cv2dGZJ5QdJ/ciUjIwWZpF2qtBKQpye/LwAYC5WLUCAwEAAQ==
-----END PUBLIC KEY-----
`)
var PRIVATE = []byte(`
-----BEGIN RSA PRIVATE KEY-----
MIIBOQIBAAJBALAEZjmAtbVl5534yrDD4n5UUcH1NdNLTQzEPojUvf4/o7cv2dGZ
J5QdJ/ciUjIwWZpF2qtBKQpye/LwAYC5WLUCAwEAAQJALugtOdvEEOBkViPsGClL
nbDozNCFFJb3pJRTufE/5JYt7hM1rGinPvBO4eTzjz38Iw6Aa2nIfgaULlRa+Lht
7QIhANSBEy9bO0ugbYPIyEHOfQYWyFARxnnMHZDdSrSE4K2jAiEA1At0misbtpSM
XMeO+OtZ7BZD3suxti3xcmSSEK4cFccCIF/KR0GjmFkA2hz7lvnDAKyL/IPLX3Jr
xjAU8KXq9/SNAiBEXbUoh8GVqmte9pBoPSlu7vbO/Im9nS59nWNisWAovQIgVD8g
I93EUVHy2gAskSSoh1q4EYbGxNUSyO6Y3hU0ApA=
-----END RSA PRIVATE KEY-----
`)
var iv = []byte("abcdefghijk1mnop")
// aes 解密
func AesDecrypt(crypted, key []byte) ([]byte, error) {
key = addSuffix(key)
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
blockMode := cipher.NewCBCDecrypter(block, iv)
origData := make([]byte, len(crypted))
blockMode.CryptBlocks(origData, crypted)
origData = PKCS5UnPadding(origData)
return origData, nil
}
// aes 加密
func AesEncrypt(origData, key []byte) ([]byte, error) {
key = addSuffix(key)
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
blockSize := block.BlockSize()
origData = PKCS5Padding(origData, blockSize)
blockMode := cipher.NewCBCEncrypter(block, iv)
crypted := make([]byte, len(origData))
blockMode.CryptBlocks(crypted, origData)
return crypted, nil
}
// aes规定秘钥必须是8的倍数,8还不行,所以在这里固定成16
func addSuffix(secret []byte) []byte {
s := string(secret)
len := len(s)
for i:=0; i<16-len; i++ {
s += "="
}
return []byte(s)
}
func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
padding := blockSize - len(ciphertext)%blockSize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
return append(ciphertext, padtext...)
}
func PKCS5UnPadding(origData []byte) []byte {
length := len(origData)
unpadding := int(origData[length-1])
return origData[:(length - unpadding)]
}
func ParsePrivateKey(private []byte) (*rsa.PrivateKey, error) {
block, _ := pem.Decode(private)
if block == nil {
return nil, privateDecodeErr
}
priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
return priv, nil
}
// 解密
func PrivateDecrypt(ciphertext, privateKey []byte) ([]byte, error) {
priv, err := ParsePrivateKey(privateKey)
if err != nil {
return nil, err
}
var b bytes.Buffer
begin, size, end := 0, 128, len(ciphertext)
for end-begin > 0 {
var temp []byte
if end <= begin+size {
temp = ciphertext[begin:end]
} else {
temp = ciphertext[begin : begin+size]
}
tres, err := rsa.DecryptPKCS1v15(rand.Reader, priv, temp)
if err != nil {
return nil, err
}
b.Write(tres)
begin += size
}
return b.Bytes(), err
}
func getHisResult(result shim.HistoryQueryIteratorInterface) (bytes.Buffer, error) {
//由于查询的结果是一个集合,所以要将结果集转成字符串,方便传输
var buffer bytes.Buffer
buffer.WriteString("[")
bArrayMemberAlreadyWritten := false
for result.HasNext() {
response, err := result.Next()
if err != nil {
return buffer, err
}
if bArrayMemberAlreadyWritten {
buffer.WriteString(",")
}
buffer.WriteString("{\"TxId\":\"")
buffer.WriteString(response.TxId)
buffer.WriteString("\",\"value\":")
//if response.IsDelete {
// buffer.WriteString("null")
//} else {
buffer.WriteString(string(response.Value))
//}
buffer.WriteString(", \"Timestamp\":\"")
buffer.WriteString(time.Unix(response.Timestamp.Seconds, int64(response.Timestamp.Nanos)).Format("2006-01-02 15:04:05"))
buffer.WriteString("\"")
buffer.WriteString(", \"IsDelete\":\"")
buffer.WriteString(strconv.FormatBool(response.IsDelete))
buffer.WriteString("\"")
buffer.WriteString("}")
//item,_:= json.Marshal(response)
//buffer.Write(item)
bArrayMemberAlreadyWritten = true
}
buffer.WriteString("]")
return buffer, nil
}