2021-12-04 16:42:11 +00:00
|
|
|
// Copyright 2017-2021 DERO Project. All rights reserved.
|
|
|
|
// Use of this source code in any form is governed by RESEARCH license.
|
|
|
|
// license can be found in the LICENSE file.
|
|
|
|
// GPG: 0F39 E425 8C65 3947 702A 8234 08B2 0360 A03A 9DE8
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
|
|
|
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
|
|
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
|
|
|
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
|
|
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
|
|
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
|
|
|
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
2022-01-26 10:05:01 +00:00
|
|
|
package dvm
|
2021-12-04 16:42:11 +00:00
|
|
|
|
|
|
|
// this file implements necessary structure to SC handling
|
|
|
|
|
|
|
|
import "fmt"
|
|
|
|
import "bytes"
|
|
|
|
import "runtime/debug"
|
|
|
|
import "encoding/binary"
|
|
|
|
import "github.com/deroproject/derohe/cryptography/crypto"
|
|
|
|
|
2022-01-26 10:05:01 +00:00
|
|
|
import "github.com/deroproject/derohe/config"
|
2021-12-04 16:42:11 +00:00
|
|
|
import "github.com/deroproject/derohe/rpc"
|
|
|
|
import "github.com/deroproject/derohe/globals"
|
2022-01-26 10:05:01 +00:00
|
|
|
import "github.com/deroproject/graviton"
|
|
|
|
|
|
|
|
//import "github.com/deroproject/derohe/transaction"
|
2021-12-04 16:42:11 +00:00
|
|
|
|
|
|
|
// currently DERO hash 2 contract types
|
|
|
|
// 1 OPEN
|
|
|
|
// 2 PRIVATE
|
|
|
|
type SC_META_DATA struct {
|
|
|
|
Type byte // 0 Open, 1 Private
|
|
|
|
DataHash crypto.Hash // hash of SC data tree is here, so as the meta tree verifies all SC DATA
|
|
|
|
}
|
|
|
|
|
|
|
|
// serialize the structure
|
|
|
|
func (meta SC_META_DATA) MarshalBinary() (buf []byte) {
|
|
|
|
buf = make([]byte, 33, 33)
|
|
|
|
buf[0] = meta.Type
|
|
|
|
copy(buf[1+len(meta.DataHash):], meta.DataHash[:])
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (meta *SC_META_DATA) UnmarshalBinary(buf []byte) (err error) {
|
|
|
|
if len(buf) != 1+32 {
|
|
|
|
return fmt.Errorf("input buffer should be of 33 bytes in length")
|
|
|
|
}
|
|
|
|
meta.Type = buf[0]
|
|
|
|
copy(meta.DataHash[:], buf[1+len(meta.DataHash):])
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-03-04 15:05:04 +00:00
|
|
|
// serialize the structure
|
|
|
|
func (meta SC_META_DATA) MarshalBinaryGood() (buf []byte) {
|
|
|
|
buf = make([]byte, 0, 33)
|
|
|
|
buf = append(buf, meta.Type)
|
|
|
|
buf = append(buf, meta.DataHash[:]...)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (meta *SC_META_DATA) UnmarshalBinaryGood(buf []byte) (err error) {
|
|
|
|
if len(buf) != 1+32 {
|
|
|
|
return fmt.Errorf("input buffer should be of 33 bytes in length")
|
|
|
|
}
|
|
|
|
meta.Type = buf[0]
|
|
|
|
copy(meta.DataHash[:], buf[1:])
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-12-04 16:42:11 +00:00
|
|
|
func SC_Meta_Key(scid crypto.Hash) []byte {
|
|
|
|
return scid[:]
|
|
|
|
}
|
|
|
|
func SC_Code_Key(scid crypto.Hash) []byte {
|
2022-01-26 10:05:01 +00:00
|
|
|
return Variable{Type: String, ValueString: "C"}.MarshalBinaryPanic()
|
2021-12-04 16:42:11 +00:00
|
|
|
}
|
|
|
|
func SC_Asset_Key(asset crypto.Hash) []byte {
|
|
|
|
return asset[:]
|
|
|
|
}
|
|
|
|
|
2022-01-26 10:05:01 +00:00
|
|
|
// used to wrap a graviton tree, so it could be discarded at any time
|
|
|
|
type Tree_Wrapper struct {
|
|
|
|
Tree *graviton.Tree
|
|
|
|
Entries map[string][]byte
|
|
|
|
Transfere []TransferExternal
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Tree_Wrapper) Get(key []byte) ([]byte, error) {
|
|
|
|
if value, ok := t.Entries[string(key)]; ok {
|
|
|
|
return value, nil
|
|
|
|
} else {
|
|
|
|
return t.Tree.Get(key)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Tree_Wrapper) Put(key []byte, value []byte) error {
|
|
|
|
t.Entries[string(key)] = append([]byte{}, value...)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// checks cache and returns a wrapped tree if possible
|
|
|
|
func Wrapped_tree(cache map[crypto.Hash]*graviton.Tree, ss *graviton.Snapshot, id crypto.Hash) *Tree_Wrapper {
|
|
|
|
if cached_tree, ok := cache[id]; ok { // tree is in cache return it
|
|
|
|
return &Tree_Wrapper{Tree: cached_tree, Entries: map[string][]byte{}}
|
|
|
|
}
|
|
|
|
|
|
|
|
if tree, err := ss.GetTree(string(id[:])); err != nil {
|
|
|
|
panic(err)
|
|
|
|
} else {
|
|
|
|
return &Tree_Wrapper{Tree: tree, Entries: map[string][]byte{}}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-04 16:42:11 +00:00
|
|
|
// this will process the SC transaction
|
|
|
|
// the tx should only be processed , if it has been processed
|
|
|
|
|
2022-01-26 10:05:01 +00:00
|
|
|
func Execute_sc_function(w_sc_tree *Tree_Wrapper, data_tree *Tree_Wrapper, scid crypto.Hash, bl_height, bl_topoheight, bl_timestamp uint64, blid crypto.Hash, txid crypto.Hash, sc_parsed SmartContract, entrypoint string, hard_fork_version_current int64, balance_at_start uint64, signer [33]byte, incoming_value map[crypto.Hash]uint64, SCDATA rpc.Arguments, gasstorage_incoming uint64, simulator bool) (gascompute, gasstorage uint64, err error) {
|
2021-12-04 16:42:11 +00:00
|
|
|
defer func() {
|
|
|
|
if r := recover(); r != nil { // safety so if anything wrong happens, verification fails
|
|
|
|
if err == nil {
|
|
|
|
err = fmt.Errorf("Stack trace \n%s", debug.Stack())
|
|
|
|
}
|
2022-01-26 10:05:01 +00:00
|
|
|
//logger.V(1).Error(err, "Recovered while rewinding chain,", "r", r, "stack trace", string(debug.Stack()))
|
2021-12-04 16:42:11 +00:00
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2022-01-26 10:05:01 +00:00
|
|
|
//fmt.Printf("executing entrypoint %s values %+v feees %d\n", entrypoint, incoming_value, fees)
|
2021-12-04 16:42:11 +00:00
|
|
|
|
2022-01-26 10:05:01 +00:00
|
|
|
tx_store := Initialize_TX_store()
|
2021-12-04 16:42:11 +00:00
|
|
|
|
|
|
|
// used as value loader from disk
|
|
|
|
// this function is used to load any data required by the SC
|
2022-01-26 10:05:01 +00:00
|
|
|
balance_loader := func(key DataKey) (result uint64) {
|
2021-12-04 16:42:11 +00:00
|
|
|
var found bool
|
|
|
|
_ = found
|
2022-01-26 10:05:01 +00:00
|
|
|
result, found = LoadSCAssetValue(data_tree, key.SCID, key.Asset)
|
2021-12-04 16:42:11 +00:00
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2022-01-26 10:05:01 +00:00
|
|
|
diskloader := func(key DataKey, found *uint64) (result Variable) {
|
2021-12-04 16:42:11 +00:00
|
|
|
var exists bool
|
2022-01-26 10:05:01 +00:00
|
|
|
if result, exists = LoadSCValue(data_tree, key.SCID, key.MarshalBinaryPanic()); exists {
|
2021-12-04 16:42:11 +00:00
|
|
|
*found = uint64(1)
|
|
|
|
}
|
|
|
|
//fmt.Printf("Loading from disk %+v result %+v found status %+v \n", key, result, exists)
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
diskloader_raw := func(key []byte) (value []byte, found bool) {
|
|
|
|
var err error
|
|
|
|
value, err = data_tree.Get(key[:])
|
|
|
|
if err != nil {
|
|
|
|
return value, false
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(value) == 0 {
|
|
|
|
return value, false
|
|
|
|
}
|
|
|
|
//fmt.Printf("Loading from disk %+v result %+v found status %+v \n", key, result, exists)
|
|
|
|
|
|
|
|
return value, true
|
|
|
|
}
|
|
|
|
|
|
|
|
//fmt.Printf("sc_parsed %+v\n", sc_parsed)
|
|
|
|
// if we found the SC in parsed form, check whether entrypoint is found
|
|
|
|
function, ok := sc_parsed.Functions[entrypoint]
|
|
|
|
if !ok {
|
|
|
|
err = fmt.Errorf("stored SC does not contain entrypoint '%s' scid %s \n", entrypoint, scid)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// setup block hash, height, topoheight correctly
|
2022-01-26 10:05:01 +00:00
|
|
|
state := &Shared_State{
|
2021-12-04 16:42:11 +00:00
|
|
|
Store: tx_store,
|
|
|
|
Assets: map[crypto.Hash]uint64{},
|
2022-01-26 10:05:01 +00:00
|
|
|
RamStore: map[Variable]Variable{},
|
2021-12-04 16:42:11 +00:00
|
|
|
SCIDSELF: scid,
|
2022-01-26 10:05:01 +00:00
|
|
|
Chain_inputs: &Blockchain_Input{
|
2021-12-04 16:42:11 +00:00
|
|
|
BL_HEIGHT: bl_height,
|
|
|
|
BL_TOPOHEIGHT: uint64(bl_topoheight),
|
|
|
|
BL_TIMESTAMP: bl_timestamp,
|
|
|
|
SCID: scid,
|
2022-01-26 10:05:01 +00:00
|
|
|
BLID: blid,
|
|
|
|
TXID: txid,
|
2021-12-04 16:42:11 +00:00
|
|
|
Signer: string(signer[:]),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2022-01-26 10:05:01 +00:00
|
|
|
tx_store.DiskLoader = diskloader // hook up loading from chain
|
|
|
|
tx_store.DiskLoaderRaw = diskloader_raw
|
|
|
|
tx_store.BalanceLoader = balance_loader
|
|
|
|
tx_store.BalanceAtStart = balance_at_start
|
|
|
|
tx_store.SCID = scid
|
|
|
|
tx_store.State = state
|
2021-12-04 16:42:11 +00:00
|
|
|
|
2022-01-26 10:05:01 +00:00
|
|
|
if _, ok = globals.Arguments["--debug"]; ok && globals.Arguments["--debug"] != nil && simulator {
|
|
|
|
state.Trace = true // enable tracing for dvm simulator
|
2021-12-04 16:42:11 +00:00
|
|
|
}
|
|
|
|
|
2022-01-26 10:05:01 +00:00
|
|
|
for asset, value := range incoming_value {
|
2021-12-04 16:42:11 +00:00
|
|
|
var new_value [8]byte
|
2022-01-26 10:05:01 +00:00
|
|
|
stored_value, _ := LoadSCAssetValue(data_tree, scid, asset)
|
|
|
|
binary.BigEndian.PutUint64(new_value[:], stored_value+value)
|
|
|
|
StoreSCValue(data_tree, scid, asset[:], new_value[:])
|
|
|
|
state.Assets[asset] += value
|
2021-12-04 16:42:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// we have an entrypoint, now we must setup parameters and dvm
|
|
|
|
// all parameters are in string form to bypass translation issues in middle layers
|
|
|
|
params := map[string]interface{}{}
|
|
|
|
|
|
|
|
for _, p := range function.Params {
|
|
|
|
var zerohash crypto.Hash
|
|
|
|
switch {
|
2022-01-26 10:05:01 +00:00
|
|
|
case p.Type == Uint64 && p.Name == "value":
|
2021-12-04 16:42:11 +00:00
|
|
|
params[p.Name] = fmt.Sprintf("%d", state.Assets[zerohash]) // overide value
|
2022-01-26 10:05:01 +00:00
|
|
|
case p.Type == Uint64 && SCDATA.Has(p.Name, rpc.DataUint64):
|
|
|
|
params[p.Name] = fmt.Sprintf("%d", SCDATA.Value(p.Name, rpc.DataUint64).(uint64))
|
|
|
|
case p.Type == String && SCDATA.Has(p.Name, rpc.DataString):
|
|
|
|
params[p.Name] = SCDATA.Value(p.Name, rpc.DataString).(string)
|
|
|
|
case p.Type == String && SCDATA.Has(p.Name, rpc.DataHash):
|
|
|
|
h := SCDATA.Value(p.Name, rpc.DataHash).(crypto.Hash)
|
2021-12-04 16:42:11 +00:00
|
|
|
params[p.Name] = string(h[:])
|
2022-01-26 10:05:01 +00:00
|
|
|
//fmt.Printf("%s:%x\n", p.Name, string(h[:]))
|
2021-12-04 16:42:11 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
err = fmt.Errorf("entrypoint '%s' parameter type missing or not yet supported (%+v)", entrypoint, p)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-26 10:05:01 +00:00
|
|
|
state.GasComputeLimit = int64(10000000) // everyone has fixed amount of compute gas
|
|
|
|
if state.GasComputeLimit > 0 {
|
|
|
|
state.GasComputeCheck = true
|
|
|
|
}
|
|
|
|
|
|
|
|
// gas consumed in parameters to avoid tx bloats
|
|
|
|
if gasstorage_incoming > 0 {
|
|
|
|
if gasstorage_incoming > config.MAX_STORAGE_GAS_ATOMIC_UNITS {
|
|
|
|
gasstorage_incoming = config.MAX_STORAGE_GAS_ATOMIC_UNITS // whatever gas may be provided, upper limit of gas is this
|
|
|
|
}
|
|
|
|
|
|
|
|
state.GasStoreLimit = int64(gasstorage_incoming)
|
|
|
|
state.GasStoreCheck = true
|
|
|
|
}
|
|
|
|
|
|
|
|
// deduct gas from whatever has been included in TX
|
|
|
|
var scdata_bytes []byte
|
|
|
|
if scdata_bytes, err = SCDATA.MarshalBinary(); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
scdata_length := len(scdata_bytes)
|
|
|
|
state.ConsumeStorageGas(int64(scdata_length))
|
|
|
|
|
|
|
|
result, err := RunSmartContract(&sc_parsed, entrypoint, state, params)
|
|
|
|
|
|
|
|
if state.GasComputeUsed > 0 {
|
|
|
|
gascompute = uint64(state.GasComputeUsed)
|
|
|
|
}
|
|
|
|
if state.GasStoreUsed > 0 {
|
|
|
|
gasstorage = uint64(state.GasStoreUsed)
|
|
|
|
}
|
2021-12-04 16:42:11 +00:00
|
|
|
|
|
|
|
//fmt.Printf("result value %+v\n", result)
|
|
|
|
|
|
|
|
if err != nil {
|
2022-01-26 10:05:01 +00:00
|
|
|
//logger.V(2).Error(err, "error execcuting SC", "entrypoint", entrypoint, "scid", scid)
|
2021-12-04 16:42:11 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-01-26 10:05:01 +00:00
|
|
|
if err == nil && result.Type == Uint64 && result.ValueUint64 == 0 { // confirm the changes
|
2021-12-04 16:42:11 +00:00
|
|
|
for k, v := range tx_store.RawKeys {
|
2022-01-26 10:05:01 +00:00
|
|
|
StoreSCValue(data_tree, scid, []byte(k), v)
|
2021-12-04 16:42:11 +00:00
|
|
|
|
2022-01-26 10:05:01 +00:00
|
|
|
// fmt.Printf("storing %x %x\n", k,v)
|
|
|
|
}
|
|
|
|
data_tree.Transfere = append(data_tree.Transfere, tx_store.Transfers[scid].TransferE...)
|
2021-12-04 16:42:11 +00:00
|
|
|
} else { // discard all changes, since we never write to store immediately, they are purged, however we need to return any value associated
|
|
|
|
err = fmt.Errorf("Discarded knowingly")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
//fmt.Printf("SC execution finished amount value %d\n", tx.Value)
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// reads SC, balance
|
2022-01-26 10:05:01 +00:00
|
|
|
func ReadSC(w_sc_tree *Tree_Wrapper, data_tree *Tree_Wrapper, scid crypto.Hash) (balance uint64, sc SmartContract, found bool) {
|
2021-12-04 16:42:11 +00:00
|
|
|
var zerohash crypto.Hash
|
2022-01-26 10:05:01 +00:00
|
|
|
balance, _ = LoadSCAssetValue(data_tree, scid, zerohash)
|
2021-12-04 16:42:11 +00:00
|
|
|
|
|
|
|
sc_bytes, err := data_tree.Get(SC_Code_Key(scid))
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-01-26 10:05:01 +00:00
|
|
|
var v Variable
|
2021-12-04 16:42:11 +00:00
|
|
|
if err = v.UnmarshalBinary(sc_bytes); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-01-26 10:05:01 +00:00
|
|
|
sc, pos, err := ParseSmartContract(v.ValueString)
|
2021-12-04 16:42:11 +00:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
_ = pos
|
|
|
|
|
|
|
|
found = true
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-01-26 10:05:01 +00:00
|
|
|
func LoadSCValue(data_tree *Tree_Wrapper, scid crypto.Hash, key []byte) (v Variable, found bool) {
|
2021-12-04 16:42:11 +00:00
|
|
|
//fmt.Printf("loading fromdb %s %s \n", scid, key)
|
|
|
|
|
|
|
|
object_data, err := data_tree.Get(key[:])
|
|
|
|
if err != nil {
|
|
|
|
return v, false
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(object_data) == 0 {
|
|
|
|
return v, false
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = v.UnmarshalBinary(object_data); err != nil {
|
|
|
|
return v, false
|
|
|
|
}
|
|
|
|
|
|
|
|
return v, true
|
|
|
|
}
|
|
|
|
|
2022-01-26 10:05:01 +00:00
|
|
|
func LoadSCAssetValue(data_tree *Tree_Wrapper, scid crypto.Hash, asset crypto.Hash) (v uint64, found bool) {
|
2021-12-04 16:42:11 +00:00
|
|
|
//fmt.Printf("loading fromdb %s %s \n", scid, key)
|
|
|
|
|
|
|
|
object_data, err := data_tree.Get(asset[:])
|
|
|
|
if err != nil {
|
|
|
|
return v, false
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(object_data) == 0 { // all assets are by default 0
|
|
|
|
return v, true
|
|
|
|
}
|
|
|
|
if len(object_data) != 8 {
|
|
|
|
return v, false
|
|
|
|
}
|
|
|
|
|
|
|
|
return binary.BigEndian.Uint64(object_data[:]), true
|
|
|
|
}
|
|
|
|
|
|
|
|
// reads a value from SC, always read balance
|
2022-01-26 10:05:01 +00:00
|
|
|
func ReadSCValue(data_tree *Tree_Wrapper, scid crypto.Hash, key interface{}) (value interface{}) {
|
2021-12-04 16:42:11 +00:00
|
|
|
var keybytes []byte
|
|
|
|
|
|
|
|
if key == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
switch k := key.(type) {
|
|
|
|
case uint64:
|
2022-01-26 10:05:01 +00:00
|
|
|
keybytes = DataKey{Key: Variable{Type: Uint64, ValueUint64: k}}.MarshalBinaryPanic()
|
2021-12-04 16:42:11 +00:00
|
|
|
case string:
|
2022-01-26 10:05:01 +00:00
|
|
|
keybytes = DataKey{Key: Variable{Type: String, ValueString: k}}.MarshalBinaryPanic()
|
2021-12-04 16:42:11 +00:00
|
|
|
//case int64:
|
|
|
|
// keybytes = dvm.DataKey{Key: dvm.Variable{Type: dvm.String, Value: k}}.MarshalBinaryPanic()
|
|
|
|
default:
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-01-26 10:05:01 +00:00
|
|
|
value_var, found := LoadSCValue(data_tree, scid, keybytes)
|
2021-12-04 16:42:11 +00:00
|
|
|
//fmt.Printf("read value %+v", value_var)
|
2022-01-26 10:05:01 +00:00
|
|
|
if found && value_var.Type != Invalid {
|
2021-12-04 16:42:11 +00:00
|
|
|
switch value_var.Type {
|
2022-01-26 10:05:01 +00:00
|
|
|
case Uint64:
|
2021-12-04 16:42:11 +00:00
|
|
|
value = value_var.ValueUint64
|
2022-01-26 10:05:01 +00:00
|
|
|
case String:
|
2021-12-04 16:42:11 +00:00
|
|
|
value = value_var.ValueString
|
|
|
|
default:
|
|
|
|
panic("This variable cannot be loaded")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// store the value in the chain
|
2022-01-26 10:05:01 +00:00
|
|
|
func StoreSCValue(data_tree *Tree_Wrapper, scid crypto.Hash, key, value []byte) {
|
2021-12-04 16:42:11 +00:00
|
|
|
if bytes.Compare(scid[:], key) == 0 { // an scid can mint its assets infinitely
|
|
|
|
return
|
|
|
|
}
|
|
|
|
data_tree.Put(key, value)
|
|
|
|
return
|
|
|
|
}
|