Skip to content

Commit

Permalink
Add How to create a Crypto Coin example (#4)
Browse files Browse the repository at this point in the history
  • Loading branch information
andrejrakic authored Apr 19, 2022
1 parent 2070c6c commit 3a5b556
Show file tree
Hide file tree
Showing 12 changed files with 320 additions and 0 deletions.
2 changes: 2 additions & 0 deletions my-crypto-coin/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.idea
.vscode
41 changes: 41 additions & 0 deletions my-crypto-coin/cli/mcc/balances.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package main

import (
"github.com/spf13/cobra"
"my-crypto-coin/ledger"
"fmt"
"os"
)

func balancesCmd() *cobra.Command {
var balancesCmd = &cobra.Command{
Use: "balances",
Short: "Interact with balances (list...).",
Run: func(cmd *cobra.Command, args []string) {
},
}

balancesCmd.AddCommand(balancesListCmd)

return balancesCmd
}

var balancesListCmd = &cobra.Command{
Use: "list",
Short: "Lists all balances.",
Run: func(cmd *cobra.Command, args []string) {
state, err := ledger.SyncState()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
defer state.Close()

fmt.Println("Accounts balances:")
fmt.Println("__________________")
fmt.Println("")
for account, balance := range state.Balances {
fmt.Println(fmt.Sprintf("%s: %d", account, balance))
}
},
}
26 changes: 26 additions & 0 deletions my-crypto-coin/cli/mcc/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package main

import (
"github.com/spf13/cobra"
"os"
"fmt"
)

func main() {
var mccCmd = &cobra.Command{
Use: "mcc",
Short: "My Crypto Coin CLI",
Run: func(cmd *cobra.Command, args []string) {
},
}

mccCmd.AddCommand(versionCmd)
mccCmd.AddCommand(balancesCmd())
mccCmd.AddCommand(txCmd())

err := mccCmd.Execute()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
62 changes: 62 additions & 0 deletions my-crypto-coin/cli/mcc/tx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package main

import (
"github.com/spf13/cobra"
"my-crypto-coin/ledger"
"fmt"
"os"
)

func txCmd() *cobra.Command {
var txsCmd = &cobra.Command{
Use: "tx",
Short: "Interact with transactions (new...).",
Run: func(cmd *cobra.Command, args []string) {
},
}

txsCmd.AddCommand(newTxCmd())

return txsCmd
}


func newTxCmd() *cobra.Command {
var cmd = &cobra.Command{
Use: "new",
Short: "Adds new TX to the ledger.",
Run: func(cmd *cobra.Command, args []string) {
from, _ := cmd.Flags().GetString("from")
to, _ := cmd.Flags().GetString("to")
value, _ := cmd.Flags().GetUint("value")

tx := ledger.NewTx(ledger.NewAccount(from), ledger.NewAccount(to), value)

state, err := ledger.SyncState()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
defer state.Close()

err = state.WriteToLedger(tx)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}

fmt.Println("TX successfully added to the ledger.")
},
}

cmd.Flags().String("from", "", "From what account to send coins")
cmd.MarkFlagRequired("from")

cmd.Flags().String("to", "", "To what account to send coins")
cmd.MarkFlagRequired("to")

cmd.Flags().Uint("value", 0, "How many coins to send")
cmd.MarkFlagRequired("value")

return cmd
}
19 changes: 19 additions & 0 deletions my-crypto-coin/cli/mcc/version.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package main

import (
"fmt"
"github.com/spf13/cobra"
)

const Major = "0"
const Minor = "1"
const Fix = "0"
const Verbal = "Genesis"

var versionCmd = &cobra.Command{
Use: "version",
Short: "Describes version.",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(fmt.Sprintf("Version: %s.%s.%s-beta %s", Major, Minor, Fix, Verbal))
},
}
5 changes: 5 additions & 0 deletions my-crypto-coin/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module my-crypto-coin

go 1.16

