282 lines
8.9 KiB
Go
Raw Normal View History

// 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 globals
import "os"
import "fmt"
import "math"
import "net/url"
import "strings"
import "strconv"
import "math/big"
import "path/filepath"
2021-02-22 17:48:14 +00:00
import "runtime/debug"
import "golang.org/x/net/proxy"
2021-02-22 17:48:14 +00:00
import "github.com/romana/rlog"
import "github.com/sirupsen/logrus"
import log "github.com/sirupsen/logrus"
import "github.com/deroproject/derohe/config"
2021-02-22 17:48:14 +00:00
import "github.com/deroproject/derohe/rpc"
type ChainState int // block chain can only be in 2 state, either SYNCRONISED or syncing
const (
SYNCRONISED ChainState = iota // 0
SYNCING // 1
)
// all the the global variables used by the program are stored here
// since the entire logic is designed around a state machine driven by external events
// once the core starts nothing changes until there is a network state change
var Incoming_Block = make([]byte, 100) // P2P feeds it, blockchain consumes it
var Outgoing_Block = make([]byte, 100) // blockchain feeds it, P2P consumes it only if a block has been mined
var Incoming_Tx = make([]byte, 100) // P2P feeds it, blockchain consumes it
var Outgoing_Tx = make([]byte, 100) // blockchain feeds it, P2P consumes it only if a user has created a Tx mined
var Subsystem_Active uint32 // atomic counter to show how many subsystems are active
var Exit_In_Progress bool
// on init this variable is updated to setup global config in 1 go
var Config config.CHAIN_CONFIG = config.Mainnet // default is mainnnet
// global logger all components will use it with context
var Logger *logrus.Logger
var Log_Level = logrus.InfoLevel // default is info level
var ilog_formatter *logrus.TextFormatter // used while tracing code
var Dialer proxy.Dialer = proxy.Direct // for proxy and direct connections
// all outgoing connections , including DNS requests must be made using this
// all program arguments are available here
var Arguments map[string]interface{}
2021-02-22 17:48:14 +00:00
func InitNetwork() {
Config = config.Mainnet // default is mainnnet
if Arguments["--testnet"].(bool) == true { // setup testnet if requested
Config = config.Testnet
}
}
func Initialize() {
var err error
_ = err
Arguments["--testnet"] = true // force testnet every where
2021-02-22 17:48:14 +00:00
InitNetwork()
// formatter := &logrus.TextFormatter{DisableColors : true}
//Logger= &logrus.Logger{Formatter:formatter}
Logger = logrus.New()
//Logger.Formatter = &logrus.TextFormatter{DisableColors : true}
Logger.SetLevel(logrus.InfoLevel)
if Arguments["--debug"].(bool) == true { // setup debug mode if requested
Log_Level = logrus.DebugLevel
Logger.SetLevel(logrus.DebugLevel)
}
Logger.AddHook(&HOOK) // add rlog hook
// choose socks based proxy if user requested so
if Arguments["--socks-proxy"] != nil {
log.Debugf("Setting up proxy using %s", Arguments["--socks-proxy"].(string))
//uri, err := url.Parse("socks5://127.0.0.1:9000") // "socks5://demo:demo@192.168.99.100:1080"
uri, err := url.Parse("socks5://" + Arguments["--socks-proxy"].(string)) // "socks5://demo:demo@192.168.99.100:1080"
if err != nil {
log.Fatalf("Error parsing socks proxy: err %s", err)
}
Dialer, err = proxy.FromURL(uri, proxy.Direct)
if err != nil {
log.Fatalf("Error creating socks proxy: err \"%s\" from data %s ", err, Arguments["--socks-proxy"].(string))
}
}
// windows and logrus have issues while printing colored messages, so disable them right now
ilog_formatter = &logrus.TextFormatter{} // this needs to be created after after top logger has been intialised
ilog_formatter.DisableColors = true
ilog_formatter.DisableTimestamp = true
// lets create data directories
err = os.MkdirAll(GetDataDirectory(), 0750)
if err != nil {
fmt.Printf("Error creating/accessing directory %s , err %s\n", GetDataDirectory(), err)
}
}
2021-02-22 17:48:14 +00:00
// used to recover in case of panics
func Recover() {
if r := recover(); r != nil {
2021-02-28 06:54:30 +00:00
rlog.Warnf("Recovered, error %s", r)
rlog.Warnf("Stack trace \n%s\n", debug.Stack())
2021-02-22 17:48:14 +00:00
}
}
// tells whether we are in mainnet mode
// if we are not mainnet, we are a testnet,
// we will only have a single mainnet ,( but we may have one or more testnets )
func IsMainnet() bool {
if Config.Name == "mainnet" {
return true
}
return false
}
// return different directories for different networks ( mainly mainnet, testnet, simulation )
// this function is specifically for daemon
func GetDataDirectory() string {
data_directory, err := os.Getwd()
if err != nil {
fmt.Printf("Error obtaining current directory, using temp dir err %s\n", err)
data_directory = os.TempDir()
}
// if user provided an option, override default
if Arguments["--data-dir"] != nil {
data_directory = Arguments["--data-dir"].(string)
}
if IsMainnet() {
return filepath.Join(data_directory, "mainnet")
}
return filepath.Join(data_directory, "testnet")
}
/* this function converts a logrus entry into a txt formater based entry with no colors for tracing*/
func CTXString(entry *logrus.Entry) string {
entry.Level = logrus.DebugLevel
data, _ := ilog_formatter.Format(entry)
return string(data)
}
// never do any division operation on money due to floating point issues
// newbies, see type the next in python interpretor "3.33-3.13"
//
func FormatMoney(amount uint64) string {
2021-02-22 17:48:14 +00:00
return FormatMoneyPrecision(amount, 5) // default is 5 precision after floating point
}
// 0
func FormatMoney0(amount uint64) string {
return FormatMoneyPrecision(amount, 0)
}
//5 precision
func FormatMoney5(amount uint64) string {
return FormatMoneyPrecision(amount, 5)
}
//8 precision
func FormatMoney8(amount uint64) string {
return FormatMoneyPrecision(amount, 8)
}
// 12 precision
func FormatMoney12(amount uint64) string {
return FormatMoneyPrecision(amount, 12) // default is 8 precision after floating point
}
// format money with specific precision
func FormatMoneyPrecision(amount uint64, precision int) string {
hard_coded_decimals := new(big.Float).SetInt64(100000)
float_amount, _, _ := big.ParseFloat(fmt.Sprintf("%d", amount), 10, 0, big.ToZero)
result := new(big.Float)
result.Quo(float_amount, hard_coded_decimals)
2021-02-22 17:48:14 +00:00
return result.Text('f', precision) // 5 is display precision after floating point
}
// this will parse and validate an address, in reference to the current main/test mode
2021-02-22 17:48:14 +00:00
func ParseValidateAddress(str string) (addr *rpc.Address, err error) {
addr, err = rpc.NewAddress(strings.TrimSpace(str))
if err != nil {
return
}
// check whether the domain is valid
if !addr.IsDERONetwork() {
err = fmt.Errorf("Invalid DERO address")
return
}
if IsMainnet() != addr.IsMainnet() {
if IsMainnet() {
err = fmt.Errorf("Address belongs to DERO testnet and is invalid on current network")
} else {
err = fmt.Errorf("Address belongs to DERO mainnet and is invalid on current network")
}
return
}
return
}
// this will covert an amount in string form to atomic units
func ParseAmount(str string) (amount uint64, err error) {
float_amount, base, err := big.ParseFloat(strings.TrimSpace(str), 10, 0, big.ToZero)
if err != nil {
err = fmt.Errorf("Amount could not be parsed err: %s", err)
return
}
if base != 10 {
err = fmt.Errorf("Amount should be in base 10 (0123456789)")
return
}
if float_amount.Cmp(new(big.Float).Abs(float_amount)) != 0 { // number and abs(num) not equal means number is neg
err = fmt.Errorf("Amount cannot be negative")
return
}
// multiply by 5 zeroes
hard_coded_decimals := new(big.Float).SetInt64(100000)
float_amount.Mul(float_amount, hard_coded_decimals)
/*if !float_amount.IsInt() {
err = fmt.Errorf("Amount is invalid %s ", float_amount.Text('f',0))
return
}*/
// convert amount to uint64
//amount, _ = float_amount.Uint64() // sanity checks again
amount, err = strconv.ParseUint(float_amount.Text('f', 0), 10, 64)
if err != nil {
err = fmt.Errorf("Amount is invalid %s ", float_amount.Text('f', 0))
return
}
//if amount == 0 {
// err = fmt.Errorf("0 cannot be transferred")
// return
//}
if amount == math.MaxUint64 {
err = fmt.Errorf("Amount is invalid")
return
}
return // return the number
}