DERO-HE STARGATE Testnet Release39

This commit is contained in:
Captain 2022-01-26 10:05:01 +00:00
parent 9f67daeb86
commit 9202d01b75
No known key found for this signature in database
GPG Key ID: 18CDB3ED5E85D2D4
54 changed files with 3017 additions and 828 deletions

View File

@ -24,12 +24,17 @@
- Few more ideas implemented and will be tested for review in upcoming technology preview. - Few more ideas implemented and will be tested for review in upcoming technology preview.
- -
###3.4 ###3.4
- DAG/MINIDAG with blocks flowing every second - DAG/MINIDAG with blocks flowing every second
- Mining Decentralization.No more mining pools, daily 100000 reward blocks, no need for pools and thus no attacks - Mining Decentralization.No more mining pools, daily 100000 reward blocks, no need for pools and thus no attacks
- Erasure coded blocks, lower bandwidth requirements, very low propagation time. Tested with upto 20 MB blocks. - Erasure coded blocks, lower bandwidth requirements, very low propagation time. Tested with upto 20 MB blocks.
- DERO Simulator for faster Development cycle - DERO Simulator for faster Development cycle
- Gas Support implemented ( both storage gas/compute gas)
- Implemented gas estimation
- DVM simulator to test all edge cases for SC dev, see dvm/simulator_test.go to see it in action for lotter SC.
###3.3 ###3.3

View File

