diff --git a/cmd/dero-wallet-cli/easymenu_post_open.go b/cmd/dero-wallet-cli/easymenu_post_open.go index 6cf01fc..73b395e 100644 --- a/cmd/dero-wallet-cli/easymenu_post_open.go +++ b/cmd/dero-wallet-cli/easymenu_post_open.go @@ -18,7 +18,7 @@ package main import "io" -//import "time" +import "time" import "fmt" //import "io/ioutil" @@ -208,18 +208,120 @@ func handle_easymenu_post_open_command(l *readline.Instance, line string) (proce var amount_to_transfer uint64 - amount_str := read_line_with_prompt(l, fmt.Sprintf("Enter amount to transfer in SCID (max TODO): ")) - - if amount_str == "" { - amount_str = ".00009" + var arguments = rpc.Arguments{ + // { rpc.RPC_DESTINATION_PORT, rpc.DataUint64,uint64(0x1234567812345678)}, + // { rpc.RPC_VALUE_TRANSFER, rpc.DataUint64,uint64(12345)}, + // { rpc.RPC_EXPIRY , rpc.DataTime, time.Now().Add(time.Hour).UTC()}, + // { rpc.RPC_COMMENT , rpc.DataString, "Purchase XYZ"}, } - amount_to_transfer, err = globals.ParseAmount(amount_str) - if err != nil { - logger.Error(err, "Err parsing amount") - break // invalid amount provided, bail out + if a.IsIntegratedAddress() { // read everything from the address + + if a.Arguments.Validate_Arguments() != nil { + logger.Error(err, "Integrated Address arguments could not be validated.") + break + } + + if !a.Arguments.Has(rpc.RPC_DESTINATION_PORT, rpc.DataUint64) { // but only it is present + logger.Error(fmt.Errorf("Integrated Address does not contain destination port."), "") + break + } + + arguments = append(arguments, rpc.Argument{rpc.RPC_DESTINATION_PORT, rpc.DataUint64, a.Arguments.Value(rpc.RPC_DESTINATION_PORT, rpc.DataUint64).(uint64)}) + // arguments = append(arguments, rpc.Argument{"Comment", rpc.DataString, "holygrail of all data is now working if you can see this"}) + + if a.Arguments.Has(rpc.RPC_EXPIRY, rpc.DataTime) { // but only it is present + + if a.Arguments.Value(rpc.RPC_EXPIRY, rpc.DataTime).(time.Time).Before(time.Now().UTC()) { + logger.Error(nil, "This address has expired.", "expiry time", a.Arguments.Value(rpc.RPC_EXPIRY, rpc.DataTime)) + break + } else { + logger.Info("This address will expire ", "expiry time", a.Arguments.Value(rpc.RPC_EXPIRY, rpc.DataTime)) + } + } + + logger.Info("Destination port is integreted in address.", "dst port", a.Arguments.Value(rpc.RPC_DESTINATION_PORT, rpc.DataUint64).(uint64)) + + if a.Arguments.Has(rpc.RPC_COMMENT, rpc.DataString) { // but only it is present + logger.Info("Integrated Message", "comment", a.Arguments.Value(rpc.RPC_COMMENT, rpc.DataString)) + } } - var arguments = rpc.Arguments{} + // arguments have been already validated + for _, arg := range a.Arguments { + if !(arg.Name == rpc.RPC_COMMENT || arg.Name == rpc.RPC_EXPIRY || arg.Name == rpc.RPC_DESTINATION_PORT || arg.Name == rpc.RPC_SOURCE_PORT || arg.Name == rpc.RPC_VALUE_TRANSFER || arg.Name == rpc.RPC_NEEDS_REPLYBACK_ADDRESS) { + switch arg.DataType { + case rpc.DataString: + if v, err := ReadString(l, arg.Name, arg.Value.(string)); err == nil { + arguments = append(arguments, rpc.Argument{arg.Name, arg.DataType, v}) + } else { + logger.Error(fmt.Errorf("%s could not be parsed (type %s),", arg.Name, arg.DataType), "") + return + } + case rpc.DataInt64: + if v, err := ReadInt64(l, arg.Name, arg.Value.(int64)); err == nil { + arguments = append(arguments, rpc.Argument{arg.Name, arg.DataType, v}) + } else { + logger.Error(fmt.Errorf("%s could not be parsed (type %s),", arg.Name, arg.DataType), "") + return + } + case rpc.DataUint64: + if v, err := ReadUint64(l, arg.Name, arg.Value.(uint64)); err == nil { + arguments = append(arguments, rpc.Argument{arg.Name, arg.DataType, v}) + } else { + logger.Error(fmt.Errorf("%s could not be parsed (type %s),", arg.Name, arg.DataType), "") + return + } + case rpc.DataFloat64: + if v, err := ReadFloat64(l, arg.Name, arg.Value.(float64)); err == nil { + arguments = append(arguments, rpc.Argument{arg.Name, arg.DataType, v}) + } else { + logger.Error(fmt.Errorf("%s could not be parsed (type %s),", arg.Name, arg.DataType), "") + return + } + case rpc.DataTime: + logger.Error(fmt.Errorf("time argument is currently not supported."), "") + break + + } + } + } + + if a.Arguments.Has(rpc.RPC_VALUE_TRANSFER, rpc.DataUint64) { // but only it is present + logger.Info("Transaction", "Value", globals.FormatMoney(a.Arguments.Value(rpc.RPC_VALUE_TRANSFER, rpc.DataUint64).(uint64))) + amount_to_transfer = a.Arguments.Value(rpc.RPC_VALUE_TRANSFER, rpc.DataUint64).(uint64) + } else { + + amount_str := read_line_with_prompt(l, fmt.Sprintf("Enter amount to transfer in DERO (max TODO): ")) + + if amount_str == "" { + amount_str = ".00001" + } + amount_to_transfer, err = globals.ParseAmount(amount_str) + if err != nil { + logger.Error(err, "Err parsing amount") + break // invalid amount provided, bail out + } + } + + // check whether the service needs the address of sender + // this is required to enable services which are completely invisisble to external entities + // external entities means anyone except sender/receiver + if a.Arguments.Has(rpc.RPC_NEEDS_REPLYBACK_ADDRESS, rpc.DataUint64) { + logger.Info("This RPC has requested your address.") + logger.Info("If you are expecting something back, it needs to be sent") + logger.Info("Your address will remain completely invisible to external entities(only sender/receiver can see your address)") + arguments = append(arguments, rpc.Argument{rpc.RPC_REPLYBACK_ADDRESS, rpc.DataAddress, wallet.GetAddress()}) + } + + // if no arguments, use space by embedding a small comment + if len(arguments) == 0 { // allow user to enter Comment + if v, err := ReadString(l, "Comment", ""); err == nil { + arguments = append(arguments, rpc.Argument{"Comment", rpc.DataString, v}) + } else { + logger.Error(fmt.Errorf("%s could not be parsed (type %s),", "Comment", rpc.DataString), "") + return + } + } if _, err := arguments.CheckPack(transaction.PAYLOAD0_LIMIT); err != nil { logger.Error(err, "Arguments packing err") diff --git a/cmd/derod/rpc/websocket_server.go b/cmd/derod/rpc/websocket_server.go index 1efa3bb..83c793d 100644 --- a/cmd/derod/rpc/websocket_server.go +++ b/cmd/derod/rpc/websocket_server.go @@ -342,29 +342,30 @@ var servicemux = handler.ServiceMap{ } type dummyassigner int + var d dummyassigner func (d dummyassigner) Assign(ctx context.Context, method string) (handler jrpc2.Handler) { - if handler = servicemux.Assign(ctx,method);handler != nil { + if handler = servicemux.Assign(ctx, method); handler != nil { return } - if handler = historical_apis.Assign(ctx,method);handler != nil { + if handler = historical_apis.Assign(ctx, method); handler != nil { return } return nil } func (d dummyassigner) Names() []string { - names := servicemux.Names() + names := servicemux.Names() hist_names := historical_apis.Names() - names = append(names,hist_names...) + names = append(names, hist_names...) sort.Strings(names) return names } - var bridge = jhttp.NewBridge(d, nil) + func translate_http_to_jsonrpc_and_vice_versa(w http.ResponseWriter, r *http.Request) { bridge.ServeHTTP(w, r) } diff --git a/cmd/rpc_examples/pong_server/pong_server.go b/cmd/rpc_examples/pong_server/pong_server.go index 68fa043..bb5114d 100644 --- a/cmd/rpc_examples/pong_server/pong_server.go +++ b/cmd/rpc_examples/pong_server/pong_server.go @@ -10,7 +10,7 @@ package main -//import "os" +import "os" import "fmt" import "time" import "crypto/sha1" @@ -18,6 +18,8 @@ import "crypto/sha1" import "etcd.io/bbolt" import "github.com/go-logr/logr" +import "gopkg.in/natefinch/lumberjack.v2" +import "github.com/deroproject/derohe/globals" // needed for logs import "github.com/deroproject/derohe/rpc" import "github.com/deroproject/derohe/walletapi" @@ -34,6 +36,7 @@ var expected_arguments = rpc.Arguments{ // { rpc.RPC_EXPIRY , rpc.DataTime, time.Now().Add(time.Hour).UTC()}, {rpc.RPC_COMMENT, rpc.DataString, "Purchase PONG"}, //{"float64", rpc.DataFloat64, float64(0.12345)}, // in atomic units + // {rpc.RPC_NEEDS_REPLYBACK_ADDRESS,rpc.DataUint64,uint64(0)}, // this service will reply to incoming request,so needs the senders address {rpc.RPC_VALUE_TRANSFER, rpc.DataUint64, uint64(12345)}, // in atomic units } @@ -53,6 +56,17 @@ var rpcClient = jsonrpc.NewClient("http://127.0.0.1:40403/json_rpc") func main() { var err error fmt.Printf("Pong Server to demonstrate RPC over dero chain.\n") + + // parse arguments and setup logging , print basic information + globals.Arguments["--debug"] = true + exename, _ := os.Executable() + globals.InitializeLog(os.Stdout, &lumberjack.Logger{ + Filename: exename + ".log", + MaxSize: 100, // megabytes + MaxBackups: 2, + }) + logger = globals.Logger + var addr *rpc.Address var addr_result rpc.GetAddress_Result err = rpcClient.CallFor(&addr_result, "GetAddress") @@ -152,17 +166,28 @@ func processing_thread(db *bbolt.DB) { logger.Error(nil, fmt.Sprintf("user transferred %d, we were expecting %d. so we will not do anything", e.Amount, value_expected)) // this is an unexpected situation continue } - // value received is what we are expecting, so time for response + /* if !e.Payload_RPC.Has(rpc.RPC_REPLYBACK_ADDRESS, rpc.DataAddress){ + logger.Error(nil, fmt.Sprintf("user has not give his address so we cannot replyback")) // this is an unexpected situation + continue + } + + destination_expected := e.Payload_RPC.Value(rpc.RPC_REPLYBACK_ADDRESS, rpc.DataAddress).(rpc.Address).String() + + logger.V(1).Info("tx should be replied", "txid", e.TXID,"replyback_address",destination_expected) + */ + destination_expected := e.Sender + + // value received is what we are expecting, so time for response response[0].Value = e.SourcePort // source port now becomes destination port, similar to TCP response[2].Value = fmt.Sprintf("Sucessfully purchased pong (could be serial, license or download link or anything).You sent %s at height %d", walletapi.FormatMoney(e.Amount), e.Height) //_, err := response.CheckPack(transaction.PAYLOAD0_LIMIT)) // we only have 144 bytes for RPC // sender of ping now becomes destination - var str string - tparams := rpc.Transfer_Params{Transfers: []rpc.Transfer{{Destination: e.Sender, Amount: uint64(1), Payload_RPC: response}}} - err = rpcClient.CallFor(&str, "Transfer", tparams) + var result rpc.Transfer_Result + tparams := rpc.Transfer_Params{Transfers: []rpc.Transfer{{Destination: destination_expected, Amount: uint64(1), Payload_RPC: response}}} + err = rpcClient.CallFor(&result, "Transfer", tparams) if err != nil { logger.Error(err, "err while transfer") continue @@ -173,9 +198,9 @@ func processing_thread(db *bbolt.DB) { return b.Put([]byte(e.TXID), []byte("done")) }) if err != nil { - logger.Error(err, "err updating db", err) + logger.Error(err, "err updating db") } else { - logger.Info("ping replied successfully with pong") + logger.Info("ping replied successfully with pong ", "result", result) } } diff --git a/config/version.go b/config/version.go index 9378c67..695567b 100644 --- a/config/version.go +++ b/config/version.go @@ -20,4 +20,4 @@ import "github.com/blang/semver/v4" // right now it has to be manually changed // do we need to include git commitsha?? -var Version = semver.MustParse("3.4.45-1.DEROHE.STARGATE+08112021") +var Version = semver.MustParse("3.4.46-1.DEROHE.STARGATE+08112021") diff --git a/globals/globals.go b/globals/globals.go index e1e1760..76fb493 100644 --- a/globals/globals.go +++ b/globals/globals.go @@ -93,7 +93,7 @@ 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{} +var Arguments = map[string]interface{}{} func InitNetwork() { Config = config.Mainnet // default is mainnnet diff --git a/rpc/rpc.go b/rpc/rpc.go index 8dcdfea..6d994e2 100644 --- a/rpc/rpc.go +++ b/rpc/rpc.go @@ -96,7 +96,7 @@ func (arg Argument) String() string { case DataHash: return fmt.Sprintf("Name:%s Type:%s Value:'%s'", arg.Name, arg.DataType, arg.Value) case DataAddress: - return fmt.Sprintf("Name:%s Type:%s Value:'%x'", arg.Name, arg.DataType, arg.Value) + return fmt.Sprintf("Name:%s Type:%s Value:'%s'", arg.Name, arg.DataType, arg.Value) case DataTime: return fmt.Sprintf("Name:%s Type:%s Value:'%s'", arg.Name, arg.DataType, arg.Value) @@ -195,7 +195,7 @@ func (args Arguments) MarshalBinary() (data []byte, err error) { case crypto.Hash: localmap[arg.Name+string(arg.DataType)] = v case Address: - localmap[arg.Name+string(arg.DataType)] = v + localmap[arg.Name+string(arg.DataType)] = v.PublicKey.EncodeCompressed() case string: localmap[arg.Name+string(arg.DataType)] = v case time.Time: @@ -352,11 +352,17 @@ func (args *Arguments) Sort() { } // some fields are already defined -const RPC_DESTINATION_PORT = "D" // mandatory,uint64, used for ID of type uint64 -const RPC_SOURCE_PORT = "S" // mandatory,uint64, used for ID -const RPC_VALUE_TRANSFER = "V" //uint64, this is representation and is only readable, value is never transferred -const RPC_COMMENT = "C" //optional,string, used for display MSG to user -const RPC_EXPIRY = "E" //optional,time used for Expiry for this service call +// TODO we need to define ABI here to use names also, we have a name service + +const RPC_DESTINATION_PORT = "D" // mandatory,uint64, used for ID of type uint64 +const RPC_SOURCE_PORT = "S" // mandatory,uint64, used for ID +const RPC_VALUE_TRANSFER = "V" //uint64, this is representation and is only readable, value is never transferred +const RPC_COMMENT = "C" //optional,string, used for display MSG to user +const RPC_EXPIRY = "E" //optional,time used for Expiry for this service call +const RPC_REPLYBACK_ADDRESS = "R" //this is mandatory this is an address,otherwise how will otherside respond +//RPC will include own address so as the other enc can respond + +const RPC_NEEDS_REPLYBACK_ADDRESS = "N" //optional, uint64 type argument_raw struct { Name string `json:"name"` // string name must be atleast 1 byte