require github.com/spf13/cobra v1.4.0 // indirect
10 changes: 10 additions & 0 deletions my-crypto-coin/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q=
github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g=
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.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
26 changes: 26 additions & 0 deletions my-crypto-coin/ledger/genesis.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package ledger

import (
"io/ioutil"
"encoding/json"
)

type Genesis struct {
Balances map[Account]uint `json:"balances"`
}

func loadGenesis(path string) (Genesis, error) {
genesisFileContent, err := ioutil.ReadFile(path)
if err != nil {
return Genesis{}, err
}

var loadedGenesis Genesis

err = json.Unmarshal(genesisFileContent, &loadedGenesis)
if err != nil {
return Genesis{}, err
}

return loadedGenesis, nil
}
7 changes: 7 additions & 0 deletions my-crypto-coin/ledger/genesis.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"genesis_time": "2022-04-12T00:00:00.000000000Z",
"chain_id": "our-blockchain",
"balances": {
"alice": 1000000
}
}
7 changes: 7 additions & 0 deletions my-crypto-coin/ledger/ledger.db
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{"from":"alice","to":"bob","value":10}
{"from":"alice","to":"alice","value":3}
{"from":"alice","to":"alice","value":500}
{"from":"alice","to":"bob","value":100}
{"from":"bob","to":"alice","value":5}
{"from":"bob","to":"carol","value":10}
{"from":"alice","to":"carol","value":10}
98 changes: 98 additions & 0 deletions my-crypto-coin/ledger/state.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package ledger

import (
"fmt"
"os"
"path/filepath"
"bufio"
"encoding/json"
)

type State struct {
Balances map[Account]uint
txMempool []Tx

dbFile *os.File
}


func SyncState() (*State, error) {
cwd, err := os.Getwd()
if err != nil {
return nil, err
}

gen, err := loadGenesis(filepath.Join(cwd, "ledger", "genesis.json"))
if err != nil {
return nil, err
}

balances := make(map[Account]uint)
for account, balance := range gen.Balances {
balances[account] = balance
}

file, err := os.OpenFile(filepath.Join(cwd, "ledger", "ledger.db"), os.O_APPEND|os.O_RDWR, 0600)
if err != nil {
return nil, err
}

scanner := bufio.NewScanner(file)

state := &State{balances, make([]Tx, 0), file}

for scanner.Scan() {
if err := scanner.Err(); err != nil {
return nil, err
}

var transaction Tx
json.Unmarshal(scanner.Bytes(), &transaction)

if err := state.writeTransaction(transaction); err != nil {
return nil, err
}
}

return state, nil
}

func (s *State) writeTransaction(tx Tx) error {
if s.Balances[tx.From] < tx.Value {
return fmt.Errorf("insufficient balance")
}

s.Balances[tx.From] -= tx.Value
s.Balances[tx.To] += tx.Value

return nil
}

func (s *State) Close() {
s.dbFile.Close()
}

func (s *State) WriteToLedger(tx Tx) error {
if err := s.writeTransaction(tx); err != nil {
return err
}

s.txMempool = append(s.txMempool, tx)

mempool := make([]Tx, len(s.txMempool))
copy(mempool, s.txMempool)

for i := 0; i < len(mempool); i++ {
txJson, err := json.Marshal(mempool[i])
if err != nil {
return err
}

if _, err = s.dbFile.Write(append(txJson, '\n')); err != nil {
return err
}
s.txMempool = s.txMempool[1:]
}

return nil
}
17 changes: 17 additions & 0 deletions my-crypto-coin/ledger/tx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package ledger

type Account string

type Tx struct {
From Account `json:"from"`
To Account `json:"to"`
Value uint `json:"value"`
}

func NewAccount(value string) Account {
return Account(value)
}

func NewTx(from Account, to Account, value uint) Tx {
return Tx{from, to, value}
}

0 comments on commit 3a5b556

Please sign in to comment.