mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
add ota storage implementation
This commit is contained in:
parent
6d7e21ebea
commit
7f5d6a0bde
1 changed files with 457 additions and 0 deletions
457
core/vm/ota_storage.go
Normal file
457
core/vm/ota_storage.go
Normal file
|
|
@ -0,0 +1,457 @@
|
||||||
|
// Copyright 2019 usechain Foundation Ltd
|
||||||
|
|
||||||
|
package vm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"math/rand"
|
||||||
|
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrUnknown = errors.New("unknown error")
|
||||||
|
ErrInvalidOTAAddr = errors.New("invalid OTA addrss")
|
||||||
|
ErrInvalidOTAAX = errors.New("invalid OTA AX")
|
||||||
|
ErrOTAExistAlready = errors.New("OTA exist already")
|
||||||
|
ErrOTABalanceIsZero = errors.New("OTA balance is 0")
|
||||||
|
)
|
||||||
|
|
||||||
|
// OTABalance2ContractAddr convert ota balance to ota storage address
|
||||||
|
//
|
||||||
|
// 1 eth --> (bigint)1000000000000000000 --> "0x0000000000000000000001000000000000000000"
|
||||||
|
//
|
||||||
|
func OTABalance2ContractAddr(balance *big.Int) common.Address {
|
||||||
|
if balance == nil {
|
||||||
|
return common.Address{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return common.HexToAddress(balance.String())
|
||||||
|
// return common.BigToAddress(balance)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAXFromUseAddr retrieve ota AX from ota UseAddr
|
||||||
|
func GetAXFromUseAddr(otaUseAddr []byte) ([]byte, error) {
|
||||||
|
if len(otaUseAddr) != common.UAddressLength {
|
||||||
|
return nil, ErrInvalidOTAAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
return otaUseAddr[1 : 1+common.HashLength], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsAXPointToUseAddr check whether AX point to otaUseAddr or not
|
||||||
|
func IsAXPointToUseAddr(AX []byte, otaUseAddr []byte) bool {
|
||||||
|
findAX, err := GetAXFromUseAddr(otaUseAddr)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes.Equal(findAX, AX)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOtaBalanceFromAX retrieve ota balance from ota AX
|
||||||
|
func GetOtaBalanceFromAX(statedb StateDB, otaAX []byte) (*big.Int, error) {
|
||||||
|
if statedb == nil {
|
||||||
|
return nil, ErrUnknown
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(otaAX) != common.HashLength {
|
||||||
|
return nil, ErrInvalidOTAAX
|
||||||
|
}
|
||||||
|
|
||||||
|
balance := statedb.GetStateByteArray(otaBalanceStorageAddr, common.BytesToHash(otaAX))
|
||||||
|
if len(balance) == 0 {
|
||||||
|
return common.Big0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return new(big.Int).SetBytes(balance), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetOtaBalanceToAX set ota balance as 'balance'. Overwrite if ota balance exist already.
|
||||||
|
func SetOtaBalanceToAX(statedb StateDB, otaAX []byte, balance *big.Int) error {
|
||||||
|
if statedb == nil || balance == nil {
|
||||||
|
return ErrUnknown
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(otaAX) != common.HashLength {
|
||||||
|
return ErrInvalidOTAAX
|
||||||
|
}
|
||||||
|
|
||||||
|
statedb.SetStateByteArray(otaBalanceStorageAddr, common.BytesToHash(otaAX), balance.Bytes())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChechOTAExist checks the OTA exist or not.
|
||||||
|
//
|
||||||
|
// In order to avoid additional ota have conflict with existing,
|
||||||
|
// even if AX exist in balance storage already, will return true.
|
||||||
|
func CheckOTAAXExist(statedb StateDB, otaAX []byte) (exist bool, balance *big.Int, err error) {
|
||||||
|
if statedb == nil {
|
||||||
|
return false, nil, ErrUnknown
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(otaAX) != common.HashLength {
|
||||||
|
return false, nil, ErrInvalidOTAAX
|
||||||
|
}
|
||||||
|
|
||||||
|
balance, err = GetOtaBalanceFromAX(statedb, otaAX[:common.HashLength])
|
||||||
|
if err != nil {
|
||||||
|
return false, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if balance.Cmp(common.Big0) == 0 {
|
||||||
|
return false, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, balance, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckOTALongAddrExist(statedb StateDB, otaLongAddr []byte) (exist bool, balance *big.Int, err error) {
|
||||||
|
if statedb == nil {
|
||||||
|
return false, nil, ErrUnknown
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(otaLongAddr) != 33 {
|
||||||
|
return false, nil, ErrInvalidOTAAX
|
||||||
|
}
|
||||||
|
|
||||||
|
otaAX := otaLongAddr[1 : 1+common.HashLength]
|
||||||
|
if err != nil {
|
||||||
|
return false, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
balance, err = GetOtaBalanceFromAX(statedb, otaAX)
|
||||||
|
if err != nil {
|
||||||
|
return false, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if balance.Cmp(common.Big0) == 0 {
|
||||||
|
return false, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
mptAddr := OTABalance2ContractAddr(balance)
|
||||||
|
otaStored := statedb.GetStateByteArray(mptAddr, common.BytesToHash(otaAX))
|
||||||
|
|
||||||
|
fmt.Println(common.ToHex(mptAddr.Bytes()))
|
||||||
|
fmt.Println(common.ToHex(otaLongAddr))
|
||||||
|
|
||||||
|
if otaStored == nil {
|
||||||
|
return false, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(otaLongAddr, otaStored[:33]) {
|
||||||
|
return false, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, balance, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func BatCheckOTAExist(statedb StateDB, otaLongAddrs [][]byte) (exist bool, balance *big.Int, unexistOta []byte, err error) {
|
||||||
|
if statedb == nil || len(otaLongAddrs) == 0 {
|
||||||
|
return false, nil, nil, ErrUnknown
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, otaLongAddr := range otaLongAddrs {
|
||||||
|
if len(otaLongAddr) != 33 {
|
||||||
|
return false, nil, otaLongAddr, ErrInvalidOTAAX
|
||||||
|
}
|
||||||
|
|
||||||
|
exist, balanceTmp, err := CheckOTALongAddrExist(statedb, otaLongAddr)
|
||||||
|
if err != nil {
|
||||||
|
return false, nil, otaLongAddr, err
|
||||||
|
} else if !exist {
|
||||||
|
return false, nil, otaLongAddr, errors.New("ota doesn't exist:" + common.ToHex(otaLongAddr))
|
||||||
|
} else if balanceTmp.Cmp(common.Big0) == 0 {
|
||||||
|
return false, nil, otaLongAddr, errors.New("ota balance is 0! ota:" + common.ToHex(otaLongAddr))
|
||||||
|
} else if balance == nil {
|
||||||
|
balance = balanceTmp
|
||||||
|
continue
|
||||||
|
} else if balance.Cmp(balanceTmp) != 0 {
|
||||||
|
return false, nil, otaLongAddr, errors.New("otas have different balances! ota:" + common.ToHex(otaLongAddr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, balance, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetUnspendOTATotalBalance(statedb StateDB) (*big.Int, error) {
|
||||||
|
if statedb == nil {
|
||||||
|
return nil, ErrUnknown
|
||||||
|
}
|
||||||
|
|
||||||
|
totalOTABalance, totalSpendedOTABalance := big.NewInt(0), big.NewInt(0)
|
||||||
|
|
||||||
|
// total history OTA balance (include spended)
|
||||||
|
statedb.ForEachStorageByteArray(otaBalanceStorageAddr, func(key common.Hash, value []byte) bool {
|
||||||
|
if len(value) == 0 {
|
||||||
|
log.Warn("total ota balance. value is empoty!", "key", key.String())
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
balance := new(big.Int).SetBytes(value)
|
||||||
|
totalOTABalance.Add(totalOTABalance, balance)
|
||||||
|
log.Debug("total ota balance.", "key", key.String(), "balance:", balance.String())
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
// total spended OTA balance
|
||||||
|
statedb.ForEachStorageByteArray(otaImageStorageAddr, func(key common.Hash, value []byte) bool {
|
||||||
|
if len(value) == 0 {
|
||||||
|
log.Warn("total spended ota balance. value is empoty!", "key", key.String())
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
balance := new(big.Int).SetBytes(value)
|
||||||
|
totalSpendedOTABalance.Add(totalSpendedOTABalance, balance)
|
||||||
|
log.Debug("total spended ota balance.", "key", key.String(), "balance:", balance.String())
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
log.Debug("total unspended OTA balance", "total history OTA balance:", totalOTABalance.String(), "total spended OTA balance:", totalSpendedOTABalance.String())
|
||||||
|
|
||||||
|
return totalOTABalance.Sub(totalOTABalance, totalSpendedOTABalance), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// setOTA storage ota info, include balance and UseAddr. Overwrite if ota exist already.
|
||||||
|
func setOTA(statedb StateDB, balance *big.Int, otaUseAddr []byte) error {
|
||||||
|
if statedb == nil || balance == nil {
|
||||||
|
return ErrUnknown
|
||||||
|
}
|
||||||
|
if len(otaUseAddr) != common.UAddressLength {
|
||||||
|
return ErrInvalidOTAAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
otaAX, _ := GetAXFromUseAddr(otaUseAddr)
|
||||||
|
//balanceOld, err := GetOtaBalanceFromAX(statedb, otaAX)
|
||||||
|
//if err != nil {
|
||||||
|
// return err
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//if balanceOld != nil && balanceOld.Cmp(common.Big0) != 0 {
|
||||||
|
// return errors.New("ota balance is not 0! old balance:" + balanceOld.String())
|
||||||
|
//}
|
||||||
|
|
||||||
|
mptAddr := OTABalance2ContractAddr(balance)
|
||||||
|
statedb.SetStateByteArray(mptAddr, common.BytesToHash(otaAX), otaUseAddr)
|
||||||
|
return SetOtaBalanceToAX(statedb, otaAX, balance)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddOTAIfNotExist storage ota info if doesn't exist already.
|
||||||
|
func AddOTAIfNotExist(statedb StateDB, balance *big.Int, otaUseAddr []byte) (bool, error) {
|
||||||
|
if statedb == nil || balance == nil {
|
||||||
|
return false, ErrUnknown
|
||||||
|
}
|
||||||
|
if len(otaUseAddr) != common.UAddressLength {
|
||||||
|
return false, ErrInvalidOTAAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
otaAX, _ := GetAXFromUseAddr(otaUseAddr)
|
||||||
|
otaAddrKey := common.BytesToHash(otaAX)
|
||||||
|
exist, _, err := CheckOTAAXExist(statedb, otaAddrKey[:])
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if exist {
|
||||||
|
return false, ErrOTAExistAlready
|
||||||
|
}
|
||||||
|
|
||||||
|
err = setOTA(statedb, balance, otaUseAddr)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOTAInfoFromAX retrieve ota info, include balance and UseAddr
|
||||||
|
func GetOTAInfoFromAX(statedb StateDB, otaAX []byte) (otaUseAddr []byte, balance *big.Int, err error) {
|
||||||
|
if statedb == nil {
|
||||||
|
return nil, nil, ErrUnknown
|
||||||
|
}
|
||||||
|
if len(otaAX) < common.HashLength {
|
||||||
|
return nil, nil, ErrInvalidOTAAX
|
||||||
|
}
|
||||||
|
|
||||||
|
otaAddrKey := common.BytesToHash(otaAX)
|
||||||
|
balance, err = GetOtaBalanceFromAX(statedb, otaAddrKey[:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if balance == nil || balance.Cmp(common.Big0) == 0 {
|
||||||
|
return nil, nil, ErrOTABalanceIsZero
|
||||||
|
}
|
||||||
|
|
||||||
|
mptAddr := OTABalance2ContractAddr(balance)
|
||||||
|
|
||||||
|
otaValue := statedb.GetStateByteArray(mptAddr, otaAddrKey)
|
||||||
|
if otaValue != nil && len(otaValue) != 0 {
|
||||||
|
return otaValue, balance, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, balance, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetOTASetEnv struct {
|
||||||
|
otaAX []byte
|
||||||
|
setNum int
|
||||||
|
getNum int
|
||||||
|
loopTimes int
|
||||||
|
rnd int
|
||||||
|
otaUseAddrSet [][]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (env *GetOTASetEnv) OTAInSet(ota []byte) bool {
|
||||||
|
for _, exist := range env.otaUseAddrSet {
|
||||||
|
if bytes.Equal(exist, ota) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (env *GetOTASetEnv) UpdateRnd() {
|
||||||
|
env.rnd = rand.Intn(100) + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (env *GetOTASetEnv) IsSetFull() bool {
|
||||||
|
return env.getNum >= env.setNum
|
||||||
|
}
|
||||||
|
|
||||||
|
func (env *GetOTASetEnv) RandomSelOTA(value []byte) bool {
|
||||||
|
env.loopTimes++
|
||||||
|
if env.loopTimes%env.rnd == 0 {
|
||||||
|
env.otaUseAddrSet = append(env.otaUseAddrSet, value)
|
||||||
|
env.getNum++
|
||||||
|
env.UpdateRnd()
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// doOTAStorageTravelCallBack implement ota mpt travel call back
|
||||||
|
func doOTAStorageTravelCallBack(env *GetOTASetEnv, value []byte) (bool, error) {
|
||||||
|
// find self, return true to continue travel loop
|
||||||
|
if IsAXPointToUseAddr(env.otaAX, value) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ota contained in set already, return true to continue travel loop
|
||||||
|
if env.OTAInSet(value) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// random select
|
||||||
|
// if set full already, return false to stop travel loop
|
||||||
|
if bGet := env.RandomSelOTA(value); bGet {
|
||||||
|
return !env.IsSetFull(), nil
|
||||||
|
} else {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOTASet retrieve the setNum of same balance OTA address of the input OTA setting by otaAX, and ota balance.
|
||||||
|
// Rules:
|
||||||
|
// 1: The result can't contain otaAX self;
|
||||||
|
// 2: The result can't contain duplicate items;
|
||||||
|
// 3: No ota exist in the mpt, return error;
|
||||||
|
// 4: OTA total count in the mpt less or equal to the setNum, return error(returned set must
|
||||||
|
// can't contain otaAX self, so need more exist ota in mpt);
|
||||||
|
// 5: If find invalid ota Useaddr, return error;
|
||||||
|
// 6: Travel the ota mpt.Record loop exist ota cumulative times as loopTimes.
|
||||||
|
// Generate a random number as rnd.
|
||||||
|
// If loopTimes%rnd == 0, collect current exist ota to result set and update the rnd.
|
||||||
|
// Loop checking exist ota and loop traveling ota mpt, untile collect enough ota or find error.
|
||||||
|
//
|
||||||
|
func GetOTASet(statedb StateDB, otaAX []byte, setNum int) (otaUseAddrs [][]byte, balance *big.Int, err error) {
|
||||||
|
if statedb == nil {
|
||||||
|
return nil, nil, ErrUnknown
|
||||||
|
}
|
||||||
|
if len(otaAX) != common.HashLength {
|
||||||
|
return nil, nil, ErrInvalidOTAAX
|
||||||
|
}
|
||||||
|
|
||||||
|
balance, err = GetOtaBalanceFromAX(statedb, otaAX)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
} else if balance == nil || balance.Cmp(common.Big0) == 0 {
|
||||||
|
return nil, nil, errors.New("can't find ota address balance!")
|
||||||
|
}
|
||||||
|
|
||||||
|
mptAddr := OTABalance2ContractAddr(balance)
|
||||||
|
log.Debug("GetOTASet", "mptAddr", common.ToHex(mptAddr[:]))
|
||||||
|
|
||||||
|
env := GetOTASetEnv{otaAX, setNum, 0, 0, 0, nil}
|
||||||
|
env.otaUseAddrSet = make([][]byte, 0, setNum)
|
||||||
|
env.UpdateRnd()
|
||||||
|
|
||||||
|
mptEleCount := 0 // total number of ota containing in mpt
|
||||||
|
|
||||||
|
for {
|
||||||
|
statedb.ForEachStorageByteArray(mptAddr, func(key common.Hash, value []byte) bool {
|
||||||
|
mptEleCount++
|
||||||
|
|
||||||
|
if len(value) != common.UAddressLength {
|
||||||
|
log.Error("invalid OTA address!", "balance", balance, "value", value)
|
||||||
|
err = errors.New(fmt.Sprint("invalid OTA address! balance:", balance, ", ota:", value))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
bContinue, err := doOTAStorageTravelCallBack(&env, value)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return bContinue
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if env.IsSetFull() {
|
||||||
|
return env.otaUseAddrSet, balance, nil
|
||||||
|
} else if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
} else if mptEleCount == 0 {
|
||||||
|
return nil, balance, errors.New("no ota exist! balance:" + balance.String())
|
||||||
|
} else if setNum >= mptEleCount {
|
||||||
|
return nil, balance, errors.New("too more required ota number! balance:" + balance.String() +
|
||||||
|
", exist count:" + strconv.Itoa(mptEleCount))
|
||||||
|
} else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckOTAImageExist checks ota image key exist already or not
|
||||||
|
func CheckOTAImageExist(statedb StateDB, otaImage []byte) (bool, []byte, error) {
|
||||||
|
if statedb == nil || len(otaImage) == 0 {
|
||||||
|
return false, nil, errors.New("invalid input param!")
|
||||||
|
}
|
||||||
|
|
||||||
|
otaImageKey := crypto.Keccak256Hash(otaImage)
|
||||||
|
otaImageValue := statedb.GetStateByteArray(otaImageStorageAddr, otaImageKey)
|
||||||
|
if otaImageValue != nil && len(otaImageValue) != 0 {
|
||||||
|
return true, otaImageValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddOTAImage storage ota image key. Overwrite if exist already.
|
||||||
|
func AddOTAImage(statedb StateDB, otaImage []byte, value []byte) error {
|
||||||
|
if statedb == nil || len(otaImage) == 0 || len(value) == 0 {
|
||||||
|
return errors.New("invalid input param!")
|
||||||
|
}
|
||||||
|
|
||||||
|
otaImageKey := crypto.Keccak256Hash(otaImage)
|
||||||
|
statedb.SetStateByteArray(otaImageStorageAddr, otaImageKey, value)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue