Browse Source

Initial Dero-Stargate release of Smart Contracts

master
Captain Dero 1 year ago
parent
commit
05c5a9b455
54 changed files with 7940 additions and 1314 deletions
  1. +11
    -2
      blockchain/blockchain.go
  2. +1
    -1
      blockchain/hardfork_core.go
  3. +75
    -3
      blockchain/outputs_index.go
  4. +18
    -1
      blockchain/rpcserver/getoutputs.bin.go
  5. +48
    -1
      blockchain/rpcserver/gettransactions.go
  6. +69
    -6
      blockchain/rpcserver/iskeyimagespent.go
  7. +120
    -5
      blockchain/rpcserver/rpcserver.go
  8. +3
    -0
      blockchain/rpcserver/sendrawtransaction.go
  9. +477
    -0
      blockchain/sc.go
  10. +7
    -0
      blockchain/store.go
  11. +39
    -1
      blockchain/transaction_verify.go
  12. +5
    -4
      build_package.sh
  13. BIN
      checkpoints/testnet_checksums.dat
  14. +1
    -1061
      checkpoints/testnet_checksums.go
  15. +1
    -1
      cmd/dero-wallet-cli/easymenu_post_open.go
  16. +1
    -2
      cmd/dero-wallet-cli/main.go
  17. +1
    -1
      cmd/dero-wallet-cli/prompt.go
  18. +49
    -5
      cmd/derod/main.go
  19. +31
    -0
      cmd/dvm/factorial.bas
  20. +83
    -0
      cmd/dvm/lottery.bas
  21. +186
    -0
      cmd/dvm/main.go
  22. +54
    -1
      cmd/explorer/explorer.go
  23. +34
    -2
      cmd/explorer/templates.go
  24. +772
    -0
      cmd/webwallet/main.go
  25. +3
    -5
      config/seed_nodes.go
  26. +1
    -1
      config/version.go
  27. +17
    -29
      crypto/edwards25519_test.go
  28. +87
    -4
      crypto/key.go
  29. +67
    -74
      crypto/precompute.go
  30. +132
    -0
      crypto/signature.go
  31. +68
    -0
      dvm/deterministic_random_number.go
  32. +58
    -0
      dvm/deterministic_random_number_test.go
  33. +1131
    -0
      dvm/dvm.go
  34. +1757
    -0
      dvm/dvm_execution_test.go
  35. +252
    -0
      dvm/dvm_functions.go
  36. +132
    -0
      dvm/dvm_functions_test.go
  37. +239
    -0
      dvm/dvm_parse_test.go
  38. +324
    -0
      dvm/dvm_store.go
  39. +66
    -0
      dvm/dvm_store_memory.go
  40. +6
    -0
      structures/daemon_rpc.go
  41. +4
    -0
      structures/wallet_rpc.go
  42. +95
    -0
      transaction/sc_transaction.go
  43. +103
    -2
      transaction/transaction_extra.go
  44. +108
    -69
      walletapi/daemon_communication.go
  45. +16
    -0
      walletapi/db.go
  46. +724
    -0
      walletapi/db_mem.go
  47. +1
    -9
      walletapi/rpc_get_transfer_by_txid.go
  48. +1
    -1
      walletapi/rpc_transfer.go
  49. +34
    -1
      walletapi/rpc_transfersplit.go
  50. +45
    -2
      walletapi/rpcserver.go
  51. +154
    -3
      walletapi/tx_creation_test.go
  52. +145
    -10
      walletapi/wallet.go
  53. +84
    -7
      walletapi/wallet_transfer.go
  54. +0
    -0
      walletapi/walletapi.test.log

+ 11
- 2
blockchain/blockchain.go View File

