-
Notifications
You must be signed in to change notification settings - Fork 92
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add How to create a Crypto Coin example (#4)
- Loading branch information
1 parent
2070c6c
commit 3a5b556
Showing
12 changed files
with
320 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
.idea | ||
.vscode |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) | ||
} | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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} | ||
} |