@ -45,6 +45,7 @@ import "github.com/deroproject/derohe/cryptography/crypto"
import "github.com/deroproject/derohe/errormsg" import "github.com/deroproject/derohe/errormsg"
import "github.com/deroproject/derohe/metrics" import "github.com/deroproject/derohe/metrics"
import "github.com/deroproject/derohe/dvm"
import "github.com/deroproject/derohe/block" import "github.com/deroproject/derohe/block"
import "github.com/deroproject/derohe/globals" import "github.com/deroproject/derohe/globals"
import "github.com/deroproject/derohe/transaction" import "github.com/deroproject/derohe/transaction"
@ -901,12 +902,12 @@ func (chain *Blockchain) Add_Complete_Block(cbl *block.Complete_Block) (err erro
// at this point, we must commit all the SCs, so entire tree hash is interlinked // at this point, we must commit all the SCs, so entire tree hash is interlinked
for scid, v := range sc_change_cache { for scid, v := range sc_change_cache {
meta_bytes, err := sc_meta.Get(SC_Meta_Key(scid)) meta_bytes, err := sc_meta.Get(dvm.SC_Meta_Key(scid))
if err != nil { if err != nil {
panic(err) panic(err)
} }
var meta SC_META_DATA // the meta contains metadata about SC var meta dvm.SC_META_DATA // the meta contains metadata about SC
if err := meta.UnmarshalBinary(meta_bytes); err != nil { if err := meta.UnmarshalBinary(meta_bytes); err != nil {
panic(err) panic(err)
} }
@ -915,7 +916,7 @@ func (chain *Blockchain) Add_Complete_Block(cbl *block.Complete_Block) (err erro
panic(err) panic(err)
} }
sc_meta.Put(SC_Meta_Key(scid), meta.MarshalBinary()) sc_meta.Put(dvm.SC_Meta_Key(scid), meta.MarshalBinary())
data_trees = append(data_trees, v) data_trees = append(data_trees, v)
/*fmt.Printf("will commit tree name %x \n", v.GetName()) /*fmt.Printf("will commit tree name %x \n", v.GetName())
@ -1163,6 +1164,14 @@ func (chain *Blockchain) Add_TX_To_Pool(tx *transaction.Transaction) error {
return fmt.Errorf("TX %s rejected Already mined in some block", txhash) return fmt.Errorf("TX %s rejected Already mined in some block", txhash)
} }
toporecord, err := chain.Store.Topo_store.Read(int64(tx.Height))
if err != nil {
return fmt.Errorf("TX %s rejected height(%d) reference not found", txhash, tx.Height)
}
if toporecord.BLOCK_ID != tx.BLID {
return fmt.Errorf("TX %s rejected block (%s) reference not found", txhash, tx.BLID)
}
hf_version := chain.Get_Current_Version_at_Height(int64(chain_height)) hf_version := chain.Get_Current_Version_at_Height(int64(chain_height))
// if TX is too big, then it cannot be mined due to fixed block size, reject such TXs here // if TX is too big, then it cannot be mined due to fixed block size, reject such TXs here

View File

@ -74,31 +74,31 @@ func (chain *Blockchain) install_hardcoded_contracts(cache map[crypto.Hash]*grav
// hard coded contracts generally do not do any initialization // hard coded contracts generally do not do any initialization
func (chain *Blockchain) install_hardcoded_sc(cache map[crypto.Hash]*graviton.Tree, ss *graviton.Snapshot, balance_tree *graviton.Tree, sc_tree *graviton.Tree, source string, scid crypto.Hash) (err error) { func (chain *Blockchain) install_hardcoded_sc(cache map[crypto.Hash]*graviton.Tree, ss *graviton.Snapshot, balance_tree *graviton.Tree, sc_tree *graviton.Tree, source string, scid crypto.Hash) (err error) {
w_sc_tree := &Tree_Wrapper{tree: sc_tree, entries: map[string][]byte{}} w_sc_tree := &dvm.Tree_Wrapper{Tree: sc_tree, Entries: map[string][]byte{}}
var w_sc_data_tree *Tree_Wrapper var w_sc_data_tree *dvm.Tree_Wrapper
meta := SC_META_DATA{} meta := dvm.SC_META_DATA{}
w_sc_data_tree = wrapped_tree(cache, ss, scid) w_sc_data_tree = dvm.Wrapped_tree(cache, ss, scid)
// install SC, should we check for sanity now, why or why not // install SC, should we check for sanity now, why or why not
w_sc_data_tree.Put(SC_Code_Key(scid), dvm.Variable{Type: dvm.String, ValueString: source}.MarshalBinaryPanic()) w_sc_data_tree.Put(dvm.SC_Code_Key(scid), dvm.Variable{Type: dvm.String, ValueString: source}.MarshalBinaryPanic())
w_sc_tree.Put(SC_Meta_Key(scid), meta.MarshalBinary()) w_sc_tree.Put(dvm.SC_Meta_Key(scid), meta.MarshalBinary())
// we must commit all the changes // we must commit all the changes
// anything below should never give error // anything below should never give error
if _, ok := cache[scid]; !ok { if _, ok := cache[scid]; !ok {
cache[scid] = w_sc_data_tree.tree cache[scid] = w_sc_data_tree.Tree
} }
for k, v := range w_sc_data_tree.entries { // commit entire data to tree for k, v := range w_sc_data_tree.Entries { // commit entire data to tree
if err = w_sc_data_tree.tree.Put([]byte(k), v); err != nil { if err = w_sc_data_tree.Tree.Put([]byte(k), v); err != nil {
return return
} }
} }
for k, v := range w_sc_tree.entries { for k, v := range w_sc_tree.Entries {
if err = w_sc_tree.tree.Put([]byte(k), v); err != nil { if err = w_sc_tree.Tree.Put([]byte(k), v); err != nil {
return return
} }
} }

View File

@ -103,7 +103,7 @@ func (pool *Mempool) HouseKeeping(height uint64) {
pool.txs.Range(func(k, value interface{}) bool { pool.txs.Range(func(k, value interface{}) bool {
txhash := k.(crypto.Hash) txhash := k.(crypto.Hash)
v := value.(*mempool_object) v := value.(*mempool_object)
if height >= (v.Tx.Height) { // if we have moved 10 heights, chances of reorg are almost nil if height >= (v.Tx.Height) { // remove all txs
delete_list = append(delete_list, txhash) delete_list = append(delete_list, txhash)
} }
return true return true

View File

@ -24,7 +24,6 @@ import "strings"
import "strconv" import "strconv"
import "runtime/debug" import "runtime/debug"
import "encoding/hex" import "encoding/hex"
import "encoding/binary"
import "math/big" import "math/big"
import "golang.org/x/xerrors" import "golang.org/x/xerrors"
@ -243,38 +242,6 @@ func (chain *Blockchain) process_transaction(changed map[crypto.Hash]*graviton.T
} }
} }
type Tree_Wrapper struct {
tree *graviton.Tree
entries map[string][]byte
transfere []dvm.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{}}
}
}
// does additional processing for SC // does additional processing for SC
// all processing occurs in wrapped trees, if any error occurs we dicard all trees // all processing occurs in wrapped trees, if any error occurs we dicard all trees
func (chain *Blockchain) process_transaction_sc(cache map[crypto.Hash]*graviton.Tree, ss *graviton.Snapshot, bl_height, bl_topoheight, bl_timestamp uint64, blid crypto.Hash, tx transaction.Transaction, balance_tree *graviton.Tree, sc_tree *graviton.Tree) (gas uint64, err error) { func (chain *Blockchain) process_transaction_sc(cache map[crypto.Hash]*graviton.Tree, ss *graviton.Snapshot, bl_height, bl_topoheight, bl_timestamp uint64, blid crypto.Hash, tx transaction.Transaction, balance_tree *graviton.Tree, sc_tree *graviton.Tree) (gas uint64, err error) {
@ -285,11 +252,13 @@ func (chain *Blockchain) process_transaction_sc(cache map[crypto.Hash]*graviton.
gas = tx.Fees() gas = tx.Fees()
w_balance_tree := &Tree_Wrapper{tree: balance_tree, entries: map[string][]byte{}} var gascompute, gasstorage uint64
w_sc_tree := &Tree_Wrapper{tree: sc_tree, entries: map[string][]byte{}}
_ = w_balance_tree _ = gascompute
var w_sc_data_tree *Tree_Wrapper _ = gasstorage
w_sc_tree := &dvm.Tree_Wrapper{Tree: sc_tree, Entries: map[string][]byte{}}
var w_sc_data_tree *dvm.Tree_Wrapper
txhash := tx.GetHash() txhash := tx.GetHash()
scid := txhash scid := txhash
@ -302,10 +271,22 @@ func (chain *Blockchain) process_transaction_sc(cache map[crypto.Hash]*graviton.
}() }()
if !tx.SCDATA.Has(rpc.SCACTION, rpc.DataUint64) { // tx doesn't have sc action if !tx.SCDATA.Has(rpc.SCACTION, rpc.DataUint64) { // tx doesn't have sc action
//err = fmt.Errorf("no scid provided")
return tx.Fees(), nil return tx.Fees(), nil
} }
incoming_value := map[crypto.Hash]uint64{}
for _, payload := range tx.Payloads {
incoming_value[payload.SCID] = payload.BurnValue
}
chain.Expand_Transaction_NonCoinbase(&tx)
signer, err := Extract_signer(&tx)
if err != nil { // allow anonymous SC transactions with condition that SC will not call Signer
// this allows anonymous voting and numerous other applications
// otherwise SC receives signer as all zeroes
}
action_code := rpc.SC_ACTION(tx.SCDATA.Value(rpc.SCACTION, rpc.DataUint64).(uint64)) action_code := rpc.SC_ACTION(tx.SCDATA.Value(rpc.SCACTION, rpc.DataUint64).(uint64))
switch action_code { switch action_code {
@ -329,21 +310,28 @@ func (chain *Blockchain) process_transaction_sc(cache map[crypto.Hash]*graviton.
break break
} }
meta := SC_META_DATA{} meta := dvm.SC_META_DATA{}
if _, ok := sc.Functions["InitializePrivate"]; ok { if _, ok := sc.Functions["InitializePrivate"]; ok {
meta.Type = 1 meta.Type = 1
} }
w_sc_data_tree = wrapped_tree(cache, ss, scid) w_sc_data_tree = dvm.Wrapped_tree(cache, ss, scid)
// install SC, should we check for sanity now, why or why not // install SC, should we check for sanity now, why or why not
w_sc_data_tree.Put(SC_Code_Key(scid), dvm.Variable{Type: dvm.String, ValueString: sc_code}.MarshalBinaryPanic()) w_sc_data_tree.Put(dvm.SC_Code_Key(scid), dvm.Variable{Type: dvm.String, ValueString: sc_code}.MarshalBinaryPanic())
w_sc_tree.Put(SC_Meta_Key(scid), meta.MarshalBinary()) w_sc_tree.Put(dvm.SC_Meta_Key(scid), meta.MarshalBinary())
entrypoint := "Initialize"
if meta.Type == 1 { // if its a a private SC if meta.Type == 1 { // if its a a private SC
gas, err = chain.execute_sc_function(w_sc_tree, w_sc_data_tree, scid, bl_height, bl_topoheight, bl_timestamp, blid, tx, "InitializePrivate", 1) entrypoint = "InitializePrivate"
}
balance, sc_parsed, found := dvm.ReadSC(w_sc_tree, w_sc_data_tree, scid)
if found {
gascompute, gasstorage, err = dvm.Execute_sc_function(w_sc_tree, w_sc_data_tree, scid, bl_height, bl_topoheight, bl_timestamp, blid, txhash, sc_parsed, entrypoint, 1, balance, signer, incoming_value, tx.SCDATA, tx.Fees(), chain.simulator)
} else { } else {
gas, err = chain.execute_sc_function(w_sc_tree, w_sc_data_tree, scid, bl_height, bl_topoheight, bl_timestamp, blid, tx, "Initialize", 1) logger.V(1).Error(nil, "SC not found", "scid", scid)
err = fmt.Errorf("SC not found %s", scid)
} }
if err != nil { if err != nil {
@ -364,17 +352,23 @@ func (chain *Blockchain) process_transaction_sc(cache map[crypto.Hash]*graviton.
scid = tx.SCDATA.Value(rpc.SCID, rpc.DataHash).(crypto.Hash) scid = tx.SCDATA.Value(rpc.SCID, rpc.DataHash).(crypto.Hash)
if _, err = w_sc_tree.Get(SC_Meta_Key(scid)); err != nil { if _, err = w_sc_tree.Get(dvm.SC_Meta_Key(scid)); err != nil {
err = fmt.Errorf("scid %s not installed", scid) err = fmt.Errorf("scid %s not installed", scid)
return return
} }
w_sc_data_tree = wrapped_tree(cache, ss, scid) w_sc_data_tree = dvm.Wrapped_tree(cache, ss, scid)
entrypoint := tx.SCDATA.Value("entrypoint", rpc.DataString).(string) entrypoint := tx.SCDATA.Value("entrypoint", rpc.DataString).(string)
//fmt.Printf("We must call the SC %s function\n", entrypoint) //fmt.Printf("We must call the SC %s function\n", entrypoint)
gas, err = chain.execute_sc_function(w_sc_tree, w_sc_data_tree, scid, bl_height, bl_topoheight, bl_timestamp, blid, tx, entrypoint, 1) balance, sc_parsed, found := dvm.ReadSC(w_sc_tree, w_sc_data_tree, scid)
if found {
gascompute, gasstorage, err = dvm.Execute_sc_function(w_sc_tree, w_sc_data_tree, scid, bl_height, bl_topoheight, bl_timestamp, blid, txhash, sc_parsed, entrypoint, 1, balance, signer, incoming_value, tx.SCDATA, tx.Fees(), chain.simulator)
} else {
logger.V(1).Error(nil, "SC not found", "scid", scid)
err = fmt.Errorf("SC not found %s", scid)
}
default: // unknown what to do default: // unknown what to do
err = fmt.Errorf("unknown action what to do scid %x", scid) err = fmt.Errorf("unknown action what to do scid %x", scid)
@ -384,48 +378,7 @@ func (chain *Blockchain) process_transaction_sc(cache map[crypto.Hash]*graviton.
// we must commit all the changes // we must commit all the changes
// check whether we are not overflowing/underflowing, means SC is not over sending // check whether we are not overflowing/underflowing, means SC is not over sending
if err == nil { if err == nil {
total_per_asset := map[crypto.Hash]uint64{} err = dvm.SanityCheckExternalTransfers(w_sc_data_tree, balance_tree, scid)
for _, transfer := range w_sc_data_tree.transfere { // do external tranfer
if transfer.Amount == 0 {
continue
}
// an SCID can generate it's token infinitely
if transfer.Asset != scid && total_per_asset[transfer.Asset]+transfer.Amount <= total_per_asset[transfer.Asset] {
err = fmt.Errorf("Balance calculation overflow")
break
} else {
total_per_asset[transfer.Asset] = total_per_asset[transfer.Asset] + transfer.Amount
}
}
if err == nil {
for asset, value := range total_per_asset {
stored_value, _ := chain.LoadSCAssetValue(w_sc_data_tree, scid, asset)
// an SCID can generate it's token infinitely
if asset != scid && stored_value-value > stored_value {
err = fmt.Errorf("Balance calculation underflow stored_value %d transferring %d\n", stored_value, value)
break
}
var new_value [8]byte
binary.BigEndian.PutUint64(new_value[:], stored_value-value)
chain.StoreSCValue(w_sc_data_tree, scid, asset[:], new_value[:])
}
}
//also check whether all destinations are registered
if err == nil {
for _, transfer := range w_sc_data_tree.transfere {
if _, err = balance_tree.Get([]byte(transfer.Address)); err == nil || xerrors.Is(err, graviton.ErrNotFound) {
// everything is okay
} else {
err = fmt.Errorf("account is unregistered")
logger.V(1).Error(err, "account is unregistered", "txhash", txhash, "scid", scid, "address", transfer.Address)
break
}
}
}
} }
if err != nil { // error occured, give everything to SC, since we may not have information to send them back if err != nil { // error occured, give everything to SC, since we may not have information to send them back
@ -433,111 +386,15 @@ func (chain *Blockchain) process_transaction_sc(cache map[crypto.Hash]*graviton.
logger.Error(err, "error executing sc", "txid", txhash) logger.Error(err, "error executing sc", "txid", txhash)
} }
for _, payload := range tx.Payloads { if signer, err1 := Extract_signer(&tx); err1 == nil { // if we can identify sender, return funds to him
var new_value [8]byte dvm.ErrorRevert(ss, cache, balance_tree, signer, scid, incoming_value)
} else { // we could not extract signer, give burned funds to SC
w_sc_data_tree = wrapped_tree(cache, ss, scid) // get a new tree, discarding everything dvm.ErrorRevert(ss, cache, balance_tree, signer, scid, incoming_value)
stored_value, _ := chain.LoadSCAssetValue(w_sc_data_tree, scid, payload.SCID)
binary.BigEndian.PutUint64(new_value[:], stored_value+payload.BurnValue)
chain.StoreSCValue(w_sc_data_tree, scid, payload.SCID[:], new_value[:])
for k, v := range w_sc_data_tree.entries { // commit incoming balances to tree
if err = w_sc_data_tree.tree.Put([]byte(k), v); err != nil {
return
}
}
//for k, v := range w_sc_tree.entries {
// if err = w_sc_tree.tree.Put([]byte(k), v); err != nil {
// return
// }
//}
} }
return return
} }
dvm.ProcessExternal(ss, cache, balance_tree, signer, scid, w_sc_data_tree, w_sc_tree)
// anything below should never give error
if _, ok := cache[scid]; !ok {
cache[scid] = w_sc_data_tree.tree
}
for k, v := range w_sc_data_tree.entries { // commit entire data to tree
if _, ok := globals.Arguments["--debug"]; ok && globals.Arguments["--debug"] != nil && chain.simulator {
logger.V(1).Info("Writing", "txid", txhash, "scid", scid, "key", fmt.Sprintf("%x", k), "value", fmt.Sprintf("%x", v))
}
if len(v) == 0 {
if err = w_sc_data_tree.tree.Delete([]byte(k)); err != nil {
return
}
} else {
if err = w_sc_data_tree.tree.Put([]byte(k), v); err != nil {
return
}
}
}
for k, v := range w_sc_tree.entries {
if err = w_sc_tree.tree.Put([]byte(k), v); err != nil {
return
}
}
for i, transfer := range w_sc_data_tree.transfere { // do external tranfer
if transfer.Amount == 0 {
continue
}
//fmt.Printf("%d sending to external %s %x\n", i,transfer.Asset,transfer.Address)
var zeroscid crypto.Hash
var curbtree *graviton.Tree
switch transfer.Asset {
case zeroscid: // main dero balance, handle it
curbtree = balance_tree
case scid: // this scid balance, handle it
curbtree = cache[scid]
default: // any other asset scid
var ok bool
if curbtree, ok = cache[transfer.Asset]; !ok {
if curbtree, err = ss.GetTree(string(transfer.Asset[:])); err != nil {
panic(err)
}
cache[transfer.Asset] = curbtree
}
}
if curbtree == nil {
panic("tree cannot be nil at this point in time")
}
addr_bytes := []byte(transfer.Address)
if _, err = balance_tree.Get(addr_bytes); err != nil { // first check whether address is registered
err = fmt.Errorf("sending to non registered account acc %x err %s", addr_bytes, err) // this can only occur, if account no registered or dis corruption
panic(err)
}
var balance_serialized []byte
balance_serialized, err = curbtree.Get(addr_bytes)
if err != nil && xerrors.Is(err, graviton.ErrNotFound) { // if the address is not found, lookup in main tree
var p bn256.G1
if err = p.DecodeCompressed(addr_bytes[:]); err != nil {
panic(fmt.Errorf("key %x could not be decompressed", addr_bytes))
}
balance := crypto.ConstructElGamal(&p, crypto.ElGamal_BASE_G) // init zero balance
nb := crypto.NonceBalance{NonceHeight: 0, Balance: balance}
balance_serialized = nb.Serialize()
} else if err != nil {
fmt.Printf("%s %d could not transfer %d %+v\n", scid, i, transfer.Amount, addr_bytes)
panic(err) // only disk corruption can reach here
}
nb := new(crypto.NonceBalance).Deserialize(balance_serialized)
nb.Balance = nb.Balance.Plus(new(big.Int).SetUint64(transfer.Amount)) // add transfer to users balance homomorphically
curbtree.Put(addr_bytes, nb.Serialize()) // reserialize and store
}
//c := w_sc_data_tree.tree.Cursor() //c := w_sc_data_tree.tree.Cursor()
//for k, v, err := c.First(); err == nil; k, v, err = c.Next() { //for k, v, err := c.First(); err == nil; k, v, err = c.Next() {
@ -553,7 +410,7 @@ func (chain *Blockchain) process_transaction_sc(cache map[crypto.Hash]*graviton.
// func extract signer from a tx, if possible // func extract signer from a tx, if possible
// extract signer is only possible if ring size is 2 // extract signer is only possible if ring size is 2
func extract_signer(tx *transaction.Transaction) (signer [33]byte, err error) { func Extract_signer(tx *transaction.Transaction) (signer [33]byte, err error) {
for t := range tx.Payloads { for t := range tx.Payloads {
if uint64(len(tx.Payloads[t].Statement.Publickeylist_compressed)) != tx.Payloads[t].Statement.RingSize { if uint64(len(tx.Payloads[t].Statement.Publickeylist_compressed)) != tx.Payloads[t].Statement.RingSize {
panic("tx is not expanded") panic("tx is not expanded")

View File

@ -177,7 +177,7 @@ func handle_easymenu_post_open_command(l *readline.Instance, line string) (proce
} }
if ConfirmYesNoDefaultNo(l, "Confirm Transaction (y/N)") { if ConfirmYesNoDefaultNo(l, "Confirm Transaction (y/N)") {
tx, err := wallet.TransferPayload0([]rpc.Transfer{rpc.Transfer{SCID: scid, Amount: amount_to_transfer, Destination: a.String()}}, 0, false, rpc.Arguments{}, false) // empty SCDATA tx, err := wallet.TransferPayload0([]rpc.Transfer{rpc.Transfer{SCID: scid, Amount: amount_to_transfer, Destination: a.String()}}, 0, false, rpc.Arguments{}, 0, false) // empty SCDATA
if err != nil { if err != nil {
logger.Error(err, "Error while building Transaction") logger.Error(err, "Error while building Transaction")
@ -332,7 +332,7 @@ func handle_easymenu_post_open_command(l *readline.Instance, line string) (proce
//src_port := uint64(0xffffffffffffffff) //src_port := uint64(0xffffffffffffffff)
tx, err := wallet.TransferPayload0([]rpc.Transfer{rpc.Transfer{Amount: amount_to_transfer, Destination: a.String(), Payload_RPC: arguments}}, 0, false, rpc.Arguments{}, false) // empty SCDATA tx, err := wallet.TransferPayload0([]rpc.Transfer{rpc.Transfer{Amount: amount_to_transfer, Destination: a.String(), Payload_RPC: arguments}}, 0, false, rpc.Arguments{}, 0, false) // empty SCDATA
if err != nil { if err != nil {
logger.Error(err, "Error while building Transaction") logger.Error(err, "Error while building Transaction")

View File

@ -216,7 +216,7 @@ func handle_prompt_command(l *readline.Instance, line string) {
//uid, err := wallet.PoolTransferWithBurn(addr, send_amount, burn_amount, data, rpc.Arguments{}) //uid, err := wallet.PoolTransferWithBurn(addr, send_amount, burn_amount, data, rpc.Arguments{})
tx, err := wallet.TransferPayload0([]rpc.Transfer{rpc.Transfer{Amount: send_amount, Burn: burn_amount, Destination: addr}}, 0, false, rpc.Arguments{}, false) // empty SCDATA tx, err := wallet.TransferPayload0([]rpc.Transfer{rpc.Transfer{Amount: send_amount, Burn: burn_amount, Destination: addr}}, 0, false, rpc.Arguments{}, 0, false) // empty SCDATA
if err != nil { if err != nil {
logger.Error(err, "Error while building Transaction") logger.Error(err, "Error while building Transaction")

View File

@ -0,0 +1,93 @@
// 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.
package rpc
import "fmt"
import "context"
import "strings"
//import "encoding/binary"
import "encoding/base64"
import "runtime/debug"
import "github.com/deroproject/derohe/cryptography/crypto"
//import "github.com/deroproject/derohe/config"
import "github.com/deroproject/derohe/rpc"
import "github.com/deroproject/derohe/dvm"
//import "github.com/deroproject/derohe/transaction"
//import "github.com/deroproject/derohe/blockchain"
import "github.com/deroproject/graviton"
func GetGasEstimate(ctx context.Context, p rpc.GasEstimate_Params) (result rpc.GasEstimate_Result, err error) {
defer func() { // safety so if anything wrong happens, we return error
if r := recover(); r != nil {
err = fmt.Errorf("panic occured. stack trace r %s %s", r, debug.Stack())
}
}()
if len(p.SC_Code) >= 1 && !strings.Contains(strings.ToLower(p.SC_Code), "initialize") { // decode SC from base64 if possible, since json hash limitations
if sc, err := base64.StdEncoding.DecodeString(p.SC_Code); err == nil {
p.SC_Code = string(sc)
}
}
var signer *rpc.Address
if len(p.Signer) > 0 {
if signer, err = rpc.NewAddress(p.Signer); err != nil {
return
}
}
incoming_values := map[crypto.Hash]uint64{}
for _, t := range p.Transfers {
if t.Burn > 0 {
incoming_values[t.SCID] += t.Burn
}
}
toporecord, err := chain.Store.Topo_store.Read(chain.Load_TOPO_HEIGHT())
// we must now fill in compressed ring members
if err == nil {
var ss *graviton.Snapshot
ss, err = chain.Store.Balance_store.LoadSnapshot(toporecord.State_Version)
if err == nil {
s := dvm.SimulatorInitialize(ss)
if len(p.SC_Code) >= 1 { // we need to install the SC
if _, result.GasCompute, result.GasStorage, err = s.SCInstall(p.SC_Code, incoming_values, p.SC_RPC, signer, 0); err != nil {
return
}
} else { // we need to estimate gas for already installed contract
if result.GasCompute, result.GasStorage, err = s.RunSC(incoming_values, p.SC_RPC, signer, 0); err != nil {
return
}
}
}
}
//fmt.Printf("p %+v\n", p)
result.Status = "OK"
err = nil
//logger.Debugf("result %+v\n", result);
return
}

View File

@ -17,6 +17,7 @@
package rpc package rpc
import "fmt" import "fmt"
import "bytes"
import "context" import "context"
import "runtime/debug" import "runtime/debug"
import "github.com/deroproject/derohe/config" import "github.com/deroproject/derohe/config"
@ -26,6 +27,7 @@ import "github.com/deroproject/derohe/rpc"
//import "github.com/deroproject/derohe/blockchain" //import "github.com/deroproject/derohe/blockchain"
// only give random members who have not been used in last 5 blocks
func GetRandomAddress(ctx context.Context, p rpc.GetRandomAddress_Params) (result rpc.GetRandomAddress_Result, err error) { func GetRandomAddress(ctx context.Context, p rpc.GetRandomAddress_Params) (result rpc.GetRandomAddress_Result, err error) {
defer func() { // safety so if anything wrong happens, we return error defer func() { // safety so if anything wrong happens, we return error
if r := recover(); r != nil { if r := recover(); r != nil {
@ -33,9 +35,10 @@ func GetRandomAddress(ctx context.Context, p rpc.GetRandomAddress_Params) (resul
} }
}() }()
topoheight := chain.Load_TOPO_HEIGHT() topoheight := chain.Load_TOPO_HEIGHT()
old_topoheight := topoheight
if topoheight > 100 { if old_topoheight > 100 {
topoheight -= 5 old_topoheight -= 5
} }
var cursor_list []string var cursor_list []string
@ -43,14 +46,22 @@ func GetRandomAddress(ctx context.Context, p rpc.GetRandomAddress_Params) (resul
{ {
toporecord, err := chain.Store.Topo_store.Read(topoheight) toporecord, err := chain.Store.Topo_store.Read(topoheight)
if err != nil { if err != nil {
panic(err) panic(err)
} }
toporecord_old, err := chain.Store.Topo_store.Read(old_topoheight)
if err != nil {
panic(err)
}
ss, err := chain.Store.Balance_store.LoadSnapshot(toporecord.State_Version) ss, err := chain.Store.Balance_store.LoadSnapshot(toporecord.State_Version)
if err != nil { if err != nil {
panic(err) panic(err)
} }
ss_old, err := chain.Store.Balance_store.LoadSnapshot(toporecord_old.State_Version)
if err != nil {
panic(err)
}
treename := config.BALANCE_TREE treename := config.BALANCE_TREE
if !p.SCID.IsZero() { if !p.SCID.IsZero() {
@ -61,15 +72,27 @@ func GetRandomAddress(ctx context.Context, p rpc.GetRandomAddress_Params) (resul
if err != nil { if err != nil {
panic(err) panic(err)
} }
balance_tree_old, err := ss_old.GetTree(treename)
if err != nil {
panic(err)
}
account_map := map[string]bool{} account_map := map[string]bool{}
for i := 0; i < 100; i++ { for i := 0; i < 100; i++ {
k, _, err := balance_tree.Random() k, v, err := balance_tree.Random()
if err != nil { if err != nil {
continue continue
} }
v_old, err := balance_tree_old.Get(k)
if err != nil {
continue
}
if bytes.Compare(v, v_old) != 0 {
continue
}
var acckey crypto.Point var acckey crypto.Point
if err := acckey.DecodeCompressed(k[:]); err != nil { if err := acckey.DecodeCompressed(k[:]); err != nil {

View File

@ -30,7 +30,6 @@ import "github.com/deroproject/derohe/rpc"
import "github.com/deroproject/derohe/dvm" import "github.com/deroproject/derohe/dvm"
//import "github.com/deroproject/derohe/transaction" //import "github.com/deroproject/derohe/transaction"
import "github.com/deroproject/derohe/blockchain"
import "github.com/deroproject/graviton" import "github.com/deroproject/graviton"
@ -87,7 +86,7 @@ func GetSC(ctx context.Context, p rpc.GetSC_Params) (result rpc.GetSC_Result, er
if p.Code { // give SC code if p.Code { // give SC code
var code_bytes []byte var code_bytes []byte
var v dvm.Variable var v dvm.Variable
if code_bytes, err = sc_data_tree.Get(blockchain.SC_Code_Key(scid)); err == nil { if code_bytes, err = sc_data_tree.Get(dvm.SC_Code_Key(scid)); err == nil {
if err = v.UnmarshalBinary(code_bytes); err != nil { if err = v.UnmarshalBinary(code_bytes); err != nil {
result.Code = "Unmarshal error" result.Code = "Unmarshal error"
} else { } else {

View File

@ -24,8 +24,10 @@ import "runtime/debug"
//import "github.com/romana/rlog" //import "github.com/romana/rlog"
import "github.com/deroproject/derohe/cryptography/crypto" import "github.com/deroproject/derohe/cryptography/crypto"
import "github.com/deroproject/derohe/cryptography/bn256"
import "github.com/deroproject/derohe/config" import "github.com/deroproject/derohe/config"
import "github.com/deroproject/derohe/rpc" import "github.com/deroproject/derohe/rpc"
import "github.com/deroproject/derohe/dvm"
import "github.com/deroproject/derohe/globals" import "github.com/deroproject/derohe/globals"
import "github.com/deroproject/derohe/transaction" import "github.com/deroproject/derohe/transaction"
import "github.com/deroproject/derohe/blockchain" import "github.com/deroproject/derohe/blockchain"
@ -115,7 +117,7 @@ func GetTransaction(ctx context.Context, p rpc.GetTransaction_Params) (result rp
if sc_data_tree, err := ss.GetTree(string(scid[:])); err == nil { if sc_data_tree, err := ss.GetTree(string(scid[:])); err == nil {
var code_bytes []byte var code_bytes []byte
if code_bytes, err = sc_data_tree.Get(blockchain.SC_Code_Key(scid)); err == nil { if code_bytes, err = sc_data_tree.Get(dvm.SC_Code_Key(scid)); err == nil {
related.Code = string(code_bytes) related.Code = string(code_bytes)
} }
@ -149,6 +151,15 @@ func GetTransaction(ctx context.Context, p rpc.GetTransaction_Params) (result rp
related.Ring = append(related.Ring, ring) related.Ring = append(related.Ring, ring)
} }
if signer, err1 := blockchain.Extract_signer(&tx); err1 == nil {
var p bn256.G1
if err = p.DecodeCompressed(signer[:]); err == nil {
s := rpc.NewAddressFromKeys((*crypto.Point)(&p))
s.Mainnet = globals.Config.Name == config.Mainnet.Name
related.Signer = s.String()
}
}
} }
} }
} }

View File

@ -115,11 +115,14 @@ func Notify_MiniBlock_Addition() {
chain.RPC_NotifyNewMiniBlock.L.Lock() chain.RPC_NotifyNewMiniBlock.L.Lock()
chain.RPC_NotifyNewMiniBlock.Wait() chain.RPC_NotifyNewMiniBlock.Wait()
chain.RPC_NotifyNewMiniBlock.L.Unlock() chain.RPC_NotifyNewMiniBlock.L.Unlock()
if globals.Arguments["--simulator"] == nil || (globals.Arguments["--simulator"] != nil && globals.Arguments["--simulator"].(bool) == false) {
go func() { go func() {
defer globals.Recover(2) defer globals.Recover(2)
SendJob() SendJob()
}() }()
} }
}
} }
func Notify_Height_Changes() { func Notify_Height_Changes() {
@ -310,6 +313,7 @@ var historical_apis = handler.Map{"getinfo": handler.New(GetInfo),
"getblocktemplate": handler.New(GetBlockTemplate), "getblocktemplate": handler.New(GetBlockTemplate),
"getencryptedbalance": handler.New(GetEncryptedBalance), "getencryptedbalance": handler.New(GetEncryptedBalance),
"getsc": handler.New(GetSC), "getsc": handler.New(GetSC),
"getgasestimate": handler.New(GetGasEstimate),
"nametoaddress": handler.New(NameToAddress)} "nametoaddress": handler.New(NameToAddress)}
var servicemux = handler.ServiceMap{ var servicemux = handler.ServiceMap{
@ -331,6 +335,7 @@ var servicemux = handler.ServiceMap{
"GetBlockTemplate": handler.New(GetBlockTemplate), "GetBlockTemplate": handler.New(GetBlockTemplate),
"GetEncryptedBalance": handler.New(GetEncryptedBalance), "GetEncryptedBalance": handler.New(GetEncryptedBalance),
"GetSC": handler.New(GetSC), "GetSC": handler.New(GetSC),
"GetGasEstimate": handler.New(GetGasEstimate),
"NameToAddress": handler.New(NameToAddress), "NameToAddress": handler.New(NameToAddress),
}, },
"DAEMON": handler.Map{ "DAEMON": handler.Map{

View File

@ -474,6 +474,7 @@ func load_tx_info_from_tx(info *txinfo, tx *transaction.Transaction) (err error)
if tx.TransactionType == transaction.SC_TX { if tx.TransactionType == transaction.SC_TX {
info.SC_Args = tx.SCDATA info.SC_Args = tx.SCDATA
} }
// if outputs cannot be located, do not panic // if outputs cannot be located, do not panic
@ -556,6 +557,10 @@ func load_tx_from_rpc(info *txinfo, txhash string) (err error) {
info.Ring = tx_result.Txs[0].Ring info.Ring = tx_result.Txs[0].Ring
if tx.TransactionType == transaction.SC_TX {
info.SC_Signer = "Unknown"
}
if tx.TransactionType == transaction.NORMAL || tx.TransactionType == transaction.BURN_TX || tx.TransactionType == transaction.SC_TX { if tx.TransactionType == transaction.NORMAL || tx.TransactionType == transaction.BURN_TX || tx.TransactionType == transaction.SC_TX {
for t := range tx.Payloads { for t := range tx.Payloads {
@ -570,6 +575,7 @@ func load_tx_from_rpc(info *txinfo, txhash string) (err error) {
a.Ring_size = len(tx_result.Txs[0].Ring[t]) a.Ring_size = len(tx_result.Txs[0].Ring[t])
a.Ring = tx_result.Txs[0].Ring[t] a.Ring = tx_result.Txs[0].Ring[t]
info.SC_Signer = tx_result.Txs[0].Signer
info.Assets = append(info.Assets, a) info.Assets = append(info.Assets, a)

View File

@ -103,8 +103,13 @@
{{range $ii, $ee := .info.Assets}} {{range $ii, $ee := .info.Assets}}
{{if eq $ee.SCID "0000000000000000000000000000000000000000000000000000000000000000" }} {{if eq $ee.SCID "0000000000000000000000000000000000000000000000000000000000000000" }}
<H5>DERO : {{$ee.Ring_size}} inputs/outputs (RING size) Fees {{$ee.Fees}} <H5>DERO : {{$ee.Ring_size}} inputs/outputs (RING size) Fees {{$ee.Fees}}
{{if eq $.info.TransactionType "SC"}} {{if eq $.info.TransactionType "SC"}}
Deposited to SC {{$ee.Burn}} Deposited to SC {{$ee.Burn}}
{{else}} {{else}}
@ -112,6 +117,7 @@
{{end}} {{end}}
</H5> </H5>
{{if eq $.info.TransactionType "SC"}} <H5>Sender : {{ $.info.SC_Signer }} </H5> {{end}}
{{else}} {{else}}
<H5>Token: {{$ee.SCID}} {{$ee.Ring_size}} inputs/outputs (RING size) Fees {{$ee.Fees}} {{if eq $.info.TransactionType "SC"}} <H5>Token: {{$ee.SCID}} {{$ee.Ring_size}} inputs/outputs (RING size) Fees {{$ee.Fees}} {{if eq $.info.TransactionType "SC"}}

View File

@ -211,7 +211,7 @@ func Test_Creation_TX(t *testing.T) {
// here we are collecting proofs for later on bennhcmarking // here we are collecting proofs for later on bennhcmarking
for j := 2; j <= 128; j = j * 2 { for j := 2; j <= 128; j = j * 2 {
wsrc.SetRingSize(j) wsrc.SetRingSize(j)
tx, err := wsrc.TransferPayload0([]rpc.Transfer{rpc.Transfer{Destination: wdst.GetAddress().String(), Amount: 1}}, 0, false, rpc.Arguments{}, false) tx, err := wsrc.TransferPayload0([]rpc.Transfer{rpc.Transfer{Destination: wdst.GetAddress().String(), Amount: 1}}, 0, false, rpc.Arguments{}, 0, false)
if err != nil { if err != nil {
t.Fatalf("Cannot create transaction, err %s", err) t.Fatalf("Cannot create transaction, err %s", err)
} else { } else {
@ -228,7 +228,7 @@ func Test_Creation_TX(t *testing.T) {
wdst.SetRingSize(2) wdst.SetRingSize(2)
// accounts are reversed // accounts are reversed
reverse_tx, err := wdst.TransferPayload0([]rpc.Transfer{rpc.Transfer{Destination: wsrc.GetAddress().String(), Amount: 1}}, 0, false, rpc.Arguments{}, false) reverse_tx, err := wdst.TransferPayload0([]rpc.Transfer{rpc.Transfer{Destination: wsrc.GetAddress().String(), Amount: 1}}, 0, false, rpc.Arguments{}, 0, false)
if err != nil { if err != nil {
t.Fatalf("Cannot create transaction, err %s", err) t.Fatalf("Cannot create transaction, err %s", err)
} }
@ -242,7 +242,7 @@ func Test_Creation_TX(t *testing.T) {
pre_transfer_src_balance, _ := wsrc.Get_Balance() pre_transfer_src_balance, _ := wsrc.Get_Balance()
pre_transfer_dst_balance, _ := wdst.Get_Balance() pre_transfer_dst_balance, _ := wdst.Get_Balance()
tx, err := wsrc.TransferPayload0([]rpc.Transfer{rpc.Transfer{Destination: wdst.GetAddress().String(), Amount: 1}}, 0, false, rpc.Arguments{}, false) tx, err := wsrc.TransferPayload0([]rpc.Transfer{rpc.Transfer{Destination: wdst.GetAddress().String(), Amount: 1}}, 0, false, rpc.Arguments{}, 0, false)
if err != nil { if err != nil {
t.Fatalf("Cannot create transaction, err %s", err) t.Fatalf("Cannot create transaction, err %s", err)
} else { } else {
@ -284,7 +284,7 @@ func Test_Creation_TX(t *testing.T) {
var tx_set []*transaction.Transaction var tx_set []*transaction.Transaction
for i := 0; i < 6; i++ { for i := 0; i < 6; i++ {
tx, err := wsrc.TransferPayload0([]rpc.Transfer{rpc.Transfer{Destination: wdst.GetAddress().String(), Amount: 1}}, 0, false, rpc.Arguments{}, false) tx, err := wsrc.TransferPayload0([]rpc.Transfer{rpc.Transfer{Destination: wdst.GetAddress().String(), Amount: 1}}, 0, false, rpc.Arguments{}, 0, false)
if err != nil { if err != nil {
t.Fatalf("Cannot create transaction, err %s", err) t.Fatalf("Cannot create transaction, err %s", err)
} else { } else {

View File

@ -21,6 +21,7 @@ import "os"
import "time" import "time"
import "fmt" import "fmt"
import "bytes" import "bytes"
import "errors"
import "strings" import "strings"
import "strconv" import "strconv"
@ -67,7 +68,7 @@ DERO : A secure, private blockchain with smart-contracts
Simulates DERO block single node which helps in development and tests Simulates DERO block single node which helps in development and tests
Usage: Usage:
simulator [--help] [--version] [--testnet] [--debug] [--sync-node] [--data-dir=<directory>] [--rpc-bind=<127.0.0.1:9999>] [--http-address=<0.0.0.0:8080>] [--clog-level=1] [--flog-level=1] simulator [--help] [--version] [--testnet] [--debug] [--noautomine] [--sync-node] [--data-dir=<directory>] [--rpc-bind=<127.0.0.1:9999>] [--http-address=<0.0.0.0:8080>] [--clog-level=1] [--flog-level=1]
simulator -h | --help simulator -h | --help
simulator --version simulator --version
@ -76,6 +77,7 @@ Options:
--version Show version. --version Show version.
--testnet Run in testnet mode. --testnet Run in testnet mode.
--debug Debug mode enabled, print more log messages --debug Debug mode enabled, print more log messages
--noautomine No blocks will be mined (except genesis), used for testing, supported only on linux
--clog-level=1 Set console log level (0 to 127) --clog-level=1 Set console log level (0 to 127)
--flog-level=1 Set file log level (0 to 127) --flog-level=1 Set file log level (0 to 127)
--data-dir=<directory> Store blockchain data at this location --data-dir=<directory> Store blockchain data at this location
@ -89,6 +91,8 @@ var logger logr.Logger
var rpcport = "127.0.0.1:20000" var rpcport = "127.0.0.1:20000"
var TRIGGER_MINE_BLOCK string = "/dev/shm/mineblocknow"
const wallet_ports_start = 30000 // all wallets will rpc activated on ports const wallet_ports_start = 30000 // all wallets will rpc activated on ports
// this is a crude function used during tests // this is a crude function used during tests
@ -100,16 +104,17 @@ func Mine_block_single(chain *blockchain.Blockchain, miner_address rpc.Address)
// return fmt.Errorf("this function can only run in simulator mode") // return fmt.Errorf("this function can only run in simulator mode")
//} //}
for i := uint64(0); i < config.BLOCK_TIME-config.MINIBLOCK_HIGHDIFF; i++ { for {
bl, mbl, _, _, err := chain.Create_new_block_template_mining(miner_address) bl, mbl, _, _, err := chain.Create_new_block_template_mining(miner_address)
if err != nil { if err != nil {
logger.Error(err, "err while request block template") logger.Error(err, "err while request block template")
return err return err
} }
if _, blid, _, err = chain.Accept_new_block(bl.Timestamp, mbl.Serialize()); blid.IsZero() || err != nil { if _, blid, _, err = chain.Accept_new_block(bl.Timestamp, mbl.Serialize()); err != nil {
if err != nil {
logger.Error(err, "err while accepting block template") logger.Error(err, "err while accepting block template")
} return err
} else if !blid.IsZero() {
break
} }
} }
return nil return nil
@ -546,26 +551,46 @@ exit:
} }
} }
func exists(path string) bool {
_, err := os.Stat(path)
return !errors.Is(err, os.ErrNotExist)
}
func trigger_block_creation() {
fmt.Printf("triggering fie creation\n")
if globals.Arguments["--noautomine"].(bool) == true {
err := os.WriteFile(TRIGGER_MINE_BLOCK, []byte("HELLO"), 0666)
fmt.Printf("triggering fie creation %s err %s\n", TRIGGER_MINE_BLOCK, err)
}
}
// generate a block as soon as tx appears in blockchain // generate a block as soon as tx appears in blockchain
// or 15 sec pass // or 15 sec pass
func mine_block_auto(chain *blockchain.Blockchain, miner_address rpc.Address) { func mine_block_auto(chain *blockchain.Blockchain, miner_address rpc.Address) {
last_block_time := time.Now() last_block_time := time.Now()
for { for {
bl, _, _, _, err := chain.Create_new_block_template_mining(miner_address) bl, _, _, _, err := chain.Create_new_block_template_mining(miner_address)
if err != nil { if err != nil {
logger.Error(err, "error while building mining block") logger.Error(err, "error while building mining block")
}
if time.Now().Sub(last_block_time) > time.Duration(config.BLOCK_TIME)*time.Second || // every X secs generate a block
len(bl.Tx_hashes) >= 1 { //pools have a tx, try to mine them ASAP
if err := Mine_block_single(chain, miner_address); err != nil {
time.Sleep(time.Second)
continue continue
} }
if globals.Arguments["--noautomine"].(bool) == true && exists(TRIGGER_MINE_BLOCK) {
if err = Mine_block_single(chain, miner_address); err == nil {
last_block_time = time.Now() last_block_time = time.Now()
os.Remove(TRIGGER_MINE_BLOCK)
} else {
logger.Error(err, "error while mining single block")
}
}
if globals.Arguments["--noautomine"].(bool) == false {
if time.Now().Sub(last_block_time) > time.Duration(config.BLOCK_TIME)*time.Second || // every X secs generate a block
len(bl.Tx_hashes) >= 1 { //pools have a tx, try to mine them ASAP
if err := Mine_block_single(chain, miner_address); err == nil {
last_block_time = time.Now()
}
}
} }
time.Sleep(900 * time.Millisecond) time.Sleep(900 * time.Millisecond)
} }

View File

@ -39,8 +39,12 @@ const SC_META = "M" // keeps all SCs balance, their state, their OWNER, the
// one are open SCs, which provide i/o privacy // one are open SCs, which provide i/o privacy
// one are private SCs which are truly private, in which no one has visibility of io or functionality // one are private SCs which are truly private, in which no one has visibility of io or functionality
// 10.25 MB block every 12 secs is equal to roughly 410 TX per second // this limits the contract size or amount of data it can store per interaction
// if we consider side blocks, TPS increase to > 500 TPS const MAX_STORAGE_GAS_ATOMIC_UNITS = 20000
// Minimum FEE calculation constants are here
const FEE_PER_KB = uint64(100) // .00100 dero per kb
// we can easily improve TPS by changing few parameters in this file // we can easily improve TPS by changing few parameters in this file
// the resources compute/network may not be easy for the developing countries // the resources compute/network may not be easy for the developing countries
// we need to trade of TPS as per community // we need to trade of TPS as per community
@ -51,9 +55,6 @@ const STARGATE_HE_MAX_TX_SIZE = 300 * 1024 // max size
const MIN_RINGSIZE = 2 // >= 2 , ringsize will be accepted const MIN_RINGSIZE = 2 // >= 2 , ringsize will be accepted
const MAX_RINGSIZE = 128 // <= 128, ringsize will be accepted const MAX_RINGSIZE = 128 // <= 128, ringsize will be accepted
// Minimum FEE calculation constants are here
const FEE_PER_KB = uint64(100) // .00100 dero per kb
type SettingsStruct struct { type SettingsStruct struct {
MAINNET_BOOTSTRAP_DIFFICULTY uint64 `env:"MAINNET_BOOTSTRAP_DIFFICULTY" envDefault:"80000000"` MAINNET_BOOTSTRAP_DIFFICULTY uint64 `env:"MAINNET_BOOTSTRAP_DIFFICULTY" envDefault:"80000000"`
MAINNET_MINIMUM_DIFFICULTY uint64 `env:"MAINNET_MINIMUM_DIFFICULTY" envDefault:"80000000"` MAINNET_MINIMUM_DIFFICULTY uint64 `env:"MAINNET_MINIMUM_DIFFICULTY" envDefault:"80000000"`
@ -106,7 +107,7 @@ var Mainnet = CHAIN_CONFIG{Name: "mainnet",
} }
var Testnet = CHAIN_CONFIG{Name: "testnet", // testnet will always have last 3 bytes 0 var Testnet = CHAIN_CONFIG{Name: "testnet", // testnet will always have last 3 bytes 0
Network_ID: uuid.FromBytesOrNil([]byte{0x59, 0xd7, 0xf7, 0xe9, 0xdd, 0x48, 0xd5, 0xfd, 0x13, 0x0a, 0xf6, 0xe0, 0x77, 0x00, 0x00, 0x00}), Network_ID: uuid.FromBytesOrNil([]byte{0x59, 0xd7, 0xf7, 0xe9, 0xdd, 0x48, 0xd5, 0xfd, 0x13, 0x0a, 0xf6, 0xe0, 0x78, 0x00, 0x00, 0x00}),
GETWORK_Default_Port: 10100, GETWORK_Default_Port: 10100,
P2P_Default_Port: 40401, P2P_Default_Port: 40401,
RPC_Default_Port: 40402, RPC_Default_Port: 40402,

View File

@ -20,4 +20,4 @@ import "github.com/blang/semver/v4"
// right now it has to be manually changed // right now it has to be manually changed
// do we need to include git commitsha?? // do we need to include git commitsha??
var Version = semver.MustParse("3.4.99-0.DEROHE.STARGATE+03012022") var Version = semver.MustParse("3.4.103-0.DEROHE.STARGATE+18012022")

View File

@ -157,10 +157,10 @@ func check_valid_name(name string) bool {
} }
func check_valid_type(name string) Vtype { func check_valid_type(name string) Vtype {
switch name { switch strings.ToLower(name) {
case "Uint64": case "uint64":
return Uint64 return Uint64
case "String": case "string":
return String return String
} }
return Invalid return Invalid
@ -406,9 +406,16 @@ type Blockchain_Input struct {
// this is necessary to prevent number of attacks // this is necessary to prevent number of attacks
type Shared_State struct { type Shared_State struct {
SCIDZERO crypto.Hash // points to DERO SCID , which is zero SCIDZERO crypto.Hash // points to DERO SCID , which is zero
SCIDSELF crypto.Hash // points to SELF SCID SCIDSELF crypto.Hash // points to SELF SCID, this separation is necessary, if we enable cross SC calls
// but note they bring all sorts of mess, bugs
Persistance bool // whether the results will be persistant or it's just a demo/test call Persistance bool // whether the results will be persistant or it's just a demo/test call
Trace bool // enables tracing to screen Trace bool // enables tracing to screen
GasComputeUsed int64
GasComputeLimit int64
GasComputeCheck bool // if gascheck is true, bail out as soon as limit is breached
GasStoreUsed int64
GasStoreLimit int64
GasStoreCheck bool // storage gas, bail out as soon as limit is breached
Chain_inputs *Blockchain_Input // all blockchain info is available here Chain_inputs *Blockchain_Input // all blockchain info is available here
@ -416,6 +423,8 @@ type Shared_State struct {
Assets_Transfer map[string]map[string]uint64 // any Assets that this TX wants to send OUT Assets_Transfer map[string]map[string]uint64 // any Assets that this TX wants to send OUT
// transfers are only processed after the contract has terminated successfully // transfers are only processed after the contract has terminated successfully
RamStore map[Variable]Variable
RND *RND // this is initialized only once while invoking entrypoint RND *RND // this is initialized only once while invoking entrypoint
Store *TX_Storage // mechanism to access a data store, can discard changes Store *TX_Storage // mechanism to access a data store, can discard changes
@ -425,10 +434,28 @@ type Shared_State struct {
} }
//consumr and check compute gas
func (state *Shared_State) ConsumeGas(c int64) {
if state != nil {
state.GasComputeUsed += c
if state.GasComputeCheck && state.GasComputeUsed > state.GasComputeLimit {
panic("Insufficient Gas")
}
}
}
// consume and check storage gas
func (state *Shared_State) ConsumeStorageGas(c int64) {
if state != nil {
state.GasStoreUsed += c
if state.GasStoreCheck && state.GasStoreUsed > state.GasStoreLimit {
panic("Insufficient Storage Gas")
}
}
}
type DVM_Interpreter struct { type DVM_Interpreter struct {
Version semver.Version // current version set by setversion Version semver.Version // current version set by setversion
Cost int64
CostLimit int64
SCID string SCID string
SC *SmartContract SC *SmartContract
EntryPoint string EntryPoint string
@ -449,10 +476,8 @@ type DVM_Interpreter struct {
func (i *DVM_Interpreter) incrementIP(newip uint64) (line []string, err error) { func (i *DVM_Interpreter) incrementIP(newip uint64) (line []string, err error) {
var ok bool var ok bool
try_again: try_again:
// increase cost here
if newip == 0 { // we are simply falling through if newip == 0 { // we are simply falling through
index := i.f.LinesNumberIndex[i.IP] // find the pos in the line numbers and find the current line_number index := i.f.LinesNumberIndex[i.IP] // find the pos in the line numbers and find the current line_number
index++ index++
@ -493,6 +518,8 @@ func (i *DVM_Interpreter) interpret_SmartContract() (err error) {
return return
} }
i.State.ConsumeGas(5000) // every line number has some gas costs
newIP = 0 // this is necessary otherwise, it will trigger an infinite loop in the case given below newIP = 0 // this is necessary otherwise, it will trigger an infinite loop in the case given below
/* /*
@ -963,6 +990,8 @@ func IsZero(value interface{}) uint64 {
func (dvm *DVM_Interpreter) evalBinaryExpr(exp *ast.BinaryExpr) interface{} { func (dvm *DVM_Interpreter) evalBinaryExpr(exp *ast.BinaryExpr) interface{} {
dvm.State.ConsumeGas(800) // every expr evaluation has some cost
left := dvm.eval(exp.X) left := dvm.eval(exp.X)
right := dvm.eval(exp.Y) right := dvm.eval(exp.Y)
@ -992,7 +1021,6 @@ func (dvm *DVM_Interpreter) evalBinaryExpr(exp *ast.BinaryExpr) interface{} {
return uint64(1) return uint64(1)
} }
return uint64(0) return uint64(0)
} }
// handle string operands // handle string operands
@ -1002,6 +1030,9 @@ func (dvm *DVM_Interpreter) evalBinaryExpr(exp *ast.BinaryExpr) interface{} {
switch exp.Op { switch exp.Op {
case token.ADD: case token.ADD:
if len(left_string)+len(right_string) >= 1024*1024 {
panic("too big string value")
}
return left_string + right_string return left_string + right_string
case token.EQL: case token.EQL:
if left_string == right_string { if left_string == right_string {
@ -1016,7 +1047,6 @@ func (dvm *DVM_Interpreter) evalBinaryExpr(exp *ast.BinaryExpr) interface{} {
default: default:
panic(fmt.Sprintf("String data type only support addition operation ('%s') not supported", exp.Op)) panic(fmt.Sprintf("String data type only support addition operation ('%s') not supported", exp.Op))
} }
} }
left_uint64 := left.(uint64) left_uint64 := left.(uint64)
@ -1075,61 +1105,3 @@ func (dvm *DVM_Interpreter) evalBinaryExpr(exp *ast.BinaryExpr) interface{} {
} }
return uint64(0) return uint64(0)
} }
/*
func main() {
const src = `
Function HelloWorld(s Uint64) Uint64
5
10 Dim x1, x2 as Uint64
20 LET x1 = 3
25 LET x1 = 3 + 5 - 1
27 LET x2 = x1 + 3
28 RETURN HelloWorld2(s*s)
30 Printf "x1=%d x2=%d s = %d" x1 x2 s
35 IF x1 == 7 THEN GOTO 100 ELSE GOTO 38
38 Dim y1, y2 as String
40 LET y1 = "first string" + "second string"
60 GOTO 100
80 GOTO 10
100 RETURN 0
500 LET y = 45
501 y = 45
End Function
Function HelloWorld2(s Uint64) Uint64
900 Return s
950 y = 45
// Comment begins at column 5.
; This line should not be included in the output.
REM jj
7000 let x.ku[1+1]=0x55
End Function
`
// we should be build an AST here
sc, pos, err := ParseSmartContract(src)
if err != nil {
fmt.Printf("Error while parsing smart contract pos %s err : %s\n", pos, err)
return
}
result, err := RunSmartContract(&sc, "HelloWorld", map[string]interface{}{"s": "9999"})
fmt.Printf("result %+v err %s\n", result, err)
}
*/

View File

@ -39,74 +39,81 @@ import "github.com/deroproject/derohe/cryptography/crypto"
// this needs more investigation // this needs more investigation
// also, more investigation is required to enable predetermined external oracles // also, more investigation is required to enable predetermined external oracles
type DVM_FUNCTION_PTR func(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result interface{}) type DVM_FUNCTION_PTR_UINT64 func(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result uint64)
type DVM_FUNCTION_PTR_STRING func(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result string)
type DVM_FUNCTION_PTR_ANY func(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result interface{})
var func_table = map[string][]func_data{} var func_table = map[string][]func_data{}
type func_data struct { type func_data struct {
Range semver.Range Range semver.Range
Cost int64 ComputeCost int64
Ptr DVM_FUNCTION_PTR StorageCost int64
PtrU DVM_FUNCTION_PTR_UINT64
PtrS DVM_FUNCTION_PTR_STRING
Ptr DVM_FUNCTION_PTR_ANY
} }
func init() { func init() {
func_table["version"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), Cost: 1, Ptr: dvm_version}} func_table["version"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), ComputeCost: 1000, StorageCost: 0, PtrU: dvm_version}}
func_table["load"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), Cost: 1, Ptr: dvm_load}} func_table["load"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), ComputeCost: 5000, StorageCost: 0, Ptr: dvm_load}}
func_table["exists"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), Cost: 1, Ptr: dvm_exists}} func_table["exists"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), ComputeCost: 5000, StorageCost: 0, PtrU: dvm_exists}}
func_table["store"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), Cost: 1, Ptr: dvm_store}} func_table["store"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), ComputeCost: 10000, StorageCost: 0, PtrU: dvm_store}}
func_table["delete"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), Cost: 1, Ptr: dvm_delete}} func_table["delete"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), ComputeCost: 3000, StorageCost: 0, PtrU: dvm_delete}}
func_table["random"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), Cost: 1, Ptr: dvm_random}} func_table["mapexists"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), ComputeCost: 1000, StorageCost: 0, PtrU: dvm_mapexists}}
func_table["scid"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), Cost: 1, Ptr: dvm_scid}} func_table["mapget"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), ComputeCost: 1000, StorageCost: 0, Ptr: dvm_mapget}}
func_table["blid"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), Cost: 1, Ptr: dvm_blid}} func_table["mapstore"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), ComputeCost: 1000, StorageCost: 0, PtrU: dvm_mapstore}}
func_table["txid"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), Cost: 1, Ptr: dvm_txid}} func_table["mapdelete"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), ComputeCost: 1000, StorageCost: 0, PtrU: dvm_mapdelete}}
func_table["dero"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), Cost: 1, Ptr: dvm_dero}} func_table["random"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), ComputeCost: 2500, StorageCost: 0, PtrU: dvm_random}}
func_table["block_height"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), Cost: 1, Ptr: dvm_block_height}} func_table["scid"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), ComputeCost: 2000, StorageCost: 0, PtrS: dvm_scid}}
func_table["block_timestamp"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), Cost: 1, Ptr: dvm_block_timestamp}} func_table["blid"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), ComputeCost: 2000, StorageCost: 0, PtrS: dvm_blid}}
func_table["signer"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), Cost: 1, Ptr: dvm_signer}} func_table["txid"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), ComputeCost: 2000, StorageCost: 0, PtrS: dvm_txid}}
func_table["update_sc_code"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), Cost: 1, Ptr: dvm_update_sc_code}} func_table["dero"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), ComputeCost: 10000, StorageCost: 0, PtrS: dvm_dero}}
func_table["is_address_valid"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), Cost: 1, Ptr: dvm_is_address_valid}} func_table["block_height"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), ComputeCost: 2000, StorageCost: 0, PtrU: dvm_block_height}}
func_table["address_raw"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), Cost: 1, Ptr: dvm_address_raw}} func_table["block_timestamp"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), ComputeCost: 2500, StorageCost: 0, PtrU: dvm_block_timestamp}}
func_table["address_string"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), Cost: 1, Ptr: dvm_address_string}} func_table["signer"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), ComputeCost: 5000, StorageCost: 0, PtrS: dvm_signer}}
func_table["send_dero_to_address"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), Cost: 1, Ptr: dvm_send_dero_to_address}} func_table["update_sc_code"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), ComputeCost: 5000, StorageCost: 0, PtrU: dvm_update_sc_code}}
func_table["send_asset_to_address"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), Cost: 1, Ptr: dvm_send_asset_to_address}} func_table["is_address_valid"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), ComputeCost: 50000, StorageCost: 0, PtrU: dvm_is_address_valid}}
func_table["derovalue"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), Cost: 1, Ptr: dvm_derovalue}} func_table["address_raw"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), ComputeCost: 60000, StorageCost: 0, PtrS: dvm_address_raw}}
func_table["assetvalue"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), Cost: 1, Ptr: dvm_assetvalue}} func_table["address_string"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), ComputeCost: 50000, StorageCost: 0, PtrS: dvm_address_string}}
func_table["atoi"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), Cost: 1, Ptr: dvm_atoi}} func_table["send_dero_to_address"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), ComputeCost: 70000, StorageCost: 0, PtrU: dvm_send_dero_to_address}}
func_table["itoa"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), Cost: 1, Ptr: dvm_itoa}} func_table["send_asset_to_address"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), ComputeCost: 90000, StorageCost: 0, PtrU: dvm_send_asset_to_address}}
func_table["sha256"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), Cost: 1, Ptr: dvm_sha256}} func_table["derovalue"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), ComputeCost: 10000, StorageCost: 0, PtrU: dvm_derovalue}}
func_table["sha3256"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), Cost: 1, Ptr: dvm_sha3256}} func_table["assetvalue"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), ComputeCost: 10000, StorageCost: 0, PtrU: dvm_assetvalue}}
func_table["keccak256"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), Cost: 1, Ptr: dvm_keccak256}} func_table["atoi"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), ComputeCost: 5000, StorageCost: 0, PtrU: dvm_atoi}}
func_table["hex"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), Cost: 1, Ptr: dvm_hex}} func_table["itoa"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), ComputeCost: 5000, StorageCost: 0, PtrS: dvm_itoa}}
func_table["hexdecode"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), Cost: 1, Ptr: dvm_hexdecode}} func_table["sha256"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), ComputeCost: 25000, StorageCost: 0, PtrS: dvm_sha256}}
func_table["min"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), Cost: 1, Ptr: dvm_min}} func_table["sha3256"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), ComputeCost: 25000, StorageCost: 0, PtrS: dvm_sha3256}}
func_table["max"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), Cost: 1, Ptr: dvm_max}} func_table["keccak256"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), ComputeCost: 25000, StorageCost: 0, PtrS: dvm_keccak256}}
func_table["strlen"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), Cost: 1, Ptr: dvm_strlen}} func_table["hex"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), ComputeCost: 10000, StorageCost: 0, PtrS: dvm_hex}}
func_table["substr"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), Cost: 1, Ptr: dvm_substr}} func_table["hexdecode"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), ComputeCost: 10000, StorageCost: 0, PtrS: dvm_hexdecode}}
} func_table["min"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), ComputeCost: 5000, StorageCost: 0, PtrU: dvm_min}}
func_table["max"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), ComputeCost: 5000, StorageCost: 0, PtrU: dvm_max}}
func (dvm *DVM_Interpreter) process_cost(c int64) { func_table["strlen"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), ComputeCost: 20000, StorageCost: 0, PtrU: dvm_strlen}}
dvm.Cost += c func_table["substr"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), ComputeCost: 20000, StorageCost: 0, PtrS: dvm_substr}}
func_table["panic"] = []func_data{func_data{Range: semver.MustParseRange(">=0.0.0"), ComputeCost: 10000, StorageCost: 0, PtrU: dvm_panic}}
// TODO check cost overflow
} }
// this will handle all internal functions which may be required/necessary to expand DVM functionality // this will handle all internal functions which may be required/necessary to expand DVM functionality
func (dvm *DVM_Interpreter) Handle_Internal_Function(expr *ast.CallExpr, func_name string) (handled bool, result interface{}) { func (dvm *DVM_Interpreter) Handle_Internal_Function(expr *ast.CallExpr, func_name string) (handled bool, result interface{}) {
var err error
_ = err
if funcs, ok := func_table[strings.ToLower(func_name)]; ok { if func_data_array, ok := func_table[strings.ToLower(func_name)]; ok {
for _, f := range funcs { for _, f := range func_data_array {
if f.Range(dvm.Version) { if f.Range(dvm.Version) {
dvm.process_cost(f.Cost) dvm.State.ConsumeGas(f.ComputeCost)
if f.PtrU != nil {
return f.PtrU(dvm, expr)
} else if f.PtrS != nil {
return f.PtrS(dvm, expr)
} else {
return f.Ptr(dvm, expr) return f.Ptr(dvm, expr)
} }
} }
return false, nil
} }
panic("function doesnot match any version")
return false, nil }
//panic("function does not exist")
return false, nil // function does not exist
} }
// the load/store functions are sandboxed and thus cannot affect any other SC storage // the load/store functions are sandboxed and thus cannot affect any other SC storage
@ -124,7 +131,6 @@ func (dvm *DVM_Interpreter) Load(key Variable) interface{} {
default: default:
panic("Unhandled data_type") panic("Unhandled data_type")
} }
} }
// whether a variable exists in store or not // whether a variable exists in store or not
@ -142,10 +148,28 @@ func (dvm *DVM_Interpreter) Delete(key Variable) {
dvm.State.Store.Delete(DataKey{SCID: dvm.State.Chain_inputs.SCID, Key: key}) dvm.State.Store.Delete(DataKey{SCID: dvm.State.Chain_inputs.SCID, Key: key})
} }
func dvm_version(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result interface{}) { // we should migrate to generics ASAP
if len(expr.Args) != 1 { // expression without limit func convertdatatovariable(datai interface{}) Variable {
panic("version expects 1 parameters") switch k := datai.(type) {
case uint64:
return Variable{Type: Uint64, ValueUint64: k}
case string:
return Variable{Type: String, ValueString: k}
default:
panic("This variable cannot be loaded")
} }
}
// checks whether necessary number of arguments have been provided
func checkargscount(expected, actual int) {
if expected != actual {
panic("incorrect number of arguments")
}
}
func dvm_version(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result uint64) {
checkargscount(1, len(expr.Args)) // check number of arguments
if version_str, ok := dvm.eval(expr.Args[0]).(string); !ok { if version_str, ok := dvm.eval(expr.Args[0]).(string); !ok {
panic("unsupported version format") panic("unsupported version format")
} else { } else {
@ -155,86 +179,78 @@ func dvm_version(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result
} }
func dvm_load(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result interface{}) { func dvm_load(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result interface{}) {
if len(expr.Args) != 1 { checkargscount(1, len(expr.Args)) // check number of arguments
panic("Load function expects a single varible as parameter")
}
key := dvm.eval(expr.Args[0]) key := dvm.eval(expr.Args[0])
switch k := key.(type) { return true, dvm.Load(convertdatatovariable(key))
case uint64:
return true, dvm.Load(Variable{Type: Uint64, ValueUint64: k})
case string:
return true, dvm.Load(Variable{Type: String, ValueString: k})
default:
panic("This variable cannot be loaded")
}
} }
func dvm_exists(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result interface{}) { func dvm_exists(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result uint64) {
if len(expr.Args) != 1 { checkargscount(1, len(expr.Args)) // check number of arguments
panic("Exists function expects a single varible as parameter")
}
key := dvm.eval(expr.Args[0]) // evaluate the argument and use the result key := dvm.eval(expr.Args[0]) // evaluate the argument and use the result
switch k := key.(type) { return true, dvm.Exists(convertdatatovariable(key))
case uint64:
return true, dvm.Exists(Variable{Type: Uint64, ValueUint64: k})
case string:
return true, dvm.Exists(Variable{Type: String, ValueString: k})
default:
panic("This variable cannot be loaded")
}
} }
func dvm_store(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result interface{}) { func dvm_store(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result uint64) {
if len(expr.Args) != 2 { checkargscount(2, len(expr.Args)) // check number of arguments
panic("Store function expects 2 variables as parameter") key := convertdatatovariable(dvm.eval(expr.Args[0]))
} value := convertdatatovariable(dvm.eval(expr.Args[1]))
key_eval := dvm.eval(expr.Args[0])
value_eval := dvm.eval(expr.Args[1])
var key, value Variable
switch k := key_eval.(type) {
case uint64:
key = Variable{Type: Uint64, ValueUint64: k}
case string:
key = Variable{Type: String, ValueString: k}
default:
panic("This variable cannot be stored")
}
switch k := value_eval.(type) {
case uint64:
value = Variable{Type: Uint64, ValueUint64: k}
case string:
value = Variable{Type: String, ValueString: k}
default:
panic("This variable cannot be stored")
}
dvm.Store(key, value) dvm.Store(key, value)
return true, nil return true, 1
} }
func dvm_delete(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result interface{}) { func dvm_delete(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result uint64) {
if len(expr.Args) != 1 { checkargscount(1, len(expr.Args)) // check number of arguments
panic("Delete function expects 2 variables as parameter") key := convertdatatovariable(dvm.eval(expr.Args[0]))
}
key_eval := dvm.eval(expr.Args[0])
var key Variable
switch k := key_eval.(type) {
case uint64:
key = Variable{Type: Uint64, ValueUint64: k}
case string:
key = Variable{Type: String, ValueString: k}
default:
panic("This variable cannot be deleted")
}
dvm.Delete(key) dvm.Delete(key)
return true, nil return true, uint64(1)
} }
func dvm_random(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result interface{}) { func dvm_mapexists(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result uint64) {
checkargscount(1, len(expr.Args)) // check number of arguments
key := convertdatatovariable(dvm.eval(expr.Args[0])) // evaluate the argument and use the result
if _, ok := dvm.State.RamStore[key]; ok {
return true, uint64(1)
}
return true, uint64(0)
}
func dvm_mapget(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result interface{}) {
checkargscount(1, len(expr.Args)) // check number of arguments
key := convertdatatovariable(dvm.eval(expr.Args[0])) // evaluate the argument and use the result
v := dvm.State.RamStore[key]
if v.Type == Uint64 {
return true, v.ValueUint64
} else if v.Type == String {
return true, v.ValueString
} else {
panic("This variable cannot be obtained")
}
}
func dvm_mapstore(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result uint64) {
checkargscount(2, len(expr.Args)) // check number of arguments
key := convertdatatovariable(dvm.eval(expr.Args[0])) // evaluate the argument and use the result
value := convertdatatovariable(dvm.eval(expr.Args[1])) // evaluate the argument and use the result
dvm.State.RamStore[key] = value
return true, uint64(1)
}
func dvm_mapdelete(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result uint64) {
checkargscount(1, len(expr.Args)) // check number of arguments
key := convertdatatovariable(dvm.eval(expr.Args[0])) // evaluate the argument and use the result
delete(dvm.State.RamStore, key)
return true, uint64(1)
}
func dvm_random(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result uint64) {
if len(expr.Args) >= 2 { if len(expr.Args) >= 2 {
panic("RANDOM function expects 0 or 1 number as parameter") panic("RANDOM function expects 0 or 1 number as parameter")
} }
@ -252,36 +268,26 @@ func dvm_random(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result
} }
} }
func dvm_scid(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result interface{}) { func dvm_scid(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result string) {
if len(expr.Args) != 0 { checkargscount(0, len(expr.Args)) // check number of arguments
panic("SCID function expects 0 parameters")
}
return true, string(dvm.State.Chain_inputs.SCID[:]) return true, string(dvm.State.Chain_inputs.SCID[:])
} }
func dvm_blid(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result interface{}) { func dvm_blid(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result string) {
if len(expr.Args) != 0 { checkargscount(0, len(expr.Args)) // check number of arguments
panic("blid function expects 0 parameters")
}
return true, string(dvm.State.Chain_inputs.BLID[:]) return true, string(dvm.State.Chain_inputs.BLID[:])
} }
func dvm_txid(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result interface{}) { func dvm_txid(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result string) {
if len(expr.Args) != 0 { checkargscount(0, len(expr.Args)) // check number of arguments
panic("txid function expects 0 parameters")
}
return true, string(dvm.State.Chain_inputs.TXID[:]) return true, string(dvm.State.Chain_inputs.TXID[:])
} }
func dvm_dero(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result interface{}) { func dvm_dero(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result string) {
if len(expr.Args) != 0 { checkargscount(0, len(expr.Args)) // check number of arguments
panic("dero function expects 0 parameters")
}
var zerohash crypto.Hash var zerohash crypto.Hash
return true, string(zerohash[:]) return true, string(zerohash[:])
} }
func dvm_block_height(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result interface{}) { func dvm_block_height(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result uint64) {
if len(expr.Args) != 0 { checkargscount(0, len(expr.Args)) // check number of arguments
panic("BLOCK_HEIGHT function expects 0 parameters")
}
return true, dvm.State.Chain_inputs.BL_HEIGHT return true, dvm.State.Chain_inputs.BL_HEIGHT
} }
@ -293,24 +299,18 @@ func dvm_block_topoheight(dvm *DVM_Interpreter, expr *ast.CallExpr)(handled bool
return true, dvm.State.Chain_inputs.BL_TOPOHEIGHT return true, dvm.State.Chain_inputs.BL_TOPOHEIGHT
} }
*/ */
func dvm_block_timestamp(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result interface{}) { func dvm_block_timestamp(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result uint64) {
if len(expr.Args) != 0 { checkargscount(0, len(expr.Args)) // check number of arguments
panic("BLOCK_TIMESTAMP function expects 0 parameters")
}
return true, dvm.State.Chain_inputs.BL_TIMESTAMP return true, dvm.State.Chain_inputs.BL_TIMESTAMP
} }
func dvm_signer(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result interface{}) { func dvm_signer(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result string) {
if len(expr.Args) != 0 { checkargscount(0, len(expr.Args)) // check number of arguments
panic("SIGNER function expects 0 parameters")
}
return true, dvm.State.Chain_inputs.Signer return true, dvm.State.Chain_inputs.Signer
} }
func dvm_update_sc_code(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result interface{}) { func dvm_update_sc_code(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result uint64) {
if len(expr.Args) != 1 { checkargscount(1, len(expr.Args)) // check number of arguments
panic("UPDATE_SC_CODE function expects 1 parameters")
}
code_eval := dvm.eval(expr.Args[0]) code_eval := dvm.eval(expr.Args[0])
switch k := code_eval.(type) { switch k := code_eval.(type) {
case string: case string:
@ -321,10 +321,8 @@ func dvm_update_sc_code(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool,
} }
} }
func dvm_is_address_valid(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result interface{}) { func dvm_is_address_valid(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result uint64) {
if len(expr.Args) != 1 { checkargscount(1, len(expr.Args)) // check number of arguments
panic("IS_ADDRESS_VALID function expects 1 parameters")
}
addr_eval := dvm.eval(expr.Args[0]) addr_eval := dvm.eval(expr.Args[0])
switch k := addr_eval.(type) { switch k := addr_eval.(type) {
@ -341,10 +339,8 @@ func dvm_is_address_valid(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled boo
} }
} }
func dvm_address_raw(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result interface{}) { func dvm_address_raw(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result string) {
if len(expr.Args) != 1 { checkargscount(1, len(expr.Args)) // check number of arguments
panic("ADDRESS_RAW function expects 1 parameters")
}
addr_eval := dvm.eval(expr.Args[0]) addr_eval := dvm.eval(expr.Args[0])
switch k := addr_eval.(type) { switch k := addr_eval.(type) {
@ -353,16 +349,14 @@ func dvm_address_raw(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, re
return true, string(addr.Compressed()) return true, string(addr.Compressed())
} }
return true, nil // fallthrough not supported in type switch return true, "" // fallthrough not supported in type switch
default: default:
return true, nil return true, ""
} }
} }
func dvm_address_string(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result interface{}) { func dvm_address_string(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result string) {
if len(expr.Args) != 1 { checkargscount(1, len(expr.Args)) // check number of arguments
panic("ADDRESS_STRING function expects 1 parameters")
}
addr_eval := dvm.eval(expr.Args[0]) addr_eval := dvm.eval(expr.Args[0])
switch k := addr_eval.(type) { switch k := addr_eval.(type) {
@ -374,16 +368,14 @@ func dvm_address_string(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool,
return true, addr.String() return true, addr.String()
} }
return true, nil // fallthrough not supported in type switch return true, "" // fallthrough not supported in type switch
default: default:
return true, nil return true, ""
} }
} }
func dvm_send_dero_to_address(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result interface{}) { func dvm_send_dero_to_address(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result uint64) {
if len(expr.Args) != 2 { checkargscount(2, len(expr.Args)) // check number of arguments
panic("SEND_DERO_TO_ADDRESS function expects 2 parameters")
}
addr_eval := dvm.eval(expr.Args[0]) addr_eval := dvm.eval(expr.Args[0])
amount_eval := dvm.eval(expr.Args[1]) amount_eval := dvm.eval(expr.Args[1])
@ -397,16 +389,14 @@ func dvm_send_dero_to_address(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled
} }
if amount_eval.(uint64) == 0 { if amount_eval.(uint64) == 0 {
return true, amount_eval return true, amount_eval.(uint64)
} }
var zerohash crypto.Hash var zerohash crypto.Hash
dvm.State.Store.SendExternal(dvm.State.Chain_inputs.SCID, zerohash, addr_eval.(string), amount_eval.(uint64)) // add record for external transfer dvm.State.Store.SendExternal(dvm.State.Chain_inputs.SCID, zerohash, addr_eval.(string), amount_eval.(uint64)) // add record for external transfer
return true, amount_eval return true, amount_eval.(uint64)
} }
func dvm_send_asset_to_address(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result interface{}) { func dvm_send_asset_to_address(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result uint64) {
if len(expr.Args) != 3 { checkargscount(3, len(expr.Args)) // check number of arguments
panic("SEND_ASSET_TO_ADDRESS function expects 3 parameters") // address, amount, asset
}
addr_eval := dvm.eval(expr.Args[0]) addr_eval := dvm.eval(expr.Args[0])
amount_eval := dvm.eval(expr.Args[1]) amount_eval := dvm.eval(expr.Args[1])
@ -427,7 +417,7 @@ func dvm_send_asset_to_address(dvm *DVM_Interpreter, expr *ast.CallExpr) (handle
//fmt.Printf("sending asset %x (%d) to address %x\n", asset_eval.(string), amount_eval.(uint64),[]byte(addr_eval.(string))) //fmt.Printf("sending asset %x (%d) to address %x\n", asset_eval.(string), amount_eval.(uint64),[]byte(addr_eval.(string)))
if amount_eval.(uint64) == 0 { if amount_eval.(uint64) == 0 {
return true, amount_eval return true, amount_eval.(uint64)
} }
if len(asset_eval.(string)) != 32 { if len(asset_eval.(string)) != 32 {
@ -438,21 +428,18 @@ func dvm_send_asset_to_address(dvm *DVM_Interpreter, expr *ast.CallExpr) (handle
dvm.State.Store.SendExternal(dvm.State.Chain_inputs.SCID, asset, addr_eval.(string), amount_eval.(uint64)) // add record for external transfer dvm.State.Store.SendExternal(dvm.State.Chain_inputs.SCID, asset, addr_eval.(string), amount_eval.(uint64)) // add record for external transfer
return true, amount_eval return true, amount_eval.(uint64)
} }
func dvm_derovalue(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result interface{}) { func dvm_derovalue(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result uint64) {
if len(expr.Args) != 0 { // expression without limit checkargscount(0, len(expr.Args)) // check number of arguments
panic("DEROVALUE expects no parameters")
} else {
return true, dvm.State.Assets[dvm.State.SCIDZERO] return true, dvm.State.Assets[dvm.State.SCIDZERO]
}
} }
func dvm_assetvalue(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result interface{}) { func dvm_assetvalue(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result uint64) {
if len(expr.Args) != 1 { // expression without limit checkargscount(1, len(expr.Args)) // check number of arguments
panic("assetVALUE expects 1 parameters")
} else {
asset_eval := dvm.eval(expr.Args[0]) asset_eval := dvm.eval(expr.Args[0])
if _, ok := asset_eval.(string); !ok { if _, ok := asset_eval.(string); !ok {
@ -465,13 +452,12 @@ func dvm_assetvalue(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, res
copy(asset[:], ([]byte(asset_eval.(string)))) copy(asset[:], ([]byte(asset_eval.(string))))
return true, dvm.State.Assets[asset] return true, dvm.State.Assets[asset]
}
} }
func dvm_itoa(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result interface{}) { func dvm_itoa(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result string) {
if len(expr.Args) != 1 { // expression without limit checkargscount(1, len(expr.Args)) // check number of arguments
panic("itoa expects 1 parameters")
} else {
asset_eval := dvm.eval(expr.Args[0]) asset_eval := dvm.eval(expr.Args[0])
if _, ok := asset_eval.(uint64); !ok { if _, ok := asset_eval.(uint64); !ok {
@ -479,13 +465,12 @@ func dvm_itoa(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result in
} }
return true, fmt.Sprintf("%d", asset_eval.(uint64)) return true, fmt.Sprintf("%d", asset_eval.(uint64))
}
} }
func dvm_atoi(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result interface{}) { func dvm_atoi(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result uint64) {
if len(expr.Args) != 1 { // expression without limit checkargscount(1, len(expr.Args)) // check number of arguments
panic("itoa expects 1 parameters")
} else {
asset_eval := dvm.eval(expr.Args[0]) asset_eval := dvm.eval(expr.Args[0])
if _, ok := asset_eval.(string); !ok { if _, ok := asset_eval.(string); !ok {
@ -497,20 +482,18 @@ func dvm_atoi(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result in
} else { } else {
return true, u return true, u
} }
}
} }
func dvm_strlen(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result interface{}) { func dvm_strlen(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result uint64) {
if len(expr.Args) != 1 { // expression without limit checkargscount(1, len(expr.Args)) // check number of arguments
panic("itoa expects 1 parameters")
} else {
asset_eval := dvm.eval(expr.Args[0]) asset_eval := dvm.eval(expr.Args[0])
if _, ok := asset_eval.(string); !ok { if _, ok := asset_eval.(string); !ok {
panic("atoi argument must be valid string") panic("atoi argument must be valid string")
} }
return true, uint64(len([]byte(asset_eval.(string)))) return true, uint64(len([]byte(asset_eval.(string))))
}
} }
func substr(input string, start uint64, length uint64) string { func substr(input string, start uint64, length uint64) string {
@ -527,10 +510,8 @@ func substr(input string, start uint64, length uint64) string {
return string(asbytes[start : start+length]) return string(asbytes[start : start+length])
} }
func dvm_substr(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result interface{}) { func dvm_substr(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result string) {
if len(expr.Args) != 3 { // expression without limit checkargscount(3, len(expr.Args)) // check number of arguments
panic("substr expects 3 parameters")
}
input_eval := dvm.eval(expr.Args[0]) input_eval := dvm.eval(expr.Args[0])
if _, ok := input_eval.(string); !ok { if _, ok := input_eval.(string); !ok {
panic("input argument must be valid string") panic("input argument must be valid string")
@ -547,10 +528,8 @@ func dvm_substr(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result
return true, substr(input_eval.(string), offset_eval.(uint64), length_eval.(uint64)) return true, substr(input_eval.(string), offset_eval.(uint64), length_eval.(uint64))
} }
func dvm_sha256(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result interface{}) { func dvm_sha256(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result string) {
if len(expr.Args) != 1 { // expression without limit checkargscount(1, len(expr.Args)) // check number of arguments
panic("sha256 expects 1 parameters")
}
input_eval := dvm.eval(expr.Args[0]) input_eval := dvm.eval(expr.Args[0])
if _, ok := input_eval.(string); !ok { if _, ok := input_eval.(string); !ok {
panic("input argument must be valid string") panic("input argument must be valid string")
@ -560,10 +539,8 @@ func dvm_sha256(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result
return true, string(hash[:]) return true, string(hash[:])
} }
func dvm_sha3256(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result interface{}) { func dvm_sha3256(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result string) {
if len(expr.Args) != 1 { // expression without limit checkargscount(1, len(expr.Args)) // check number of arguments
panic("sha3256 expects 1 parameters")
}
input_eval := dvm.eval(expr.Args[0]) input_eval := dvm.eval(expr.Args[0])
if _, ok := input_eval.(string); !ok { if _, ok := input_eval.(string); !ok {
panic("input argument must be valid string") panic("input argument must be valid string")
@ -573,10 +550,8 @@ func dvm_sha3256(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result
return true, string(hash[:]) return true, string(hash[:])
} }
func dvm_keccak256(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result interface{}) { func dvm_keccak256(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result string) {
if len(expr.Args) != 1 { // expression without limit checkargscount(1, len(expr.Args)) // check number of arguments
panic("keccak256 expects 1 parameters")
}
input_eval := dvm.eval(expr.Args[0]) input_eval := dvm.eval(expr.Args[0])
if _, ok := input_eval.(string); !ok { if _, ok := input_eval.(string); !ok {
panic("input argument must be valid string") panic("input argument must be valid string")
@ -588,20 +563,16 @@ func dvm_keccak256(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, resu
return true, string(hash[:]) return true, string(hash[:])
} }
func dvm_hex(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result interface{}) { func dvm_hex(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result string) {
if len(expr.Args) != 1 { // expression without limit checkargscount(1, len(expr.Args)) // check number of arguments
panic("hex expects 1 parameters")
}
input_eval := dvm.eval(expr.Args[0]) input_eval := dvm.eval(expr.Args[0])
if _, ok := input_eval.(string); !ok { if _, ok := input_eval.(string); !ok {
panic("input argument must be valid string") panic("input argument must be valid string")
} }
return true, hex.EncodeToString([]byte(input_eval.(string))) return true, hex.EncodeToString([]byte(input_eval.(string)))
} }
func dvm_hexdecode(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result interface{}) { func dvm_hexdecode(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result string) {
if len(expr.Args) != 1 { // expression without limit checkargscount(1, len(expr.Args)) // check number of arguments
panic("hex expects 1 parameters")
}
input_eval := dvm.eval(expr.Args[0]) input_eval := dvm.eval(expr.Args[0])
if _, ok := input_eval.(string); !ok { if _, ok := input_eval.(string); !ok {
panic("input argument must be valid string") panic("input argument must be valid string")
@ -614,10 +585,9 @@ func dvm_hexdecode(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, resu
} }
} }
func dvm_min(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result interface{}) { func dvm_min(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result uint64) {
if len(expr.Args) != 2 { // expression without limit checkargscount(2, len(expr.Args)) // check number of arguments
panic("min expects 2 parameters")
}
a1 := dvm.eval(expr.Args[0]) a1 := dvm.eval(expr.Args[0])
if _, ok := a1.(uint64); !ok { if _, ok := a1.(uint64); !ok {
panic("input argument must be uint64") panic("input argument must be uint64")
@ -629,15 +599,13 @@ func dvm_min(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result int
} }
if a1.(uint64) < a2.(uint64) { if a1.(uint64) < a2.(uint64) {
return true, a1 return true, a1.(uint64)
} }
return true, a2 return true, a2.(uint64)
} }
func dvm_max(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result interface{}) { func dvm_max(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result uint64) {
if len(expr.Args) != 2 { // expression without limit checkargscount(2, len(expr.Args)) // check number of arguments
panic("min expects 2 parameters")
}
a1 := dvm.eval(expr.Args[0]) a1 := dvm.eval(expr.Args[0])
if _, ok := a1.(uint64); !ok { if _, ok := a1.(uint64); !ok {
panic("input argument must be uint64") panic("input argument must be uint64")
@ -649,7 +617,12 @@ func dvm_max(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result int
} }
if a1.(uint64) > a2.(uint64) { if a1.(uint64) > a2.(uint64) {
return true, a1 return true, a1.(uint64)
} }
return true, a2 return true, a2.(uint64)
}
func dvm_panic(dvm *DVM_Interpreter, expr *ast.CallExpr) (handled bool, result uint64) {
panic("panic function called")
return true, uint64(0)
} }

View File

@ -219,6 +219,53 @@ var execution_tests_functions = []struct {
nil, nil,
Variable{Type: String, ValueString: string("")}, Variable{Type: String, ValueString: string("")},
}, },
{
"mapget()",
`Function TestRun(input String) String
10 mapstore("input",input)
30 return mapget("input") + mapget("input")
End Function`,
"TestRun",
map[string]interface{}{"input": string("0123456789")},
nil,
Variable{Type: String, ValueString: string("01234567890123456789")},
},
{
"mapget()",
`Function TestRun(input String) String
10 mapstore("input",input)
15 mapstore("input",input+input)
30 return mapget("input")
End Function`,
"TestRun",
map[string]interface{}{"input": string("0123456789")},
nil,
Variable{Type: String, ValueString: string("01234567890123456789")},
},
{
"mapexists()",
`Function TestRun(input String) Uint64
10 mapstore("input",input)
30 return mapexists("input") + mapexists("input1")
End Function`,
"TestRun",
map[string]interface{}{"input": string("0123456789")},
nil,
Variable{Type: Uint64, ValueUint64: uint64(1)},
},
{
"mapdelete()",
`Function TestRun(input String) Uint64
10 mapstore("input",input)
15 mapdelete("input")
30 return mapexists("input")
End Function`,
"TestRun",
map[string]interface{}{"input": string("0123456789")},
nil,
Variable{Type: Uint64, ValueUint64: uint64(0)},
},
} }
func decodeHex(s string) []byte { func decodeHex(s string) []byte {
@ -238,7 +285,7 @@ func Test_FUNCTION_execution(t *testing.T) {
} }
state := &Shared_State{Chain_inputs: &Blockchain_Input{BL_HEIGHT: 5, BL_TIMESTAMP: 9, SCID: crypto.ZEROHASH, state := &Shared_State{Chain_inputs: &Blockchain_Input{BL_HEIGHT: 5, BL_TIMESTAMP: 9, SCID: crypto.ZEROHASH,
BLID: crypto.ZEROHASH, TXID: crypto.ZEROHASH}} BLID: crypto.ZEROHASH, TXID: crypto.ZEROHASH}, RamStore: map[Variable]Variable{}}
result, err := RunSmartContract(&sc, test.EntryPoint, state, test.Args) result, err := RunSmartContract(&sc, test.EntryPoint, state, test.Args)
switch { switch {
case test.Eerr == nil && err == nil: case test.Eerr == nil && err == nil:

View File

@ -29,12 +29,6 @@ type DataKey struct {
Key Variable Key Variable
} }
type DataAtom struct {
Key DataKey
Prev_Value Variable // previous Value if any
Value Variable // current value if any
}
type TransferInternal struct { type TransferInternal struct {
Asset crypto.Hash `cbor:"Asset,omitempty" json:"Asset,omitempty"` // transfer this asset Asset crypto.Hash `cbor:"Asset,omitempty" json:"Asset,omitempty"` // transfer this asset
SCID string `cbor:"A,omitempty" json:"A,omitempty"` // transfer to this SCID SCID string `cbor:"A,omitempty" json:"A,omitempty"` // transfer to this SCID
@ -64,6 +58,8 @@ type TX_Storage struct {
RawKeys map[string][]byte // this keeps the in-transit DB updates, just in case we have to discard instantly RawKeys map[string][]byte // this keeps the in-transit DB updates, just in case we have to discard instantly
Transfers map[crypto.Hash]SC_Transfers // all transfers ( internal/external ) Transfers map[crypto.Hash]SC_Transfers // all transfers ( internal/external )
State *Shared_State // only for book keeping of storage gas
} }
// initialize tx store // initialize tx store
@ -82,17 +78,13 @@ func (tx_store *TX_Storage) RawLoad(key []byte) (value []byte, found bool) {
return return
} }
func (tx_store *TX_Storage) RawStore(key []byte, value []byte) {
tx_store.RawKeys[string(key)] = value
return
}
func (tx_store *TX_Storage) Delete(dkey DataKey) { func (tx_store *TX_Storage) Delete(dkey DataKey) {
tx_store.RawStore(dkey.MarshalBinaryPanic(), []byte{}) tx_store.RawKeys[string(dkey.MarshalBinaryPanic())] = []byte{}
return return
} }
// this will load the variable, and if the key is found // this will load the variable, and if the key is found
// loads are cheaper
func (tx_store *TX_Storage) Load(dkey DataKey, found_value *uint64) (value Variable) { func (tx_store *TX_Storage) Load(dkey DataKey, found_value *uint64) (value Variable) {
//fmt.Printf("Loading %+v \n", dkey) //fmt.Printf("Loading %+v \n", dkey)
@ -104,6 +96,15 @@ func (tx_store *TX_Storage) Load(dkey DataKey, found_value *uint64) (value Varia
if err := value.UnmarshalBinary(result); err != nil { if err := value.UnmarshalBinary(result); err != nil {
panic(err) panic(err)
} }
if tx_store.State != nil {
if value.Length() > 10 {
tx_store.State.ConsumeStorageGas(value.Length() / 10)
} else {
tx_store.State.ConsumeStorageGas(1)
}
}
return value return value
} }
@ -112,6 +113,13 @@ func (tx_store *TX_Storage) Load(dkey DataKey, found_value *uint64) (value Varia
} }
value = tx_store.DiskLoader(dkey, found_value) value = tx_store.DiskLoader(dkey, found_value)
if tx_store.State != nil {
if value.Length() > 10 {
tx_store.State.ConsumeStorageGas(value.Length() / 10)
} else {
tx_store.State.ConsumeStorageGas(1)
}
}
return return
} }
@ -120,7 +128,10 @@ func (tx_store *TX_Storage) Load(dkey DataKey, found_value *uint64) (value Varia
func (tx_store *TX_Storage) Store(dkey DataKey, v Variable) { func (tx_store *TX_Storage) Store(dkey DataKey, v Variable) {
//fmt.Printf("Storing request %+v : %+v\n", dkey, v) //fmt.Printf("Storing request %+v : %+v\n", dkey, v)
tx_store.RawKeys[string(dkey.MarshalBinaryPanic())] = v.MarshalBinaryPanic() kbytes := dkey.MarshalBinaryPanic()
vbytes := v.MarshalBinaryPanic()
tx_store.State.ConsumeStorageGas(int64(len(vbytes)) * 1)
tx_store.RawKeys[string(kbytes)] = vbytes
} }
// store variable // store variable
@ -173,6 +184,22 @@ func (dkey DataKey) MarshalBinaryPanic() (ser []byte) {
return return
} }
func (v Variable) Length() (length int64) {
switch v.Type {
case Invalid:
return
case Uint64:
var buf [binary.MaxVarintLen64]byte
done := binary.PutUvarint(buf[:], v.ValueUint64) // uint64 data type
length += int64(done) + 1
case String:
length = int64(len([]byte(v.ValueString)) + 1)
default:
panic("unknown variable type not implemented")
}
return
}
// these are used by lowest layers // these are used by lowest layers
func (v Variable) MarshalBinary() (data []byte, err error) { func (v Variable) MarshalBinary() (data []byte, err error) {
switch v.Type { switch v.Type {

View File

@ -1,77 +0,0 @@
// Copyright 2017-2018 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.
package dvm
import "fmt"
// this file implements a RAM store backend for testing purposes
type Memory_Storage struct {
Atoms []DataAtom // all modification operations have to played/reverse in this order
Keys map[DataKey]Variable
RawKeys map[string][]byte
}
var Memory_Backend Memory_Storage
func init() {
DVM_STORAGE_BACKEND = &Memory_Backend
}
func (mem_store *Memory_Storage) RawLoad(key []byte) (value []byte, found bool) {
value, found = mem_store.RawKeys[string(key)]
return
}
func (mem_store *Memory_Storage) RawStore(key []byte, value []byte) {
mem_store.RawKeys[string(key)] = value
return
}
// this will load the variable, and if the key is found
func (mem_store *Memory_Storage) Load(dkey DataKey, found_value *uint64) (value Variable) {
*found_value = 0
// if it was modified in current TX, use it
if result, ok := mem_store.Keys[dkey]; ok {
*found_value = 1
return result
}
return
}
// store variable
func (mem_store *Memory_Storage) Store(dkey DataKey, v Variable) {
fmt.Printf("Storing %+v : %+v\n", dkey, v)
var found uint64
old_value := mem_store.Load(dkey, &found)
var atom DataAtom
atom.Key = dkey
atom.Value = v
if found != 0 {
atom.Prev_Value = old_value
} else {
atom.Prev_Value = Variable{}
}
mem_store.Keys[atom.Key] = atom.Value
mem_store.Atoms = append(mem_store.Atoms, atom)
}

View File

@ -14,7 +14,7 @@
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // 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. // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package blockchain package dvm
// this file implements necessary structure to SC handling // this file implements necessary structure to SC handling
@ -24,12 +24,12 @@ import "runtime/debug"
import "encoding/binary" import "encoding/binary"
import "github.com/deroproject/derohe/cryptography/crypto" import "github.com/deroproject/derohe/cryptography/crypto"
import "github.com/deroproject/derohe/dvm" import "github.com/deroproject/derohe/config"
//import "github.com/deroproject/graviton"
import "github.com/deroproject/derohe/rpc" import "github.com/deroproject/derohe/rpc"
import "github.com/deroproject/derohe/globals" import "github.com/deroproject/derohe/globals"
import "github.com/deroproject/derohe/transaction" import "github.com/deroproject/graviton"
//import "github.com/deroproject/derohe/transaction"
// currently DERO hash 2 contract types // currently DERO hash 2 contract types
// 1 OPEN // 1 OPEN
@ -60,47 +60,74 @@ func SC_Meta_Key(scid crypto.Hash) []byte {
return scid[:] return scid[:]
} }
func SC_Code_Key(scid crypto.Hash) []byte { func SC_Code_Key(scid crypto.Hash) []byte {
return dvm.Variable{Type: dvm.String, ValueString: "C"}.MarshalBinaryPanic() return Variable{Type: String, ValueString: "C"}.MarshalBinaryPanic()
} }
func SC_Asset_Key(asset crypto.Hash) []byte { func SC_Asset_Key(asset crypto.Hash) []byte {
return asset[:] return asset[:]
} }
// 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{}}
}
}
// this will process the SC transaction // this will process the SC transaction
// the tx should only be processed , if it has been processed // the tx should only be processed , if it has been processed
func (chain *Blockchain) execute_sc_function(w_sc_tree *Tree_Wrapper, data_tree *Tree_Wrapper, scid crypto.Hash, bl_height, bl_topoheight, bl_timestamp uint64, bl_hash crypto.Hash, tx transaction.Transaction, entrypoint string, hard_fork_version_current int64) (gas uint64, err error) { 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) {
defer func() { defer func() {
if r := recover(); r != nil { // safety so if anything wrong happens, verification fails if r := recover(); r != nil { // safety so if anything wrong happens, verification fails
if err == nil { if err == nil {
err = fmt.Errorf("Stack trace \n%s", debug.Stack()) err = fmt.Errorf("Stack trace \n%s", debug.Stack())
} }
logger.V(1).Error(err, "Recovered while rewinding chain,", "r", r, "stack trace", string(debug.Stack())) //logger.V(1).Error(err, "Recovered while rewinding chain,", "r", r, "stack trace", string(debug.Stack()))
} }
}() }()
//fmt.Printf("executing entrypoint %s\n", entrypoint) //fmt.Printf("executing entrypoint %s values %+v feees %d\n", entrypoint, incoming_value, fees)
//if !tx.Verify_SC_Signature() { // if tx is not SC TX, or Signature could not be verified skip it tx_store := Initialize_TX_store()
// return
//}
tx_hash := tx.GetHash()
tx_store := dvm.Initialize_TX_store()
// used as value loader from disk // used as value loader from disk
// this function is used to load any data required by the SC // this function is used to load any data required by the SC
balance_loader := func(key DataKey) (result uint64) {
balance_loader := func(key dvm.DataKey) (result uint64) {
var found bool var found bool
_ = found _ = found
result, found = chain.LoadSCAssetValue(data_tree, key.SCID, key.Asset) result, found = LoadSCAssetValue(data_tree, key.SCID, key.Asset)
return result return result
} }
diskloader := func(key dvm.DataKey, found *uint64) (result dvm.Variable) { diskloader := func(key DataKey, found *uint64) (result Variable) {
var exists bool var exists bool
if result, exists = chain.LoadSCValue(data_tree, key.SCID, key.MarshalBinaryPanic()); exists { if result, exists = LoadSCValue(data_tree, key.SCID, key.MarshalBinaryPanic()); exists {
*found = uint64(1) *found = uint64(1)
} }
//fmt.Printf("Loading from disk %+v result %+v found status %+v \n", key, result, exists) //fmt.Printf("Loading from disk %+v result %+v found status %+v \n", key, result, exists)
@ -123,73 +150,48 @@ func (chain *Blockchain) execute_sc_function(w_sc_tree *Tree_Wrapper, data_tree
return value, true return value, true
} }
balance, sc_parsed, found := chain.ReadSC(w_sc_tree, data_tree, scid)
if !found {
logger.V(1).Error(nil, "SC not found", "scid", scid)
err = fmt.Errorf("SC not found %s", scid)
return
}
//fmt.Printf("sc_parsed %+v\n", sc_parsed) //fmt.Printf("sc_parsed %+v\n", sc_parsed)
// if we found the SC in parsed form, check whether entrypoint is found // if we found the SC in parsed form, check whether entrypoint is found
function, ok := sc_parsed.Functions[entrypoint] function, ok := sc_parsed.Functions[entrypoint]
if !ok { if !ok {
logger.V(1).Error(fmt.Errorf("stored SC does not contain entrypoint"), "", "entrypoint", entrypoint, "scid", scid)
err = fmt.Errorf("stored SC does not contain entrypoint '%s' scid %s \n", entrypoint, scid) err = fmt.Errorf("stored SC does not contain entrypoint '%s' scid %s \n", entrypoint, scid)
return return
} }
_ = function
//fmt.Printf("entrypoint found '%s' scid %s\n", entrypoint, scid)
//if len(sc_tx.Params) == 0 { // initialize params if not initialized earlier
// sc_tx.Params = map[string]string{}
//}
//sc_tx.Params["value"] = fmt.Sprintf("%d", sc_tx.Value) // overide value
tx_store.DiskLoader = diskloader // hook up loading from chain
tx_store.DiskLoaderRaw = diskloader_raw
tx_store.BalanceLoader = balance_loader
tx_store.BalanceAtStart = balance
tx_store.SCID = scid
//fmt.Printf("tx store %v\n", tx_store)
// we can skip proof check, here
if err = chain.Expand_Transaction_NonCoinbase(&tx); err != nil {
return
}
signer, err := extract_signer(&tx)
if err != nil { // allow anonymous SC transactions with condition that SC will not call Signer
// this allows anonymous voting and numerous other applications
// otherwise SC receives signer as all zeroes
}
// setup block hash, height, topoheight correctly // setup block hash, height, topoheight correctly
state := &dvm.Shared_State{ state := &Shared_State{
Store: tx_store, Store: tx_store,
Assets: map[crypto.Hash]uint64{}, Assets: map[crypto.Hash]uint64{},
RamStore: map[Variable]Variable{},
SCIDSELF: scid, SCIDSELF: scid,
Chain_inputs: &dvm.Blockchain_Input{ Chain_inputs: &Blockchain_Input{
BL_HEIGHT: bl_height, BL_HEIGHT: bl_height,
BL_TOPOHEIGHT: uint64(bl_topoheight), BL_TOPOHEIGHT: uint64(bl_topoheight),
BL_TIMESTAMP: bl_timestamp, BL_TIMESTAMP: bl_timestamp,
SCID: scid, SCID: scid,
BLID: bl_hash, BLID: blid,
TXID: tx_hash, TXID: txid,
Signer: string(signer[:]), Signer: string(signer[:]),
}, },
} }
if _, ok = globals.Arguments["--debug"]; ok && globals.Arguments["--debug"] != nil && chain.simulator { tx_store.DiskLoader = diskloader // hook up loading from chain
state.Trace = true // enable tracing for dvm simulator tx_store.DiskLoaderRaw = diskloader_raw
tx_store.BalanceLoader = balance_loader
tx_store.BalanceAtStart = balance_at_start
tx_store.SCID = scid
tx_store.State = state
if _, ok = globals.Arguments["--debug"]; ok && globals.Arguments["--debug"] != nil && simulator {
state.Trace = true // enable tracing for dvm simulator
} }
for _, payload := range tx.Payloads { for asset, value := range incoming_value {
var new_value [8]byte var new_value [8]byte
stored_value, _ := chain.LoadSCAssetValue(data_tree, scid, payload.SCID) stored_value, _ := LoadSCAssetValue(data_tree, scid, asset)
binary.BigEndian.PutUint64(new_value[:], stored_value+payload.BurnValue) binary.BigEndian.PutUint64(new_value[:], stored_value+value)
chain.StoreSCValue(data_tree, scid, payload.SCID[:], new_value[:]) StoreSCValue(data_tree, scid, asset[:], new_value[:])
state.Assets[payload.SCID] += payload.BurnValue state.Assets[asset] += value
} }
// we have an entrypoint, now we must setup parameters and dvm // we have an entrypoint, now we must setup parameters and dvm
@ -199,16 +201,16 @@ func (chain *Blockchain) execute_sc_function(w_sc_tree *Tree_Wrapper, data_tree
for _, p := range function.Params { for _, p := range function.Params {
var zerohash crypto.Hash var zerohash crypto.Hash
switch { switch {
case p.Type == dvm.Uint64 && p.Name == "value": case p.Type == Uint64 && p.Name == "value":
params[p.Name] = fmt.Sprintf("%d", state.Assets[zerohash]) // overide value params[p.Name] = fmt.Sprintf("%d", state.Assets[zerohash]) // overide value
case p.Type == dvm.Uint64 && tx.SCDATA.Has(p.Name, rpc.DataUint64): case p.Type == Uint64 && SCDATA.Has(p.Name, rpc.DataUint64):
params[p.Name] = fmt.Sprintf("%d", tx.SCDATA.Value(p.Name, rpc.DataUint64).(uint64)) params[p.Name] = fmt.Sprintf("%d", SCDATA.Value(p.Name, rpc.DataUint64).(uint64))
case p.Type == dvm.String && tx.SCDATA.Has(p.Name, rpc.DataString): case p.Type == String && SCDATA.Has(p.Name, rpc.DataString):
params[p.Name] = tx.SCDATA.Value(p.Name, rpc.DataString).(string) params[p.Name] = SCDATA.Value(p.Name, rpc.DataString).(string)
case p.Type == dvm.String && tx.SCDATA.Has(p.Name, rpc.DataHash): case p.Type == String && SCDATA.Has(p.Name, rpc.DataHash):
h := tx.SCDATA.Value(p.Name, rpc.DataHash).(crypto.Hash) h := SCDATA.Value(p.Name, rpc.DataHash).(crypto.Hash)
params[p.Name] = string(h[:]) params[p.Name] = string(h[:])
fmt.Printf("%s:%x\n", p.Name, string(h[:])) //fmt.Printf("%s:%x\n", p.Name, string(h[:]))
default: default:
err = fmt.Errorf("entrypoint '%s' parameter type missing or not yet supported (%+v)", entrypoint, p) err = fmt.Errorf("entrypoint '%s' parameter type missing or not yet supported (%+v)", entrypoint, p)
@ -216,22 +218,53 @@ func (chain *Blockchain) execute_sc_function(w_sc_tree *Tree_Wrapper, data_tree
} }
} }
result, err := dvm.RunSmartContract(&sc_parsed, entrypoint, state, params) 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)
}
//fmt.Printf("result value %+v\n", result) //fmt.Printf("result value %+v\n", result)
if err != nil { if err != nil {
logger.V(2).Error(err, "error execcuting SC", "entrypoint", entrypoint, "scid", scid) //logger.V(2).Error(err, "error execcuting SC", "entrypoint", entrypoint, "scid", scid)
return return
} }
if err == nil && result.Type == dvm.Uint64 && result.ValueUint64 == 0 { // confirm the changes if err == nil && result.Type == Uint64 && result.ValueUint64 == 0 { // confirm the changes
for k, v := range tx_store.RawKeys { for k, v := range tx_store.RawKeys {
chain.StoreSCValue(data_tree, scid, []byte(k), v) StoreSCValue(data_tree, scid, []byte(k), v)
// fmt.Printf("storing %x %x\n", k,v)
} }
data_tree.Transfere = append(data_tree.Transfere, tx_store.Transfers[scid].TransferE...)
data_tree.transfere = append(data_tree.transfere, tx_store.Transfers[scid].TransferE...)
} else { // discard all changes, since we never write to store immediately, they are purged, however we need to return any value associated } 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") err = fmt.Errorf("Discarded knowingly")
return return
@ -243,7 +276,7 @@ func (chain *Blockchain) execute_sc_function(w_sc_tree *Tree_Wrapper, data_tree
} }
// reads SC, balance // reads SC, balance
func (chain *Blockchain) ReadSC(w_sc_tree *Tree_Wrapper, data_tree *Tree_Wrapper, scid crypto.Hash) (balance uint64, sc dvm.SmartContract, found bool) { func ReadSC(w_sc_tree *Tree_Wrapper, data_tree *Tree_Wrapper, scid crypto.Hash) (balance uint64, sc SmartContract, found bool) {
meta_bytes, err := w_sc_tree.Get(SC_Meta_Key(scid)) meta_bytes, err := w_sc_tree.Get(SC_Meta_Key(scid))
if err != nil { if err != nil {
return return
@ -254,19 +287,19 @@ func (chain *Blockchain) ReadSC(w_sc_tree *Tree_Wrapper, data_tree *Tree_Wrapper
return return
} }
var zerohash crypto.Hash var zerohash crypto.Hash
balance, _ = chain.LoadSCAssetValue(data_tree, scid, zerohash) balance, _ = LoadSCAssetValue(data_tree, scid, zerohash)
sc_bytes, err := data_tree.Get(SC_Code_Key(scid)) sc_bytes, err := data_tree.Get(SC_Code_Key(scid))
if err != nil { if err != nil {
return return
} }
var v dvm.Variable var v Variable
if err = v.UnmarshalBinary(sc_bytes); err != nil { if err = v.UnmarshalBinary(sc_bytes); err != nil {
return return
} }
sc, pos, err := dvm.ParseSmartContract(v.ValueString) sc, pos, err := ParseSmartContract(v.ValueString)
if err != nil { if err != nil {
return return
} }
@ -277,7 +310,7 @@ func (chain *Blockchain) ReadSC(w_sc_tree *Tree_Wrapper, data_tree *Tree_Wrapper
return return
} }
func (chain *Blockchain) LoadSCValue(data_tree *Tree_Wrapper, scid crypto.Hash, key []byte) (v dvm.Variable, found bool) { func LoadSCValue(data_tree *Tree_Wrapper, scid crypto.Hash, key []byte) (v Variable, found bool) {
//fmt.Printf("loading fromdb %s %s \n", scid, key) //fmt.Printf("loading fromdb %s %s \n", scid, key)
object_data, err := data_tree.Get(key[:]) object_data, err := data_tree.Get(key[:])
@ -296,7 +329,7 @@ func (chain *Blockchain) LoadSCValue(data_tree *Tree_Wrapper, scid crypto.Hash,
return v, true return v, true
} }
func (chain *Blockchain) LoadSCAssetValue(data_tree *Tree_Wrapper, scid crypto.Hash, asset crypto.Hash) (v uint64, found bool) { func LoadSCAssetValue(data_tree *Tree_Wrapper, scid crypto.Hash, asset crypto.Hash) (v uint64, found bool) {
//fmt.Printf("loading fromdb %s %s \n", scid, key) //fmt.Printf("loading fromdb %s %s \n", scid, key)
object_data, err := data_tree.Get(asset[:]) object_data, err := data_tree.Get(asset[:])
@ -315,7 +348,7 @@ func (chain *Blockchain) LoadSCAssetValue(data_tree *Tree_Wrapper, scid crypto.H
} }
// reads a value from SC, always read balance // reads a value from SC, always read balance
func (chain *Blockchain) ReadSCValue(data_tree *Tree_Wrapper, scid crypto.Hash, key interface{}) (value interface{}) { func ReadSCValue(data_tree *Tree_Wrapper, scid crypto.Hash, key interface{}) (value interface{}) {
var keybytes []byte var keybytes []byte
if key == nil { if key == nil {
@ -323,22 +356,22 @@ func (chain *Blockchain) ReadSCValue(data_tree *Tree_Wrapper, scid crypto.Hash,
} }
switch k := key.(type) { switch k := key.(type) {
case uint64: case uint64:
keybytes = dvm.DataKey{Key: dvm.Variable{Type: dvm.Uint64, ValueUint64: k}}.MarshalBinaryPanic() keybytes = DataKey{Key: Variable{Type: Uint64, ValueUint64: k}}.MarshalBinaryPanic()
case string: case string:
keybytes = dvm.DataKey{Key: dvm.Variable{Type: dvm.String, ValueString: k}}.MarshalBinaryPanic() keybytes = DataKey{Key: Variable{Type: String, ValueString: k}}.MarshalBinaryPanic()
//case int64: //case int64:
// keybytes = dvm.DataKey{Key: dvm.Variable{Type: dvm.String, Value: k}}.MarshalBinaryPanic() // keybytes = dvm.DataKey{Key: dvm.Variable{Type: dvm.String, Value: k}}.MarshalBinaryPanic()
default: default:
return return
} }
value_var, found := chain.LoadSCValue(data_tree, scid, keybytes) value_var, found := LoadSCValue(data_tree, scid, keybytes)
//fmt.Printf("read value %+v", value_var) //fmt.Printf("read value %+v", value_var)
if found && value_var.Type != dvm.Invalid { if found && value_var.Type != Invalid {
switch value_var.Type { switch value_var.Type {
case dvm.Uint64: case Uint64:
value = value_var.ValueUint64 value = value_var.ValueUint64
case dvm.String: case String:
value = value_var.ValueString value = value_var.ValueString
default: default:
panic("This variable cannot be loaded") panic("This variable cannot be loaded")
@ -348,7 +381,7 @@ func (chain *Blockchain) ReadSCValue(data_tree *Tree_Wrapper, scid crypto.Hash,
} }
// store the value in the chain // store the value in the chain
func (chain *Blockchain) StoreSCValue(data_tree *Tree_Wrapper, scid crypto.Hash, key, value []byte) { func StoreSCValue(data_tree *Tree_Wrapper, scid crypto.Hash, key, value []byte) {
if bytes.Compare(scid[:], key) == 0 { // an scid can mint its assets infinitely if bytes.Compare(scid[:], key) == 0 { // an scid can mint its assets infinitely
return return
} }

382
dvm/simulator.go Normal file
View File

@ -0,0 +1,382 @@
// 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.
package dvm
// this file implements necessary structure to SC handling
import "fmt"
//import "bytes"
//import "runtime/debug"
import "encoding/binary"
import "time"
import "math/big"
import "math/rand"
import "github.com/deroproject/derohe/cryptography/crypto"
import "github.com/deroproject/derohe/cryptography/bn256"
import "golang.org/x/xerrors"
import "github.com/deroproject/graviton"
import "github.com/deroproject/derohe/rpc"
import "github.com/deroproject/derohe/config"
//import "github.com/deroproject/derohe/transaction"
type Simulator struct {
ss *graviton.Snapshot
balance_tree *graviton.Tree
sc_tree *graviton.Tree
cache map[crypto.Hash]*graviton.Tree
height uint64
Balances map[string]map[string]uint64
}
func SimulatorInitialize(ss *graviton.Snapshot) *Simulator {
var s Simulator
var err error
if ss == nil {
store, err := graviton.NewMemStore()
if err != nil {
panic(err)
}
ss, err = store.LoadSnapshot(0)
if err != nil {
panic(err)
}
s.ss = ss
}
s.ss = ss
s.balance_tree, err = ss.GetTree(config.BALANCE_TREE)
if err != nil {
panic(err)
}
s.sc_tree, err = ss.GetTree(config.SC_META)
if err != nil {
panic(err)
}
s.cache = map[crypto.Hash]*graviton.Tree{}
s.Balances = map[string]map[string]uint64{}
//w_balance_tree := &dvm.Tree_Wrapper{Tree: balance_tree, Entries: map[string][]byte{}}
//w_sc_tree := &dvm.Tree_Wrapper{Tree: sc_tree, Entries: map[string][]byte{}}
return &s
}
// this is for testing some edge case in simulator
func (s *Simulator) AccountAddBalance(addr rpc.Address, scid crypto.Hash, balance_value uint64) {
balance := crypto.ConstructElGamal((*bn256.G1)(addr.PublicKey), crypto.ElGamal_BASE_G) // init zero balance
nb := crypto.NonceBalance{NonceHeight: 0, Balance: balance}
s.balance_tree.Put(addr.Compressed(), nb.Serialize())
}
func (s *Simulator) SCInstall(sc_code string, incoming_values map[crypto.Hash]uint64, SCDATA rpc.Arguments, signer_addr *rpc.Address, fees uint64) (scid crypto.Hash, gascompute, gasstorage uint64, err error) {
var blid crypto.Hash
rand.Seed(time.Now().Unix())
rand.Read(scid[:])
rand.Read(blid[:])
var sc SmartContract
if sc, _, err = ParseSmartContract(sc_code); err != nil {
//logger.V(2).Error(err, "error Parsing sc", "txid", txhash, "pos", pos)
return
}
var meta SC_META_DATA
if _, ok := sc.Functions["InitializePrivate"]; ok {
meta.Type = 1
}
w_sc_data_tree := Wrapped_tree(s.cache, s.ss, scid)
w_sc_data_tree.Put(SC_Code_Key(scid), Variable{Type: String, ValueString: sc_code}.MarshalBinaryPanic())
w_sc_tree := &Tree_Wrapper{Tree: s.sc_tree, Entries: map[string][]byte{}}
w_sc_tree.Put(SC_Meta_Key(scid), meta.MarshalBinary())
entrypoint := "Initialize"
if meta.Type == 1 { // if its a a private SC
entrypoint = "InitializePrivate"
}
gascompute, gasstorage, err = s.common(w_sc_tree, w_sc_data_tree, scid, s.height, s.height, uint64(time.Now().UnixMilli()), blid, scid, sc, entrypoint, 1, 0, signer_addr, incoming_values, SCDATA, fees, true)
return
}
func (s *Simulator) RunSC(incoming_values map[crypto.Hash]uint64, SCDATA rpc.Arguments, signer_addr *rpc.Address, fees uint64) (gascompute, gasstorage uint64, err error) {
var blid, txid crypto.Hash
rand.Read(blid[:])
rand.Read(txid[:])
action_code := rpc.SC_ACTION(SCDATA.Value(rpc.SCACTION, rpc.DataUint64).(uint64))
switch action_code {
case rpc.SC_INSTALL: // request to install an SC
err = fmt.Errorf("cannot install code using this api")
return
case rpc.SC_CALL: // trigger a CALL
if !SCDATA.Has(rpc.SCID, rpc.DataHash) { // but only if it is present
err = fmt.Errorf("no scid provided")
return
}
if !SCDATA.Has("entrypoint", rpc.DataString) { // but only if it is present
err = fmt.Errorf("no entrypoint provided")
return
}
scid := SCDATA.Value(rpc.SCID, rpc.DataHash).(crypto.Hash)
w_sc_tree := &Tree_Wrapper{Tree: s.sc_tree, Entries: map[string][]byte{}}
if _, err = w_sc_tree.Get(SC_Meta_Key(scid)); err != nil {
err = fmt.Errorf("scid %s not installed", scid)
return
}
w_sc_data_tree := Wrapped_tree(s.cache, s.ss, scid)
entrypoint := SCDATA.Value("entrypoint", rpc.DataString).(string)
balance, sc, _ := ReadSC(w_sc_tree, w_sc_data_tree, scid)
gascompute, gasstorage, err = s.common(w_sc_tree, w_sc_data_tree, scid, s.height, s.height, uint64(time.Now().UnixMilli()), blid, scid, sc, entrypoint, 1, balance, signer_addr, incoming_values, SCDATA, fees, true)
return
default:
err = fmt.Errorf("unknown action_code code %d", action_code)
}
return
}
func (s *Simulator) common(w_sc_tree, w_sc_data_tree *Tree_Wrapper, scid crypto.Hash, bl_height, bl_topoheight, bl_timestamp uint64, blid crypto.Hash, txid crypto.Hash, sc SmartContract, entrypoint string, hard_fork_version_current int64, balance_at_start uint64, signer_addr *rpc.Address, incoming_values map[crypto.Hash]uint64, SCDATA rpc.Arguments, fees uint64, simulator bool) (gascompute, gasstorage uint64, err error) {
var signer [33]byte
if signer_addr != nil {
copy(signer[:], signer_addr.Compressed())
}
gascompute, gasstorage, err = Execute_sc_function(w_sc_tree, w_sc_data_tree, scid, bl_height, bl_topoheight, uint64(time.Now().UnixMilli()), blid, scid, sc, entrypoint, 1, 0, signer, incoming_values, SCDATA, fees, simulator)
fmt.Printf("sc execution error %s\n", err)
// we must commit all the changes
// check whether we are not overflowing/underflowing, means SC is not over sending
if err == nil {
err = SanityCheckExternalTransfers(w_sc_data_tree, s.balance_tree, scid)
}
if err != nil { // error occured, give everything to SC, since we may not have information to send them back
var zeroaddress [33]byte
if signer != zeroaddress { // if we can identify sender, return funds to him
ErrorRevert(s.ss, s.cache, s.balance_tree, signer, scid, incoming_values)
} else { // we could not extract signer, give burned funds to SC
ErrorRevert(s.ss, s.cache, s.balance_tree, signer, scid, incoming_values)
}
return
}
ProcessExternal(s.ss, s.cache, s.balance_tree, signer, scid, w_sc_data_tree, w_sc_tree)
return
}
// this is core function used to evaluate when we are overflowing/underflowing
func SanityCheckExternalTransfers(w_sc_data_tree *Tree_Wrapper, balance_tree *graviton.Tree, scid crypto.Hash) (err error) {
total_per_asset := map[crypto.Hash]uint64{}
for _, transfer := range w_sc_data_tree.Transfere { // do external tranfer
if transfer.Amount == 0 {
continue
}
// an SCID can generate it's token infinitely
if transfer.Asset != scid && total_per_asset[transfer.Asset]+transfer.Amount <= total_per_asset[transfer.Asset] {
err = fmt.Errorf("Balance calculation overflow")
return
} else {
total_per_asset[transfer.Asset] = total_per_asset[transfer.Asset] + transfer.Amount
}
}
if err == nil {
for asset, value := range total_per_asset {
stored_value, _ := LoadSCAssetValue(w_sc_data_tree, scid, asset)
// an SCID can generate it's token infinitely
if asset != scid && stored_value-value > stored_value {
err = fmt.Errorf("Balance calculation underflow stored_value %d transferring %d\n", stored_value, value)
return
}
var new_value [8]byte
binary.BigEndian.PutUint64(new_value[:], stored_value-value)
StoreSCValue(w_sc_data_tree, scid, asset[:], new_value[:])
}
}
//also check whether all destinations are registered
if err == nil {
for _, transfer := range w_sc_data_tree.Transfere {
if _, err = balance_tree.Get([]byte(transfer.Address)); err == nil || xerrors.Is(err, graviton.ErrNotFound) {
// everything is okay
} else {
err = fmt.Errorf("account is unregistered")
//logger.V(1).Error(err, "account is unregistered", "txhash", txhash, "scid", scid, "address", transfer.Address)
return
}
}
}
return
}
// any error during this will panic
func ErrorRevert(ss *graviton.Snapshot, cache map[crypto.Hash]*graviton.Tree, balance_tree *graviton.Tree, signer [33]byte, scid crypto.Hash, incoming_values map[crypto.Hash]uint64) {
var err error
for scid_asset, burnvalue := range incoming_values {
var zeroscid crypto.Hash
var curbtree *graviton.Tree
switch scid_asset {
case zeroscid: // main dero balance, handle it
curbtree = balance_tree
case scid: // this scid balance, handle it
curbtree = cache[scid]
default: // any other asset scid
var ok bool
if curbtree, ok = cache[scid_asset]; !ok {
if curbtree, err = ss.GetTree(string(scid_asset[:])); err != nil {
panic(err)
}
cache[scid_asset] = curbtree
}
}
if curbtree == nil {
panic("tree cannot be nil at this point in time")
}
if balance_serialized, err1 := curbtree.Get(signer[:]); err1 != nil { // no error can occur
panic(err1) // only disk corruption can reach here
} else {
nb := new(crypto.NonceBalance).Deserialize(balance_serialized)
nb.Balance = nb.Balance.Plus(new(big.Int).SetUint64(burnvalue)) // add back burn value to users balance homomorphically
curbtree.Put(signer[:], nb.Serialize()) // reserialize and store
}
}
}
func ErrorDeposit(ss *graviton.Snapshot, cache map[crypto.Hash]*graviton.Tree, balance_tree *graviton.Tree, signer [33]byte, scid crypto.Hash, incoming_values map[crypto.Hash]uint64) {
for scid_asset, burnvalue := range incoming_values {
var new_value [8]byte
w_sc_data_tree := Wrapped_tree(cache, ss, scid) // get a new tree, discarding everything
stored_value, _ := LoadSCAssetValue(w_sc_data_tree, scid, scid_asset)
binary.BigEndian.PutUint64(new_value[:], stored_value+burnvalue)
StoreSCValue(w_sc_data_tree, scid, scid_asset[:], new_value[:])
for k, v := range w_sc_data_tree.Entries { // commit incoming balances to tree
if err := w_sc_data_tree.Tree.Put([]byte(k), v); err != nil {
panic(err)
}
}
cache[scid] = w_sc_data_tree.Tree
}
}
func ProcessExternal(ss *graviton.Snapshot, cache map[crypto.Hash]*graviton.Tree, balance_tree *graviton.Tree, signer [33]byte, scid crypto.Hash, w_sc_data_tree, w_sc_tree *Tree_Wrapper) {
var err error
// anything below should never give error
cache[scid] = w_sc_data_tree.Tree
for k, v := range w_sc_data_tree.Entries { // commit entire data to tree
//if _, ok := globals.Arguments["--debug"]; ok && globals.Arguments["--debug"] != nil && chain.simulator {
// logger.V(1).Info("Writing", "txid", txhash, "scid", scid, "key", fmt.Sprintf("%x", k), "value", fmt.Sprintf("%x", v))
// }
if len(v) == 0 {
if err = w_sc_data_tree.Tree.Delete([]byte(k)); err != nil {
panic(err)
}
} else {
if err = w_sc_data_tree.Tree.Put([]byte(k), v); err != nil {
panic(err)
}
}
}
for k, v := range w_sc_tree.Entries {
if err = w_sc_tree.Tree.Put([]byte(k), v); err != nil {
panic(err)
}
}
for i, transfer := range w_sc_data_tree.Transfere { // do external tranfer
if transfer.Amount == 0 {
continue
}
//fmt.Printf("%d sending to external %s %x\n", i,transfer.Asset,transfer.Address)
var zeroscid crypto.Hash
var curbtree *graviton.Tree
switch transfer.Asset {
case zeroscid: // main dero balance, handle it
curbtree = balance_tree
case scid: // this scid balance, handle it
curbtree = cache[scid]
default: // any other asset scid
var ok bool
if curbtree, ok = cache[transfer.Asset]; !ok {
if curbtree, err = ss.GetTree(string(transfer.Asset[:])); err != nil {
panic(err)
}
cache[transfer.Asset] = curbtree
}
}
if curbtree == nil {
panic("tree cannot be nil at this point in time")
}
addr_bytes := []byte(transfer.Address)
if _, err = balance_tree.Get(addr_bytes); err != nil { // first check whether address is registered
err = fmt.Errorf("sending to non registered account acc %x err %s", addr_bytes, err) // this can only occur, if account no registered or dis corruption
panic(err)
}
var balance_serialized []byte
balance_serialized, err = curbtree.Get(addr_bytes)
if err != nil && xerrors.Is(err, graviton.ErrNotFound) { // if the address is not found, lookup in main tree
var p bn256.G1
if err = p.DecodeCompressed(addr_bytes[:]); err != nil {
panic(fmt.Errorf("key %x could not be decompressed", addr_bytes))
}
balance := crypto.ConstructElGamal(&p, crypto.ElGamal_BASE_G) // init zero balance
nb := crypto.NonceBalance{NonceHeight: 0, Balance: balance}
balance_serialized = nb.Serialize()
} else if err != nil {
fmt.Printf("%s %d could not transfer %d %+v\n", scid, i, transfer.Amount, addr_bytes)
panic(err) // only disk corruption can reach here
}
nb := new(crypto.NonceBalance).Deserialize(balance_serialized)
nb.Balance = nb.Balance.Plus(new(big.Int).SetUint64(transfer.Amount)) // add transfer to users balance homomorphically
curbtree.Put(addr_bytes, nb.Serialize()) // reserialize and store
}
}

174
dvm/simulator_test.go Normal file
View File

@ -0,0 +1,174 @@
// Copyright 2017-2018 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.
package dvm
//import "fmt"
//import "reflect"
import "strings"
import "testing"
import "github.com/deroproject/derohe/rpc"
import "github.com/deroproject/derohe/cryptography/crypto"
var sc = `/* Lottery Smart Contract Example in DVM-BASIC.
This lottery smart contract will give lottery wins on every second try in following default contract.
Make depost transaction to this SCID to play lottery.
Check https://github.com/deroproject/derohe/blob/main/guide/examples/lottery_sc_guide.md
*/
Function Lottery() Uint64
10 dim deposit_count,winner as Uint64
20 LET deposit_count = LOAD("deposit_count")+1
25 IF DEROVALUE() == 0 THEN GOTO 110 // if deposit amount is 0, simply return
30 STORE("depositor_address" + (deposit_count-1), SIGNER()) // store address for later on payment
40 STORE("deposit_total", LOAD("deposit_total") + DEROVALUE() )
50 STORE("deposit_count",deposit_count)
60 IF LOAD("lotteryeveryXdeposit") > deposit_count THEN GOTO 110 // we will wait till X players join in
// we are here means all players have joined in, roll the DICE,
70 LET winner = RANDOM() % deposit_count // we have a winner
80 SEND_DERO_TO_ADDRESS(LOAD("depositor_address" + winner) , LOAD("lotterygiveback")*LOAD("deposit_total")/10000)
// Re-Initialize for another round
90 STORE("deposit_count", 0) // initial players
100 STORE("deposit_total", 0) // total deposit of all players
110 RETURN 0
End Function
// This function is used to initialize parameters during install time
Function Initialize() Uint64
5 version("1.2.3")
10 STORE("owner", SIGNER()) // store in DB ["owner"] = address
20 STORE("lotteryeveryXdeposit", 2) // lottery will reward every X deposits
// How much will lottery giveback in 1/10000 parts, granularity .01 %
30 STORE("lotterygiveback", 9900) // lottery will give reward 99% of deposits, 1 % is accumulated for owner to withdraw
33 STORE("deposit_count", 0) // initial players
34 STORE("deposit_total", 0) // total deposit of all players
// 35 printf "Initialize executed"
40 RETURN 0
End Function
// Function to tune lottery parameters
Function TuneLotteryParameters(input Uint64, lotteryeveryXdeposit Uint64, lotterygiveback Uint64) Uint64
10 dim key,stored_owner as String
20 dim value_uint64 as Uint64
30 IF LOAD("owner") == SIGNER() THEN GOTO 100 // check whether owner is real owner
40 RETURN 1
100 STORE("lotteryeveryXdeposit", lotteryeveryXdeposit) // lottery will reward every X deposits
130 STORE("lotterygiveback", value_uint64) // how much will lottery giveback in 1/10000 parts, granularity .01 %
140 RETURN 0 // return success
End Function
// This function is used to change owner
// owner is an string form of address
Function TransferOwnership(newowner String) Uint64
10 IF LOAD("owner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 STORE("tmpowner",ADDRESS_RAW(newowner))
40 RETURN 0
End Function
// Until the new owner claims ownership, existing owner remains owner
Function ClaimOwnership() Uint64
10 IF LOAD("tmpowner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 STORE("owner",SIGNER()) // ownership claim successful
40 RETURN 0
End Function
// If signer is owner, withdraw any requested funds
// If everthing is okay, they will be showing in signers wallet
Function Withdraw( amount Uint64) Uint64
10 IF LOAD("owner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 SEND_DERO_TO_ADDRESS(SIGNER(),amount)
40 RETURN 0
End Function
// If signer is owner, provide him rights to update code anytime
// make sure update is always available to SC
Function UpdateCode( code String) Uint64
10 IF LOAD("owner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 UPDATE_SC_CODE(code)
40 RETURN 0
End Function
`
// run the test
func Test_Simulator_execution(t *testing.T) {
s := SimulatorInitialize(nil)
var addr *rpc.Address
var err error
if addr, err = rpc.NewAddress(strings.TrimSpace("deto1qy0ehnqjpr0wxqnknyc66du2fsxyktppkr8m8e6jvplp954klfjz2qqdzcd8p")); err != nil {
panic(err)
}
var zerohash crypto.Hash
s.AccountAddBalance(*addr, zerohash, 500)
scid, gascompute, gasstorage, err := s.SCInstall(sc, map[crypto.Hash]uint64{}, rpc.Arguments{}, addr, 0)
if err != nil {
t.Fatalf("cannot install contract %s\n", err)
}
_ = gascompute
_ = gasstorage
// trigger first time lottery play
gascompute, gasstorage, err = s.RunSC(map[crypto.Hash]uint64{zerohash: 45}, rpc.Arguments{{rpc.SCACTION, rpc.DataUint64, uint64(rpc.SC_CALL)}, {rpc.SCID, rpc.DataHash, scid}, rpc.Argument{"entrypoint", rpc.DataString, "Lottery"}}, addr, 0)
if err != nil {
t.Fatalf("cannot run contract %s\n", err)
}
w_sc_data_tree := Wrapped_tree(s.cache, s.ss, scid)
stored_value, _ := LoadSCAssetValue(w_sc_data_tree, scid, zerohash)
if 45 != stored_value {
t.Fatalf("storage corruption dero value")
}
if uint64(45) != ReadSCValue(w_sc_data_tree, scid, "deposit_total") {
t.Fatalf("storage corruption")
}
// trigger second time lottery play
gascompute, gasstorage, err = s.RunSC(map[crypto.Hash]uint64{zerohash: 55}, rpc.Arguments{{rpc.SCACTION, rpc.DataUint64, uint64(rpc.SC_CALL)}, {rpc.SCID, rpc.DataHash, scid}, rpc.Argument{"entrypoint", rpc.DataString, "Lottery"}}, addr, 0)
if err != nil {
t.Fatalf("cannot run contract %s\n", err)
}
w_sc_data_tree = Wrapped_tree(s.cache, s.ss, scid)
// only 1 is left ( which is profit)
if stored_value, _ = LoadSCAssetValue(w_sc_data_tree, scid, zerohash); 1 != stored_value {
t.Fatalf("storage corruption dero value")
}
// total deposit must be zero
if uint64(0) != ReadSCValue(w_sc_data_tree, scid, "deposit_total") {
t.Fatalf("storage corruption")
}
}

View File

@ -217,6 +217,7 @@ type (
ValidBlock string `json:"valid_block"` // TX is valid in this block ValidBlock string `json:"valid_block"` // TX is valid in this block
InvalidBlock []string `json:"invalid_block"` // TX is invalid in this block, 0 or more InvalidBlock []string `json:"invalid_block"` // TX is invalid in this block, 0 or more
Ring [][]string `json:"ring"` // ring members completed, since tx contains compressed Ring [][]string `json:"ring"` // ring members completed, since tx contains compressed
Signer string `json:"signer"` // if signer could be extracted, it will be placed here
Balance uint64 `json:"balance"` // if tx is SC, give SC balance at start Balance uint64 `json:"balance"` // if tx is SC, give SC balance at start
Code string `json:"code"` // smart contract code at start Code string `json:"code"` // smart contract code at start
BalanceNow uint64 `json:"balancenow"` // if tx is SC, give SC balance at current topo height BalanceNow uint64 `json:"balancenow"` // if tx is SC, give SC balance at current topo height
@ -269,27 +270,6 @@ type (
} }
) )
/*
{
"id": "0",
"jsonrpc": "2.0",
"result": {
"alt_blocks_count": 5,
"difficulty": 972165250,
"grey_peerlist_size": 2280,
"height": 993145,
"incoming_connections_count": 0,
"outgoing_connections_count": 8,
"status": "OK",
"target": 60,
"target_height": 993137,
"testnet": false,
"top_block_hash": "",
"tx_count": 564287,
"tx_pool_size": 45,
"white_peerlist_size": 529
}
}*/
type ( type (
GetInfo_Params struct{} // no params GetInfo_Params struct{} // no params
GetInfo_Result struct { GetInfo_Result struct {
@ -319,3 +299,10 @@ type (
Status string `json:"status"` Status string `json:"status"`
} }
) )
type GasEstimate_Params Transfer_Params // same structure as used by transfer call
type GasEstimate_Result struct {
GasCompute uint64 `json:"gascompute"`
GasStorage uint64 `json:"gasstorage"`
Status string `json:"status"`
}

View File

@ -205,6 +205,8 @@ type (
SC_ID string `json:"scid"` SC_ID string `json:"scid"`
SC_RPC Arguments `json:"sc_rpc"` SC_RPC Arguments `json:"sc_rpc"`
Ringsize uint64 `json:"ringsize"` Ringsize uint64 `json:"ringsize"`
Fees uint64 `json:"fees"`
Signer string `json:"signer"` // only used for gas estimation
} }
Transfer_Result struct { Transfer_Result struct {
TXID string `json:"txid,omitempty"` TXID string `json:"txid,omitempty"`

103
run_integration_test.sh Executable file
View File

@ -0,0 +1,103 @@
#!/usr/bin/env bash
CURDIR=`/bin/pwd`
BASEDIR=$(dirname $0)
ABSPATH=$(readlink -f $0)
ABSDIR=$(dirname $ABSPATH)
# set -x # to enable debug and verbose printing of each and every command
_DEBUG="on"
function DEBUG()
{
[ "$_DEBUG" == "on" ] && $@
}
function check_errs()
{
# Function. Parameter 1 is the return code
# Para. 2 is text to display on failure.
if [ "${1}" -ne "0" ]; then
echo "ERROR # ${1} : ${2}"
# if [ "$#" -eq "3"]; then
echo "logs "
cat /dev/shm/dtest/log.txt
# cat "${3}"
# fi
# as a bonus, make our script exit with the right error code.
# rm -rf /dev/shm/dtest >/dev/null 2>&1
killall -9 simulator >/dev/null 2>&1
exit ${1}
fi
}
function run_single_test()
{
test=${1}
killall -9 simulator >/dev/null 2>&1
rm -rf /dev/shm/dtest >/dev/null 2>&1
mkdir /dev/shm/dtest
cd /dev/shm/dtest
/tmp/simulator --clog-level 2 $disableautomine >/dev/shm/dtest/dero.log 2>&1 &
disown
check_errs $? "Could not run simulator"
echo "Simulator started"
sleep 4
DEBUG echo "running test $test"
rm /dev/shm/dtest/log.txt >/dev/null 2>&1
rm /dev/shm/dtest/log.txt >/dev/null 2>&1
cd $test
$test/run_test.sh >/dev/shm/dtest/log.txt 2>&1
check_errs $? "test failed $test"
killall -9 simulator >/dev/null 2>&1
cd $CURDIR
}
function run_tests()
{
tests=$(find ${1} -mindepth 1 -maxdepth 1 -type d)
for test in $tests; do
run_single_test "$test"
done
}
cd $ABSDIR
go build -o /tmp/ ./cmd/...
check_errs $? "Could not build binaries"
cd $CURDIR
if [[ $# -eq 1 ]]; then # if user requsted single test
if [ -d "$ABSDIR/tests/normal/${1}" ]; then
run_single_test "$ABSDIR/tests/normal/${1}"
exit 0
fi
if [ -d "$ABSDIR/tests/specific/${1}" ]; then
disableautomine="--noautomine"
run_single_test "$ABSDIR/tests/specific/${1}"
exit 0
fi
echo "no such test found ${1}"
exit 0
fi
#we will first run specific/special test cases
disableautomine="--noautomine"
run_tests $ABSDIR/tests/specific
#we will first run normal test cases
disableautomine=""
run_tests $ABSDIR/tests/normal

View File

@ -0,0 +1,58 @@
#!/usr/bin/env bash
#set -x # to enable debug and verbose printing of each and every command
CURDIR=`/bin/pwd`
BASEDIR=$(dirname $0)
ABSPATH=$(readlink -f $0)
ABSDIR=$(dirname $ABSPATH)
command -v curl >/dev/null 2>&1 || { echo "I require curl but it's not installed. Aborting." >&2; exit 1; }
command -v jq >/dev/null 2>&1 || { echo "I require jq but it's not installed. Aborting." >&2; exit 1; }
command -v base64 >/dev/null 2>&1 || { echo "I require base64 but it's not installed. Aborting." >&2; exit 1; }
daemon_rpc_port="20000" # daemon rpc is listening on this port
# we have number of wallets listening at ports from 30000
# we will be using 3 wallets, named owner, player1,player2
owner_rpc_port="30000"
player1_rpc_port="30001"
player2_rpc_port="30002"
owner_address=$(curl --silent http://127.0.0.1:$owner_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getaddress"}' -H 'Content-Type: application/json'| jq -r ".result.address")
player1_address=$(curl --silent http://127.0.0.1:$player1_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getaddress"}' -H 'Content-Type: application/json'| jq -r ".result.address")
player2_address=$(curl --silent http://127.0.0.1:$player2_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getaddress"}' -H 'Content-Type: application/json'| jq -r ".result.address")
function balance(){
curl --silent http://127.0.0.1:$1/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getbalance"}' -H 'Content-Type: application/json'| jq -r ".result.balance"
}
function scbalance(){
curl --silent http://127.0.0.1:$daemon_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getsc","params":{ "scid":"'"$1"'" , "code":false, "keysstring":["deposit_count"]}}' -H 'Content-Type: application/json' | jq -r ".result.balance"
}
echo "SC owner address" $owner_address
echo "player1 address" $player1_address
echo "player2 address" $player2_address
# use owner wallet to load/install an lotter sc to chain
#scid=$(curl --silent --request POST --data-binary @test.bas http://127.0.0.1:$owner_rpc_port/install_sc| jq -r ".txid")
#echo "SCID" $scid
#sleep 1
gas=`curl --silent http://127.0.0.1:$daemon_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getgasestimate","params":{ "transfers":[{"amount":100000,"destination":"deto1qxsz7v707t8mla4mslptlf6w7zkgrukvg5wfna0tha48yfjcahwh64qxnus7f"}], "sc":"'$(base64 -w 0 test.bas)'" }}' -H 'Content-Type: application/json' | jq -r ".result.gasstorage"`
if [[ $gas -gt 10 ]]
then
exit 0
else
exit 1
fi

View File

@ -0,0 +1,93 @@
/* Lottery Smart Contract Example in DVM-BASIC.
This lottery smart contract will give lottery wins on every second try in following default contract.
Make depost transaction to this SCID to play lottery.
Check https://github.com/deroproject/derohe/blob/main/guide/examples/lottery_sc_guide.md
*/
Function Lottery() Uint64
10 dim deposit_count,winner as Uint64
20 LET deposit_count = LOAD("deposit_count")+1
25 IF DEROVALUE() == 0 THEN GOTO 110 // if deposit amount is 0, simply return
30 STORE("depositor_address" + (deposit_count-1), SIGNER()) // store address for later on payment
40 STORE("deposit_total", LOAD("deposit_total") + DEROVALUE() )
50 STORE("deposit_count",deposit_count)
60 IF LOAD("lotteryeveryXdeposit") > deposit_count THEN GOTO 110 // we will wait till X players join in
// we are here means all players have joined in, roll the DICE,
70 LET winner = RANDOM() % deposit_count // we have a winner
80 SEND_DERO_TO_ADDRESS(LOAD("depositor_address" + winner) , LOAD("lotterygiveback")*LOAD("deposit_total")/10000)
// Re-Initialize for another round
90 STORE("deposit_count", 0) // initial players
100 STORE("deposit_total", 0) // total deposit of all players
110 RETURN 0
End Function
// This function is used to initialize parameters during install time
Function Initialize() Uint64
5 version("1.2.3")
10 STORE("owner", SIGNER()) // store in DB ["owner"] = address
20 STORE("lotteryeveryXdeposit", 2) // lottery will reward every X deposits
// How much will lottery giveback in 1/10000 parts, granularity .01 %
30 STORE("lotterygiveback", 9900) // lottery will give reward 99% of deposits, 1 % is accumulated for owner to withdraw
33 STORE("deposit_count", 0) // initial players
34 STORE("deposit_total", 0) // total deposit of all players
// 35 printf "Initialize executed"
40 RETURN 0
End Function
// Function to tune lottery parameters
Function TuneLotteryParameters(input Uint64, lotteryeveryXdeposit Uint64, lotterygiveback Uint64) Uint64
10 dim key,stored_owner as String
20 dim value_uint64 as Uint64
30 IF LOAD("owner") == SIGNER() THEN GOTO 100 // check whether owner is real owner
40 RETURN 1
100 STORE("lotteryeveryXdeposit", lotteryeveryXdeposit) // lottery will reward every X deposits
130 STORE("lotterygiveback", value_uint64) // how much will lottery giveback in 1/10000 parts, granularity .01 %
140 RETURN 0 // return success
End Function
// This function is used to change owner
// owner is an string form of address
Function TransferOwnership(newowner String) Uint64
10 IF LOAD("owner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 STORE("tmpowner",ADDRESS_RAW(newowner))
40 RETURN 0
End Function
// Until the new owner claims ownership, existing owner remains owner
Function ClaimOwnership() Uint64
10 IF LOAD("tmpowner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 STORE("owner",SIGNER()) // ownership claim successful
40 RETURN 0
End Function
// If signer is owner, withdraw any requested funds
// If everthing is okay, they will be showing in signers wallet
Function Withdraw( amount Uint64) Uint64
10 IF LOAD("owner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 SEND_DERO_TO_ADDRESS(SIGNER(),amount)
40 RETURN 0
End Function
// If signer is owner, provide him rights to update code anytime
// make sure update is always available to SC
Function UpdateCode( code String) Uint64
10 IF LOAD("owner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 UPDATE_SC_CODE(code)
40 RETURN 0
End Function

View File

@ -0,0 +1,58 @@
#!/usr/bin/env bash
#set -x # to enable debug and verbose printing of each and every command
CURDIR=`/bin/pwd`
BASEDIR=$(dirname $0)
ABSPATH=$(readlink -f $0)
ABSDIR=$(dirname $ABSPATH)
command -v curl >/dev/null 2>&1 || { echo "I require curl but it's not installed. Aborting." >&2; exit 1; }
command -v jq >/dev/null 2>&1 || { echo "I require jq but it's not installed. Aborting." >&2; exit 1; }
command -v base64 >/dev/null 2>&1 || { echo "I require base64 but it's not installed. Aborting." >&2; exit 1; }
daemon_rpc_port="20000" # daemon rpc is listening on this port
# we have number of wallets listening at ports from 30000
# we will be using 3 wallets, named owner, player1,player2
owner_rpc_port="30000"
player1_rpc_port="30001"
player2_rpc_port="30002"
owner_address=$(curl --silent http://127.0.0.1:$owner_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getaddress"}' -H 'Content-Type: application/json'| jq -r ".result.address")
player1_address=$(curl --silent http://127.0.0.1:$player1_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getaddress"}' -H 'Content-Type: application/json'| jq -r ".result.address")
player2_address=$(curl --silent http://127.0.0.1:$player2_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getaddress"}' -H 'Content-Type: application/json'| jq -r ".result.address")
function balance(){
curl --silent http://127.0.0.1:$1/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getbalance"}' -H 'Content-Type: application/json'| jq -r ".result.balance"
}
function scbalance(){
curl --silent http://127.0.0.1:$daemon_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getsc","params":{ "scid":"'"$1"'" , "code":false, "keysstring":["deposit_count"]}}' -H 'Content-Type: application/json' | jq -r ".result.balance"
}
echo "SC owner address" $owner_address
echo "player1 address" $player1_address
echo "player2 address" $player2_address
# use owner wallet to load/install an lotter sc to chain
scid=$(curl --silent --request POST --data-binary @test.bas http://127.0.0.1:$owner_rpc_port/install_sc| jq -r ".txid")
echo "SCID" $scid
sleep 3
gasstorage=$(curl --silent http://127.0.0.1:$daemon_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getgasestimate","params":{ "transfers":[{"amount":100000,"destination":"deto1qxsz7v707t8mla4mslptlf6w7zkgrukvg5wfna0tha48yfjcahwh64qxnus7f"}], "sc_rpc":[{"name":"SC_ID","datatype":"H","value":"'"$scid"'"}, {"name":"SC_ACTION","datatype":"U","value":0},{"name":"entrypoint","datatype":"S","value":"Lottery"}] }}' -H 'Content-Type: application/json' | jq -r ".result.gasstorage")
if [[ $gasstorage -gt 50 ]]
then
exit 0
else
exit 1
fi

View File

@ -0,0 +1,93 @@
/* Lottery Smart Contract Example in DVM-BASIC.
This lottery smart contract will give lottery wins on every second try in following default contract.
Make depost transaction to this SCID to play lottery.
Check https://github.com/deroproject/derohe/blob/main/guide/examples/lottery_sc_guide.md
*/
Function Lottery() Uint64
10 dim deposit_count,winner as Uint64
20 LET deposit_count = LOAD("deposit_count")+1
25 IF DEROVALUE() == 0 THEN GOTO 110 // if deposit amount is 0, simply return
30 STORE("depositor_address" + (deposit_count-1), SIGNER()) // store address for later on payment
40 STORE("deposit_total", LOAD("deposit_total") + DEROVALUE() )
50 STORE("deposit_count",deposit_count)
60 IF LOAD("lotteryeveryXdeposit") > deposit_count THEN GOTO 110 // we will wait till X players join in
// we are here means all players have joined in, roll the DICE,
70 LET winner = RANDOM() % deposit_count // we have a winner
80 SEND_DERO_TO_ADDRESS(LOAD("depositor_address" + winner) , LOAD("lotterygiveback")*LOAD("deposit_total")/10000)
// Re-Initialize for another round
90 STORE("deposit_count", 0) // initial players
100 STORE("deposit_total", 0) // total deposit of all players
110 RETURN 0
End Function
// This function is used to initialize parameters during install time
Function Initialize() Uint64
5 version("1.2.3")
10 STORE("owner", SIGNER()) // store in DB ["owner"] = address
20 STORE("lotteryeveryXdeposit", 2) // lottery will reward every X deposits
// How much will lottery giveback in 1/10000 parts, granularity .01 %
30 STORE("lotterygiveback", 9900) // lottery will give reward 99% of deposits, 1 % is accumulated for owner to withdraw
33 STORE("deposit_count", 0) // initial players
34 STORE("deposit_total", 0) // total deposit of all players
// 35 printf "Initialize executed"
40 RETURN 0
End Function
// Function to tune lottery parameters
Function TuneLotteryParameters(input Uint64, lotteryeveryXdeposit Uint64, lotterygiveback Uint64) Uint64
10 dim key,stored_owner as String
20 dim value_uint64 as Uint64
30 IF LOAD("owner") == SIGNER() THEN GOTO 100 // check whether owner is real owner
40 RETURN 1
100 STORE("lotteryeveryXdeposit", lotteryeveryXdeposit) // lottery will reward every X deposits
130 STORE("lotterygiveback", value_uint64) // how much will lottery giveback in 1/10000 parts, granularity .01 %
140 RETURN 0 // return success
End Function
// This function is used to change owner
// owner is an string form of address
Function TransferOwnership(newowner String) Uint64
10 IF LOAD("owner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 STORE("tmpowner",ADDRESS_RAW(newowner))
40 RETURN 0
End Function
// Until the new owner claims ownership, existing owner remains owner
Function ClaimOwnership() Uint64
10 IF LOAD("tmpowner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 STORE("owner",SIGNER()) // ownership claim successful
40 RETURN 0
End Function
// If signer is owner, withdraw any requested funds
// If everthing is okay, they will be showing in signers wallet
Function Withdraw( amount Uint64) Uint64
10 IF LOAD("owner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 SEND_DERO_TO_ADDRESS(SIGNER(),amount)
40 RETURN 0
End Function
// If signer is owner, provide him rights to update code anytime
// make sure update is always available to SC
Function UpdateCode( code String) Uint64
10 IF LOAD("owner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 UPDATE_SC_CODE(code)
40 RETURN 0
End Function

View File

@ -0,0 +1,68 @@
#!/usr/bin/env bash
#set -x # to enable debug and verbose printing of each and every command
CURDIR=`/bin/pwd`
BASEDIR=$(dirname $0)
ABSPATH=$(readlink -f $0)
ABSDIR=$(dirname $ABSPATH)
command -v curl >/dev/null 2>&1 || { echo "I require curl but it's not installed. Aborting." >&2; exit 1; }
command -v jq >/dev/null 2>&1 || { echo "I require jq but it's not installed. Aborting." >&2; exit 1; }
daemon_rpc_port="20000" # daemon rpc is listening on this port
# we have number of wallets listening at ports from 30000
# we will be using 3 wallets, named owner, player1,player2
owner_rpc_port="30000"
player1_rpc_port="30001"
player2_rpc_port="30002"
owner_address=$(curl --silent http://127.0.0.1:$owner_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getaddress"}' -H 'Content-Type: application/json'| jq -r ".result.address")
player1_address=$(curl --silent http://127.0.0.1:$player1_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getaddress"}' -H 'Content-Type: application/json'| jq -r ".result.address")
player2_address=$(curl --silent http://127.0.0.1:$player2_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getaddress"}' -H 'Content-Type: application/json'| jq -r ".result.address")
function balance(){
curl --silent http://127.0.0.1:$1/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getbalance"}' -H 'Content-Type: application/json'| jq -r ".result.balance"
}
function scbalance(){
curl --silent http://127.0.0.1:$daemon_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getsc","params":{ "scid":"'"$1"'" , "code":false, "keysstring":["deposit_count"]}}' -H 'Content-Type: application/json' | jq -r ".result.balance"
}
echo "SC owner address" $owner_address
echo "player1 address" $player1_address
echo "player2 address" $player2_address
# use owner wallet to load/install an lotter sc to chain
scid=$(curl --silent --request POST --data-binary @test.bas http://127.0.0.1:$owner_rpc_port/install_sc| jq -r ".txid")
echo "SCID" $scid
sleep 1
#curl --silent http://127.0.0.1:$daemon_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getsc","params":{ "scid":"'"$scid"'" , "code":false, "keysstring":["deposit_count"]}}' -H 'Content-Type: application/json'
echo -n "Player 1 play txid "
curl --silent http://127.0.0.1:$player1_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"scinvoke","params":{"sc_dero_deposit":3000,"scid":"'"$scid"'","ringsize":2, "sc_rpc":[{"name":"entrypoint","datatype":"S","value":"Lottery"}] }}' -H 'Content-Type: application/json' | jq -r ".result.txid"
echo -n "Player 2 play txid "
curl --silent http://127.0.0.1:$player2_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"scinvoke","params":{"sc_dero_deposit":3000,"scid":"'"$scid"'","ringsize":2, "sc_rpc":[{"name":"entrypoint","datatype":"S","value":"Lottery"}] }}' -H 'Content-Type: application/json' | jq -r ".result.txid"
sleep 2
curl --silent http://127.0.0.1:$daemon_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getsc","params":{ "scid":"'"$scid"'" , "code":false, "keysstring":["deposit_count"]}}' -H 'Content-Type: application/json' | jq -r ".result.balance"
echo "SC balance" $(scbalance $scid)
echo "SC owner balance" $(balance $owner_rpc_port)
echo "SC player1 balance" $(balance $player1_rpc_port)
echo "SC player2 balance" $(balance $player2_rpc_port)
if [[ $(balance $player1_rpc_port) -gt 800000 || $(balance $player2_rpc_port) -gt 800000 ]]
then
exit 0
else
exit 1
fi

View File

@ -0,0 +1,93 @@
/* Lottery Smart Contract Example in DVM-BASIC.
This lottery smart contract will give lottery wins on every second try in following default contract.
Make depost transaction to this SCID to play lottery.
Check https://github.com/deroproject/derohe/blob/main/guide/examples/lottery_sc_guide.md
*/
Function Lottery() Uint64
10 dim deposit_count,winner as Uint64
20 LET deposit_count = LOAD("deposit_count")+1
25 IF DEROVALUE() == 0 THEN GOTO 110 // if deposit amount is 0, simply return
30 STORE("depositor_address" + (deposit_count-1), SIGNER()) // store address for later on payment
40 STORE("deposit_total", LOAD("deposit_total") + DEROVALUE() )
50 STORE("deposit_count",deposit_count)
60 IF LOAD("lotteryeveryXdeposit") > deposit_count THEN GOTO 110 // we will wait till X players join in
// we are here means all players have joined in, roll the DICE,
70 LET winner = RANDOM() % deposit_count // we have a winner
80 SEND_DERO_TO_ADDRESS(LOAD("depositor_address" + winner) , LOAD("lotterygiveback")*LOAD("deposit_total")/10000)
// Re-Initialize for another round
90 STORE("deposit_count", 0) // initial players
100 STORE("deposit_total", 0) // total deposit of all players
110 RETURN 0
End Function
// This function is used to initialize parameters during install time
Function Initialize() Uint64
5 version("1.2.3")
10 STORE("owner", SIGNER()) // store in DB ["owner"] = address
20 STORE("lotteryeveryXdeposit", 2) // lottery will reward every X deposits
// How much will lottery giveback in 1/10000 parts, granularity .01 %
30 STORE("lotterygiveback", 9900) // lottery will give reward 99% of deposits, 1 % is accumulated for owner to withdraw
33 STORE("deposit_count", 0) // initial players
34 STORE("deposit_total", 0) // total deposit of all players
// 35 printf "Initialize executed"
40 RETURN 0
End Function
// Function to tune lottery parameters
Function TuneLotteryParameters(input Uint64, lotteryeveryXdeposit Uint64, lotterygiveback Uint64) Uint64
10 dim key,stored_owner as String
20 dim value_uint64 as Uint64
30 IF LOAD("owner") == SIGNER() THEN GOTO 100 // check whether owner is real owner
40 RETURN 1
100 STORE("lotteryeveryXdeposit", lotteryeveryXdeposit) // lottery will reward every X deposits
130 STORE("lotterygiveback", value_uint64) // how much will lottery giveback in 1/10000 parts, granularity .01 %
140 RETURN 0 // return success
End Function
// This function is used to change owner
// owner is an string form of address
Function TransferOwnership(newowner String) Uint64
10 IF LOAD("owner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 STORE("tmpowner",ADDRESS_RAW(newowner))
40 RETURN 0
End Function
// Until the new owner claims ownership, existing owner remains owner
Function ClaimOwnership() Uint64
10 IF LOAD("tmpowner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 STORE("owner",SIGNER()) // ownership claim successful
40 RETURN 0
End Function
// If signer is owner, withdraw any requested funds
// If everthing is okay, they will be showing in signers wallet
Function Withdraw( amount Uint64) Uint64
10 IF LOAD("owner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 SEND_DERO_TO_ADDRESS(SIGNER(),amount)
40 RETURN 0
End Function
// If signer is owner, provide him rights to update code anytime
// make sure update is always available to SC
Function UpdateCode( code String) Uint64
10 IF LOAD("owner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 UPDATE_SC_CODE(code)
40 RETURN 0
End Function

View File

@ -0,0 +1,25 @@
/* Private Token Smart Contract Example in DVM-BASIC.
DERO Smart Contracts Tokens privacy can be understood just like banks handle cash. Once cash is out from the bank, bank is not aware about it (who owns what value), until it is deposited back.
Smart contract only maintains supply and other necessary items to keep it working.
DERO Tokens can be tranfered to other wallets just like native DERO with Homomorphic Encryption and without involvement of issuing Smart Contracts.
Token issuing Smart Contract cannot hold/freeze/control their tokens once they are issued and sent to any wallet.
This token is Private. Use Function InitializePrivate() Uint64 to make any Smart Contract private.
*/
// Issue Asset after depositing DERO (Convert DERO to TOKENX)
Function IssueAsset() Uint64
10 SEND_ASSET_TO_ADDRESS(SIGNER(), DEROVALUE(),SCID()) // send asset without knowing original balance, this is done homomorphically
20 RETURN 0
End Function
// This function is used to initialize parameters during install time
// InitializePrivate initializes a private SC
Function InitializePrivate() Uint64
40 RETURN 0
End Function

View File

@ -0,0 +1,67 @@
// Asset Interchange/Exchnage Smart Contract Example in DVM-BASIC.
// This SC allows you to deposit an arbitrary token, into it
// and later allows you to swap one token with another
// if the SC has enough balance to cover outgoing transfer it will be done
// deposits an arbitrary token
// owner should deposits all arbitrary types
Function Deposit() Uint64
20 RETURN 0
End Function
// incoming represents incoming asset type basicallly an SCID
// outgoing represents outgoing asset type basicallly an SCID
Function Interchange(incoming String, outgoing String) Uint64
10 SEND_ASSET_TO_ADDRESS(SIGNER(),ASSETVALUE(incoming)/2, outgoing) // 1 to 1 interchange of assets
20 RETURN 0
End Function
Function Initialize() Uint64
10 STORE("owner", SIGNER()) // store in DB ["owner"] = address
40 RETURN 0
End Function
// everything below this is supplementary and not required
// This function is used to change owner
// owner is an string form of address
Function TransferOwnership(newowner String) Uint64
10 IF LOAD("owner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 STORE("tmpowner",ADDRESS_RAW(newowner))
40 RETURN 0
End Function
// Until the new owner claims ownership, existing owner remains owner
Function ClaimOwnership() Uint64
10 IF LOAD("tmpowner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 STORE("owner",SIGNER()) // ownership claim successful
40 RETURN 0
End Function
// if signer is owner, withdraw any requested funds
// if everthing is okay, they will be showing in signers wallet
Function Withdraw(amount Uint64, asset String) Uint64
10 IF LOAD("owner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 SEND_ASSET_TO_ADDRESS(SIGNER(),amount,asset)
40 RETURN 0
End Function
// if signer is owner, provide him rights to update code anytime
// make sure update is always available to SC
Function UpdateCode( code String) Uint64
10 IF LOAD("owner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 UPDATE_SC_CODE(code)
40 RETURN 0
End Function

View File

@ -0,0 +1,110 @@
#!/usr/bin/env bash
#set -x # to enable debug and verbose printing of each and every command
# the SC will trigger panic and but cannot return the funds to sender since ringsize > 2, instead deposits to SC
CURDIR=`/bin/pwd`
BASEDIR=$(dirname $0)
ABSPATH=$(readlink -f $0)
ABSDIR=$(dirname $ABSPATH)
command -v curl >/dev/null 2>&1 || { echo "I require curl but it's not installed. Aborting." >&2; exit 1; }
command -v jq >/dev/null 2>&1 || { echo "I require jq but it's not installed. Aborting." >&2; exit 1; }
baseasset="0000000000000000000000000000000000000000000000000000000000000000"
daemon_rpc_port="20000" # daemon rpc is listening on this port
# we have number of wallets listening at ports from 30000
# we will be using 3 wallets, named owner, player1,player2
owner_rpc_port="30000"
player1_rpc_port="30001"
player2_rpc_port="30002"
owner_address=$(curl --silent http://127.0.0.1:$owner_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getaddress"}' -H 'Content-Type: application/json'| jq -r ".result.address")
player1_address=$(curl --silent http://127.0.0.1:$player1_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getaddress"}' -H 'Content-Type: application/json'| jq -r ".result.address")
player2_address=$(curl --silent http://127.0.0.1:$player2_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getaddress"}' -H 'Content-Type: application/json'| jq -r ".result.address")
function balance(){
curl --silent http://127.0.0.1:$1/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getbalance"}' -H 'Content-Type: application/json'| jq -r ".result.balance"
}
function scassetbalance(){
curl --silent http://127.0.0.1:$daemon_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getsc","params":{ "scid":"'"$1"'" , "code":false, "variables":true}}' -H 'Content-Type: application/json'| jq -r '.result.balances."'$2'"'
}
function tokenbalance(){
curl --silent http://127.0.0.1:$1/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getbalance" , "params":{ "scid":"'"$2"'" }}' -H 'Content-Type: application/json'| jq -r ".result.balance"
}
owner_rpc_port="30000"
user1_rpc_port="30001"
user2_rpc_port="30002"
owner_address=$(curl --silent http://127.0.0.1:$owner_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getaddress"}' -H 'Content-Type: application/json'| jq -r ".result.address")
user1_address=$(curl --silent http://127.0.0.1:$user1_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getaddress"}' -H 'Content-Type: application/json'| jq -r ".result.address")
user2_address=$(curl --silent http://127.0.0.1:$user2_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getaddress"}' -H 'Content-Type: application/json'| jq -r ".result.address")
# use owner wallet to load/install an lotter sc to chain
exchangescid=$(curl --silent --request POST --data-binary @asset_exchange.bas http://127.0.0.1:$owner_rpc_port/install_sc| jq -r ".txid")
echo "exchange SCID" $exchangescid
sleep 2
asset1scid=$(curl --silent --request POST --data-binary @asset.bas http://127.0.0.1:$owner_rpc_port/install_sc| jq -r ".txid")
echo "asset1 SCID" $asset1scid
sleep 2
asset2scid=$(curl --silent --request POST --data-binary @asset.bas http://127.0.0.1:$owner_rpc_port/install_sc| jq -r ".txid")
echo "asset2 SCID" $asset2scid
sleep 2
echo -n "owner exchanging dero 1000 for asset1 "
curl --silent http://127.0.0.1:$owner_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"scinvoke","params":{"sc_dero_deposit":1000,"scid":"'"$asset1scid"'","ringsize":2, "sc_rpc":[{"name":"entrypoint","datatype":"S","value":"IssueAsset"}] }}' -H 'Content-Type: application/json' | jq -r ".result.txid"
sleep 2
echo -n "owner exchanging dero 1000 for asset2 "
curl --silent http://127.0.0.1:$owner_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"scinvoke","params":{"sc_dero_deposit":1000,"scid":"'"$asset2scid"'","ringsize":2, "sc_rpc":[{"name":"entrypoint","datatype":"S","value":"IssueAsset"}] }}' -H 'Content-Type: application/json' | jq -r ".result.txid"
sleep 2
echo -n "owner depositing triple assets ( dero, asset1, asset2 ) to exchangescid "
curl --silent http://127.0.0.1:$owner_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"transfer","params":{"scid":"'"$exchangescid"'","ringsize":2, "sc_rpc":[{"name":"entrypoint","datatype":"S","value":"Deposit"}], "transfers": [{"burn":1234,"destination":"'"$user2_address"'"}, {"scid":"'"$asset1scid"'", "burn":123}, {"scid":"'"$asset2scid"'", "burn":555}] }}' -H 'Content-Type: application/json' | jq -r ".result.txid"
sleep 2
echo "SC DERO balance" $(scassetbalance $exchangescid $baseasset )
echo "SC Asset1 balance" $(scassetbalance $exchangescid $asset1scid )
echo "SC Asset2 balance" $(scassetbalance $exchangescid $asset2scid )
if [[ $(scassetbalance $exchangescid $baseasset) -ne 1234 || $(scassetbalance $exchangescid $asset1scid) -ne 123 || $(scassetbalance $exchangescid $asset2scid) -ne 555 ]] ; then
echo "condition failed"
exit 1
fi
echo -n "user1 exchanging dero 2000 for asset1 "
curl --silent http://127.0.0.1:$user1_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"scinvoke","params":{"sc_dero_deposit":2000,"scid":"'"$asset1scid"'","ringsize":2, "sc_rpc":[{"name":"entrypoint","datatype":"S","value":"IssueAsset"}] }}' -H 'Content-Type: application/json' | jq -r ".result.txid"
sleep 2
echo "wallet1 received asset1 tokens in return" $(tokenbalance $user1_rpc_port $asset1scid)
echo -n "user1 depositing 122 asset1 to obtain 61 asset2"
curl --silent http://127.0.0.1:$user1_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"transfer","params":{"scid":"'"$exchangescid"'","ringsize":2, "sc_rpc":[{"name":"entrypoint","datatype":"S","value":"Interchange"},{"name":"incoming","datatype":"H","value":"'"$asset1scid"'"},{"name":"outgoing","datatype":"H","value":"'"$asset2scid"'"}], "transfers": [{"scid":"'"$asset1scid"'", "burn":122}] }}' -H 'Content-Type: application/json' | jq -r ".result.txid"
#curl --silent http://127.0.0.1:$user1_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"scinvoke","params":{"sc_dero_deposit":10000,"scid":"'"$exchangescid"'","ringsize":2, "sc_rpc":[] }}' -H 'Content-Type: application/json' | jq -r ".result.txid"
sleep 2
echo "wallet1 asset1 tokens " $(tokenbalance $user1_rpc_port $asset1scid)
echo "wallet1 asset2 tokens after exchange " $(tokenbalance $user1_rpc_port $asset2scid)
if [[ $(tokenbalance $user1_rpc_port $asset1scid) -ne 1878 || $(tokenbalance $user1_rpc_port $asset2scid) -ne 61 ]] ; then
echo "condition failed"
exit 1
fi
exit 0

View File

@ -0,0 +1,63 @@
#!/usr/bin/env bash
#set -x # to enable debug and verbose printing of each and every command
# the SC will trigger panic and but cannot return the funds to sender since ringsize > 2, instead deposits to SC
CURDIR=`/bin/pwd`
BASEDIR=$(dirname $0)
ABSPATH=$(readlink -f $0)
ABSDIR=$(dirname $ABSPATH)
command -v curl >/dev/null 2>&1 || { echo "I require curl but it's not installed. Aborting." >&2; exit 1; }
command -v jq >/dev/null 2>&1 || { echo "I require jq but it's not installed. Aborting." >&2; exit 1; }
baseasset="0000000000000000000000000000000000000000000000000000000000000000"
daemon_rpc_port="20000" # daemon rpc is listening on this port
# we have number of wallets listening at ports from 30000
# we will be using 3 wallets, named owner, player1,player2
owner_rpc_port="30000"
player1_rpc_port="30001"
player2_rpc_port="30002"
owner_address=$(curl --silent http://127.0.0.1:$owner_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getaddress"}' -H 'Content-Type: application/json'| jq -r ".result.address")
player1_address=$(curl --silent http://127.0.0.1:$player1_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getaddress"}' -H 'Content-Type: application/json'| jq -r ".result.address")
player2_address=$(curl --silent http://127.0.0.1:$player2_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getaddress"}' -H 'Content-Type: application/json'| jq -r ".result.address")
function balance(){
curl --silent http://127.0.0.1:$1/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getbalance"}' -H 'Content-Type: application/json'| jq -r ".result.balance"
}
function scassetbalance(){
curl --silent http://127.0.0.1:$daemon_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getsc","params":{ "scid":"'"$1"'" , "code":false, "variables":true}}' -H 'Content-Type: application/json'| jq -r '.result.balances."'$2'"'
}
echo "SC owner address" $owner_address
echo "player1 address" $player1_address
echo "player2 address" $player2_address
# use owner wallet to load/install an lotter sc to chain
scid=$(curl --silent --request POST --data-binary @test.bas http://127.0.0.1:$owner_rpc_port/install_sc| jq -r ".txid")
echo "SCID" $scid
sleep 2
echo -n "Player 1 play txid "
curl --silent http://127.0.0.1:$player1_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"scinvoke","params":{"sc_dero_deposit":500000,"scid":"'"$scid"'","ringsize":4, "sc_rpc":[{"name":"entrypoint","datatype":"S","value":"Lottery"}] }}' -H 'Content-Type: application/json' | jq -r ".result.txid"
sleep 4
echo "SC DERO balance" $(scassetbalance $scid $baseasset )
echo "SC owner balance" $(balance $owner_rpc_port)
echo "SC player1 balance" $(balance $player1_rpc_port)
player1_balance=$(balance $player1_rpc_port)
if [[ $player1_balance -gt 700000 ]] ; then
exit 1
else
exit 0
fi

View File

@ -0,0 +1,94 @@
/* Lottery Smart Contract Example in DVM-BASIC.
This lottery smart contract will give lottery wins on every second try in following default contract.
Make depost transaction to this SCID to play lottery.
Check https://github.com/deroproject/derohe/blob/main/guide/examples/lottery_sc_guide.md
*/
Function Lottery() Uint64
5 PANIC()
10 dim deposit_count,winner as Uint64
20 LET deposit_count = LOAD("deposit_count")+1
25 IF DEROVALUE() == 0 THEN GOTO 110 // if deposit amount is 0, simply return
30 STORE("depositor_address" + (deposit_count-1), SIGNER()) // store address for later on payment
40 STORE("deposit_total", LOAD("deposit_total") + DEROVALUE() )
50 STORE("deposit_count",deposit_count)
60 IF LOAD("lotteryeveryXdeposit") > deposit_count THEN GOTO 110 // we will wait till X players join in
// we are here means all players have joined in, roll the DICE,
70 LET winner = RANDOM() % deposit_count // we have a winner
80 SEND_DERO_TO_ADDRESS(LOAD("depositor_address" + winner) , LOAD("lotterygiveback")*LOAD("deposit_total")/10000)
// Re-Initialize for another round
90 STORE("deposit_count", 0) // initial players
100 STORE("deposit_total", 0) // total deposit of all players
110 RETURN 0
End Function
// This function is used to initialize parameters during install time
Function Initialize() Uint64
5 version("1.2.3")
10 STORE("owner", SIGNER()) // store in DB ["owner"] = address
20 STORE("lotteryeveryXdeposit", 2) // lottery will reward every X deposits
// How much will lottery giveback in 1/10000 parts, granularity .01 %
30 STORE("lotterygiveback", 9900) // lottery will give reward 99% of deposits, 1 % is accumulated for owner to withdraw
33 STORE("deposit_count", 0) // initial players
34 STORE("deposit_total", 0) // total deposit of all players
// 35 printf "Initialize executed"
40 RETURN 0
End Function
// Function to tune lottery parameters
Function TuneLotteryParameters(input Uint64, lotteryeveryXdeposit Uint64, lotterygiveback Uint64) Uint64
10 dim key,stored_owner as String
20 dim value_uint64 as Uint64
30 IF LOAD("owner") == SIGNER() THEN GOTO 100 // check whether owner is real owner
40 RETURN 1
100 STORE("lotteryeveryXdeposit", lotteryeveryXdeposit) // lottery will reward every X deposits
130 STORE("lotterygiveback", value_uint64) // how much will lottery giveback in 1/10000 parts, granularity .01 %
140 RETURN 0 // return success
End Function
// This function is used to change owner
// owner is an string form of address
Function TransferOwnership(newowner String) Uint64
10 IF LOAD("owner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 STORE("tmpowner",ADDRESS_RAW(newowner))
40 RETURN 0
End Function
// Until the new owner claims ownership, existing owner remains owner
Function ClaimOwnership() Uint64
10 IF LOAD("tmpowner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 STORE("owner",SIGNER()) // ownership claim successful
40 RETURN 0
End Function
// If signer is owner, withdraw any requested funds
// If everthing is okay, they will be showing in signers wallet
Function Withdraw( amount Uint64) Uint64
10 IF LOAD("owner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 SEND_DERO_TO_ADDRESS(SIGNER(),amount)
40 RETURN 0
End Function
// If signer is owner, provide him rights to update code anytime
// make sure update is always available to SC
Function UpdateCode( code String) Uint64
10 IF LOAD("owner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 UPDATE_SC_CODE(code)
40 RETURN 0
End Function

View File

@ -0,0 +1,67 @@
#!/usr/bin/env bash
#set -x # to enable debug and verbose printing of each and every command
# the SC will trigger panic and will return the funds
CURDIR=`/bin/pwd`
BASEDIR=$(dirname $0)
ABSPATH=$(readlink -f $0)
ABSDIR=$(dirname $ABSPATH)
command -v curl >/dev/null 2>&1 || { echo "I require curl but it's not installed. Aborting." >&2; exit 1; }
command -v jq >/dev/null 2>&1 || { echo "I require jq but it's not installed. Aborting." >&2; exit 1; }
daemon_rpc_port="20000" # daemon rpc is listening on this port
# we have number of wallets listening at ports from 30000
# we will be using 3 wallets, named owner, player1,player2
owner_rpc_port="30000"
player1_rpc_port="30001"
player2_rpc_port="30002"
owner_address=$(curl --silent http://127.0.0.1:$owner_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getaddress"}' -H 'Content-Type: application/json'| jq -r ".result.address")
player1_address=$(curl --silent http://127.0.0.1:$player1_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getaddress"}' -H 'Content-Type: application/json'| jq -r ".result.address")
player2_address=$(curl --silent http://127.0.0.1:$player2_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getaddress"}' -H 'Content-Type: application/json'| jq -r ".result.address")
function balance(){
curl --silent http://127.0.0.1:$1/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getbalance"}' -H 'Content-Type: application/json'| jq -r ".result.balance"
}
function scbalance(){
curl --silent http://127.0.0.1:$daemon_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getsc","params":{ "scid":"'"$1"'" , "code":false, "keysstring":["deposit_count"]}}' -H 'Content-Type: application/json' | jq -r ".result.balance"
}
echo "SC owner address" $owner_address
echo "player1 address" $player1_address
echo "player2 address" $player2_address
# use owner wallet to load/install an lotter sc to chain
scid=$(curl --silent --request POST --data-binary @test.bas http://127.0.0.1:$owner_rpc_port/install_sc| jq -r ".txid")
echo "SCID" $scid
sleep 1
#curl --silent http://127.0.0.1:$daemon_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getsc","params":{ "scid":"'"$scid"'" , "code":false, "keysstring":["deposit_count"]}}' -H 'Content-Type: application/json'
echo -n "Player 1 play txid "
curl --silent http://127.0.0.1:$player1_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"scinvoke","params":{"sc_dero_deposit":300000,"scid":"'"$scid"'","ringsize":2, "sc_rpc":[{"name":"entrypoint","datatype":"S","value":"Lottery"}] }}' -H 'Content-Type: application/json' | jq -r ".result.txid"
curl --silent http://127.0.0.1:$daemon_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getsc","params":{ "scid":"'"$scid"'" , "code":false, "keysstring":["deposit_count"]}}' -H 'Content-Type: application/json' | jq -r ".result.balance"
sleep 2
echo "SC balance" $(scbalance $scid)
echo "SC owner balance" $(balance $owner_rpc_port)
echo "SC player1 balance" $(balance $player1_rpc_port)
player1_balance=$(balance $player1_rpc_port)
echo "player1 balance $player1_balance"
if [[ $player1_balance -gt 700000 ]] ; then
exit 0
else
exit 1
fi

View File

@ -0,0 +1,94 @@
/* Lottery Smart Contract Example in DVM-BASIC.
This lottery smart contract will give lottery wins on every second try in following default contract.
Make depost transaction to this SCID to play lottery.
Check https://github.com/deroproject/derohe/blob/main/guide/examples/lottery_sc_guide.md
*/
Function Lottery() Uint64
5 PANIC()
10 dim deposit_count,winner as Uint64
20 LET deposit_count = LOAD("deposit_count")+1
25 IF DEROVALUE() == 0 THEN GOTO 110 // if deposit amount is 0, simply return
30 STORE("depositor_address" + (deposit_count-1), SIGNER()) // store address for later on payment
40 STORE("deposit_total", LOAD("deposit_total") + DEROVALUE() )
50 STORE("deposit_count",deposit_count)
60 IF LOAD("lotteryeveryXdeposit") > deposit_count THEN GOTO 110 // we will wait till X players join in
// we are here means all players have joined in, roll the DICE,
70 LET winner = RANDOM() % deposit_count // we have a winner
80 SEND_DERO_TO_ADDRESS(LOAD("depositor_address" + winner) , LOAD("lotterygiveback")*LOAD("deposit_total")/10000)
// Re-Initialize for another round
90 STORE("deposit_count", 0) // initial players
100 STORE("deposit_total", 0) // total deposit of all players
110 RETURN 0
End Function
// This function is used to initialize parameters during install time
Function Initialize() Uint64
5 version("1.2.3")
10 STORE("owner", SIGNER()) // store in DB ["owner"] = address
20 STORE("lotteryeveryXdeposit", 2) // lottery will reward every X deposits
// How much will lottery giveback in 1/10000 parts, granularity .01 %
30 STORE("lotterygiveback", 9900) // lottery will give reward 99% of deposits, 1 % is accumulated for owner to withdraw
33 STORE("deposit_count", 0) // initial players
34 STORE("deposit_total", 0) // total deposit of all players
// 35 printf "Initialize executed"
40 RETURN 0
End Function
// Function to tune lottery parameters
Function TuneLotteryParameters(input Uint64, lotteryeveryXdeposit Uint64, lotterygiveback Uint64) Uint64
10 dim key,stored_owner as String
20 dim value_uint64 as Uint64
30 IF LOAD("owner") == SIGNER() THEN GOTO 100 // check whether owner is real owner
40 RETURN 1
100 STORE("lotteryeveryXdeposit", lotteryeveryXdeposit) // lottery will reward every X deposits
130 STORE("lotterygiveback", value_uint64) // how much will lottery giveback in 1/10000 parts, granularity .01 %
140 RETURN 0 // return success
End Function
// This function is used to change owner
// owner is an string form of address
Function TransferOwnership(newowner String) Uint64
10 IF LOAD("owner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 STORE("tmpowner",ADDRESS_RAW(newowner))
40 RETURN 0
End Function
// Until the new owner claims ownership, existing owner remains owner
Function ClaimOwnership() Uint64
10 IF LOAD("tmpowner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 STORE("owner",SIGNER()) // ownership claim successful
40 RETURN 0
End Function
// If signer is owner, withdraw any requested funds
// If everthing is okay, they will be showing in signers wallet
Function Withdraw( amount Uint64) Uint64
10 IF LOAD("owner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 SEND_DERO_TO_ADDRESS(SIGNER(),amount)
40 RETURN 0
End Function
// If signer is owner, provide him rights to update code anytime
// make sure update is always available to SC
Function UpdateCode( code String) Uint64
10 IF LOAD("owner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 UPDATE_SC_CODE(code)
40 RETURN 0
End Function

View File

@ -0,0 +1,68 @@
#!/usr/bin/env bash
#set -x # to enable debug and verbose printing of each and every command
CURDIR=`/bin/pwd`
BASEDIR=$(dirname $0)
ABSPATH=$(readlink -f $0)
ABSDIR=$(dirname $ABSPATH)
command -v curl >/dev/null 2>&1 || { echo "I require curl but it's not installed. Aborting." >&2; exit 1; }
command -v jq >/dev/null 2>&1 || { echo "I require jq but it's not installed. Aborting." >&2; exit 1; }
daemon_rpc_port="20000" # daemon rpc is listening on this port
# we have number of wallets listening at ports from 30000
# we will be using 3 wallets, named owner, player1,player2
owner_rpc_port="30000"
player1_rpc_port="30001"
player2_rpc_port="30002"
owner_address=$(curl --silent http://127.0.0.1:$owner_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getaddress"}' -H 'Content-Type: application/json'| jq -r ".result.address")
player1_address=$(curl --silent http://127.0.0.1:$player1_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getaddress"}' -H 'Content-Type: application/json'| jq -r ".result.address")
player2_address=$(curl --silent http://127.0.0.1:$player2_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getaddress"}' -H 'Content-Type: application/json'| jq -r ".result.address")
function balance(){
curl --silent http://127.0.0.1:$1/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getbalance"}' -H 'Content-Type: application/json'| jq -r ".result.balance"
}
function scbalance(){
curl --silent http://127.0.0.1:$daemon_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getsc","params":{ "scid":"'"$1"'" , "code":false, "keysstring":["deposit_count"]}}' -H 'Content-Type: application/json' | jq -r ".result.balance"
}
echo "SC owner address" $owner_address
echo "player1 address" $player1_address
echo "player2 address" $player2_address
# use owner wallet to load/install an lotter sc to chain
scid=$(curl --silent --request POST --data-binary @test.bas http://127.0.0.1:$owner_rpc_port/install_sc| jq -r ".txid")
echo "SCID" $scid
sleep 1
#curl --silent http://127.0.0.1:$daemon_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getsc","params":{ "scid":"'"$scid"'" , "code":false, "keysstring":["deposit_count"]}}' -H 'Content-Type: application/json'
echo -n "Player 1 play txid "
curl --silent http://127.0.0.1:$player1_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"scinvoke","params":{"sc_dero_deposit":3000,"scid":"'"$scid"'","ringsize":2, "sc_rpc":[{"name":"entrypoint","datatype":"S","value":"Lottery"}] }}' -H 'Content-Type: application/json' | jq -r ".result.txid"
echo -n "Player 2 play txid "
curl --silent http://127.0.0.1:$player2_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"scinvoke","params":{"sc_dero_deposit":3000,"scid":"'"$scid"'","ringsize":2, "sc_rpc":[{"name":"entrypoint","datatype":"S","value":"Lottery"}] }}' -H 'Content-Type: application/json' | jq -r ".result.txid"
sleep 2
curl --silent http://127.0.0.1:$daemon_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getsc","params":{ "scid":"'"$scid"'" , "code":false, "keysstring":["deposit_count"]}}' -H 'Content-Type: application/json' | jq -r ".result.balance"
echo "SC balance" $(scbalance $scid)
echo "SC owner balance" $(balance $owner_rpc_port)
echo "SC player1 balance" $(balance $player1_rpc_port)
echo "SC player2 balance" $(balance $player2_rpc_port)
if [[ $(balance $player1_rpc_port) -gt 800000 || $(balance $player2_rpc_port) -gt 800000 ]]
then
exit 0
else
exit 1
fi

View File

@ -0,0 +1,101 @@
/* Lottery Smart Contract Example in DVM-BASIC.
This lottery smart contract will give lottery wins on every second try in following default contract.
Make depost transaction to this SCID to play lottery.
Check https://github.com/deroproject/derohe/blob/main/guide/examples/lottery_sc_guide.md
*/
Function Lottery() Uint64
10 dim deposit_count,winner as Uint64
20 LET deposit_count = LOAD("deposit_count")+1
25 IF DEROVALUE() == 0 THEN GOTO 110 // if deposit amount is 0, simply return
30 STORE("depositor_address" + (deposit_count-1), SIGNER()) // store address for later on payment
40 STORE("deposit_total", LOAD("deposit_total") + DEROVALUE() )
50 STORE("deposit_count",deposit_count)
60 IF LOAD("lotteryeveryXdeposit") > deposit_count THEN GOTO 110 // we will wait till X players join in
// we are here means all players have joined in, roll the DICE,
70 LET winner = RANDOM() % deposit_count // we have a winner
80 SEND_DERO_TO_ADDRESS(LOAD("depositor_address" + winner) , LOAD("lotterygiveback")*LOAD("deposit_total")/10000)
// Re-Initialize for another round
90 STORE("deposit_count", 0) // initial players
100 STORE("deposit_total", 0) // total deposit of all players
110 RETURN 0
End Function
function q()
10 MAPSTORE(2, 2) // this will store to ram
20 return
End function
// This function is used to initialize parameters during install time
Function Initialize() Uint64
5 MAPSTORE("owner", SIGNER())
10 STORE("owner", MAPGET("owner")) // store in DB ["owner"] = address
15 q()
20 STORE("lotteryeveryXdeposit", MAPGET(2)) //,, recover value stored in function q, lottery will reward every X deposits
// How much will lottery giveback in 1/10000 parts, granularity .01 %
30 STORE("lotterygiveback", 9900) // lottery will give reward 99% of deposits, 1 % is accumulated for owner to withdraw
33 STORE("deposit_count", 0) // initial players
34 STORE("deposit_total", 0) // total deposit of all players
// 35 printf "Initialize executed"
40 RETURN 0
End Function
// Function to tune lottery parameters
Function TuneLotteryParameters(input Uint64, lotteryeveryXdeposit Uint64, lotterygiveback Uint64) Uint64
10 dim key,stored_owner as String
20 dim value_uint64 as Uint64
30 IF LOAD("owner") == SIGNER() THEN GOTO 100 // check whether owner is real owner
40 RETURN 1
100 STORE("lotteryeveryXdeposit", lotteryeveryXdeposit) // lottery will reward every X deposits
130 STORE("lotterygiveback", value_uint64) // how much will lottery giveback in 1/10000 parts, granularity .01 %
140 RETURN 0 // return success
End Function
// This function is used to change owner
// owner is an string form of address
Function TransferOwnership(newowner String) Uint64
10 IF LOAD("owner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 STORE("tmpowner",ADDRESS_RAW(newowner))
40 RETURN 0
End Function
// Until the new owner claims ownership, existing owner remains owner
Function ClaimOwnership() Uint64
10 IF LOAD("tmpowner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 STORE("owner",SIGNER()) // ownership claim successful
40 RETURN 0
End Function
// If signer is owner, withdraw any requested funds
// If everthing is okay, they will be showing in signers wallet
Function Withdraw( amount Uint64) Uint64
10 IF LOAD("owner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 SEND_DERO_TO_ADDRESS(SIGNER(),amount)
40 RETURN 0
End Function
// If signer is owner, provide him rights to update code anytime
// make sure update is always available to SC
Function UpdateCode( code String) Uint64
10 IF LOAD("owner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 UPDATE_SC_CODE(code)
40 RETURN 0
End Function

View File

@ -0,0 +1,70 @@
#!/usr/bin/env bash
#set -x # to enable debug and verbose printing of each and every command
# this will test case if lottery can run within a block
CURDIR=`/bin/pwd`
BASEDIR=$(dirname $0)
ABSPATH=$(readlink -f $0)
ABSDIR=$(dirname $ABSPATH)
command -v curl >/dev/null 2>&1 || { echo "I require curl but it's not installed. Aborting." >&2; exit 1; }
command -v jq >/dev/null 2>&1 || { echo "I require jq but it's not installed. Aborting." >&2; exit 1; }
baseasset="0000000000000000000000000000000000000000000000000000000000000000"
daemon_rpc_port="20000" # daemon rpc is listening on this port
# we have number of wallets listening at ports from 30000
# we will be using 3 wallets, named owner, player1,player2
owner_rpc_port="30000"
player1_rpc_port="30001"
player2_rpc_port="30002"
owner_address=$(curl --silent http://127.0.0.1:$owner_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getaddress"}' -H 'Content-Type: application/json'| jq -r ".result.address")
player1_address=$(curl --silent http://127.0.0.1:$player1_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getaddress"}' -H 'Content-Type: application/json'| jq -r ".result.address")
player2_address=$(curl --silent http://127.0.0.1:$player2_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getaddress"}' -H 'Content-Type: application/json'| jq -r ".result.address")
function balance(){
curl --silent http://127.0.0.1:$1/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getbalance"}' -H 'Content-Type: application/json'| jq -r ".result.balance"
}
function scassetbalance(){
curl --silent http://127.0.0.1:$daemon_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getsc","params":{ "scid":"'"$1"'" , "code":false, "variables":true}}' -H 'Content-Type: application/json'| jq -r '.result.balances."'$2'"'
}
function mineblock(){
touch /dev/shm/mineblocknow
}
echo "SC owner address" $owner_address
echo "player1 address" $player1_address
echo "player2 address" $player2_address
# use owner wallet to load/install an lotter sc to chain
scid=$(curl --silent --request POST --data-binary @test.bas http://127.0.0.1:$owner_rpc_port/install_sc| jq -r ".txid")
echo "SCID" $scid
mineblock
sleep 2
echo -n "2 players playing txid "
curl --silent http://127.0.0.1:$player1_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"scinvoke","params":{"sc_dero_deposit":500000,"scid":"'"$scid"'","ringsize":2, "sc_rpc":[{"name":"entrypoint","datatype":"S","value":"Lottery"}] }}' -H 'Content-Type: application/json' | jq -r ".result.txid"
curl --silent http://127.0.0.1:$player2_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"scinvoke","params":{"sc_dero_deposit":400000,"scid":"'"$scid"'","ringsize":2, "sc_rpc":[{"name":"entrypoint","datatype":"S","value":"Lottery"}] }}' -H 'Content-Type: application/json' | jq -r ".result.txid"
mineblock
sleep 4
echo "SC DERO balance" $(scassetbalance $scid $baseasset )
echo "SC owner balance" $(balance $owner_rpc_port)
echo "SC player1 balance" $(balance $player1_rpc_port)
echo "SC player2 balance" $(balance $player2_rpc_port)
if [[ $(balance $player1_rpc_port) -lt 300000 && $(balance $player2_rpc_port) -gt 790000 ]] ; then
exit 0
else
echo "test failed"
exit 1
fi

View File

@ -0,0 +1,96 @@
/* Lottery Smart Contract Example in DVM-BASIC.
This lottery smart contract will give lottery wins on every second try in following default contract.
Make depost transaction to this SCID to play lottery.
Check https://github.com/deroproject/derohe/blob/main/guide/examples/lottery_sc_guide.md
*/
Function Lottery() Uint64
10 dim deposit_count,winner as Uint64
20 LET deposit_count = LOAD("deposit_count")+1
25 IF DEROVALUE() == 0 THEN GOTO 110 // if deposit amount is 0, simply return
27 IF DEROVALUE() == 400000 THEN GOTO 120 // if deposit amount is 400000, panic
30 STORE("depositor_address" + (deposit_count-1), SIGNER()) // store address for later on payment
40 STORE("deposit_total", LOAD("deposit_total") + DEROVALUE() )
50 STORE("deposit_count",deposit_count)
60 IF LOAD("lotteryeveryXdeposit") > deposit_count THEN GOTO 110 // we will wait till X players join in
// we are here means all players have joined in, roll the DICE,
70 LET winner = RANDOM() % deposit_count // we have a winner
80 SEND_DERO_TO_ADDRESS(LOAD("depositor_address" + winner) , LOAD("lotterygiveback")*LOAD("deposit_total")/10000)
// Re-Initialize for another round
90 STORE("deposit_count", 0) // initial players
100 STORE("deposit_total", 0) // total deposit of all players
110 RETURN 0
120 PANIC()
130 RETURN 0
End Function
// This function is used to initialize parameters during install time
Function Initialize() Uint64
5 version("1.2.3")
10 STORE("owner", SIGNER()) // store in DB ["owner"] = address
20 STORE("lotteryeveryXdeposit", 2) // lottery will reward every X deposits
// How much will lottery giveback in 1/10000 parts, granularity .01 %
30 STORE("lotterygiveback", 9900) // lottery will give reward 99% of deposits, 1 % is accumulated for owner to withdraw
33 STORE("deposit_count", 0) // initial players
34 STORE("deposit_total", 0) // total deposit of all players
// 35 printf "Initialize executed"
40 RETURN 0
End Function
// Function to tune lottery parameters
Function TuneLotteryParameters(input Uint64, lotteryeveryXdeposit Uint64, lotterygiveback Uint64) Uint64
10 dim key,stored_owner as String
20 dim value_uint64 as Uint64
30 IF LOAD("owner") == SIGNER() THEN GOTO 100 // check whether owner is real owner
40 RETURN 1
100 STORE("lotteryeveryXdeposit", lotteryeveryXdeposit) // lottery will reward every X deposits
130 STORE("lotterygiveback", value_uint64) // how much will lottery giveback in 1/10000 parts, granularity .01 %
140 RETURN 0 // return success
End Function
// This function is used to change owner
// owner is an string form of address
Function TransferOwnership(newowner String) Uint64
10 IF LOAD("owner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 STORE("tmpowner",ADDRESS_RAW(newowner))
40 RETURN 0
End Function
// Until the new owner claims ownership, existing owner remains owner
Function ClaimOwnership() Uint64
10 IF LOAD("tmpowner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 STORE("owner",SIGNER()) // ownership claim successful
40 RETURN 0
End Function
// If signer is owner, withdraw any requested funds
// If everthing is okay, they will be showing in signers wallet
Function Withdraw( amount Uint64) Uint64
10 IF LOAD("owner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 SEND_DERO_TO_ADDRESS(SIGNER(),amount)
40 RETURN 0
End Function
// If signer is owner, provide him rights to update code anytime
// make sure update is always available to SC
Function UpdateCode( code String) Uint64
10 IF LOAD("owner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 UPDATE_SC_CODE(code)
40 RETURN 0
End Function

View File

@ -0,0 +1,71 @@
#!/usr/bin/env bash
#set -x # to enable debug and verbose printing of each and every command
# this will test case if lottery can run within a block
CURDIR=`/bin/pwd`
BASEDIR=$(dirname $0)
ABSPATH=$(readlink -f $0)
ABSDIR=$(dirname $ABSPATH)
command -v curl >/dev/null 2>&1 || { echo "I require curl but it's not installed. Aborting." >&2; exit 1; }
command -v jq >/dev/null 2>&1 || { echo "I require jq but it's not installed. Aborting." >&2; exit 1; }
baseasset="0000000000000000000000000000000000000000000000000000000000000000"
daemon_rpc_port="20000" # daemon rpc is listening on this port
# we have number of wallets listening at ports from 30000
# we will be using 3 wallets, named owner, player1,player2
owner_rpc_port="30000"
player1_rpc_port="30001"
player2_rpc_port="30002"
owner_address=$(curl --silent http://127.0.0.1:$owner_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getaddress"}' -H 'Content-Type: application/json'| jq -r ".result.address")
player1_address=$(curl --silent http://127.0.0.1:$player1_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getaddress"}' -H 'Content-Type: application/json'| jq -r ".result.address")
player2_address=$(curl --silent http://127.0.0.1:$player2_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getaddress"}' -H 'Content-Type: application/json'| jq -r ".result.address")
function balance(){
curl --silent http://127.0.0.1:$1/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getbalance"}' -H 'Content-Type: application/json'| jq -r ".result.balance"
}
function scassetbalance(){
curl --silent http://127.0.0.1:$daemon_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"getsc","params":{ "scid":"'"$1"'" , "code":false, "variables":true}}' -H 'Content-Type: application/json'| jq -r '.result.balances."'$2'"'
}
function mineblock(){
touch /dev/shm/mineblocknow
}
echo "SC owner address" $owner_address
echo "player1 address" $player1_address
echo "player2 address" $player2_address
# use owner wallet to load/install an lotter sc to chain
scid=$(curl --silent --request POST --data-binary @test.bas http://127.0.0.1:$owner_rpc_port/install_sc| jq -r ".txid")
echo "SCID" $scid
mineblock
sleep 2
echo -n "2 players playing txid "
curl --silent http://127.0.0.1:$player1_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"scinvoke","params":{"sc_dero_deposit":500000,"scid":"'"$scid"'","ringsize":2, "sc_rpc":[{"name":"entrypoint","datatype":"S","value":"Lottery"}] }}' -H 'Content-Type: application/json' | jq -r ".result.txid"
curl --silent http://127.0.0.1:$player2_rpc_port/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"scinvoke","params":{"sc_dero_deposit":500000,"scid":"'"$scid"'","ringsize":2, "sc_rpc":[{"name":"entrypoint","datatype":"S","value":"Lottery"}] }}' -H 'Content-Type: application/json' | jq -r ".result.txid"
mineblock
sleep 4
echo "SC DERO balance" $(scassetbalance $scid $baseasset )
echo "SC owner balance" $(balance $owner_rpc_port)
echo "SC player1 balance" $(balance $player1_rpc_port)
echo "SC player2 balance" $(balance $player2_rpc_port)
if [[ $(balance $player1_rpc_port) -gt 1200000 || $(balance $player2_rpc_port) -gt 1200000 ]]
then
exit 0
else
echo "test failed"
exit 1
fi

View File

@ -0,0 +1,93 @@
/* Lottery Smart Contract Example in DVM-BASIC.
This lottery smart contract will give lottery wins on every second try in following default contract.
Make depost transaction to this SCID to play lottery.
Check https://github.com/deroproject/derohe/blob/main/guide/examples/lottery_sc_guide.md
*/
Function Lottery() Uint64
10 dim deposit_count,winner as Uint64
20 LET deposit_count = LOAD("deposit_count")+1
25 IF DEROVALUE() == 0 THEN GOTO 110 // if deposit amount is 0, simply return
30 STORE("depositor_address" + (deposit_count-1), SIGNER()) // store address for later on payment
40 STORE("deposit_total", LOAD("deposit_total") + DEROVALUE() )
50 STORE("deposit_count",deposit_count)
60 IF LOAD("lotteryeveryXdeposit") > deposit_count THEN GOTO 110 // we will wait till X players join in
// we are here means all players have joined in, roll the DICE,
70 LET winner = RANDOM() % deposit_count // we have a winner
80 SEND_DERO_TO_ADDRESS(LOAD("depositor_address" + winner) , LOAD("lotterygiveback")*LOAD("deposit_total")/10000)
// Re-Initialize for another round
90 STORE("deposit_count", 0) // initial players
100 STORE("deposit_total", 0) // total deposit of all players
110 RETURN 0
End Function
// This function is used to initialize parameters during install time
Function Initialize() Uint64
5 version("1.2.3")
10 STORE("owner", SIGNER()) // store in DB ["owner"] = address
20 STORE("lotteryeveryXdeposit", 2) // lottery will reward every X deposits
// How much will lottery giveback in 1/10000 parts, granularity .01 %
30 STORE("lotterygiveback", 9900) // lottery will give reward 99% of deposits, 1 % is accumulated for owner to withdraw
33 STORE("deposit_count", 0) // initial players
34 STORE("deposit_total", 0) // total deposit of all players
// 35 printf "Initialize executed"
40 RETURN 0
End Function
// Function to tune lottery parameters
Function TuneLotteryParameters(input Uint64, lotteryeveryXdeposit Uint64, lotterygiveback Uint64) Uint64
10 dim key,stored_owner as String
20 dim value_uint64 as Uint64
30 IF LOAD("owner") == SIGNER() THEN GOTO 100 // check whether owner is real owner
40 RETURN 1
100 STORE("lotteryeveryXdeposit", lotteryeveryXdeposit) // lottery will reward every X deposits
130 STORE("lotterygiveback", value_uint64) // how much will lottery giveback in 1/10000 parts, granularity .01 %
140 RETURN 0 // return success
End Function
// This function is used to change owner
// owner is an string form of address
Function TransferOwnership(newowner String) Uint64
10 IF LOAD("owner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 STORE("tmpowner",ADDRESS_RAW(newowner))
40 RETURN 0
End Function
// Until the new owner claims ownership, existing owner remains owner
Function ClaimOwnership() Uint64
10 IF LOAD("tmpowner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 STORE("owner",SIGNER()) // ownership claim successful
40 RETURN 0
End Function
// If signer is owner, withdraw any requested funds
// If everthing is okay, they will be showing in signers wallet
Function Withdraw( amount Uint64) Uint64
10 IF LOAD("owner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 SEND_DERO_TO_ADDRESS(SIGNER(),amount)
40 RETURN 0
End Function
// If signer is owner, provide him rights to update code anytime
// make sure update is always available to SC
Function UpdateCode( code String) Uint64
10 IF LOAD("owner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 UPDATE_SC_CODE(code)
40 RETURN 0
End Function

View File

@ -78,7 +78,7 @@ type Transaction_Prefix struct {
TransactionType TransactionType `json:"txtype"` TransactionType TransactionType `json:"txtype"`
Value uint64 `json:"value"` // represents value for premine, SC, BURN transactions Value uint64 `json:"value"` // represents value for premine, Gas for SC, BURN transactions
MinerAddress [33]byte `json:"miner_address"` // miner address // 33 bytes also used for registration MinerAddress [33]byte `json:"miner_address"` // miner address // 33 bytes also used for registration
C [32]byte `json:"c"` // used for registration C [32]byte `json:"c"` // used for registration
S [32]byte `json:"s"` // used for registration S [32]byte `json:"s"` // used for registration
@ -226,6 +226,27 @@ func (tx *Transaction) Fees() (fees uint64) {
return fees return fees
} }
// tx storage gas
func (tx *Transaction) GasStorage() (fees uint64) {
return tx.Fees()
/*
if !tx.IsSC(){
return 0
}
var zero_scid [32]byte
count := 0
for i := range tx.Payloads {
if zero_scid == tx.Payloads[i].SCID {
count++
}
}
if count == 1 {
return tx.Value
}
return 0
*/
}
func (tx *Transaction) IsRegistrationValid() (result bool) { func (tx *Transaction) IsRegistrationValid() (result bool) {
var u bn256.G1 var u bn256.G1
@ -293,7 +314,7 @@ func (tx *Transaction) Deserialize(buf []byte) (err error) {
panic("unknown transaction type") panic("unknown transaction type")
} }
if tx.TransactionType == PREMINE || tx.TransactionType == BURN_TX || tx.TransactionType == SC_TX { if tx.TransactionType == PREMINE || tx.TransactionType == SC_TX { // represents Gas in SC tx
tx.Value, done = binary.Uvarint(buf) tx.Value, done = binary.Uvarint(buf)
if done <= 0 { if done <= 0 {
return fmt.Errorf("Invalid Premine value in Transaction\n") return fmt.Errorf("Invalid Premine value in Transaction\n")
@ -420,7 +441,7 @@ func (tx *Transaction) SerializeHeader() []byte {
n = binary.PutUvarint(buf, uint64(tx.TransactionType)) n = binary.PutUvarint(buf, uint64(tx.TransactionType))
serialised_header.Write(buf[:n]) serialised_header.Write(buf[:n])
if tx.TransactionType == PREMINE || tx.TransactionType == BURN_TX || tx.TransactionType == SC_TX { if tx.TransactionType == PREMINE || tx.TransactionType == SC_TX {
n := binary.PutUvarint(buf, tx.Value) n := binary.PutUvarint(buf, tx.Value)
serialised_header.Write(buf[:n]) serialised_header.Write(buf[:n])
} }

View File

@ -64,15 +64,18 @@ func Transfer(ctx context.Context, p rpc.Transfer_Params) (result rpc.Transfer_R
p.SC_RPC = append(p.SC_RPC, rpc.Argument{Name: rpc.SCID, DataType: rpc.DataHash, Value: crypto.HashHexToHash(p.SC_ID)}) p.SC_RPC = append(p.SC_RPC, rpc.Argument{Name: rpc.SCID, DataType: rpc.DataHash, Value: crypto.HashHexToHash(p.SC_ID)})
} }
tx, err := w.wallet.TransferPayload0(p.Transfers, p.Ringsize, false, p.SC_RPC, false) var tx *transaction.Transaction
for tries := 0; tries < 2; tries++ {
tx, err = w.wallet.TransferPayload0(p.Transfers, p.Ringsize, false, p.SC_RPC, p.Fees, false)
if err != nil { if err != nil {
w.logger.V(1).Error(err, "Error building tx") w.logger.V(1).Error(err, "Error building tx")
return result, err return result, err
} }
err = w.wallet.SendTransaction(tx) err = w.wallet.SendTransaction(tx)
if err != nil { if err == nil {
return result, err break
}
} }
// we must return a txid if everything went alright // we must return a txid if everything went alright

View File

@ -19,7 +19,7 @@ type GenerateProofFunc func(scid crypto.Hash, scid_index int, s *crypto.Statemen
var GenerateProoffuncptr GenerateProofFunc = crypto.GenerateProof var GenerateProoffuncptr GenerateProofFunc = crypto.GenerateProof
// generate proof etc // generate proof etc
func (w *Wallet_Memory) BuildTransaction(transfers []rpc.Transfer, emap [][][]byte, rings [][]*bn256.G1, block_hash crypto.Hash, height uint64, scdata rpc.Arguments, roothash []byte, max_bits int) *transaction.Transaction { func (w *Wallet_Memory) BuildTransaction(transfers []rpc.Transfer, emap [][][]byte, rings [][]*bn256.G1, block_hash crypto.Hash, height uint64, scdata rpc.Arguments, roothash []byte, max_bits int, fees uint64) *transaction.Transaction {
sender := w.account.Keys.Public.G1() sender := w.account.Keys.Public.G1()
sender_secret := w.account.Keys.Secret.BigInt() sender_secret := w.account.Keys.Secret.BigInt()
@ -34,12 +34,7 @@ rebuild_tx:
tx.Height = height tx.Height = height
tx.BLID = block_hash tx.BLID = block_hash
tx.TransactionType = transaction.NORMAL tx.TransactionType = transaction.NORMAL
/*
if burn_value >= 1 {
tx.TransactionType = transaction.BURN_TX
tx.Value = burn_value
}
*/
if len(scdata) >= 1 { if len(scdata) >= 1 {
tx.TransactionType = transaction.SC_TX tx.TransactionType = transaction.SC_TX
tx.SCDATA = scdata tx.SCDATA = scdata
@ -138,15 +133,14 @@ rebuild_tx:
asset.SCID = transfers[t].SCID asset.SCID = transfers[t].SCID
asset.BurnValue = transfers[t].Burn asset.BurnValue = transfers[t].Burn
fees := uint64(0)
value := transfers[t].Amount value := transfers[t].Amount
burn_value := transfers[t].Burn burn_value := transfers[t].Burn
if asset.SCID.IsZero() && !fees_done { if fees == 0 && asset.SCID.IsZero() && !fees_done {
fees = uint64(len(transfers)+2) * config.FEE_PER_KB fees = fees + uint64(len(transfers)+2)*config.FEE_PER_KB
if data, err := scdata.MarshalBinary(); err != nil { if data, err := scdata.MarshalBinary(); err != nil {
panic(err) panic(err)
} else { } else {
fees = fees + uint64((len(data)/1024)+3)*config.FEE_PER_KB fees = fees + (uint64(len(data))*15)/10
} }
fees_done = true fees_done = true
} }
@ -155,8 +149,14 @@ rebuild_tx:
var x bn256.G1 var x bn256.G1
switch { switch {
case i == witness_index[0]: case i == witness_index[0]:
if asset.SCID.IsZero() {
x.ScalarMult(crypto.G, new(big.Int).SetInt64(0-int64(value)-int64(fees)-int64(burn_value))) // decrease senders balance x.ScalarMult(crypto.G, new(big.Int).SetInt64(0-int64(value)-int64(fees)-int64(burn_value))) // decrease senders balance
} else {
x.ScalarMult(crypto.G, new(big.Int).SetInt64(0-int64(value)-int64(burn_value))) // decrease senders balance
}
//fmt.Printf("sender %d %s \n", i, sender.String()) //fmt.Printf("sender %d %s \n", i, sender.String())
case i == witness_index[1]: case i == witness_index[1]:
x.ScalarMult(crypto.G, new(big.Int).SetInt64(int64(value))) // increase receiver's balance x.ScalarMult(crypto.G, new(big.Int).SetInt64(int64(value))) // increase receiver's balance
//fmt.Printf("receiver %d %s \n",i, receiver.String()) //fmt.Printf("receiver %d %s \n",i, receiver.String())
@ -217,12 +217,16 @@ rebuild_tx:
//fmt.Printf("t %d scid %s balance %d\n", t, transfers[t].SCID, balance) //fmt.Printf("t %d scid %s balance %d\n", t, transfers[t].SCID, balance)
// time for bullets-sigma // time for bullets-sigma
statement := GenerateStatement(CLn, CRn, publickeylist, C, &D, fees) // generate statement fees_currentasset := uint64(0)
if asset.SCID.IsZero() {
fees_currentasset = fees
}
statement := GenerateStatement(CLn, CRn, publickeylist, C, &D, fees_currentasset) // generate statement
copy(statement.Roothash[:], roothash[:]) copy(statement.Roothash[:], roothash[:])
statement.Bytes_per_publickey = byte(max_bits / 8) statement.Bytes_per_publickey = byte(max_bits / 8)
witness := GenerateWitness(sender_secret, r, value, balance-value-fees-burn_value, witness_index) witness := GenerateWitness(sender_secret, r, value, balance-value-fees_currentasset-burn_value, witness_index)
witness_list = append(witness_list, witness) witness_list = append(witness_list, witness)

View File

@ -218,7 +218,7 @@ func Test_Creation_TX(t *testing.T) {
// here we are collecting proofs for later on bennhcmarking // here we are collecting proofs for later on bennhcmarking
for j := 2; j <= 128; j = j * 2 { for j := 2; j <= 128; j = j * 2 {
wsrc.account.Ringsize = j wsrc.account.Ringsize = j
tx, err := wsrc.TransferPayload0([]rpc.Transfer{rpc.Transfer{Destination: wdst.GetAddress().String(), Amount: 1}}, 0, false, rpc.Arguments{}, false) tx, err := wsrc.TransferPayload0([]rpc.Transfer{rpc.Transfer{Destination: wdst.GetAddress().String(), Amount: 1}}, 0, false, rpc.Arguments{}, 0, false)
if err != nil { if err != nil {
t.Fatalf("Cannot create transaction, err %s", err) t.Fatalf("Cannot create transaction, err %s", err)
} else { } else {
@ -236,7 +236,7 @@ func Test_Creation_TX(t *testing.T) {
// accounts are reversed // accounts are reversed
wdst.Sync_Wallet_Memory_With_Daemon() wdst.Sync_Wallet_Memory_With_Daemon()
reverse_tx, err := wdst.TransferPayload0([]rpc.Transfer{rpc.Transfer{Destination: wsrc.GetAddress().String(), Amount: 1}}, 0, false, rpc.Arguments{}, false) reverse_tx, err := wdst.TransferPayload0([]rpc.Transfer{rpc.Transfer{Destination: wsrc.GetAddress().String(), Amount: 1}}, 0, false, rpc.Arguments{}, 0, false)
if err != nil { if err != nil {
t.Fatalf("Cannot create transaction, err %s", err) t.Fatalf("Cannot create transaction, err %s", err)
} }
@ -250,7 +250,7 @@ func Test_Creation_TX(t *testing.T) {
pre_transfer_src_balance := wsrc.account.Balance_Mature pre_transfer_src_balance := wsrc.account.Balance_Mature
pre_transfer_dst_balance := wdst.account.Balance_Mature pre_transfer_dst_balance := wdst.account.Balance_Mature
tx, err := wsrc.TransferPayload0([]rpc.Transfer{rpc.Transfer{Destination: wdst.GetAddress().String(), Amount: 1}}, 0, false, rpc.Arguments{}, false) tx, err := wsrc.TransferPayload0([]rpc.Transfer{rpc.Transfer{Destination: wdst.GetAddress().String(), Amount: 1}}, 0, false, rpc.Arguments{}, 0, false)
if err != nil { if err != nil {
t.Fatalf("Cannot create transaction, err %s", err) t.Fatalf("Cannot create transaction, err %s", err)
} else { } else {
@ -292,7 +292,7 @@ func Test_Creation_TX(t *testing.T) {
var tx_set []*transaction.Transaction var tx_set []*transaction.Transaction
for i := 0; i < 6; i++ { for i := 0; i < 6; i++ {
tx, err := wsrc.TransferPayload0([]rpc.Transfer{rpc.Transfer{Destination: wdst.GetAddress().String(), Amount: 1}}, 0, false, rpc.Arguments{}, false) tx, err := wsrc.TransferPayload0([]rpc.Transfer{rpc.Transfer{Destination: wdst.GetAddress().String(), Amount: 1}}, 0, false, rpc.Arguments{}, 0, false)
if err != nil { if err != nil {
t.Fatalf("Cannot create transaction, err %s", err) t.Fatalf("Cannot create transaction, err %s", err)
} else { } else {

View File

@ -86,6 +86,10 @@ func Test_Creation_TX_morecheck(t *testing.T) {
t.Fatalf("Cannot add regtx to pool err %s", err) t.Fatalf("Cannot add regtx to pool err %s", err)
} }
simulator_chain_mineblock(chain, wgenesis.GetAddress(), t) // mine a block at tip
simulator_chain_mineblock(chain, wgenesis.GetAddress(), t) // mine a block at tip
simulator_chain_mineblock(chain, wgenesis.GetAddress(), t) // mine a block at tip
simulator_chain_mineblock(chain, wgenesis.GetAddress(), t) // mine a block at tip
simulator_chain_mineblock(chain, wgenesis.GetAddress(), t) // mine a block at tip simulator_chain_mineblock(chain, wgenesis.GetAddress(), t) // mine a block at tip
wgenesis.SetDaemonAddress(rpcport) wgenesis.SetDaemonAddress(rpcport)
@ -102,6 +106,9 @@ func Test_Creation_TX_morecheck(t *testing.T) {
if err = wsrc.Sync_Wallet_Memory_With_Daemon(); err != nil { if err = wsrc.Sync_Wallet_Memory_With_Daemon(); err != nil {
t.Fatalf("wallet sync error err %s", err) t.Fatalf("wallet sync error err %s", err)
} }
if err = wdst.Sync_Wallet_Memory_With_Daemon(); err != nil {
t.Fatalf("wallet sync error err %s", err)
}
wsrc.account.Ringsize = 2 wsrc.account.Ringsize = 2
wdst.account.Ringsize = 2 wdst.account.Ringsize = 2
@ -109,7 +116,12 @@ func Test_Creation_TX_morecheck(t *testing.T) {
var txs []transaction.Transaction var txs []transaction.Transaction
for i := 0; i < 7; i++ { for i := 0; i < 7; i++ {
tx, err := wsrc.TransferPayload0([]rpc.Transfer{rpc.Transfer{Destination: wdst.GetAddress().String(), Amount: 700000}}, 0, false, rpc.Arguments{}, false) wsrc.Sync_Wallet_Memory_With_Daemon()
wdst.Sync_Wallet_Memory_With_Daemon()
t.Logf("Chain height %d\n", chain.Get_Height())
tx, err := wsrc.TransferPayload0([]rpc.Transfer{rpc.Transfer{Destination: wdst.GetAddress().String(), Amount: 700000}}, 0, false, rpc.Arguments{}, 100000, false)
if err != nil { if err != nil {
t.Fatalf("Cannot create transaction, err %s", err) t.Fatalf("Cannot create transaction, err %s", err)
} else { } else {

View File

@ -57,7 +57,7 @@ func (w *Wallet_Memory) Transfer_Simplified(addr string, value uint64, data []by
// we should reply to an entry // we should reply to an entry
// send amount to specific addresses // send amount to specific addresses
func (w *Wallet_Memory) TransferPayload0(transfers []rpc.Transfer, ringsize uint64, transfer_all bool, scdata rpc.Arguments, dry_run bool) (tx *transaction.Transaction, err error) { func (w *Wallet_Memory) TransferPayload0(transfers []rpc.Transfer, ringsize uint64, transfer_all bool, scdata rpc.Arguments, gasstorage uint64, dry_run bool) (tx *transaction.Transaction, err error) {
// var transfer_details structures.Outgoing_Transfer_Details // var transfer_details structures.Outgoing_Transfer_Details
w.transfer_mutex.Lock() w.transfer_mutex.Lock()
@ -215,6 +215,10 @@ func (w *Wallet_Memory) TransferPayload0(transfers []rpc.Transfer, ringsize uint
var block_hash crypto.Hash var block_hash crypto.Hash
var zeroscid crypto.Hash var zeroscid crypto.Hash
// noncetopo should be verified for all ring members simultaneously
// this can lead to tx rejection
// we currently bypass this since random members are chosen which have not been used in last 5 block
_, noncetopo, block_hash, self_e, err := w.GetEncryptedBalanceAtTopoHeight(zeroscid, -1, w.GetAddress().String()) _, noncetopo, block_hash, self_e, err := w.GetEncryptedBalanceAtTopoHeight(zeroscid, -1, w.GetAddress().String())
if err != nil { if err != nil {
return return
@ -362,7 +366,7 @@ func (w *Wallet_Memory) TransferPayload0(transfers []rpc.Transfer, ringsize uint
max_bits += 6 // extra 6 bits max_bits += 6 // extra 6 bits
if !dry_run { if !dry_run {
tx = w.BuildTransaction(transfers, rings_balances, rings, block_hash, height, scdata, treehash_raw, max_bits) tx = w.BuildTransaction(transfers, rings_balances, rings, block_hash, height, scdata, treehash_raw, max_bits, gasstorage)
} }
if tx == nil { if tx == nil {