@@ -483,8 +483,7 @@ func (chain *Blockchain) Add_TX_To_Pool(tx *transaction.Transaction) (result boo

// chain lock is no longer required as we only do readonly processing
// chain.Lock()
// defer chain.Unlock()

// defer chain.Unlock()
dbtx, err := chain.store.BeginTX(false)
if err != nil {
logger.Warnf("Could NOT create DB transaction err %s", err)
@@ -1368,6 +1367,11 @@ func (chain *Blockchain) client_protocol(dbtx storage.DBTX, bl *block.Block, bli

//mark tx found in this block is valid
chain.mark_TX(dbtx, blid, bl.Tx_hashes[i], true)
// execute SC TX if HF is active
if bl.Major_Version >= 4{
chain.Process_SC(dbtx,bl,tx,int64(bl.Major_Version) )
}

} else { // TX is double spend or reincluded by 2 blocks simultaneously
rlog.Tracef(1,"Double spend TX is being ignored %s %s", blid, bl.Tx_hashes[i])
@@ -1395,6 +1399,11 @@ func (chain *Blockchain) client_protocol_reverse(dbtx storage.DBTX, bl *block.Bl

//mark tx found in this block is invalid
chain.mark_TX(dbtx, blid, bl.Tx_hashes[i], false)
if bl.Major_Version >= 4{
chain.Revert_SC(dbtx,crypto.Key(bl.Tx_hashes[i]),int64(bl.Major_Version) )
}

} else { // TX is double spend or reincluded by 2 blocks simultaneously
// invalid tx is related


+ 1
- 1
blockchain/hardfork_core.go View File

@@ -53,7 +53,7 @@ var mainnet_hard_forks = []Hard_fork{
// current testnet_hard_forks
var testnet_hard_forks = []Hard_fork{
{1, 0, 0, 0, 0, true}, // version 1 hard fork where genesis block landed
{2, 1984, 0, 0, 0, true}, // version 2 hard fork where we started , it's mandatory
{4, 1, 0, 0, 0, true}, // version 4 hard fork where we started , it's mandatory
}

// current simulation_hard_forks


+ 75
- 3
blockchain/outputs_index.go View File

@@ -26,6 +26,7 @@ import "fmt"
//import "io/ioutil"
//import "sync"
//import "encoding/binary"
import "runtime/debug"

import "github.com/romana/rlog"
import "github.com/vmihailenco/msgpack"
@@ -287,9 +288,72 @@ func (chain *Blockchain) write_output_index(dbtx storage.DBTX, block_id crypto.H
dbtx.StoreObject(BLOCKCHAIN_UNIVERSE, GALAXY_OUTPUT_INDEX, GALAXY_OUTPUT_INDEX, itob(uint64(index_start)), serialized)

// fmt.Printf("index %d %x\n",index_start,d.InKey.Destination)
index_start++
index_within_tx++
index_within_tx++
var zero crypto.Key
if o.InKey.Destination != zero { // cut SC inputs from outputs
index_start++
}
}
// lets add sc transactions in the output table
// these transactions are similiar to miner transactions, open in amount
// smart contracts are live HF 4
if hard_fork_version_current >= 4 {
func(){
// safety so if anything wrong happens during SC outputs, ignore output , should we revert the TX
// what about losing some value
if r := recover(); r != nil {
logger.Warnf("Recovered while building output index for SC , Stack trace below block_hash ")
logger.Warnf("Stack trace \n%s", debug.Stack())
}
changelog := chain.Load_SCChangelog(dbtx,crypto.Key(bl.Tx_hashes[i]))
//fmt.Printf("HF version 4 changelog %d %d\n", len(changelog),len(changelog[0].TransferE) )
index_within_tx = 0
if len(changelog) >= 1 && len(changelog[0].TransferE) >= 1 {
for j := uint64(0); j < uint64(len(changelog[0].TransferE)); j++ {
o.BLID = block_id // store block id
o.TXID = bl.Tx_hashes[i]
o.Height = uint64(height)
o.SigType = 0
o.Unlock_Height = 0
o.PaymentID = o.PaymentID[:0]
o.Index_within_tx = index_within_tx
// generate one time keys
o.Tx_Public_Key,o.InKey.Destination = GetEphermalKey(crypto.Key(bl.Tx_hashes[i]),index_within_tx, changelog[0].TransferE[j].Address)
o.Amount = changelog[0].TransferE[j].Amount
o.InKey.Mask = ringct.ZeroCommitment_From_Amount(changelog[0].TransferE[j].Amount)
o.Index_Global = uint64(index_start)
fmt.Printf("writing SC output index %d %s\n",index_start, o.InKey.Destination )
serialized, err := msgpack.Marshal(&o)
if err != nil {
panic(err)
}

dbtx.StoreObject(BLOCKCHAIN_UNIVERSE, GALAXY_OUTPUT_INDEX, GALAXY_OUTPUT_INDEX, itob(uint64(index_start)), serialized)

index_start++
index_within_tx++
}
}
}() }

}

@@ -400,7 +464,15 @@ func (chain *Blockchain) Find_TX_Output_Index(tx_hash crypto.Hash) (offset int64
}

// tx has been loaded, now lets get the vout
vout_count := int64(len(tx.Vout))
//vout_count := int64(len(tx.Vout))
vout_count:= int64(0)
var zero crypto.Key
for j := uint64(0); j < uint64(len(tx.Vout)); j++ {
if crypto.Key(tx.Vout[j].Target.(transaction.Txout_to_key).Key) != zero { // cut SC inputs from outputs
vout_count++
}
}
offset += vout_count
}



+ 18
- 1
blockchain/rpcserver/getoutputs.bin.go View File

@@ -31,6 +31,8 @@ func getoutputs(rw http.ResponseWriter, req *http.Request) {
start := int64(0)
stop := int64(0)
start_height := int64(0)
stop_height := int64(0)

{ // parse start query parameter
keys, ok := req.URL.Query()["startheight"]
@@ -56,6 +58,12 @@ func getoutputs(rw http.ResponseWriter, req *http.Request) {
}

}
stop_height = start_height + 5000
if stop_height > int64(chain.Load_TOPO_HEIGHT(nil)){
stop_height = int64(chain.Load_TOPO_HEIGHT(nil))
}
}
}

@@ -92,6 +100,7 @@ func getoutputs(rw http.ResponseWriter, req *http.Request) {
//biggest_output_index := chain.Block_Count_Vout(nil,top_id) + chain.Get_Block_Output_Index(nil,top_id)

biggest_output_index := int64(0)

// convert height to block
top_block, err := chain.Load_Block_Topological_order_at_index(nil, chain.Load_TOPO_HEIGHT(nil))
@@ -100,6 +109,14 @@ func getoutputs(rw http.ResponseWriter, req *http.Request) {
_, biggest_output_index = chain.Get_Block_Output_Index(nil, top_block)

}
if stop_height != 0 {
top_block, err := chain.Load_Block_Topological_order_at_index(nil,stop_height)
if err == nil {
_, biggest_output_index = chain.Get_Block_Output_Index(nil, top_block)
}
}

if stop == 0 || stop > biggest_output_index {
stop = biggest_output_index
@@ -109,7 +126,7 @@ func getoutputs(rw http.ResponseWriter, req *http.Request) {
if start >= stop {
start = stop - 1
}
// lz4writer := lz4.NewWriter(rw)
//lz4writer.HighCompression = true // enable extreme but slow compression
//lz4writer.BlockMaxSize = 256*1024 // small block size to decrease memory consumption


+ 48
- 1
blockchain/rpcserver/gettransactions.go View File

@@ -16,6 +16,8 @@

package rpcserver

import "fmt"
import "strconv"
import "net/http"
import "encoding/hex"
import "encoding/json"
@@ -102,6 +104,41 @@ func gettransactions_fill(p structures.GetTransaction_Params) (result structures
related.Ring[i][j] = ring_data
}
}
related.SCBalance, _ = chain.ReadSCValue(nil,crypto.Key(hash),nil)
for _,v := range p.SC_Keys {
var key interface{}
if s, err := strconv.ParseUint(v, 10, 64); err == nil {
key = s
}else{
key = v
}
_, value := chain.ReadSCValue(nil,crypto.Key(hash),key)
if value == nil {
related.SC_Keys[v]=""
}else if _,ok := value.(uint64);ok {
related.SC_Keys[v]= fmt.Sprintf("%d",value.(uint64))
}else if _,ok := value.(string);ok {
related.SC_Keys[v]= value.(string)
}else{
related.SC_Keys[v]=""
}
}
tx.Parse_Extra()
if _,ok := tx.Extra_map[transaction.TX_EXTRA_SCDATA];ok {
related.SCRAW = string( tx.Extra_map[transaction.TX_EXTRA_SCDATA].([]byte))
}
// parsed SC
related.SC,_ = chain.ReadSC(nil,crypto.Key(hash))
err = nil
}

@@ -128,11 +165,21 @@ func gettransactions_fill(p structures.GetTransaction_Params) (result structures
// logger.Infof("TX hash %s height %d",hash, related.Block_Height)
for i := 0; i < len(tx.Vout); i++ {
if index >= 0 {
related.Output_Indices = append(related.Output_Indices, uint64(index+int64(i)))
var zero crypto.Key
if crypto.Key(tx.Vout[i].Target.(transaction.Txout_to_key).Key) != zero {
related.Output_Indices = append(related.Output_Indices, uint64(index))
}else{
related.Output_Indices = append(related.Output_Indices, 0)
}
} else {
related.Output_Indices = append(related.Output_Indices, 0)
}
index++
}
// todo we should add SC outputs also
result.Txs_as_hex = append(result.Txs_as_hex, hex.EncodeToString(tx.Serialize()))
result.Txs = append(result.Txs, related)
}


+ 69
- 6
blockchain/rpcserver/iskeyimagespent.go View File

@@ -16,11 +16,16 @@

package rpcserver

import "io"
import "fmt"
import "net/http"
import "strings"
import "context"

//import "encoding/hex"
import "encoding/json"
import "github.com/intel-go/fastjson"
import "github.com/osamingo/jsonrpc"

import "github.com/deroproject/derosuite/crypto"
import "github.com/deroproject/derosuite/structures"
@@ -35,15 +40,16 @@ import "github.com/deroproject/derosuite/structures"

type IsKeyImageSpent_Handler struct{}

func iskeyimagespent(rw http.ResponseWriter, req *http.Request) {
decoder := json.NewDecoder(req.Body)
func (ki IsKeyImageSpent_Handler) ServeJSONRPC(c context.Context, params *fastjson.RawMessage) (interface{}, *jsonrpc.Error) {
var p structures.Is_Key_Image_Spent_Params
var result structures.Is_Key_Image_Spent_Result
err := decoder.Decode(&p)
if err != nil {
panic(err)

if err := jsonrpc.Unmarshal(params, &p); err != nil {
return result, nil
fmt.Printf("Key_images handler json unmarshal err %s \n", err)
return nil, err
}
defer req.Body.Close()

for i := range p.Key_images {
hash := crypto.HashHexToHash(strings.TrimSpace(p.Key_images[i]))
@@ -63,6 +69,63 @@ func iskeyimagespent(rw http.ResponseWriter, req *http.Request) {
result.Spent_Status = append(result.Spent_Status, 0) // 0 mark means unspent
}

result.Status = "OK"

return result, nil
}

func iskeyimagespent(rw http.ResponseWriter, req *http.Request) {

rw.Header().Set("content-type", "application/json")
decoder := json.NewDecoder(req.Body)
var p structures.Is_Key_Image_Spent_Params
var result structures.Is_Key_Image_Spent_Result

// if it's a request with keyimage in url, process and return here
q := req.URL.Query()
if q["ki"] != nil && q["ki"][0] != "" {
hash := crypto.HashHexToHash(strings.TrimSpace(q["ki"][0]))

// check in blockchain
if _, ok := chain.Read_KeyImage_Status(nil, hash); ok {
result.Spent_Status = append(result.Spent_Status, 1) // 1 mark means spent in blockchain

} else if chain.Mempool.Mempool_Keyimage_Spent(hash) {
result.Spent_Status = append(result.Spent_Status, 2) // 2 mark means spent in pool

} else {

result.Spent_Status = append(result.Spent_Status, 0) // 0 mark means unspent
}

} else {

err := decoder.Decode(&p)
if err != nil {
if err != io.EOF {
panic(err)
}
}
defer req.Body.Close()

for i := range p.Key_images {
hash := crypto.HashHexToHash(strings.TrimSpace(p.Key_images[i]))

// check in blockchain
if _, ok := chain.Read_KeyImage_Status(nil, hash); ok {
result.Spent_Status = append(result.Spent_Status, 1) // 1 mark means spent in blockchain
continue
}

// check in pool
if chain.Mempool.Mempool_Keyimage_Spent(hash) {
result.Spent_Status = append(result.Spent_Status, 2) // 2 mark means spent in pool
continue
}

result.Spent_Status = append(result.Spent_Status, 0) // 0 mark means unspent
}
}
result.Status = "OK"
//logger.Debugf("Request %+v", p)



+ 120
- 5
blockchain/rpcserver/rpcserver.go View File

@@ -17,12 +17,15 @@
package rpcserver

import "io"
import "os"
import "fmt"
import "net"
import "time"
import "context"
import "strings"
import "sync"
import "sync/atomic"
import "crypto/tls"

//import "context"
import "net/http"
@@ -155,6 +158,10 @@ func (r *RPCServer) Run() {
if err := mr.RegisterMethod("gettxpool", GetTxPool_Handler{}, structures.GetTxPool_Params{}, structures.GetTxPool_Result{}); err != nil {
log.Fatalln(err)
}
if err := mr.RegisterMethod("is_key_image_spent", IsKeyImageSpent_Handler{}, structures.Is_Key_Image_Spent_Params{}, structures.Is_Key_Image_Spent_Result{}); err != nil {
log.Fatalln(err)
}

// create a new mux
r.mux = http.NewServeMux()
@@ -178,12 +185,41 @@ func (r *RPCServer) Run() {
}
}

// if we pin the certificate, we can have the perfect score
// but for certain reasons, we are not pinning them
//w.Header().Add("Strict-Transport-Security", "max-age=63072000; includeSubDomains")
cfg := &tls.Config{
/* MinVersion: tls.VersionTLS12,
CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256},
PreferServerCipherSuites: true,
CipherSuites: []uint16{
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
},*/
}
logger.Infof("RPC will listen on %s", default_address)
r.Lock()
r.srv = &http.Server{Addr: default_address, Handler: r.mux}
r.srv = &http.Server{
Addr: default_address,
Handler: r.mux,
ReadTimeout: 5 * time.Second,
ReadHeaderTimeout: 5 * time.Second,
WriteTimeout: 40 * time.Second,
TLSConfig: cfg,
TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler), 0),
}
r.Unlock()

r.mux.HandleFunc("/", hello)
// serve any static files from this directory
r.mux.Handle("/static/", http.FileServer(http.Dir("./webroot")))
//r.mux.HandleFunc("/", hello)
r.mux.Handle("/json_rpc", mr)

// handle nasty http requests
@@ -216,15 +252,94 @@ func (r *RPCServer) Run() {
r.mux.HandleFunc("/metrics", prometheus.InstrumentHandler("dero", promhttp.HandlerFor(metrics.Registry, promhttp.HandlerOpts{})))

}

//r.mux.HandleFunc("/json_rpc/debug", mr.ServeDebug)

if err := r.srv.ListenAndServe(); err != http.ErrServerClosed {
//r.srv.SetKeepAlivesEnabled(true)
wrappedMux := NewInternalOrFileSystem(r.mux)
r.srv.Handler = wrappedMux;

// the tls keys self certified can be generated using
// openssl ecparam -genkey -name secp384r1 -out server.key
// openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650

if _, err := os.Stat("./keys/server.crt"); !os.IsNotExist(err){
if _, err := os.Stat("./keys/server.key"); !os.IsNotExist(err){
if err := r.srv.ListenAndServeTLS("keys/server.crt","keys/server.key"); err != http.ErrServerClosed {
logger.Warnf("ERR TLS listening to address err %s", err)
}
}
}else{
if err := r.srv.ListenAndServe(); err != http.ErrServerClosed {
logger.Warnf("ERR listening to address err %s", err)
}
}
}

}


//this middleware see whether a request can be served from the filesystem
type InternalOrFileSystem struct {
handler http.Handler
}

//ServeHTTP handles the request by passing it to the real
//handler
func (l *InternalOrFileSystem) ServeHTTP(w http.ResponseWriter, r *http.Request) {
start := time.Now()
_ = start
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Max-Age", "3600")
w.Header().Set("Access-Control-Allow-Credentials", "true")
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With")
// more protections
w.Header().Set("X-Frame-Options", "DENY") // never load in an iframe
w.Header().Set("X-XSS-Protection", "1; mode=block") // do we need XSS protection, since everything is on client
w.Header().Set("Content-Security-Policy", "default-src 'self' 'unsafe-inline' 'unsafe-eval' ; img-src 'self' data: ") // CSP disable access to all other domains

// enable caching for static assets
if strings.Contains(r.URL.Path,"/static") || r.URL.Path == "/" {
w.Header().Set("Cache-Control", "public, max-age=7776000, must-revalidate")
}
// NOTE: redirect / to wallet
// NOTE: use hard-coded paths, other directory traversal attacks are possible
if r.URL.Path == "/" || r.URL.Path == "/index.html" {
//http.ServeFile(w, r, "./webroot/static/wallet.html") // this does not sent content-type
file, err := os.Open("./webroot/static/wallet.html")
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
http.Error(w, fmt.Sprintf("Unable to open and read file : %v", err),404)
return
}
defer file.Close()
http.ServeContent(w, r, "index.html", time.Now(), file) // this will properly set the headers
return
}
l.handler.ServeHTTP(w, r) // pass request to RPC handler
// log.Printf("%s %s %v", r.Method, r.URL.Path, time.Since(start))
}

//NewLogger constructs a new middleware handler to hook some static file requests
func NewInternalOrFileSystem(handlerToWrap http.Handler) *InternalOrFileSystem {
return &InternalOrFileSystem{handlerToWrap}
}


func hello(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "Hello world!")
}

+ 3
- 0
blockchain/rpcserver/sendrawtransaction.go View File

@@ -16,6 +16,7 @@

package rpcserver

import "io"
import "fmt"
import "net/http"
import "encoding/hex"
@@ -48,8 +49,10 @@ func SendRawTransaction_Handler(rw http.ResponseWriter, req *http.Request) {
}()
err := decoder.Decode(&p)
if err != nil {
if err != io.EOF {
logger.Warnf("err while decoding incoming sendrawtransaaction json err: %s", err)
return
}
}
defer req.Body.Close()



+ 477
- 0
blockchain/sc.go View File

@@ -0,0 +1,477 @@
// 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 blockchain

import "fmt"
import "bytes"
import "sort"

import "github.com/deroproject/derosuite/address"
import "github.com/deroproject/derosuite/storage"
import "github.com/deroproject/derosuite/dvm"
import "github.com/deroproject/derosuite/crypto"
import "github.com/deroproject/derosuite/block"
import "github.com/deroproject/derosuite/transaction"

import "github.com/vmihailenco/msgpack"

// this will process the SC transaction
// the tx should only be processed , if it has been processed

func (chain *Blockchain) Process_SC(dbtx storage.DBTX, bl *block.Block, tx *transaction.Transaction, hard_fork_version_current int64) {

var err error
if !tx.Verify_SC_Signature() { // if tx is not SC TX, or Signature could not be verified skip it
return
}

bl_hash := bl.GetHash()
tx_hash := tx.GetHash()

addri, _ := tx.Extra_map[transaction.TX_EXTRA_ADDRESS].(address.Address)

/*
addr,result := addri.(address.Address)
if !result {
return
}*/

fmt.Printf("Processing TX SC data %d \n", len(tx.Extra_map[transaction.TX_EXTRA_SCDATA].([]byte)))

if len(tx.Extra_map[transaction.TX_EXTRA_SCDATA].([]byte)) < 3 {
fmt.Printf("Cannot process SCTX, since data is less than 3 bytes\n")
return
}

// lets decode SC transaction from msgpack
var sc_tx transaction.SC_Transaction
err = msgpack.Unmarshal(tx.Extra_map[transaction.TX_EXTRA_SCDATA].([]byte), &sc_tx)
if err != nil {
fmt.Printf("SC msgpack unmarshal err %s\n", err)
return
}

fmt.Printf("sctx %+v\n", sc_tx)

// dicard any value provided with the tx and calculate from ring signature
sc_tx.Value = 0 // make sure value is zero

// check if any DERO value is attached, if yes, attach it
for i := 0; i < len(tx.Vout); i++ {
var zero crypto.Key
if tx.Vout[i].Amount != 0 && tx.Vout[i].Target.(transaction.Txout_to_key).Key == zero { // allow SC amounts to be open
// amount has already been verified as genuine by ringct

sc_tx.Value = tx.Vout[i].Amount
break

}
}

tx_store := dvm.Initialize_TX_store()

// used as value loader from disk
// this function is used to load any data required by the SC

diskloader := func(key dvm.DataKey, found *uint64) (result dvm.Variable) {
var exists bool
keyhash := crypto.Key(crypto.Keccak256(dvm.Serialize_DataKey(key)))
result, exists = chain.LoadSCValue(dbtx, key.SCID, keyhash)

fmt.Printf("Loading from disk %+v result %+v found status %+v \n", key, result, exists)
if exists {

*found = uint64(1)
}
return
}

tx_store.DiskLoader = diskloader // hook up loading from chain

entrypoint := ""
var scid crypto.Key
var sc_parsed dvm.SmartContract
execute := false

// if we are installing SMART CONTRACT, do it and call initialize
if len(sc_tx.SC) > 0 {

pos := ""
sc_parsed, pos, err = dvm.ParseSmartContract(string(sc_tx.SC))

if err != nil {
fmt.Printf("error Parsing sc txid %s err %s pos %s\n", tx_hash, err, pos)
return
}

fmt.Printf("SC parsed\n%+v\n", sc_parsed)

dbtx.StoreObject(BLOCKCHAIN_UNIVERSE, GALAXY_TRANSACTION, tx_hash[:], PLANET_TX_SC_BYTES, []byte(sc_tx.SC))

serialized, err := msgpack.Marshal(sc_parsed)

if err != nil {
fmt.Printf("err serial SC err %s\n", err)
}

dbtx.StoreObject(BLOCKCHAIN_UNIVERSE, GALAXY_TRANSACTION, tx_hash[:], PLANET_TX_SC_PROCESSED, serialized)

tx_store.DiskLoader = diskloader // hook up loading from chain

tx_store.Store(dvm.GetBalanceKey(crypto.Key(tx_hash)), dvm.Variable{Type: dvm.Uint64, Value: uint64(0)})

entrypoint = "Initialize"
scid = crypto.Key(tx_hash)

if _, ok := sc_parsed.Functions[entrypoint]; ok {
execute = true
} else {
fmt.Printf("stored SC does not contain entrypoint '%s' scid %s \n", entrypoint, scid)
}

// store state changes
//chain.store_changes(dbtx, crypto.Key(tx_hash),tx_store)

// we must also initialize and give the SC 0 balance
fmt.Printf(" SMART contract parsed \n")

} else {
// check if scid can be hex decoded

// load smart contract bytes, if loading failed , dero value is lost
sc_parsed_bytes, err := dbtx.LoadObject(BLOCKCHAIN_UNIVERSE, GALAXY_TRANSACTION, sc_tx.SCID[:], PLANET_TX_SC_PROCESSED)
if err != nil {
fmt.Printf("No such stored SC found %s\n", sc_tx.SCID)
return
}

// deserialise
err = msgpack.Unmarshal(sc_parsed_bytes, &sc_parsed)
if err != nil {
fmt.Printf("stored SC (parsed) could not be deserialised scid %s err %s\n", sc_tx.SCID, err)
return
}

if sc_tx.EntryPoint != "Initialize" { // initialize cannot be triggerred again
execute = true
entrypoint = sc_tx.EntryPoint
}
scid = sc_tx.SCID

}

if execute {
// if we found the SC in parsed form, check whether entrypoint is found
function, ok := sc_parsed.Functions[entrypoint]
if !ok {
fmt.Printf("stored SC does not contain entrypoint '%s' scid %s \n", entrypoint, scid)
return
}

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

// we have an entrypoint, now we must setup parameters and dvm
// all parameters are in string form to bypass translation issues in middle layers
params := map[string]interface{}{}
for _, p := range function.Params {
if param_value, ok := sc_tx.Params[p.Name]; ok {
params[p.Name] = param_value
} else { // necessary parameter is missing, bailout
fmt.Printf("entrypoint '%s' scid %s parameter missing '%s' \n", entrypoint, scid, p.Name)
return
}
}

tx_store.DiskLoader = diskloader // hook up loading from chain

// setup balance correctly
tx_store.Balance(scid) // transfer any value to make sure its not lost
tx_store.ReceiveInternal(scid, sc_tx.Value)

// setup block hash, height, topoheight correctly
state := &dvm.Shared_State{
DERO_Received: sc_tx.Value,
Store: tx_store,
Chain_inputs: &dvm.Blockchain_Input{
BL_HEIGHT: uint64(chain.Load_Height_for_BL_ID(dbtx, bl_hash)),
BL_TOPOHEIGHT: uint64(chain.Load_Block_Topological_order(dbtx, bl_hash)),
SCID: scid,
BLID: crypto.Key(bl_hash),
TXID: crypto.Key(tx_hash),
Signer: addri},
}

result, err := dvm.RunSmartContract(&sc_parsed, entrypoint, state, params)

fmt.Printf("result value %+v\n", result)

if err != nil {
fmt.Printf("entrypoint '%s' scid %s err execution '%s' \n", entrypoint, scid, err)
}

if err == nil && result.Type == dvm.Uint64 && result.Value.(uint64) == 0 {
// confirm the changes
} else { // discard all changes
tx_store = dvm.Initialize_TX_store()
tx_store.DiskLoader = diskloader // hook up loading from chain
tx_store.Balance(scid) // transfer any value to make sure its not lost arbitrarily to the network
tx_store.ReceiveInternal(scid, sc_tx.Value)
}

}
// store state changes
chain.store_changes(dbtx, crypto.Key(tx_hash), tx_store)

// chain.Revert_SC(dbtx,crypto.Key(tx_hash),hard_fork_version_current)

fmt.Printf("SC execution finished amount value %d\n", sc_tx.Value)

fmt.Printf("SC processing finished %d\n", len(sc_tx.SC))

}

// this will revert the SC transaction changes to the DB
func (chain *Blockchain) Revert_SC(dbtx storage.DBTX, tx_hash crypto.Key, hard_fork_version_current int64) {

changelog := chain.Load_SCChangelog(dbtx, tx_hash)
// if we are here everything is ok, lets write the values, in the reverse order
if len(changelog) == 0 {
return
}
for i := len(changelog) - 1; i >= 0; i-- {
change := changelog[i]
fmt.Printf("Reverting todb %s %s %s\n", change.SCID, change.Key, change.Previous)
chain.StoreSCValue(dbtx, change.SCID, change.Key, change.Previous)
}
}

func (chain *Blockchain) Load_SCChangelog(dbtx storage.DBTX, tx_hash crypto.Key) (changes []TX_SC_storage) {

object_data, err := dbtx.LoadObject(BLOCKCHAIN_UNIVERSE, GALAXY_TRANSACTION, tx_hash[:], PLANET_TX_SC_CHANGELOG)

if err != nil { // change log is not present
return
}

if len(object_data) == 0 { // change log is present but 0 bytes
return
}

err = msgpack.Unmarshal(object_data, &changes)
if err != nil {
changes = changes[:0]
return changes
}

return changes
}

// this will store the changes
// TODO: FIXME this should be integrated with POW for guarantees
func (chain *Blockchain) store_changes(dbtx storage.DBTX, tx_hash crypto.Key, changes *dvm.TX_Storage) {
var bulk_changes []TX_SC_storage
for _, atom := range changes.Atoms {
/*if atom.Prev_Value == atom.Value {
continue
}*/
keyhash := crypto.Key(crypto.Keccak256(dvm.Serialize_DataKey(atom.Key)))
prev := dvm.Serialize_Variable(atom.Prev_Value)
current := dvm.Serialize_Variable(atom.Value)

bulk_changes = append(bulk_changes, TX_SC_storage{SCID: atom.Key.SCID, Key: keyhash, Previous: prev, Current: current})

}

// map has different order for different iterations,so we sort them in some fixed order,
var keyarray []crypto.Key
for k, _ := range changes.Transfers {
var tmp crypto.Key
copy(tmp[:], k[:])
keyarray = append(keyarray, tmp)
}

sort.Slice(keyarray, func(i, j int) bool { return bytes.Compare(keyarray[i][:], keyarray[j][:]) == -1 })

// let calculate any balance updates
for i := range keyarray {
k := keyarray[i]
v := changes.Transfers[k]
keyhash := crypto.Key(crypto.Keccak256(dvm.Serialize_DataKey(dvm.GetBalanceKey(k))))
prev := dvm.Serialize_Variable(dvm.Variable{Type: dvm.Uint64, Value: v.BalanceAtStart})
current := dvm.Serialize_Variable(dvm.Variable{Type: dvm.Uint64, Value: changes.Balance(k)})

bulk_changes = append(bulk_changes, TX_SC_storage{SCID: k, Key: keyhash, Previous: prev, Current: current})

// this lines processes external outputs
bulk_changes[0].TransferE = append(bulk_changes[0].TransferE, v.TransferE...)
}

// if we are here everything is ok, lets write the values
for _, change := range bulk_changes {
fmt.Printf("storing todb %s %s %s\n", change.SCID, change.Key, change.Current)
chain.StoreSCValue(dbtx, change.SCID, change.Key, change.Current)
}

// store the change log itself
serialized_change_log, _ := msgpack.Marshal(bulk_changes)
dbtx.StoreObject(BLOCKCHAIN_UNIVERSE, GALAXY_TRANSACTION, tx_hash[:], PLANET_TX_SC_CHANGELOG, serialized_change_log)

}

// this will load the value from the chain
func (chain *Blockchain) LoadSCValue(dbtx storage.DBTX, scid crypto.Key, keyhash crypto.Key) (v dvm.Variable, found bool) {

var err error
if dbtx == nil {
dbtx, err = chain.store.BeginTX(false)
if err != nil {
logger.Warnf("Could NOT load SC Value. Error opening writable TX, err %s", err)
return
}

defer dbtx.Rollback()

}

fmt.Printf("loading fromdb %s %s \n", scid, keyhash)
object_data, err := dbtx.LoadObject(SMARTCONTRACT_UNIVERSE, SMARTCONTRACT_UNIVERSE, scid[:], keyhash[:])

if err != nil {
return v, false
}

if len(object_data) == 0 {
return v, false
}

v = dvm.Deserialize_Variable(object_data).(dvm.Variable)

return v, true
}

// reads a value from SC, always read balance
func (chain *Blockchain) ReadSC(dbtx storage.DBTX, scid crypto.Key) (sc dvm.SmartContract, found bool) {

var err error
if dbtx == nil {
dbtx, err = chain.store.BeginTX(false)
if err != nil {
logger.Warnf("Could NOT load SC Value. Error opening writable TX, err %s", err)
return
}

defer dbtx.Rollback()

}

sc_parsed_bytes, err := dbtx.LoadObject(BLOCKCHAIN_UNIVERSE, GALAXY_TRANSACTION, scid[:], PLANET_TX_SC_PROCESSED)
if err != nil {
return
}

// deserialise
err = msgpack.Unmarshal(sc_parsed_bytes, &sc)
if err != nil {
return
}

found = true
return
}

// reads a value from SC, always read balance
func (chain *Blockchain) ReadSCValue(dbtx storage.DBTX, scid crypto.Key, key interface{}) (balance uint64, value interface{}) {

keyhash := crypto.Key(crypto.Keccak256(dvm.Serialize_DataKey(dvm.GetBalanceKey(scid))))

balance_var, found := chain.LoadSCValue(dbtx, scid, keyhash)

if !found { // no balance = no SC
return
}
if balance_var.Type == dvm.Uint64 {
balance = balance_var.Value.(uint64)
}

if key == nil {
return
}
switch k := key.(type) {
case uint64:
keyhash = crypto.Key(crypto.Keccak256(dvm.Serialize_DataKey(
dvm.DataKey{Key: dvm.Variable{Type: dvm.Uint64, Value: k}})))
case string:
keyhash = crypto.Key(crypto.Keccak256(dvm.Serialize_DataKey(
dvm.DataKey{Key: dvm.Variable{Type: dvm.String, Value: k}})))
default:
return
}

value_var, _ := chain.LoadSCValue(dbtx, scid, keyhash)
fmt.Printf("read value %+v", value_var)
if value_var.Type != dvm.Invalid {
value = value_var.Value
}
return
}

// store the value in the chain
func (chain *Blockchain) StoreSCValue(dbtx storage.DBTX, scid crypto.Key, keyhash crypto.Key, value []byte) {
dbtx.StoreObject(SMARTCONTRACT_UNIVERSE, SMARTCONTRACT_UNIVERSE, scid[:], keyhash[:], value[:])
return
}

// all these are stored in a tx container
type TX_SC_storage struct {
SCID crypto.Key `msgpack:"S,omitempty"`
Key crypto.Key `msgpack:"K,omitempty"`
Previous []byte `msgpack:"P,omitempty"` // previous value
Current []byte `msgpack:"-"` // current value // this need not be stored

TransferE []dvm.TransferExternal `msgpack:"T,omitempty"`
}

// get public and ephermal key to pay to address
// TODO we can also payto blobs which even hide address
// both have issues, this requires address to be public
// blobs require blobs as paramters
func GetEphermalKey(txid crypto.Key, index_within_tx uint64, address_input string) (tx_public_key, ehphermal_public_key crypto.Key) {

var tx_secret_key crypto.Key

copy(tx_secret_key[:], txid[:])
crypto.ScReduce32(&tx_secret_key)
tx_public_key = *tx_secret_key.PublicKey()

addr, err := address.NewAddress(address_input)

// test case to pay to devs, if user passed invalid address
// TODO: expose a function to user to validate an address
if err != nil {
//panic(fmt.Sprintf("Invalid input address while generating blob %s err %s", address_input, err ))
addr, _ = address.NewAddress("dERoYnipRygd8RZxcpbgcMRvjHVRy2Tr2Jzu8rFWQQdfbPGp8SJYN2QLeKSzzJsbqh3CyRA7ebGvVT3ETWTV8FGh8dkb2NaWVt")
}

derivation := crypto.KeyDerivation(&addr.ViewKey, &tx_secret_key) // keyderivation using wallet address view key

// this becomes the key within Vout
ehphermal_public_key = derivation.KeyDerivation_To_PublicKey(index_within_tx, addr.SpendKey)
return
}

+ 7
- 0
blockchain/store.go View File

@@ -44,6 +44,7 @@ import "github.com/deroproject/derosuite/transaction"
//var CHAIN = []byte("CHAIN") // this stores the actual chain, parents keeps child list, starts from genesis block

var BLOCKCHAIN_UNIVERSE = []byte("U") //[]byte("BLOCKCHAIN_UNIVERSE") // all block chain data is store in this BLOCKCHAIN_UNIVERSE
var SMARTCONTRACT_UNIVERSE = []byte("SC")

// there are only 8 galaxies
var GALAXY_BLOCK = []byte("GB")
@@ -66,6 +67,8 @@ var TOP_HEIGHT = []byte("TOP_HEIGHT") // stores current TOP HEIGHT, only store
var TOPO_HEIGHT = []byte("TOPO_HEIGHT") // stores current TOPO HEIGHT, only stores single value
var TIPS = []byte("TIPS") // this stores tips



// the unique TXID or block ID becomes the solar system , which is common and saves lot of space

// individual attributes becomes the planets
@@ -105,6 +108,10 @@ var PLANET_TX_MINED_IN_BLOCK = []byte("MBL") //[]byte("MINERBLOCK") // which blo
var PLANET_TX_MINED = []byte("MIN") // all blocks where tx is mined in in
var PLANET_TX_SIZE = []byte("SIZE")

var PLANET_TX_SC_BYTES = []byte("SC")
var PLANET_TX_SC_PROCESSED = []byte("SCP")
var PLANET_TX_SC_CHANGELOG = []byte("SCL")

// the universe concept is there, as we bring in smart contracts, we will give each of them a universe to play within
// while communicating with external universe



+ 39
- 1
blockchain/transaction_verify.go View File

@@ -189,12 +189,50 @@ func (chain *Blockchain) Verify_Transaction_NonCoinbase(dbtx storage.DBTX, hf_ve
}

// Vout should have amount 0
for i := 0; i < len(tx.Vout); i++ {
if hf_version <= 3 { // pre SC check
for i := 0; i < len(tx.Vout); i++ {
if tx.Vout[i].Amount != 0 {
logger.WithFields(log.Fields{"txid": tx_hash, "Amount": tx.Vout[i].Amount}).Warnf("Amount must be zero in ringCT world")
return false
}
}
}else{
for i := 0; i < len(tx.Vout); i++ {
var zero crypto.Key
if tx.Vout[i].Amount != 0 {
// allow SC amounts to be open but make sure ring sigs still protect the open amount
if tx.Vout[i].Target.(transaction.Txout_to_key).Key == zero {
var V,sv crypto.Key
sv[0] = byte(tx.Vout[i].Amount & 255)
sv[1] = byte((tx.Vout[i].Amount >> 8) & 255)
sv[2] = byte((tx.Vout[i].Amount >> 16) & 255)
sv[3] = byte((tx.Vout[i].Amount >> 24) & 255)
sv[4] = byte((tx.Vout[i].Amount >> 32) & 255)
sv[5] = byte((tx.Vout[i].Amount >> 40) & 255)
sv[6] = byte((tx.Vout[i].Amount >> 48) & 255)
sv[7] = byte((tx.Vout[i].Amount >> 56) & 255)
crypto.AddKeys2(&V, &tx.RctSignature.ECdhInfo[i].Mask, &sv, &crypto.H)
if sv != tx.RctSignature.ECdhInfo[i].Amount {
logger.WithFields(log.Fields{"txid": tx_hash, "Amount": tx.Vout[i].Amount}).Warnf("Tampered amount in ringCT world")
return false
}
if V != tx.RctSignature.OutPk[i].Mask {
logger.WithFields(log.Fields{"txid": tx_hash, "Amount": tx.Vout[i].Amount}).Warnf("Tampered amount2 in ringCT world")
return false
}
}else{
logger.WithFields(log.Fields{"txid": tx_hash, "Amount": tx.Vout[i].Amount}).Warnf("Amount must be zero in ringCT world")
return false
}
}
}
}


// check the mixin , it should be atleast 4 and should be same through out the tx ( all other inputs)
// someone did send a mixin of 3 in 12006 block height


+ 5
- 4
build_package.sh View File

@@ -42,13 +42,13 @@ for PLATFORM in $PLATFORMS; do
GOARCH=${PLATFORM#*/}
OUTPUT_DIR="${ABSDIR}/build/dero_${GOOS}_${GOARCH}"
BIN_FILENAME="${OUTPUT}-${GOOS}-${GOARCH}"
echo mkdir -p $OUTPUT_DIR
echo mkdir -p $OUTPUT_DIR
if [[ "${GOOS}" == "windows" ]]; then BIN_FILENAME="${BIN_FILENAME}.exe"; fi
CMD="GOOS=${GOOS} GOARCH=${GOARCH} go build -o $OUTPUT_DIR/${BIN_FILENAME} $package"
echo "${CMD}"
eval $CMD || FAILURES="${FAILURES} ${PLATFORM}"
# build docker image for linux amd64 competely static
# build docker image for linux amd64 competely static
if [[ "${GOOS}" == "linux" && "${GOARCH}" == "amd64" && "${OUTPUT}" != "explorer" ]]; then
BIN_FILENAME="docker-${OUTPUT}-${GOOS}-${GOARCH}"
CMD="GOOS=${GOOS} GOARCH=${GOARCH} CGO_ENABLED=0 go build -o $OUTPUT_DIR/${BIN_FILENAME} $package"
@@ -56,9 +56,10 @@ echo mkdir -p $OUTPUT_DIR
eval $CMD || FAILURES="${FAILURES} ${PLATFORM}"
fi

done



# ARM64 builds only for linux
if [[ $PLATFORMS_ARM == *"linux"* ]]; then
GOOS="linux"


BIN
checkpoints/testnet_checksums.dat View File


+ 1
- 1061
checkpoints/testnet_checksums.go
File diff suppressed because it is too large
View File


+ 1
- 1
cmd/dero-wallet-cli/easymenu_post_open.go View File

@@ -161,7 +161,7 @@ func handle_easymenu_post_open_command(l *readline.Instance, line string) (proce
addr_list := []address.Address{*a}
amount_list := []uint64{amount_to_transfer} // transfer 50 dero, 2 dero
fees_per_kb := uint64(0) // fees must be calculated by walletapi
tx, inputs, input_sum, change, err := wallet.Transfer(addr_list, amount_list, 0, hex.EncodeToString(payment_id), fees_per_kb, 0)
tx, inputs, input_sum, change, err := wallet.Transfer(addr_list, amount_list, 0, hex.EncodeToString(payment_id), fees_per_kb, 0,nil)
_ = inputs
if err != nil {
globals.Logger.Warnf("Error while building Transaction err %s\n", err)


+ 1
- 2
cmd/dero-wallet-cli/main.go View File

@@ -315,8 +315,7 @@ func main() {
continue
}
} else if err == io.EOF {
// break
time.Sleep(time.Second)
break
}

// pass command to suitable handler


+ 1
- 1
cmd/dero-wallet-cli/prompt.go View File

@@ -257,7 +257,7 @@ func handle_prompt_command(l *readline.Instance, line string) {
}

offline := offline_mode
tx, inputs, input_sum, change, err := wallet.Transfer(addr_list, amount_list, 0, payment_id, 0, 0)
tx, inputs, input_sum, change, err := wallet.Transfer(addr_list, amount_list, 0, payment_id, 0, 0,nil)
build_relay_transaction(l, tx, inputs, input_sum, change, err, offline, amount_list)

case "q", "bye", "exit", "quit":


+ 49
- 5
cmd/derod/main.go View File

@@ -137,7 +137,7 @@ func main() {
globals.Logger.Infof("Daemon in %s mode", globals.Config.Name)
globals.Logger.Infof("Daemon data directory %s", globals.GetDataDirectory())

go check_update_loop ()
//go check_update_loop ()

params := map[string]interface{}{}

@@ -324,8 +324,10 @@ func main() {
return nil, 0, false
})
l.Refresh() // refresh the prompt

go func (){
go func (){
var gracefulStop = make(chan os.Signal)
signal.Notify(gracefulStop,os.Interrupt) // listen to all signals
for {
@@ -340,6 +342,12 @@ func main() {

for {
line, err := l.Readline()
select {
case <-Exit_In_Progress: fmt.Printf("exiting channel");break;
default:
}
if err == readline.ErrInterrupt {
if len(line) == 0 {
fmt.Print("Ctrl-C received, Exit in progress\n")
@@ -349,9 +357,13 @@ func main() {
continue
}
} else if err == io.EOF {
<-Exit_In_Progress
break
<-Exit_In_Progress
break
}


line = strings.TrimSpace(line)
line_parts := strings.Fields(line)
@@ -600,6 +612,36 @@ func main() {
fmt.Printf("mining stopped\n")
}
mining = false
case command == "sc_value":
if len(line_parts) == 1 || len(line_parts) >= 4 {
fmt.Printf("sc_value needs argument scid to print info\n")
continue
}

txid, err := hex.DecodeString(strings.ToLower(line_parts[1]))

if err != nil {
fmt.Printf("err while decoding txid err %s\n", err)
continue
}
var scid crypto.Key
copy(scid[:32], []byte(txid))
var key interface{}
if len(line_parts) >= 3 {
if s, err := strconv.ParseUint(line_parts[2], 10, 64); err == nil {
key = s
}else{
key = line_parts[2]
}
}
balance, value := chain.ReadSCValue(nil,scid,key)
fmt.Printf("sc %s balance %s DERO\n", scid, globals.FormatMoney12(balance))
fmt.Printf("sc %s key \"%+v\": %+v\n", scid, key,value)


case command == "print_tree": // prints entire block chain tree
//WriteBlockChainTree(chain, "/tmp/graph.dot")
@@ -978,6 +1020,7 @@ func usage(w io.Writer) {
io.WriteString(w, "\t\033[1mstart_mining\033[0m\tStart mining <dero address> <number of threads>\n")
io.WriteString(w, "\t\033[1mstop_mining\033[0m\tStop daemon mining\n")
io.WriteString(w, "\t\033[1mpeer_list\033[0m\tPrint peer list\n")
io.WriteString(w, "\t\033[1msc_value\033[0m\tPrint sc balance and stored <scid> <key>\n")
io.WriteString(w, "\t\033[1msync_info\033[0m\tPrint information about connected peers and their state\n")
io.WriteString(w, "\t\033[1mbye\033[0m\t\tQuit the daemon\n")
io.WriteString(w, "\t\033[1mban\033[0m\t\tBan specific ip from making any connections\n")
@@ -1026,6 +1069,7 @@ var completer = readline.NewPrefixCompleter(
readline.PcItem("print_block"),
readline.PcItem("print_height"),
readline.PcItem("print_tx"),
readline.PcItem("sc_value"),
readline.PcItem("status"),
readline.PcItem("start_mining"),
readline.PcItem("stop_mining"),


+ 31
- 0
cmd/dvm/factorial.bas View File

@@ -0,0 +1,31 @@
/* Factorial implementation in DVM-BASIC */


/* iterative implementation of factorial */
Function Factorial(input Uint64) Uint64
10 dim result,input_copy as Uint64
15 LET input_copy = input
20 LET result = 1
30 LET result = result * input
40 LET input = input - 1
50 IF input >= 2 THEN GOTO 30
60 printf "FACTORIAL of %d = %d" input_copy result
70 RETURN result
End Function

/* recursive implementation of factorial */
Function Factorial_recursive(input Uint64) Uint64
10 IF input == 1 THEN GOTO 20 ELSE GOTO 30
20 RETURN 1
30 RETURN input * Factorial_recursive(input - 1)
End Function
Function Factorialr(input Uint64) Uint64
10 dim result as Uint64
20 LET result = Factorial_recursive(input)
30 printf "FACTORIAL of %d = %d " input result
40 RETURN result
End Function



+ 83
- 0
cmd/dvm/lottery.bas View File

@@ -0,0 +1,83 @@
/* Lotter Smart Contract in DVM-BASIC
This lottery smart contract will give lottery wins every 6th try on average
*/



Function Lottery(value Uint64) Uint64
10 dim deposit_count,winner as Uint64
20 LET deposit_count = LOAD("deposit_count")+1
25 IF value == 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") + value )
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
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
// used 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 ADDRESS_RAW(LOAD("owner")) == ADDRESS_RAW(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 ADDRESS_RAW(LOAD("owner")) == ADDRESS_RAW(SIGNER()) THEN GOTO 30
20 RETURN 1
30 STORE("tmpowner",newowner)
40 RETURN 0
End Function
// until the new owner claims ownership, existing owner remains owner
Function ClaimOwnership() Uint64
10 IF ADDRESS_RAW(LOAD("tmpowner")) == ADDRESS_RAW(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, thety will be showing in signers wallet
Function Withdraw( amount Uint64) Uint64
10 IF ADDRESS_RAW(LOAD("owner")) == ADDRESS_RAW(SIGNER()) THEN GOTO 30
20 RETURN 1
30 SEND_DERO_TO_ADDRESS(SIGNER(),amount)
40 RETURN 0
End Function



+ 186
- 0
cmd/dvm/main.go View File

@@ -0,0 +1,186 @@
package main

import (
"fmt"
"io"
"io/ioutil"
"os"
"strings"
"reflect"

)

import "github.com/chzyer/readline"
import "github.com/deroproject/derosuite/dvm"
import "github.com/deroproject/derosuite/crypto"

func main() {
err := run()
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
}

type TabCompleter struct{}

var dummy_autocomplete TabCompleter
var global_sc dvm.SmartContract

func (t TabCompleter) Do(line []rune, linelen int) ([][]rune, int) {
var result [][]rune
for k,_ := range global_sc.Functions {
runek := []rune(k)
if linelen == 0 { // add all results
result = append(result,[]rune(k))
continue
}
if len([]rune(k)) >= linelen && reflect.DeepEqual(runek[:linelen], line[:linelen]) {
result = append(result,[]rune(k[linelen:]))
continue
}
}
return result, linelen
}

// this sets up the interpreter
func debug_run(sc * dvm.SmartContract, code string) {
/* defer func() {
if r := recover(); r != nil {
fmt.Printf("panic occurred", r)
}
}() */
temp_sc, _, err := dvm.ParseSmartContract(`Function REPL() Uint64
50 RETURN ` + code + `
End Function`)
if err != nil {
fmt.Printf("err while parsing REPL SC err %s\n",err)
return
}
// install our function in the original SC
sc.Functions["REPL"] = temp_sc.Functions["REPL"]
state := &dvm.Shared_State {
Chain_inputs:&dvm.Blockchain_Input{BL_HEIGHT:5 , BL_TOPOHEIGHT:9,SCID: crypto.Identity,
BLID: crypto.Identity,TXID: crypto.Identity,},
}
_,err = dvm.RunSmartContract ( sc, "REPL",state,map[string]interface{}{})
fmt.Printf("err while executing SC err %s\n",err)
fmt.Printf("Recursion %d\n", state.Monitor_recursion)
fmt.Printf("Interpreted %d\n", state.Monitor_lines_interpreted)
fmt.Printf("Evaluated %d\n", state.Monitor_ops)

}

func run() error {
var data []byte
if len(os.Args) > 1 {
if strings.HasPrefix(os.Args[1], "-") {
return fmt.Errorf("usage: dvm [<source file>]")
}
f, err := os.Open(os.Args[1])
if err != nil {
return err
}
defer f.Close()
data, err = ioutil.ReadAll(f)
if err != nil {
return err
}
}
sc, pos, err := dvm.ParseSmartContract(string(data))
if err != nil {
fmt.Printf("Error while parsing smart contract pos %s err : %s\n", pos,err)
return err
}
global_sc = sc
debug_run(&sc,"fact(20)")
debug_run(&sc,"factr(20)")
//return nil

var prompt string = "\033[92mDERO DVM:\033[32m>>>\033[0m "
// We need to initialize readline first, so it changes stderr to ansi processor on windows
l, err := readline.NewEx(&readline.Config{
Prompt: prompt,
HistoryFile: "",
AutoComplete: dummy_autocomplete,
InterruptPrompt: "^C",
EOFPrompt: "exit",

HistorySearchFold: true,
FuncFilterInputRune: filterInput,
})
if err != nil {
panic(err)
}
defer l.Close()


for {
line, err := l.Readline()
if err == readline.ErrInterrupt {
if len(line) == 0 {
fmt.Printf("Ctrl-C received, Exit in progress\n")
break
} else {
continue
}
} else if err == io.EOF {
break
}
if err == io.EOF {
break
}
if err != nil {
return err
}
if strings.EqualFold(line,"exit"){
break;
}
debug_run(&sc,line)
}
fmt.Printf("Exiting")
return nil
}


// filter out specfic inputs from input processing
// currently we only skip CtrlZ background key
func filterInput(r rune) (rune, bool) {
switch r {
// block CtrlZ feature
case readline.CharCtrlZ:
return r, false
}
return r, true
}

+ 54
- 1
cmd/explorer/explorer.go View File

@@ -42,10 +42,12 @@ import "io/ioutil"
import "github.com/docopt/docopt-go"
import log "github.com/sirupsen/logrus"
import "github.com/ybbus/jsonrpc"
import "github.com/vmihailenco/msgpack"

import "github.com/deroproject/derosuite/block"
import "github.com/deroproject/derosuite/crypto"
import "github.com/deroproject/derosuite/globals"
import "github.com/deroproject/derosuite/address"
import "github.com/deroproject/derosuite/transaction"
import "github.com/deroproject/derosuite/structures"
import "github.com/deroproject/derosuite/proof"
@@ -166,6 +168,7 @@ type txinfo struct {
Keyimages []string // key images within tx
OutAddress []string // contains output secret key
OutOffset []uint64 // contains index offsets
OutAmount []string // contains output amounts
Type string // ringct or ruffct ( bulletproof)
ValidBlock string // the tx is valid in which block
InvalidBlock []string // the tx is invalid in which block
@@ -183,7 +186,15 @@ type txinfo struct {
Proof_amount string // decoded amount
Proof_PayID8 string // decrypted 8 byte payment id
Proof_error string // error if any while decoding proof

SC_TX_Available string //bool // whether this contains an SC TX
SC_Signer string // whether SC signer
SC_Signer_verified string // whether SC signer can be verified successfully
SC_DataSize int64
SC_Balance uint64 // SC SC_Balance in atomic units
SC_Balance_string string // SC_Balance in DERO
SC_Keys map[string]string // SC key value of
SC_TX transaction.SC_Transaction
}

// any information for block which needs to be printed
@@ -360,6 +371,14 @@ func load_tx_info_from_tx(info *txinfo, tx *transaction.Transaction) (err error)

for i := 0; i < len(tx.Vout); i++ {
info.OutAddress = append(info.OutAddress, tx.Vout[i].Target.(transaction.Txout_to_key).Key.String())
amt := "?"
if tx.Vout[i].Amount != 0 {
amt = globals.FormatMoney12(tx.Vout[i].Amount)
}
info.OutAmount = append(info.OutAmount, amt)
}

// if outputs cannot be located, do not panic
@@ -380,6 +399,34 @@ func load_tx_info_from_tx(info *txinfo, tx *transaction.Transaction) (err error)
case 4:
info.Type = "RingCT/4 Simple Bulletproof"
}
// add SC info
if tx.Verify_SC_Signature() {
info.SC_TX_Available = "True"
info.SC_Signer_verified = "True"
addr := tx.Extra_map[transaction.TX_EXTRA_ADDRESS].(address.Address)
info.SC_Signer = addr.String()
info.SC_DataSize = int64(len(tx.Extra_map[transaction.TX_EXTRA_SCDATA].([]byte)))
err = msgpack.Unmarshal(tx.Extra_map[transaction.TX_EXTRA_SCDATA].([]byte), &info.SC_TX)
if err != nil {
info.SC_TX.SC = fmt.Sprintf("Explorer error while unmarshaling SC TX err %s", err)
}
// check if any DERO value is attached, if yes, attach it
for i := 0; i < len(tx.Vout); i++ {
var zero crypto.Key
if tx.Vout[i].Amount != 0 && tx.Vout[i].Target.(transaction.Txout_to_key).Key == zero {
// amount has already been verified as genuine by ringct
info.SC_TX.Value = tx.Vout[i].Amount
break;
}
}
}

if !info.In_Pool { // find the age of block and other meta
var blinfo block_info
@@ -464,6 +511,12 @@ func load_tx_from_rpc(info *txinfo, txhash string) (err error) {

info.Ring = tx_result.Txs[0].Ring

info.SC_Balance = tx_result.Txs[0].SCBalance
info.SC_Balance_string = globals.FormatMoney12(tx_result.Txs[0].SCBalance)
for k,v := range tx_result.Txs[0].SC_Keys{
info.SC_Keys[k]=v
}
//fmt.Printf("tx_result %+v\n",tx_result.Txs)

return load_tx_info_from_tx(info, &tx)


+ 34
- 2
cmd/explorer/templates.go View File

@@ -342,7 +342,13 @@ var tx_template string = `{{define "tx"}}
<td>Signature type: {{.info.Type}}</td>
</tr>
<tr>
<td colspan="3">Extra: {{.info.Extra}}</td>
<td colspan="3">Extra:

<div style=" word-break: break-all; height:100px;width:100%;border:1px solid #ccc;overflow:auto;">
{{.info.Extra}}
</div>
</td>

</tr>
</table>
<h3>{{.info.Out}} output(s) for total of {{.info.Amount}} dero</h3>
@@ -357,12 +363,38 @@ var tx_template string = `{{define "tx"}}
{{range $i, $e := .info.OutAddress}}
<tr>
<td>{{ $e }}</td>
<td>{{$.info.Amount}}</td>
<td>{{index $.info.OutAmount $i}}</td>
<td>{{index $.info.OutOffset $i}}</td>
</tr>
{{end}}
</table>
</div>

{{if .info.SC_TX_Available}}
<div style="text-align:left;border:2px solid grey ">
<table>

<H3 style="margin:5px">Contains SC Transaction (data len : {{.info.SC_DataSize }} bytes)</H3>
<H5 style="margin:5px">SC Balance: {{.info.SC_Balance_string }} DERO </H5>
<H5 style="margin:5px">Signed by : {{.info.SC_Signer }} </H5>
<H5 style="margin:5px">SC DERO input : {{.info.SC_TX.Value }} (atomic units ( divide by 10^12)) </H5>
<H5 style="margin:5px">SC Code : </H5> <br/> <pre> {{.info.SC_TX.SC }} </pre>
<H5 style="margin:5px">SCID : {{.info.SC_TX.SCID }} </H5>
<H5 style="margin:5px">Entrypoint : {{.info.SC_TX.EntryPoint }} </H5>

<tr>
<H5 style="margin:5px;text-align:center">Parameters : </H5>
</tr>
{{range $k, $v := .info.SC_TX.Params}}
<tr>
<H5 style="margin:5px;text-align:center;color:red;"> {{ $k }} : {{$v}}</h5>
{{end}}
</table>
</div>

{{end}}

<!-- TODO currently we donot enable user to prove or decode something -->
<br/>


+ 772
- 0
cmd/webwallet/main.go View File

@@ -0,0 +1,772 @@
// +build js,wasm

package main

import (
//"encoding/base64"
"encoding/hex"
"encoding/json"
//"io/ioutil"
//"log"
//"net/http"
"fmt"
"net/url"
"strconv"
"syscall/js"
"time"
// "bytes"
"runtime/debug"
"strings"
)
import "github.com/romana/rlog"
import "github.com/deroproject/derosuite/walletapi"
import "github.com/deroproject/derosuite/globals"
import "github.com/deroproject/derosuite/config"
import "github.com/deroproject/derosuite/address"
import "github.com/deroproject/derosuite/transaction"
import "github.com/deroproject/derosuite/crypto"

var miner_tx bool = false

var Local_wallet_instance *walletapi.Wallet

func register_wallet_callbacks() {

js_ping := func(params []js.Value) {}
js.Global().Set("go_pinger", js.NewCallback(js_ping))

js_Create_New_Wallet := func(params []js.Value) {
error_message := "error"
filename := params[0].String()
password := params[1].String()

w, err := walletapi.Create_Encrypted_Wallet_Random(filename, password)

if err == nil {
error_message = "success"
Local_wallet_instance = w
Local_wallet_instance.SetDaemonAddress(daemon_address)
} else {
error_message = err.Error()
}

js.Global().Set("error_message", error_message)
}
js.Global().Set("DERO_JS_Create_New_Wallet", js.NewCallback(js_Create_New_Wallet))

js_Create_Encrypted_Wallet_From_Recovery_Words := func(params []js.Value) {
error_message := "error"

w, err := walletapi.Create_Encrypted_Wallet_From_Recovery_Words(params[0].String(), params[1].String(), params[2].String())

if err == nil {
error_message = "success"
Local_wallet_instance = w
Local_wallet_instance.SetDaemonAddress(daemon_address)
} else {
error_message = err.Error()
}

js.Global().Set("error_message", error_message)

}
js.Global().Set("DERO_JS_Create_Encrypted_Wallet_From_Recovery_Words", js.NewCallback(js_Create_Encrypted_Wallet_From_Recovery_Words))

js_Open_Encrypted_Wallet := func(params []js.Value) {

error_message := "error"

// convert typed array to go array
// this may be slow and needs to be optimized
// as optimization we are converting the data in javascript to hex
// and here we are hex decoding as it is faster than converting each value of typed array
// TODO: later when this gets fixed by go devs, we can incorporate it
/*
db_array := make([]byte,params[2].Length(),params[2].Length())
for i := 0; i < len(db_array); i++ {
db_array[i]= byte(params[2].Index(i).Int())
}
*/

src := []byte(params[2].String())
db_array := make([]byte, hex.DecodedLen(len(src)))
n, err := hex.Decode(db_array, src)
db_array = db_array[:n]

if err != nil {

rlog.Warnf("error decoding hex string \n", err)
}

rlog.Infof("i passed DB of size %d\n", len(db_array))
w, err := walletapi.Open_Encrypted_Wallet(params[0].String(), params[1].String(), db_array)
if err == nil {
error_message = "success"
Local_wallet_instance = w
Local_wallet_instance.SetDaemonAddress(daemon_address)

rlog.Infof("Successfully opened wallet\n")
} else {
error_message = err.Error()

rlog.Warnf("Error opened wallet %s\n", err)
}

js.Global().Set("error_message", error_message)
}
js.Global().Set("DERO_JS_Open_Encrypted_Wallet", js.NewCallback(js_Open_Encrypted_Wallet))

js_Create_Wallet := func(params []js.Value) {

filename := params[0].String()
password := params[1].String()
seed_hex := params[2].String()
error_message := "error"

var seed crypto.Key
seed_raw, err := hex.DecodeString(strings.TrimSpace(seed_hex))
if len(seed_raw) != 32 || err != nil {
err = fmt.Errorf("Recovery Only key must be 64 chars hexadecimal chars")
rlog.Errorf("err %s", err)
error_message = err.Error()
} else {

copy(seed[:], seed_raw[:32])
wallet, err := walletapi.Create_Encrypted_Wallet(filename, password, seed)

if err != nil {
error_message = err.Error()
} else {
error_message = "success"
Local_wallet_instance = wallet
Local_wallet_instance.SetDaemonAddress(daemon_address)
}
}

js.Global().Set("error_message", error_message)

}
js.Global().Set("DERO_JS_Create_Wallet", js.NewCallback(js_Create_Wallet))

js_Create_Encrypted_Wallet_ViewOnly := func(params []js.Value) {
filename := params[0].String()
password := params[1].String()
viewkey := params[2].String()
error_message := "error"

wallet, err := walletapi.Create_Encrypted_Wallet_ViewOnly(filename, password, viewkey)

if err != nil {
error_message = err.Error()
} else {
error_message = "success"
Local_wallet_instance = wallet
Local_wallet_instance.SetDaemonAddress(daemon_address)
}

js.Global().Set("error_message", error_message)
}
js.Global().Set("DERO_JS_Create_Encrypted_Wallet_ViewOnly", js.NewCallback(js_Create_Encrypted_Wallet_ViewOnly))

js_GenerateIAddress := func(params []js.Value) {
generate_integrated_address()
}
js.Global().Set("DERO_JS_GenerateIAddress", js.NewCallback(js_GenerateIAddress))

js_GetSeedinLanguage := func(params []js.Value) {
seed := "Some error occurred"
if Local_wallet_instance != nil && len(params) == 1 {
seed = Local_wallet_instance.GetSeedinLanguage(params[0].String())
}
js.Global().Set("wallet_seed", seed)
}
js.Global().Set("DERO_JS_GetSeedinLanguage", js.NewCallback(js_GetSeedinLanguage))

js_TX_history := func(params []js.Value) {
go func() {
error_message := "Wallet is Closed"
var buffer []byte
var err error

defer func() {
js.Global().Set("tx_history", string(buffer))
js.Global().Set("error_message", error_message)
}()

if Local_wallet_instance != nil {

min_height, _ := strconv.ParseUint(params[6].String(), 0, 64)
max_height, _ := strconv.ParseUint(params[7].String(), 0, 64)

entries := Local_wallet_instance.Show_Transfers(params[0].Bool(), params[1].Bool(), params[2].Bool(), params[3].Bool(), params[4].Bool(), params[5].Bool(), min_height, max_height)

if len(entries) == 0 {
return
}
buffer, err = json.Marshal(entries)
if err != nil {
error_message = err.Error()
return
}
}

}()
}
js.Global().Set("DERO_JS_TX_History", js.NewCallback(js_TX_history))

js_Transfer2 := func(params []js.Value) {
transfer_error := "error"
var transfer_txid, transfer_txhex, transfer_fee, transfer_amount, transfer_inputs_sum, transfer_change string

defer func() {
rlog.Warnf("setting values of tranfer variables")
js.Global().Set("transfer_txid", transfer_txid)
js.Global().Set("transfer_txhex", transfer_txhex)
js.Global().Set("transfer_amount", transfer_amount)
js.Global().Set("transfer_fee", transfer_fee)
js.Global().Set("transfer_inputs_sum", transfer_inputs_sum)
js.Global().Set("transfer_change", transfer_change)
js.Global().Set("transfer_error", transfer_error)
rlog.Warnf("setting values of tranfesr variables %s ", transfer_error)
}()