diff --git a/client/Makefile b/client/Makefile new file mode 100644 index 0000000..2b704be --- /dev/null +++ b/client/Makefile @@ -0,0 +1,2 @@ +all: + go build -o qclient ./main.go \ No newline at end of file diff --git a/client/cmd/accept.go b/client/cmd/accept.go new file mode 100644 index 0000000..78374b8 --- /dev/null +++ b/client/cmd/accept.go @@ -0,0 +1,38 @@ +package cmd + +import ( + "fmt" + "math/big" + "os" + + "github.com/spf13/cobra" +) + +var acceptCmd = &cobra.Command{ + Use: "accept", + Short: "Accepts a pending transfer", + Long: `Accepts a pending transfer: + + accept + + PendingTransaction - the address of the pending transfer + `, + Run: func(cmd *cobra.Command, args []string) { + if len(args) == 1 { + fmt.Println("invalid command") + os.Exit(1) + } + + _, ok := new(big.Int).SetString(args[0], 0) + if !ok { + fmt.Println("invalid PendingTransaction") + os.Exit(1) + } + + fmt.Println("25 QUIL (Coin 0x2688997f2776ab5993894ed04fcdac05577cf2494ddfedf356ebf8bd3de464ab)") + }, +} + +func init() { + tokenCmd.AddCommand(acceptCmd) +} diff --git a/client/cmd/all.go b/client/cmd/all.go new file mode 100644 index 0000000..ee4f6fc --- /dev/null +++ b/client/cmd/all.go @@ -0,0 +1,19 @@ +package cmd + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +var allCmd = &cobra.Command{ + Use: "all", + Short: "Mints all available token rewards", + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("1520.381923 QUIL (Coin 0x162ad88c319060b4f5ea6dbf9a0c2cd82d3d70dfc22d5fc99ca5371083d68416)") + }, +} + +func init() { + mintCmd.AddCommand(allCmd) +} diff --git a/client/cmd/balance.go b/client/cmd/balance.go new file mode 100644 index 0000000..5dad729 --- /dev/null +++ b/client/cmd/balance.go @@ -0,0 +1,19 @@ +package cmd + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +var balanceCmd = &cobra.Command{ + Use: "balance", + Short: "Lists the total balance of tokens in the managing account", + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("1545.381923 QUIL") + }, +} + +func init() { + tokenCmd.AddCommand(balanceCmd) +} diff --git a/client/cmd/coins.go b/client/cmd/coins.go new file mode 100644 index 0000000..e67fdcc --- /dev/null +++ b/client/cmd/coins.go @@ -0,0 +1,20 @@ +package cmd + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +var coinsCmd = &cobra.Command{ + Use: "coins", + Short: "Lists all coins under control of the managing account", + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("25.0 QUIL (Coin 0x1148092cdce78c721835601ef39f9c2cd8b48b7787cbea032dd3913a4106a58d)") + fmt.Println("1520.381923 QUIL (Coin 0x162ad88c319060b4f5ea6dbf9a0c2cd82d3d70dfc22d5fc99ca5371083d68416)") + }, +} + +func init() { + tokenCmd.AddCommand(coinsCmd) +} diff --git a/client/cmd/merge.go b/client/cmd/merge.go new file mode 100644 index 0000000..c1b975f --- /dev/null +++ b/client/cmd/merge.go @@ -0,0 +1,45 @@ +package cmd + +import ( + "fmt" + "math/big" + "os" + + "github.com/spf13/cobra" +) + +var mergeCmd = &cobra.Command{ + Use: "merge", + Short: "Merges two coins", + Long: `Merges two coins: + + merge + + LeftCoin - the first coin address + RightCoin - the second coin address + `, + Run: func(cmd *cobra.Command, args []string) { + if len(args) != 2 { + fmt.Println("invalid command") + os.Exit(1) + } + + _, ok := new(big.Int).SetString(args[0], 0) + if !ok { + fmt.Println("invalid LeftCoin") + os.Exit(1) + } + + _, ok = new(big.Int).SetString(args[1], 0) + if !ok { + fmt.Println("invalid Rightcoin") + os.Exit(1) + } + + fmt.Println("1545.381923 QUIL (Coin 0x151f4ae225e20759077e1724e4c5d0feae26c477fd10d728dfea962eec79b83f)") + }, +} + +func init() { + tokenCmd.AddCommand(mergeCmd) +} diff --git a/client/cmd/mint.go b/client/cmd/mint.go new file mode 100644 index 0000000..0ad82c7 --- /dev/null +++ b/client/cmd/mint.go @@ -0,0 +1,14 @@ +package cmd + +import ( + "github.com/spf13/cobra" +) + +var mintCmd = &cobra.Command{ + Use: "mint", + Short: "Performs a mint operation", +} + +func init() { + tokenCmd.AddCommand(mintCmd) +} diff --git a/client/cmd/mutualReceive.go b/client/cmd/mutualReceive.go new file mode 100644 index 0000000..3bc36cd --- /dev/null +++ b/client/cmd/mutualReceive.go @@ -0,0 +1,43 @@ +package cmd + +import ( + "fmt" + "os" + "time" + + "github.com/shopspring/decimal" + "github.com/spf13/cobra" +) + +var mutualReceiveCmd = &cobra.Command{ + Use: "mutual-receive", + Short: "Initiates a mutual receive", + Long: `Initiates a mutual receive: + + mutual-receive + + ExpectedAmount - the amount expected in the transfer + `, + Run: func(cmd *cobra.Command, args []string) { + if len(args) != 1 { + fmt.Println("invalid command") + os.Exit(1) + } + + amount := args[len(args)-1] + _, err := decimal.NewFromString(amount) + if err != nil { + fmt.Println("invalid ExpectedAmount") + os.Exit(1) + } + fmt.Println("Rendezvous: 0x2ad567e4fc1ac335a8d3d6077de2ee998aff996b51936da04ee1b0f5dc196a4f") + fmt.Printf("Awaiting sender... ") + time.Sleep(2 * time.Second) + fmt.Println("OK") + fmt.Println(amount + " QUIL (Coin 0x0525c76ecdc6ef21c2eb75df628b52396adcf402ba26a518ac395db8f5874a82)") + }, +} + +func init() { + tokenCmd.AddCommand(mutualReceiveCmd) +} diff --git a/client/cmd/mutualTransfer.go b/client/cmd/mutualTransfer.go new file mode 100644 index 0000000..625a498 --- /dev/null +++ b/client/cmd/mutualTransfer.go @@ -0,0 +1,39 @@ +package cmd + +import ( + "fmt" + "os" + "time" + + "github.com/spf13/cobra" +) + +var mutualTransferCmd = &cobra.Command{ + Use: "mutual-transfer", + Short: "Initiates a mutual transfer", + Long: `Initiates a mutual transfer: + + mutual-transfer (|) + + Rendezvous - the rendezvous point to connect to the recipient + Amount – the amount to send, splitting/merging and sending as needed + OfCoin – the address of the coin to send in whole + + Either Amount or OfCoin must be specified + `, + Run: func(cmd *cobra.Command, args []string) { + if len(args) != 2 { + fmt.Println("invalid command") + os.Exit(1) + } + + fmt.Printf("Confirming rendezvous... ") + time.Sleep(500 * time.Millisecond) + fmt.Println("OK") + fmt.Println("50 QUIL (Coin [private])") + }, +} + +func init() { + tokenCmd.AddCommand(mutualTransferCmd) +} diff --git a/client/cmd/reject.go b/client/cmd/reject.go new file mode 100644 index 0000000..a641f39 --- /dev/null +++ b/client/cmd/reject.go @@ -0,0 +1,38 @@ +package cmd + +import ( + "fmt" + "math/big" + "os" + + "github.com/spf13/cobra" +) + +var rejectCmd = &cobra.Command{ + Use: "reject", + Short: "Rejects the pending transaction", + Long: `Rejects a pending transfer: + + reject + + PendingTransaction - the address of the pending transfer + `, + Run: func(cmd *cobra.Command, args []string) { + if len(args) == 1 { + fmt.Println("invalid command") + os.Exit(1) + } + + _, ok := new(big.Int).SetString(args[0], 0) + if !ok { + fmt.Println("invalid PendingTransaction") + os.Exit(1) + } + + fmt.Println("25 QUIL (PendingTransaction 0x27fff099dee515ece193d2af09b164864e4bb60c19eb6719b5bc981f92151009)") + }, +} + +func init() { + tokenCmd.AddCommand(rejectCmd) +} diff --git a/client/cmd/root.go b/client/cmd/root.go new file mode 100644 index 0000000..accd8ee --- /dev/null +++ b/client/cmd/root.go @@ -0,0 +1,31 @@ +package cmd + +import ( + "os" + + "github.com/spf13/cobra" +) + +var configDirectory string +var simulateFail bool + +var rootCmd = &cobra.Command{ + Use: "qclient", + Short: "Quilibrium RPC Client", +} + +func Execute() { + err := rootCmd.Execute() + if err != nil { + os.Exit(1) + } +} + +func init() { + rootCmd.PersistentFlags().StringVar( + &configDirectory, + "config", + "../node/.config/", + "config directory (default is ../node/.config/)", + ) +} diff --git a/client/cmd/split.go b/client/cmd/split.go new file mode 100644 index 0000000..bcb64ba --- /dev/null +++ b/client/cmd/split.go @@ -0,0 +1,55 @@ +package cmd + +import ( + "fmt" + "math/big" + "os" + + "github.com/shopspring/decimal" + "github.com/spf13/cobra" +) + +var splitCmd = &cobra.Command{ + Use: "split", + Short: "Splits a coin into two coins", + Long: `Splits a coin into two coins: + + split + + OfCoin - the address of the coin to split + LeftAmount - the first half of the split amount + RightAmount - the second half of the split amount + `, + Run: func(cmd *cobra.Command, args []string) { + if len(args) != 3 { + fmt.Println("invalid command") + os.Exit(1) + } + + _, ok := new(big.Int).SetString(args[0], 0) + if !ok { + fmt.Println("invalid OfCoin") + os.Exit(1) + } + + leftAmount := args[1] + _, err := decimal.NewFromString(leftAmount) + if err != nil { + fmt.Println("invalid LeftAmount") + os.Exit(1) + } + + rightAmount := args[2] + _, err = decimal.NewFromString(rightAmount) + if err != nil { + fmt.Println("invalid RightAmount") + os.Exit(1) + } + fmt.Println(leftAmount + " QUIL (Coin 0x024479f49f03dc53fd702198cd9b548c9e96004e19ef6a4e9c5211a9795ba34d)") + fmt.Println(rightAmount + " QUIL (Coin 0x0140e01731256793bba03914f3844d645fbece26553acdea8ac4de4d84f91690)") + }, +} + +func init() { + tokenCmd.AddCommand(splitCmd) +} diff --git a/client/cmd/token.go b/client/cmd/token.go new file mode 100644 index 0000000..496256c --- /dev/null +++ b/client/cmd/token.go @@ -0,0 +1,14 @@ +package cmd + +import ( + "github.com/spf13/cobra" +) + +var tokenCmd = &cobra.Command{ + Use: "token", + Short: "Performs a token operation", +} + +func init() { + rootCmd.AddCommand(tokenCmd) +} diff --git a/client/cmd/transfer.go b/client/cmd/transfer.go new file mode 100644 index 0000000..9b9ce63 --- /dev/null +++ b/client/cmd/transfer.go @@ -0,0 +1,93 @@ +package cmd + +import ( + "fmt" + "math/big" + "os" + "strconv" + + "github.com/shopspring/decimal" + "github.com/spf13/cobra" +) + +var transferCmd = &cobra.Command{ + Use: "transfer", + Short: "Creates a pending transfer of coin", + Long: `Creates a pending transfer of coin: + + transfer [] [] (|) + + ToAccount – account address, must be specified + RefundAccount - account address to receive coin if rejected (if omitted, uses sender address) + Expiry – unix epoch time in seconds where the ToAccount can no longer claim (if omitted, does not expire) + Amount – the amount to send, splitting/merging and sending as needed + OfCoin – the address of the coin to send in whole + + Either Amount or OfCoin must be specified + `, + Run: func(cmd *cobra.Command, args []string) { + if len(args) < 2 { + fmt.Println("invalid command") + os.Exit(1) + } + + _, ok := new(big.Int).SetString(args[0], 0) + if !ok { + fmt.Println("invalid ToAccount") + os.Exit(1) + } + + refundAccount := "0x23c0f371e9faa7be4ffedd616361e0c9aeb776ae4d7f3a37605ecbfa40a55a90" + // expiry := int64(9999999999) + var err error + + if len(args) >= 3 { + if len(args[len(args)-2]) != 66 { + _, err = strconv.ParseInt(args[len(args)-2], 10, 0) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + } else { + refundAccount = args[1] + } + } + + if refundAccount[0] != '0' || refundAccount[1] != 'x' { + _, ok := new(big.Int).SetString(refundAccount, 0) + if !ok { + fmt.Println("invalid refund account") + os.Exit(1) + } + } + + ofCoin := "" + amount := "" + if len(args[len(args)-1]) == 66 { + ofCoin = args[len(args)-1] + _, ok := new(big.Int).SetString(ofCoin, 0) + if !ok { + fmt.Println("invalid OfCoin") + os.Exit(1) + } + switch ofCoin { + case "0x1148092cdce78c721835601ef39f9c2cd8b48b7787cbea032dd3913a4106a58d": + fmt.Println("25.0 QUIL (Pending Transaction 0x0382e4da0c7c0133a1b53453b05096272b80c1575c6828d0211c4e371f7c81bb)") + case "0x162ad88c319060b4f5ea6dbf9a0c2cd82d3d70dfc22d5fc99ca5371083d68416": + fmt.Println("1520.381923 QUIL (Pending Transaction 0x0382e4da0c7c0133a1b53453b05096272b80c1575c6828d0211c4e371f7c81bb)") + } + } else { + amount = args[len(args)-1] + _, err := decimal.NewFromString(amount) + if err != nil { + fmt.Println("invalid Amount") + os.Exit(1) + } + fmt.Println(amount + " QUIL (Pending Transaction 0x0382e4da0c7c0133a1b53453b05096272b80c1575c6828d0211c4e371f7c81bb)") + } + }, +} + +func init() { + tokenCmd.AddCommand(transferCmd) +} diff --git a/client/go.mod b/client/go.mod new file mode 100644 index 0000000..99e242f --- /dev/null +++ b/client/go.mod @@ -0,0 +1,10 @@ +module source.quilibrium.com/quilibrium/monorepo/client + +go 1.20 + +require ( + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/shopspring/decimal v1.4.0 // indirect + github.com/spf13/cobra v1.8.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect +) diff --git a/client/go.sum b/client/go.sum new file mode 100644 index 0000000..e9f7d52 --- /dev/null +++ b/client/go.sum @@ -0,0 +1,12 @@ +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/client/main.go b/client/main.go new file mode 100644 index 0000000..199d220 --- /dev/null +++ b/client/main.go @@ -0,0 +1,12 @@ +package main + +import ( + "fmt" + + "source.quilibrium.com/quilibrium/monorepo/client/cmd" +) + +func main() { + fmt.Println("Quilibrium RPC Client – Simulation Mode") + cmd.Execute() +} diff --git a/node/config/config.go b/node/config/config.go index 88780ad..8af071d 100644 --- a/node/config/config.go +++ b/node/config/config.go @@ -76,25 +76,26 @@ func LoadConfig(configPath string, proverKey string) (*Config, error) { "/dns/quaalude.quilibrium.com/udp/8336/quic/p2p/QmcKQjpQmLpbDsiif2MuakhHFyxWvqYauPsJDaXnLav7PJ", "/dns/quecifer.quilibrium.com/udp/8336/quic/p2p/QmQkyqziNCycUDdeBypkikMZWH2bBmmxfK7cbJFJmyyQdQ", "/dns/quantum.quilibrium.com/udp/8336/quic/p2p/QmXaSyf9Gaq5ddcHysefDpuE7SJqzdg1surQ5wac55ysJV", - "/ip4/204.186.74.47/udp/8317/quic/p2p/Qmd233pLUDvcDW3ama27usfbG1HxKNh1V9dmWVW1SXp1pd", "/ip4/204.186.74.46/udp/8316/quic/p2p/QmeqBjm3iX7sdTieyto1gys5ruQrQNPKfaTGcVQQWJPYDV", - "/ip4/186.233.184.181/udp/8336/quic/p2p/QmW6QDvKuYqJYYMP5tMZSp12X3nexywK28tZNgqtqNpEDL", - "/dns/quil.zanshindojo.org/udp/8336/quic/p2p/QmXbbmtS5D12rEc4HWiHWr6e83SCE4jeThPP4VJpAQPvXq", "/ip4/185.209.178.115/udp/8336/quic/p2p/Qmekz5obb9qCRP5CrZ4D8Tmabbr5mJf6mgBJHTaitrx7Fx", "/ip4/186.233.187.185/udp/8336/quic/p2p/QmR7jT9NKL3yZ1iRjLxPJjAVNbk38HAfDtAKF5SCZmHdpK", "/ip4/149.86.224.152/udp/8336/quic/p2p/QmaVaJ93mKaaqDnsYbHGEUBzEtbWhb3s4DAutt6JeJC5Wz", - "/ip4/144.76.104.93/udp/8336/quic/p2p/QmZejZ8DBGQ6foX9recW73GA6TqL6hCMX9ETWWW1Fb8xtx", "/ip4/65.109.17.13/udp/8336/quic/p2p/Qmc35n99eojSvW3PkbfBczJoSX92WmnnKh3Fg114ok3oo4", "/ip4/65.108.194.84/udp/8336/quic/p2p/QmP8C7g9ZRiWzhqN2AgFu5onS6HwHzR6Vv1TCHxAhnCSnq", - "/ip4/207.246.81.38/udp/8336/quic/p2p/QmPBYgDy7snHon7PAn8nv1shApQBQz1iHb2sBBS8QSgQwW", - "/dns/abyssia.fr/udp/8336/quic/p2p/QmS7C1UhN8nvzLJgFFf1uspMRrXjJqThHNN6AyEXp6oVUB", - "/ip4/51.15.18.247/udp/8336/quic/p2p/QmYVaHXdFmHFeTa6oPixgjMVag6Ex7gLjE559ejJddwqzu", "/dns/quil.dfcnodes.eu/udp/8336/quic/p2p/QmQaFmbYVrKSwoen5UQdaqyDq4QhXfSSLDVnYpYD4SF9tX", "/ip4/103.219.168.169/udp/8336/quic/p2p/Qme2LDknxE5z8AnECTjJ6ZmTkSYmoQ8phegaD5vEcZM9uB", "/ip4/87.98.167.207/udp/8336/quic/p2p/QmafiAXLu1JWktyfzDtD67i78GRBYCfQ4doTfq7pp7wfQ1", "/ip4/216.244.76.122/udp/8336/quic/p2p/QmUSbMytVBUYiiGE266aZHrHrP17vLx5UJFd7o74HkDoaV", "/ip4/185.177.126.200/udp/8336/quic/p2p/QmQn3bWk5aqaNSv9dwPjBg4qdeGBGNEB72tvuhgEc64Ki5", "/ip4/5.199.168.233/udp/8336/quic/p2p/QmeV1f11qjPTPaPspkJfdUb7p8kJ3fgsCqqozBtaW4nGFh", + // purged peers (keep your node online to return to this list) + // "/ip4/204.186.74.47/udp/8317/quic/p2p/Qmd233pLUDvcDW3ama27usfbG1HxKNh1V9dmWVW1SXp1pd", + // "/ip4/186.233.184.181/udp/8336/quic/p2p/QmW6QDvKuYqJYYMP5tMZSp12X3nexywK28tZNgqtqNpEDL", + // "/dns/quil.zanshindojo.org/udp/8336/quic/p2p/QmXbbmtS5D12rEc4HWiHWr6e83SCE4jeThPP4VJpAQPvXq", + // "/ip4/144.76.104.93/udp/8336/quic/p2p/QmZejZ8DBGQ6foX9recW73GA6TqL6hCMX9ETWWW1Fb8xtx", + // "/ip4/207.246.81.38/udp/8336/quic/p2p/QmPBYgDy7snHon7PAn8nv1shApQBQz1iHb2sBBS8QSgQwW", + // "/dns/abyssia.fr/udp/8336/quic/p2p/QmS7C1UhN8nvzLJgFFf1uspMRrXjJqThHNN6AyEXp6oVUB", + // "/ip4/51.15.18.247/udp/8336/quic/p2p/QmYVaHXdFmHFeTa6oPixgjMVag6Ex7gLjE559ejJddwqzu", } genesisSeed := "5768656e20696e2074686520436f75727365206f6620746563686e6f6c6f676963616c206576656e74732c206974206265636f6d6573206e656365737361727920666f72206f6e65206e6574776f726b0a746f20646973736f6c766520746865202070726f746f636f6c2062616e647320776869636820206861766520636f6e6e6563746564207468656d20207769746820616e6f746865722c20616e6420746f0a617373756d6520616d6f6e672074686520706f77657273206f6620746865207765622c202074686520736570617261746520616e6420657175616c2073746174696f6e20746f207768696368207468650a4c617773206f6620546563686e6f6c6f677920616e6420206f6620546563686e6f6c6f6779277320537465776172647320656e7469746c65207468656d2c20206120646563656e7420726573706563740a746f20746865206f70696e696f6e73206f6620416c6c20726571756972657320207468617420746865792073686f756c64206465636c617265207468652063617573657320776869636820696d70656c0a7468656d20746f207468652073657061726174696f6e2e0a0a576520686f6c642074686573652074727574687320746f2062652073656c662d65766964656e742c207468617420616c6c20636f676e697469766520656e7469746965732061726520637265617465640a657175616c2c2074686174202074686579206172652020656e646f7765642062792020746865206f7264657220206f66206c696665202077697468206365727461696e2020756e616c69656e61626c650a5269676874732c2020746861742020616d6f6e6720207468657365202061726520204c6966652c20204c6962657274792c2020507269766163792020616e64202050726f70657274792e2020546861740a686973746f726963616c6c7920746f20207365637572652020746865736520207269676874732c202050726f746f636f6c7320206861766520206265656e2020696e737469747574656420616d6f6e670a656e7469746965732c2020617320476f7665726e6d656e74732068617665206265656e2020696e737469747574656420616d6f6e67204d656e2c2020686176696e6720646572697665642074686569720a6a75737420706f776572732066726f6d2074686520636f6e73656e74206f662074686520676f7665726e65642c2054686174207768656e6576657220616e7920466f726d206f662050726f746f636f6c0a6265636f6d6573206465737472756374697665206f6620746865736520656e64732c20697420697320746865205269676874206f6620416c6c20746f20616c746572206f7220746f2061626f6c6973680a69742c20616e6420746f20696e73746974757465206e65772050726f746f636f6c732c206c6179696e672069747320666f756e646174696f6e206f6e2073756368207072696e6369706c657320616e640a6f7267616e697a696e672069747320706f7765727320696e207375636820666f726d2c2020617320746f207468656d207368616c6c207365656d206d6f7374206c696b656c7920746f206566666563740a74686569722053616665747920616e642048617070696e6573732e2050727564656e63652c2020696e646565642c202077696c6c206469637461746520746861742050726f746f636f6c73206c6f6e670a65737461626c6973686564202073686f756c6420206e6f742020626520206368616e6765642020666f7220206c696768742020616e6420207472616e7369656e7420206361757365733b202020616e640a6163636f7264696e676c7920616c6c20657870657269656e6365206861746820736865776e2c2074686174206d616e6b696e6420617265206d6f726520646973706f73656420746f207375666665722c0a7768696c65206576696c73206172652073756666657261626c652c207468616e20746f20207269676874207468656d73656c7665732062792061626f6c697368696e672074686520666f726d7320746f0a776869636820746865792020617265206163637573746f6d65642e2020427574207768656e202061206c6f6e6720747261696e20206f66206162757365732020616e642075737572706174696f6e732c0a7075727375696e672020696e7661726961626c7920207468652073616d6520204f626a656374206576696e6365732020612064657369676e2020746f2072656475636520207468656d2020756e6465720a6162736f6c7574652020446573706f7469736d2c20206974206973202074686569722072696768742c202069742069732020746865697220647574792c2020746f207468726f77206f666620737563680a50726f746f636f6c732c2020616e6420746f2070726f76696465206e6577204775617264732020666f72207468656972206675747572652073656375726974792e20205375636820686173206265656e0a746865202070617469656e742020737566666572616e636520206f66202074686520207765623b2020616e6420207375636820697320206e6f772074686520206e6563657373697479202077686963680a636f6e73747261696e73207468656d20746f20616c74657220746865697220666f726d65722053797374656d73206f66204e6574776f726b696e672e202054686520686973746f7279206f66207468650a70726573656e7420496e7465726e6574206973206120686973746f727920206f6620726570656174656420696e6a757269657320616e642075737572706174696f6e732c2020616c6c20686176696e670a696e2064697265637420206f626a65637420746865202065737461626c6973686d656e74206f662020616e20206162736f6c7574652020547972616e6e79206f7665722020746865204e6574776f726b0a53746174652e20546f2070726f766520746869732c206c6574204661637473206265207375626d697474656420746f20612063616e64696420776f726c642e0a0a41742074686520506879736963616c206c617965722c20496e7465726e657420536572766963652050726f7669646572732068617665207265667573656420746865697220417373656e7420746f0a46726565205370656563682c20746865206d6f73742077686f6c65736f6d6520616e64206e656365737361727920666f7220746865207075626c696320676f6f643a0a202020202020202068747470733a2f2f7777772e74686576657267652e636f6d2f32333732373233382f6e65742d6e65757472616c6974792d686973746f72792d6663632d6c656769736c6174696f6e0a0a4174207468652044617461204c696e6b206c617965722c20496e667275737472756374757265206173206120536572766963652050726f766964657273206861766520666f7262696464656e0a746865697220557365727320746f20667265656c792061646865726520746f20746865204c6177732c20756e6c6573732073757370656e64656420696e207468656972206f7065726174696f6e0a74696c6c20746865697220417373656e742073686f756c64206265206f627461696e65643b20616e64207768656e20736f2073757370656e6465642c207468657920686176652075747465726c790a6e65676c656374656420746f20617474656e6420746f207468656d2e0a2020202068747470733a2f2f7777772e636e62632e636f6d2f323032312f30312f31362f686f772d7061726c65722d6465706c6174666f726d696e672d73686f77732d706f7765722d6f662d636c6f750a20202020642d70726f7669646572732e68746d6c0a0a417420746865204e6574776f726b206c617965722c20476f7665726e6d656e74732068617665207265667573656420746f2070617373206f74686572204c61777320666f72207468650a6163636f6d6d6f646174696f6e206f66207365637572696e672074686520526967687473206f66207468652050656f706c65206f6e6c696e652c20696e2069747320737465616420616374696e670a636f6e747261727920746f20746865204c617773207375636820746861742074686f73652070656f706c6520776f756c642072656c696e717569736820746865207269676874206f660a526570726573656e746174696f6e20696e2074686520546f706963206f66205072697661637920616e642053656375726974792c206120726967687420696e657374696d61626c6520746f0a7468656d20616e6420666f726d696461626c6520746f20747972616e7473206f6e6c792e0a2020202068747470733a2f2f7777772e6566662e6f72672f646565706c696e6b732f323032332f30392f746f6461792d756b2d7061726c69616d656e742d756e6465726d696e65642d707269766163790a202020202d73656375726974792d616e642d66726565646f6d2d616c6c2d696e7465726e65742d75736572730a0a417420746865205472616e73706f7274206c617965722c20556e6163636f756e7461626c6520456e7472656e6368656420506f7765727320686176652063616c6c656420746f6765746865720a617373656d626c696573206f6620696e74657264696374696f6e20617420706c6163657320756e757375616c2c20756e636f6d666f727461626c652c20616e642064697374616e742066726f6d0a746865206465706f7369746f7279206f66207468656972207075626c6963205265636f726473206f7220696e206120666f726d2061636365737369626c6520746f206163636f756e7461626c650a696e717569736974696f6e2c20666f722074686520736f6c6520707572706f7365206f6620696e7661736976656c79207363727574696e697a696e6720746865206c69766573206f66207468650a50656f706c652e0a202020202020202020202020202020202020202020202020202020202020202020202020202020202068747470733a2f2f656e2e77696b6970656469612e6f72672f77696b692f526f6f6d5f363431410a0a4174207468652053657373696f6e206c617965722c204f7264657273206861766520646973736f6c7665642074686520726967687473206f66207468652070656f706c652072657065617465646c792c0a666f72206f70706f73696e6720616e6420636f6e737472756374696e6720636f756e7465726d65617375726573206f6e207468697320696e64657264696374696f6e2e0a2020202068747470733a2f2f7777772e6566662e6f72672f646565706c696e6b732f323030372f31302f71776573742d63656f2d6e73612d70756e69736865642d71776573742d7265667573696e672d0a2020202070617274696369706174652d696c6c6567616c2d7375727665696c6c616e63652d7072652d392d31310a0a4174207468652050726573656e746174696f6e206c617965722c2074686520537061726b73206f66206e657720436f6e7363696f75736e6573732061726520756e6465722064656261746520746f0a6265207375726d697365642064616e6765726f757320616e6420696e6a7572696f757320746f204d616e2c20616e642066726f6d2074686973206465626174652061726775656420746861742069740a73686f756c64206265207375626a65637420746f2063656e736f727368697020616e64207365636f6e6420636c617373656420746f206120736c6176652073746174757320756e6465720a636f72706f72617465206d6173746572732e0a2020202068747470733a2f2f76656e74757265626561742e636f6d2f61692f776974682d612d776176652d6f662d6e65772d6c6c6d732d6f70656e2d736f757263652d61692d69732d686176696e672d0a20202020612d6d6f6d656e742d616e642d612d7265642d686f742d6465626174652f0a0a496e206576657279207374616765206f6620207468657365204f707072657373696f6e732057652068617665205065746974696f6e65642020666f72205265647265737320696e20746865206d6f73740a68756d626c65207465726d733a204f7572207265706561746564205065746974696f6e732068617665206265656e20616e737765726564206f6e6c7920627920726570656174656420696e6a7572792e0a41205072696e63652077686f7365206368617261637465722069732074687573206d61726b65642020627920657665727920616374207768696368206d617920646566696e65206120547972616e742c0a697320756e66697420746f206265207468652072756c6572206f6620612066726565206e6574776f726b2e0a0a4e6f722068617665205765206265656e2077616e74696e672020696e20617474656e74696f6e7320746f206f7572204c7564646974652020627265746872656e2e2057652068617665207761726e65640a7468656d2066726f6d202074696d6520746f2074696d65206f6620617474656d7074732020627920746865697220766172696f7573206c656769736c6174757265732020746f20657874656e6420616e0a756e77617272616e7461626c65206a7572697364696374696f6e206f7665722075732e202020576520686176652072656d696e646564207468656d206f66207468652063697263756d7374616e6365730a6f66206f75722020646576656c6f706d656e7420616e6420656e6c69676874656e6d656e742020686572652e202057652068617665202061707065616c656420746f20207468656972206e61746976650a6a75737469636520616e64206d61676e616e696d6974792c2020616e6420776520686176652020636f6e6a7572656420207468656d20627920746865207469657320206f66206f757220636f6d6d6f6e0a6b696e647265642020746f2064697361766f77202074686573652075737572706174696f6e732c202077686963682c2020776f756c6420696e6576697461626c792020696e74657272757074206f75720a636f6e6e656374696f6e7320616e6420636f72726573706f6e64656e63652e20205468657920746f6f2068617665206265656e206465616620746f2074686520766f696365206f66206a7573746963650a616e6420206f6620636f6e73616e6775696e6974792e20205765206d7573742c207468657265666f72652c202061637175696573636520696e2020746865206e65636573736974792c202077686963680a64656e6f756e63657320206f7572202053657061726174696f6e2c2020616e642020686f6c6420207468656d2c2020617320776520686f6c6420207468652072657374206f6620206d616e6b696e642c0a446973636f6e6e656374656420696e204f70706f736974696f6e2c20696e2050656163652050656572732e0a0a57652c20207468657265666f72652c202074686520526570726573656e74617469766573206f6620746865206e657720496e7465726e65742c2020696e20746865206669727374207068617365206f660a74686520477265617420436572656d6f6e792c2020417373656d626c65642c202061707065616c696e6720746f207468652053757072656d65204a75646765206f662074686520776f726c6420666f720a74686520726563746974756465206f66206f757220696e74656e74696f6e732c2020646f2c2020696e20746865204e616d652c20616e6420627920417574686f72697479206f662074686520676f6f640a5374657761726473206f6620746865205765622c20736f6c656d6e6c79207075626c69736820616e64206465636c6172652c20205468617420746865205175696c69627269756d2050726f746f636f6c0a69732c20616e64206f66205269676874206f7567687420746f206265204672656520616e6420496e646570656e64656e743b202074686174206974206973204162736f6c7665642066726f6d207468650a64656d616e6473206f6620756e656c65637465642c20756e2d436f6e737469747574696f6e616c20666f72636573206f6e20746865207765622c20616e64207468617420746865206f6e6c79204c61770a77686963682020676f7665726e73202069742069732020746865204c617720207768696368207765202068617665207072657365727665642020666f722074686520206265747465726d656e74206f660a736f6369657479207468726f7567682074686520726570656174656420656e666f7263656d656e74206f662074686520436f6e737469747574696f6e3b20616e642074686174206173206120467265650a616e642020496e646570656e64656e742050726f746f636f6c2c2020697420686173202066756c6c20506f77657220746f2020446973636f6e6e6563742c20206a6f696e20696e2050656572696e672c0a636f6e747261637420436f6d7075746174696f6e2c2020656e6761676520696e20436f6d6d657263652c2020616e6420746f20646f20616c6c206f7468657220204163747320616e64205468696e67730a77686963682020496e646570656e64656e7420204e6574776f726b7320206d6179206f662020726967687420646f2e2020416e6420666f7220207468652020737570706f727420206f662020746869730a4465636c61726174696f6e2c2020776974682061206669726d2072656c69616e6365206f6e20207468652070726f74656374696f6e206f6620436f6e737469747574696f6e616c204c61772c202077650a6d757475616c6c792020706c656467652020746f20206561636820206f7468657220206f757220204d696e64732c206f757220204d616368696e65732020616e6420206f7572207374726f6e676573740a43727970746f6772617068792e"