This commit is contained in:
number571 2024-03-31 19:22:20 +07:00
parent 07df6d212f
commit e80b91c176
13 changed files with 3401 additions and 6 deletions

12
.gitignore vendored
View File

@ -17,9 +17,9 @@ contracts/Contract.go
build/WorldSkills.abi
build/WorldSkills.bin
abigen
!./cmd/node
!./cmd/client
!./cmd/client_eth
!./cmd/gclient
!./cmd/gclient_eth
!./cmd/deploy
!cmd/node
!cmd/client
!cmd/client_eth
!cmd/gclient
!cmd/gclient_eth
!cmd/deploy

240
cmd/client/client.go Normal file
View File

@ -0,0 +1,240 @@
package main
import (
bc "blockchain-from-scratch/blockchain"
"bufio"
"encoding/json"
"fmt"
"os"
"strconv"
"strings"
nt "blockchain-from-scratch/network"
)
func init() {
if len(os.Args) < 2 {
panic("failed: len(os.Args) < 2")
}
var (
addrStr = ""
userNewStr = ""
userLoadStr = ""
)
var (
addrExist = false
userNewExist = false
userLoadExist = false
)
for i := 1; i < len(os.Args); i++ {
arg := os.Args[i]
switch {
case strings.HasPrefix(arg, "-loadaddr:"):
addrStr = strings.Replace(arg, "-loadaddr:", "", 1)
addrExist = true
case strings.HasPrefix(arg, "-newuser:"):
userNewStr = strings.Replace(arg, "-newuser:", "", 1)
userNewExist = true
case strings.HasPrefix(arg, "-loaduser:"):
userLoadStr = strings.Replace(arg, "-loaduser:", "", 1)
userLoadExist = true
}
}
if !(userNewExist || userLoadExist) || !addrExist {
panic("failed: !(userNewExist || userLoadExist) || !addrExist")
}
err := json.Unmarshal([]byte(readFile(addrStr)), &Addresses)
if err != nil {
panic("failed: load addresses")
}
if len(Addresses) == 0 {
panic("failed: len(Addresses) == 0")
}
if userNewExist {
User = userNew(userNewStr)
}
if userLoadExist {
User = userLoad(userLoadStr)
}
if User == nil {
panic("failed: load user")
}
}
func main() {
handleClient()
}
func handleClient() {
var (
message string
splited []string
)
for {
message = inputString("> ")
splited = strings.Split(message, " ")
switch splited[0] {
case "/exit":
os.Exit(0)
case "/user":
if len(splited) < 2 {
fmt.Println("failed: len(user) < 2\n")
continue
}
switch splited[1] {
case "address":
userAddress()
case "purse":
userPurse()
case "balance":
userBalance()
default:
fmt.Println("command undefined\n")
}
case "/chain":
if len(splited) < 2 {
fmt.Println("failed: len(chain) < 2\n")
continue
}
switch splited[1] {
case "print":
chainPrint()
case "tx":
chainTX(splited[1:])
case "balance":
chainBalance(splited[1:])
case "block":
chainBlock(splited[1:])
case "size":
chainSize()
default:
fmt.Println("command undefined\n")
}
default:
fmt.Println("command undefined\n")
}
}
}
func chainSize() {
res := nt.Send(Addresses[0], &nt.Package{
Option: GET_CSIZE,
})
if res == nil || res.Data == "" {
fmt.Println("failed: getSize\n")
return
}
fmt.Printf("Size: %s blocks\n\n", res.Data)
}
func chainBlock(splited []string) {
if len(splited) != 2 {
fmt.Println("failed: len(splited) != 2\n")
return
}
num, err := strconv.Atoi(splited[1])
if err != nil {
fmt.Println("failed: strconv.Atoi(num)\n")
return
}
res := nt.Send(Addresses[0], &nt.Package{
Option: GET_BLOCK,
Data: fmt.Sprintf("%d", num-1),
})
if res == nil || res.Data == "" {
fmt.Println("failed: getBlock\n")
return
}
fmt.Printf("[%d] => %s\n", num, res.Data)
}
func chainPrint() {
for i := 0; ; i++ {
res := nt.Send(Addresses[0], &nt.Package{
Option: GET_BLOCK,
Data: fmt.Sprintf("%d", i),
})
if res == nil || res.Data == "" {
break
}
fmt.Printf("[%d] => %s\n", i+1, res.Data)
}
fmt.Println()
}
func chainTX(splited []string) {
if len(splited) != 3 {
fmt.Println("failed: len(splited) != 3\n")
return
}
num, err := strconv.Atoi(splited[2])
if err != nil {
fmt.Println("failed: strconv.Atoi(num)\n")
return
}
for _, addr := range Addresses {
res := nt.Send(addr, &nt.Package{
Option: GET_LHASH,
})
if res == nil {
continue
}
tx := bc.NewTransaction(User, bc.Base64Decode(res.Data), splited[1], uint64(num))
res = nt.Send(addr, &nt.Package{
Option: ADD_TRNSX,
Data: bc.SerializeTX(tx),
})
if res == nil {
continue
}
if res.Data == "ok" {
fmt.Printf("ok: (%s)\n", addr)
} else {
fmt.Printf("fail: (%s)\n", addr)
}
}
fmt.Println()
}
func chainBalance(splited []string) {
if len(splited) != 2 {
fmt.Println("fail: len(splited) != 2\n")
return
}
printBalance(splited[1])
}
func printBalance(useraddr string) {
for _, addr := range Addresses {
res := nt.Send(addr, &nt.Package{
Option: GET_BLNCE,
Data: useraddr,
})
if res == nil {
continue
}
fmt.Printf("Balance (%s): %s coins\n", addr, res.Data)
}
fmt.Println()
}
func userAddress() {
fmt.Println("Address:", User.Address(), "\n")
}
func userPurse() {
fmt.Println("Purse:", User.Purse(), "\n")
}
func userBalance() {
printBalance(User.Address())
}
func inputString(begin string) string {
fmt.Print(begin)
msg, _ := bufio.NewReader(os.Stdin).ReadString('\n')
return strings.Replace(msg, "\n", "", 1)
}

60
cmd/client/values.go Normal file
View File

@ -0,0 +1,60 @@
package main
import (
bc "blockchain-from-scratch/blockchain"
"io/ioutil"
)
var (
Addresses []string
User *bc.User
)
const (
SEPARATOR = "_SEPARATOR_"
)
const (
ADD_BLOCK = iota + 1
ADD_TRNSX
GET_BLOCK
GET_LHASH
GET_BLNCE
GET_CSIZE
)
func userNew(filename string) *bc.User {
user := bc.NewUser()
if user == nil {
return nil
}
err := writeFile(filename, user.Purse())
if err != nil {
return nil
}
return user
}
func userLoad(filename string) *bc.User {
priv := readFile(filename)
if priv == "" {
return nil
}
user := bc.LoadUser(priv)
if user == nil {
return nil
}
return user
}
func writeFile(filename string, data string) error {
return ioutil.WriteFile(filename, []byte(data), 0644)
}
func readFile(filename string) string {
data, err := ioutil.ReadFile(filename)
if err != nil {
return ""
}
return string(data)
}

View File

@ -0,0 +1,622 @@
package main
import (
"os"
"fmt"
"bufio"
"context"
"strings"
"math/big"
"encoding/json"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
)
func init() {
if len(os.Args) < 2 {
panic("failed: len(os.Args) < 2")
}
var (
userLoadStr = ""
userLoadExist = false
)
for i := 1; i < len(os.Args); i++ {
arg := os.Args[i]
switch {
case strings.HasPrefix(arg, "-loaduser:"):
userLoadStr = strings.Replace(arg, "-loaduser:", "", 1)
userLoadExist = true
}
}
if !userLoadExist {
panic("failed: !userLoadExist")
}
if ClientETH == nil {
panic("failed: connect to ETH")
}
if Instance == nil {
panic("failed: instance is nil")
}
User = loadUser(userLoadStr)
if User == nil {
panic("failed: load user")
}
}
func main() {
var (
message string
splited []string
)
for {
message = inputString("> ")
splited = strings.Split(message, " ")
switch splited[0] {
case "/exit":
os.Exit(0)
case "/user":
if len(splited) < 2 {
fmt.Println("failed: len(user) < 2\n")
continue
}
switch splited[1] {
case "address":
userAddress()
case "purse":
userPurse()
case "balance":
userBalance()
default:
fmt.Println("command undefined\n")
}
case "/chain":
if len(splited) < 3 {
fmt.Println("failed: len(chain) < 3\n")
continue
}
switch splited[1] {
case "get":
chainGet(splited[2], splited[2:])
case "create":
switch splited[2] {
case "estate":
// chain create estate address info squere usefulSquere
chainCreateEstate(splited[2:])
case "present":
// chain create present id_estate address
chainCreatePresent(splited[2:])
case "sale":
// chain create sale id_estate money
chainCreateSale(splited[2:])
case "rent":
// chain create rent id_estate hours price
chainCreateRent(splited[2:])
default:
fmt.Println("command undefined\n")
}
case "cancel":
chainCancel(splited[2], splited[2:])
case "confirm":
switch splited[2] {
case "present":
// chain confirm present id_present
chainConfirmPresent(splited[2:])
case "sale":
// chain confirm sale id_sale id_customer
chainConfirmSale(splited[2:])
case "rent":
// chain confirm rent id_rent
chainConfirmRent(splited[2:])
default:
fmt.Println("command undefined\n")
}
case "try-buy":
// chain try-buy id_sale money
chainTryBuy(splited[1:])
case "cancel-buy":
// chain cancel-buy id_sale
chainCancelBuy(splited[1:])
case "finish-rent":
// chain finish-rent id_rent
finishRent(splited[1:])
default:
fmt.Println("command undefined\n")
}
default:
fmt.Println("command undefined\n")
}
}
}
func chainTryBuy(splited []string) {
if len(splited) != 3 {
fmt.Println("failed: len(splited) != 3\n")
return
}
var (
saleNumber = new(big.Int)
value = new(big.Int)
ok bool
)
saleNumber, ok = saleNumber.SetString(splited[1], 10)
if !ok {
fmt.Println("failed: conv(str1) to num\n")
return
}
value, ok = value.SetString(splited[2], 10)
if !ok {
fmt.Println("failed: conv(str2) to num\n")
return
}
auth := resetAuth(User)
if auth == nil {
fmt.Println("failed: auth is nil\n")
return
}
auth.Value = value
tx, err := Instance.CheckToBuy(
auth,
saleNumber,
)
if err != nil {
fmt.Println(err, "\n")
return
}
fmt.Println("Tx:", tx.Hash().Hex(), "\n")
}
func chainCancelBuy(splited []string) {
if len(splited) != 2 {
fmt.Println("failed: len(splited) != 2\n")
return
}
var (
saleNumber = new(big.Int)
ok bool
)
saleNumber, ok = saleNumber.SetString(splited[1], 10)
if !ok {
fmt.Println("failed: conv(str1) to num\n")
return
}
tx, err := Instance.CancelToBuy(
resetAuth(User),
saleNumber,
)
if err != nil {
fmt.Println(err, "\n")
return
}
fmt.Println("Tx:", tx.Hash().Hex(), "\n")
}
func finishRent(splited []string) {
if len(splited) != 2 {
fmt.Println("failed: len(splited) != 2\n")
return
}
var (
rentNumber = new(big.Int)
ok bool
)
rentNumber, ok = rentNumber.SetString(splited[1], 10)
if !ok {
fmt.Println("failed: conv(str1) to num\n")
return
}
tx, err := Instance.FinishRent(
resetAuth(User),
rentNumber,
)
if err != nil {
fmt.Println(err, "\n")
return
}
fmt.Println("Tx:", tx.Hash().Hex(), "\n")
}
func chainCreateEstate(splited []string) {
if len(splited) != 5 {
fmt.Println("failed: len(splited) != 5\n")
return
}
var (
squere = new(big.Int)
usefulSquere = new(big.Int)
ok bool
)
squere, ok = squere.SetString(splited[3], 10)
if !ok {
fmt.Println("failed: conv(str1) to num\n")
return
}
usefulSquere, ok = usefulSquere.SetString(splited[4], 10)
if !ok {
fmt.Println("failed: conv(str2) to num\n")
return
}
var address common.Address
if splited[1] == "my" {
address = User.AddressEth
} else {
address = common.HexToAddress(splited[1])
}
tx, err := Instance.CreateEstate(
resetAuth(User),
address,
splited[2],
squere,
usefulSquere,
)
if err != nil {
fmt.Println(err, "\n")
return
}
fmt.Println("Tx:", tx.Hash().Hex(), "\n")
}
func chainCreatePresent(splited []string) {
if len(splited) != 3 {
fmt.Println("failed: len(splited) != 3\n")
return
}
var (
estateId = new(big.Int)
ok bool
)
estateId, ok = estateId.SetString(splited[1], 10)
if !ok {
fmt.Println("failed: conv(str1) to num\n")
return
}
tx, err := Instance.CreatePresent(
resetAuth(User),
estateId,
common.HexToAddress(splited[2]),
)
if err != nil {
fmt.Println(err, "\n")
return
}
fmt.Println("Tx:", tx.Hash().Hex(), "\n")
}
func chainCreateSale(splited []string) {
if len(splited) != 3 {
fmt.Println("failed: len(splited) != 3\n")
return
}
var (
estateId = new(big.Int)
price = new(big.Int)
ok bool
)
estateId, ok = estateId.SetString(splited[1], 10)
if !ok {
fmt.Println("failed: conv(str1) to num\n")
return
}
price, ok = price.SetString(splited[2], 10)
if !ok {
fmt.Println("failed: conv(str2) to num\n")
return
}
tx, err := Instance.CreateSale(
resetAuth(User),
estateId,
price,
)
if err != nil {
fmt.Println(err, "\n")
return
}
fmt.Println("Tx:", tx.Hash().Hex(), "\n")
}
func chainCreateRent(splited []string) {
if len(splited) != 4 {
fmt.Println("failed: len(splited) != 4\n")
return
}
var (
estateId = new(big.Int)
hours = new(big.Int)
price = new(big.Int)
ok bool
)
estateId, ok = estateId.SetString(splited[1], 10)
if !ok {
fmt.Println("failed: conv(str1) to num\n")
return
}
hours, ok = hours.SetString(splited[2], 10)
if !ok {
fmt.Println("failed: conv(str2) to num\n")
return
}
price, ok = price.SetString(splited[3], 10)
if !ok {
fmt.Println("failed: conv(str3) to num\n")
return
}
tx, err := Instance.CreateRent(
resetAuth(User),
estateId,
hours,
price,
)
if err != nil {
fmt.Println(err, "\n")
return
}
fmt.Println("Tx:", tx.Hash().Hex(), "\n")
}
func chainCancel(category string, splited []string) {
if len(splited) != 2 {
fmt.Println("failed: len(splited) != 2\n")
return
}
var (
tx *types.Transaction
err error
num = new(big.Int)
ok bool
)
num, ok = num.SetString(splited[1], 10)
if !ok {
fmt.Println("failed: conv(str1) to num\n")
return
}
switch category {
case "present":
tx, err = Instance.CancelPresent(
resetAuth(User),
num,
)
case "sale":
tx, err = Instance.CancelSale(
resetAuth(User),
num,
)
case "rent":
tx, err = Instance.CancelRent(
resetAuth(User),
num,
)
}
if err != nil {
fmt.Println(err, "\n")
return
}
fmt.Println("Tx:", tx.Hash().Hex(), "\n")
}
func chainConfirmPresent(splited []string) {
if len(splited) != 2 {
fmt.Println("failed: len(splited) != 2\n")
return
}
var (
presentNumber = new(big.Int)
ok bool
)
presentNumber, ok = presentNumber.SetString(splited[1], 10)
if !ok {
fmt.Println("failed: conv(str1) to num\n")
return
}
tx, err := Instance.ConfirmPresent(
resetAuth(User),
presentNumber,
)
if err != nil {
fmt.Println(err, "\n")
return
}
fmt.Println("Tx:", tx.Hash().Hex(), "\n")
}
func chainConfirmSale(splited []string) {
if len(splited) != 3 {
fmt.Println("failed: len(splited) != 3\n")
return
}
var (
saleNumber = new(big.Int)
saleTo = new(big.Int)
ok bool
)
saleNumber, ok = saleNumber.SetString(splited[1], 10)
if !ok {
fmt.Println("failed: conv(str1) to num\n")
return
}
saleTo, ok = saleTo.SetString(splited[2], 10)
if !ok {
fmt.Println("failed: conv(str2) to num\n")
return
}
tx, err := Instance.ConfirmSale(
resetAuth(User),
saleNumber,
saleTo,
)
if err != nil {
fmt.Println(err, "\n")
return
}
fmt.Println("Tx:", tx.Hash().Hex(), "\n")
}
func chainConfirmRent(splited []string) {
if len(splited) != 2 {
fmt.Println("failed: len(splited) != 2\n")
return
}
var (
rentNumber = new(big.Int)
ok bool
)
rentNumber, ok = rentNumber.SetString(splited[1], 10)
if !ok {
fmt.Println("failed: conv(str1) to num\n")
return
}
rent := getRents(rentNumber)
if rent == nil {
fmt.Println("failed: get rent\n")
return
}
auth := resetAuth(User)
if auth == nil {
fmt.Println("failed: auth is nil\n")
return
}
auth.Value = rent.Money
tx, err := Instance.ToRent(
auth,
rentNumber,
)
if err != nil {
fmt.Println(err, "\n")
return
}
fmt.Println("Tx:", tx.Hash().Hex(), "\n")
}
func chainGet(category string, splited []string) {
if len(splited) != 2 {
fmt.Println("failed: len(splited) != 2\n")
return
}
var (
inc = big.NewInt(1)
err error
num *big.Int
jsonData []byte
)
switch category {
case "estates":
num, err = Instance.GetEstatesNumber(&bind.CallOpts{From: User.AddressEth})
case "presents":
num, err = Instance.GetPresentsNumber(&bind.CallOpts{From: User.AddressEth})
case "sales":
num, err = Instance.GetSalesNumber(&bind.CallOpts{From: User.AddressEth})
case "rents":
num, err = Instance.GetRentsNumber(&bind.CallOpts{From: User.AddressEth})
default:
fmt.Println("undefined category\n")
return
}
if err != nil {
fmt.Println(err, "\n")
return
}
for index := big.NewInt(0); index.Cmp(num) == -1; index.Add(index, inc) {
switch category {
case "estates":
data := getEstates(index)
if data == nil {
fmt.Println("data is nil\n")
return
}
if splited[1] == "my" && User.AddressHex != data.Owner.Hex() {
continue
}
if splited[1] != "all" && splited[1] != "my" &&
strings.ToLower(splited[1]) != strings.ToLower(data.Owner.Hex()) {
continue
}
jsonData, err = json.MarshalIndent(data, "", "\t")
case "presents":
data := getPresents(index)
if data == nil {
fmt.Println("data is nil\n")
return
}
if data.Finished {
continue
}
if splited[1] == "my" &&
(User.AddressHex != data.AddressFrom.Hex() && User.AddressHex != data.AddressTo.Hex()){
continue
}
if splited[1] != "all" && splited[1] != "my" &&
(strings.ToLower(splited[1]) != strings.ToLower(data.AddressFrom.Hex()) &&
strings.ToLower(splited[1]) != strings.ToLower(data.AddressTo.Hex())) {
continue
}
jsonData, err = json.MarshalIndent(data, "", "\t")
case "sales":
data := getSales(index)
if data == nil {
fmt.Println("data is nil\n")
return
}
if data.Finished {
continue
}
if splited[1] == "my" && User.AddressHex != data.Owner.Hex() {
continue
}
if splited[1] != "all" && splited[1] != "my" &&
strings.ToLower(splited[1]) != strings.ToLower(data.Owner.Hex()) {
continue
}
jsonData, err = json.MarshalIndent(data, "", "\t")
case "rents":
data := getRents(index)
if data == nil {
fmt.Println("data is nil\n")
return
}
if data.Finished {
continue
}
if splited[1] == "my" &&
(User.AddressHex != data.Owner.Hex() && User.AddressHex != data.Renter.Hex()){
continue
}
if splited[1] != "all" && splited[1] != "my" &&
(strings.ToLower(splited[1]) != strings.ToLower(data.Owner.Hex()) &&
strings.ToLower(splited[1]) != strings.ToLower(data.Renter.Hex())) {
continue
}
jsonData, err = json.MarshalIndent(data, "", "\t")
default:
fmt.Println("undefined category\n")
return
}
if err != nil {
fmt.Println(err, "\n")
return
}
fmt.Println(string(jsonData))
}
fmt.Println()
}
func userAddress() {
fmt.Println("Address:", User.AddressHex, "\n")
}
func userPurse() {
fmt.Println("Purse:", User.Purse, "\n")
}
func userBalance() {
balance, err := ClientETH.BalanceAt(context.Background(), User.AddressEth, nil)
if err != nil {
fmt.Println(err, "\n")
return
}
fmt.Println("Balance:", balance, "\n")
}
func inputString(begin string) string {
fmt.Print(begin)
msg, _ := bufio.NewReader(os.Stdin).ReadString('\n')
return strings.Replace(msg, "\n", "", 1)
}

View File

@ -0,0 +1,304 @@
package main
import (
contract "blockchain-from-scratch/contracts"
"context"
"crypto/ecdsa"
"io/ioutil"
"math/big"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
)
type UserType struct {
Purse string
AddressHex string
AddressEth common.Address
PublicKey *ecdsa.PublicKey
PrivateKey *ecdsa.PrivateKey
}
type Estate struct {
Id *big.Int
Owner common.Address
Info string
Squere *big.Int
UsefulSquere *big.Int
RenterAddress common.Address
PresentStatus bool
SaleStatus bool
RentStatus bool
}
type Present struct {
Id *big.Int
EstateId *big.Int
AddressFrom common.Address
AddressTo common.Address
Finished bool
}
type Sale struct {
Id *big.Int
EstateId *big.Int
Owner common.Address
Price *big.Int
Customers []common.Address
Prices []*big.Int
Finished bool
}
type Rent struct {
Id *big.Int
EstateId *big.Int
Owner common.Address
Renter common.Address
Time *big.Int
Money *big.Int
DeadLine *big.Int
Finished bool
}
var (
User *UserType
ClientETH = connectToETH("http://127.0.0.1:5555")
Instance = newContract(
common.HexToAddress(readFile("contract.address")),
ClientETH,
)
)
func loadUser(purse string) *UserType {
priv, err := crypto.HexToECDSA(purse)
if err != nil {
return nil
}
pub, ok := priv.Public().(*ecdsa.PublicKey)
if !ok {
return nil
}
addressHex := crypto.PubkeyToAddress(*pub).Hex()
addressEth := common.HexToAddress(addressHex)
return &UserType{
Purse: purse,
AddressHex: addressHex,
AddressEth: addressEth,
PublicKey: pub,
PrivateKey: priv,
}
}
func newContract(contractAddr common.Address, clientEth *ethclient.Client) *contract.Contract {
instance, err := contract.NewContract(contractAddr, clientEth)
if err != nil {
return nil
}
return instance
}
func connectToETH(address string) *ethclient.Client {
client, err := ethclient.Dial(address)
if err != nil {
return nil
}
return client
}
func readFile(filename string) string {
data, err := ioutil.ReadFile(filename)
if err != nil {
return ""
}
return string(data)
}
func resetAuth(user *UserType) *bind.TransactOpts {
nonce, err := ClientETH.PendingNonceAt(context.Background(), crypto.PubkeyToAddress(*user.PublicKey))
if err != nil {
return nil
}
gasPrice, err := ClientETH.SuggestGasPrice(context.Background())
if err != nil {
return nil
}
auth := bind.NewKeyedTransactor(user.PrivateKey)
auth.Nonce = big.NewInt(int64(nonce))
auth.Value = big.NewInt(0)
auth.GasLimit = uint64(3000000)
auth.GasPrice = gasPrice
return auth
}
func getEstates(index *big.Int) *Estate {
// (*big.Int, common.Address, string, *big.Int, *big.Int, common.Address, error)
id, owner, info, squere, usefulsquere, renteraddress, err := Instance.GetEstates(&bind.CallOpts{From: User.AddressEth}, index)
if err != nil {
return nil
}
presentS, saleS, rentS, err := Instance.GetEstatesStatuses(&bind.CallOpts{From: User.AddressEth}, index)
if err != nil {
return nil
}
return &Estate{
Id: id,
Owner: owner,
Info: info,
Squere: squere,
UsefulSquere: usefulsquere,
RenterAddress: renteraddress,
PresentStatus: presentS,
SaleStatus: saleS,
RentStatus: rentS,
}
}
func getPresents(index *big.Int) *Present {
// (*big.Int, common.Address, common.Address, bool, error)
id, from, to, finished, err := Instance.GetPresents(&bind.CallOpts{From: User.AddressEth}, index)
if err != nil {
return nil
}
return &Present{
Id: index,
EstateId: id,
AddressFrom: from,
AddressTo: to,
Finished: finished,
}
}
func getSales(index *big.Int) *Sale {
// (*big.Int, common.Address, *big.Int, []common.Address, []*big.Int, bool, error)
id, owner, price, customers, prices, finished, err := Instance.GetSales(&bind.CallOpts{From: User.AddressEth}, index)
if err != nil {
return nil
}
return &Sale{
Id: index,
EstateId: id,
Owner: owner,
Price: price,
Customers: customers,
Prices: prices,
Finished: finished,
}
}
func getRents(index *big.Int) *Rent {
// (*big.Int, common.Address, common.Address, *big.Int, *big.Int, *big.Int, bool, error)
id, owner, renter, time, money, deadline, finished, err := Instance.GetRents(&bind.CallOpts{From: User.AddressEth}, index)
if err != nil {
return nil
}
return &Rent{
Id: index,
EstateId: id,
Owner: owner,
Renter: renter,
Time: time,
Money: money,
DeadLine: deadline,
Finished: finished,
}
}
type EstateStr struct {
Id *big.Int
Owner string
Info string
Squere *big.Int
UsefulSquere *big.Int
RenterAddress string
PresentStatus bool
SaleStatus bool
RentStatus bool
}
func estatesToString(estate *Estate) *EstateStr {
return &EstateStr{
Id: estate.Id,
Owner: estate.Owner.Hex(),
Info: estate.Info,
Squere: estate.Squere,
UsefulSquere: estate.UsefulSquere,
RenterAddress: estate.RenterAddress.Hex(),
PresentStatus: estate.PresentStatus,
SaleStatus: estate.SaleStatus,
RentStatus: estate.RentStatus,
}
}
type PresentStr struct {
Id *big.Int
EstateId *big.Int
AddressFrom string
AddressTo string
Finished bool
}
func presentsToString(present *Present) *PresentStr {
return &PresentStr{
Id: present.Id,
EstateId: present.EstateId,
AddressFrom: present.AddressFrom.Hex(),
AddressTo: present.AddressTo.Hex(),
Finished: present.Finished,
}
}
type SaleStr struct {
Id *big.Int
EstateId *big.Int
Owner string
Price *big.Int
Customers []string
Prices []*big.Int
Finished bool
}
func salesToString(sale *Sale) *SaleStr {
var customers []string
for _, cust := range sale.Customers {
customers = append(customers, cust.Hex())
}
return &SaleStr{
Id: sale.Id,
EstateId: sale.EstateId,
Owner: sale.Owner.Hex(),
Price: sale.Price,
Customers: customers,
Prices: sale.Prices,
Finished: sale.Finished,
}
}
type RentStr struct {
Id *big.Int
EstateId *big.Int
Owner string
Renter string
Time *big.Int
Money *big.Int
DeadLine *big.Int
Finished bool
}
func rentsToString(rent *Rent) *RentStr {
return &RentStr{
Id: rent.Id,
EstateId: rent.EstateId,
Owner: rent.Owner.Hex(),
Renter: rent.Renter.Hex(),
Time: rent.Time,
Money: rent.Money,
DeadLine: rent.DeadLine,
Finished: rent.Finished,
}
}

136
cmd/deploy/deploy.go Normal file
View File

@ -0,0 +1,136 @@
package main
import (
contract "blockchain-from-scratch/contracts"
"context"
"crypto/ecdsa"
"fmt"
"io/ioutil"
"log"
"math/big"
"os"
"strings"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
)
type UserType struct {
Purse string
AddressHex string
AddressEth common.Address
PublicKey *ecdsa.PublicKey
PrivateKey *ecdsa.PrivateKey
}
var (
ClientETH = connectToETH("http://127.0.0.1:5555")
User *UserType
)
func init() {
if len(os.Args) < 2 {
panic("failed: len(os.Args) < 2")
}
var (
userLoadStr = ""
userLoadExist = false
)
for i := 1; i < len(os.Args); i++ {
arg := os.Args[i]
switch {
case strings.HasPrefix(arg, "-loaduser:"):
userLoadStr = strings.Replace(arg, "-loaduser:", "", 1)
userLoadExist = true
}
}
if !userLoadExist {
panic("failed: !userLoadExist")
}
if ClientETH == nil {
panic("failed: connect to ETH")
}
User = userLoad(userLoadStr)
if User == nil {
panic("failed: load user")
}
}
// Deploy contract and save address in file.
func main() {
auth := resetAuth(User)
address, tx, instance, err := contract.DeployContract(auth, ClientETH)
if err != nil {
log.Fatal(err)
}
fmt.Println(address.Hex())
fmt.Println(tx.Hash().Hex())
_ = instance
contractFile := "contract.address"
writeFile(contractFile, address.Hex())
}
func writeFile(filename string, data string) error {
return ioutil.WriteFile(filename, []byte(data), 0644)
}
func userLoad(purse string) *UserType {
priv, err := crypto.HexToECDSA(purse)
if err != nil {
return nil
}
pub, ok := priv.Public().(*ecdsa.PublicKey)
if !ok {
return nil
}
addressHex := crypto.PubkeyToAddress(*pub).Hex()
addressEth := common.HexToAddress(addressHex)
return &UserType{
Purse: purse,
AddressHex: addressHex,
AddressEth: addressEth,
PublicKey: pub,
PrivateKey: priv,
}
}
func connectToETH(address string) *ethclient.Client {
client, err := ethclient.Dial(address)
if err != nil {
return nil
}
return client
}
func resetAuth(user *UserType) *bind.TransactOpts {
nonce, err := ClientETH.PendingNonceAt(context.Background(), User.AddressEth)
if err != nil {
return nil
}
gasPrice, err := ClientETH.SuggestGasPrice(context.Background())
if err != nil {
return nil
}
auth := bind.NewKeyedTransactor(user.PrivateKey)
auth.Nonce = big.NewInt(int64(nonce))
auth.Value = big.NewInt(0)
// auth.GasLimit = uint64(3000000)
auth.GasPrice = gasPrice
return auth
}
func fileIsExist(name string) bool {
if _, err := os.Stat(name); os.IsNotExist(err) {
return false
}
return true
}

285
cmd/gclient/gclient.go Normal file
View File

@ -0,0 +1,285 @@
package main
import (
bc "blockchain-from-scratch/blockchain"
"encoding/json"
"fmt"
"html/template"
"net/http"
"os"
"strconv"
"strings"
nt "blockchain-from-scratch/network"
)
const (
STTC_PATH = "static/"
TMPL_PATH = "templates/"
ADDR_FILE = "addr.json"
)
func init() {
err := json.Unmarshal([]byte(readFile(ADDR_FILE)), &Addresses)
if err != nil {
panic("failed: load addresses")
}
if len(Addresses) == 0 {
panic("failed: len(Addresses) == 0")
}
}
func main() {
fmt.Println("Server is running ...")
http.Handle("/static/", http.StripPrefix(
"/static/",
handleFileServer(http.Dir(STTC_PATH))),
)
http.HandleFunc("/", indexPage)
http.HandleFunc("/login", loginPage)
http.HandleFunc("/signup", signupPage)
http.HandleFunc("/logout", logoutPage)
http.HandleFunc("/account", accountPage)
http.HandleFunc("/transaction", transactionPage)
http.HandleFunc("/blockchain", blockchainPage)
http.HandleFunc("/blockchain/", blockchainXPage)
http.ListenAndServe(":7545", nil)
}
func handleFileServer(fs http.FileSystem) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if _, err := fs.Open(r.URL.Path); os.IsNotExist(err) {
indexPage(w, r)
return
}
http.FileServer(fs).ServeHTTP(w, r)
})
}
func indexPage(w http.ResponseWriter, r *http.Request) {
t, err := template.ParseFiles(
TMPL_PATH+"base.html",
TMPL_PATH+"index.html",
)
if err != nil {
panic("can't load hmtl files")
}
var data struct {
User *bc.User
}
data.User = User
t.Execute(w, data)
}
func accountPage(w http.ResponseWriter, r *http.Request) {
t, err := template.ParseFiles(
TMPL_PATH+"base.html",
TMPL_PATH+"account.html",
)
if err != nil {
panic("can't load hmtl files")
}
var data struct {
User *bc.User
Address string
Balance string
}
data.User = User
if data.User != nil {
data.Address = User.Address()
res := nt.Send(Addresses[0], &nt.Package{
Option: GET_BLNCE,
Data: data.Address,
})
if res != nil {
data.Balance = res.Data
}
} else {
http.Redirect(w, r, "/", 302)
return
}
t.Execute(w, data)
}
func loginPage(w http.ResponseWriter, r *http.Request) {
t, err := template.ParseFiles(
TMPL_PATH+"base.html",
TMPL_PATH+"login.html",
)
if err != nil {
panic("can't load hmtl files")
}
var data struct {
User *bc.User
Error string
}
if r.Method == "POST" {
r.ParseForm()
User = bc.LoadUser(r.FormValue("private"))
if User == nil {
data.Error = "Load Private Key Error"
} else {
http.Redirect(w, r, "/", 302)
return
}
}
data.User = User
t.Execute(w, data)
}
func logoutPage(w http.ResponseWriter, r *http.Request) {
User = nil
http.Redirect(w, r, "/", 302)
}
func signupPage(w http.ResponseWriter, r *http.Request) {
t, err := template.ParseFiles(
TMPL_PATH+"base.html",
TMPL_PATH+"signup.html",
)
if err != nil {
panic("can't load hmtl files")
}
var data struct {
User *bc.User
PrivateKey string
}
data.User = User
if r.Method == "POST" {
data.PrivateKey = bc.NewUser().Purse()
}
t.Execute(w, data)
}
func transactionPage(w http.ResponseWriter, r *http.Request) {
t, err := template.ParseFiles(
TMPL_PATH+"base.html",
TMPL_PATH+"transaction.html",
)
if err != nil {
panic("can't load hmtl files")
}
var data struct {
User *bc.User
Error string
}
data.User = User
if r.Method == "POST" {
r.ParseForm()
if data.User == nil {
data.Error = "User not authorizated"
t.Execute(w, data)
return
}
receiver := r.FormValue("receiver")
num, err := strconv.Atoi(r.FormValue("value"))
if err != nil {
data.Error = "strconv.Atoi error"
t.Execute(w, data)
return
}
flag := false
for _, addr := range Addresses {
res := nt.Send(addr, &nt.Package{
Option: GET_LHASH,
})
if res == nil {
continue
}
tx := bc.NewTransaction(User, bc.Base64Decode(res.Data), receiver, uint64(num))
res = nt.Send(addr, &nt.Package{
Option: ADD_TRNSX,
Data: bc.SerializeTX(tx),
})
if res == nil || res.Data != "ok" {
continue
}
flag = true
}
if !flag {
data.Error = "TX failed"
} else {
data.Error = "TX success"
}
}
t.Execute(w, data)
}
func blockchainPage(w http.ResponseWriter, r *http.Request) {
t, err := template.ParseFiles(
TMPL_PATH+"base.html",
TMPL_PATH+"blockchain.html",
)
if err != nil {
panic("can't load hmtl files")
}
var data struct {
Error string
Size []bool
Address string
Balance string
User *bc.User
}
data.User = User
if r.Method == "POST" {
data.Address = r.FormValue("address")
res := nt.Send(Addresses[0], &nt.Package{
Option: GET_BLNCE,
Data: data.Address,
})
if res != nil {
data.Balance = res.Data
}
}
res := nt.Send(Addresses[0], &nt.Package{
Option: GET_CSIZE,
})
if res == nil || res.Data == "" {
data.Error = "Receive error"
t.Execute(w, data)
return
}
num, err := strconv.Atoi(res.Data)
if err != nil {
data.Error = "strconv.Atoi error"
t.Execute(w, data)
return
}
data.Size = make([]bool, num)
t.Execute(w, data)
}
func blockchainXPage(w http.ResponseWriter, r *http.Request) {
t, err := template.ParseFiles(
TMPL_PATH+"base.html",
TMPL_PATH+"blockchainX.html",
)
if err != nil {
panic("can't load hmtl files")
}
var data struct {
Error string
Block *bc.Block
User *bc.User
}
data.User = User
res := nt.Send(Addresses[0], &nt.Package{
Option: GET_BLOCK,
Data: strings.Replace(r.URL.Path, "/blockchain/", "", 1),
})
if res == nil || res.Data == "" {
data.Error = "Receive error"
t.Execute(w, data)
return
}
data.Block = bc.DeserializeBlock(res.Data)
if data.Block == nil {
data.Error = "Block is nil"
t.Execute(w, data)
return
}
t.Execute(w, data)
}

60
cmd/gclient/values.go Normal file
View File

@ -0,0 +1,60 @@
package main
import (
bc "blockchain-from-scratch/blockchain"
"io/ioutil"
)
var (
Addresses []string
User *bc.User
)
const (
SEPARATOR = "_SEPARATOR_"
)
const (
ADD_BLOCK = iota + 1
ADD_TRNSX
GET_BLOCK
GET_LHASH
GET_BLNCE
GET_CSIZE
)
func userNew(filename string) *bc.User {
user := bc.NewUser()
if user == nil {
return nil
}
err := writeFile(filename, user.Purse())
if err != nil {
return nil
}
return user
}
func userLoad(filename string) *bc.User {
priv := readFile(filename)
if priv == "" {
return nil
}
user := bc.LoadUser(priv)
if user == nil {
return nil
}
return user
}
func writeFile(filename string, data string) error {
return ioutil.WriteFile(filename, []byte(data), 0644)
}
func readFile(filename string) string {
data, err := ioutil.ReadFile(filename)
if err != nil {
return ""
}
return string(data)
}

View File

@ -0,0 +1,905 @@
package main
import (
"os"
"fmt"
"strings"
"context"
"net/http"
"math/big"
"html/template"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
)
const (
STTC_PATH = "static/"
TMPL_PATH = "templates_eth/"
)
func init() {
if ClientETH == nil {
panic("failed: connect to ETH")
}
if Instance == nil {
panic("failed: instance is nil")
}
}
func main() {
fmt.Println("Server is running ...")
http.Handle("/static/", http.StripPrefix(
"/static/",
handleFileServer(http.Dir(STTC_PATH))),
)
http.HandleFunc("/", indexPage)
http.HandleFunc("/login", loginPage)
http.HandleFunc("/logout", logoutPage)
http.HandleFunc("/account", accountPage)
http.HandleFunc("/blockchain", blockchainPage)
http.HandleFunc("/blockchain/estates", blockchainEstatesPage)
http.HandleFunc("/blockchain/presents", blockchainPresentsPage)
http.HandleFunc("/blockchain/sales", blockchainSalesPage)
http.HandleFunc("/blockchain/rents", blockchainRentsPage)
http.HandleFunc("/blockchain/estates/", blockchainEstatesXPage)
http.HandleFunc("/blockchain/presents/", blockchainPresentsXPage)
http.HandleFunc("/blockchain/sales/", blockchainSalesXPage)
http.HandleFunc("/blockchain/rents/", blockchainRentsXPage)
http.HandleFunc("/blockchain/presents/do/", blockchainPresentsDoPage)
http.HandleFunc("/blockchain/sales/do/", blockchainSalesDoPage)
http.HandleFunc("/blockchain/rents/do/", blockchainRentsDoPage)
http.ListenAndServe(":7545", nil)
}
func handleFileServer(fs http.FileSystem) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if _, err := fs.Open(r.URL.Path); os.IsNotExist(err) {
indexPage(w, r)
return
}
http.FileServer(fs).ServeHTTP(w, r)
})
}
func indexPage(w http.ResponseWriter, r *http.Request) {
t, err := template.ParseFiles(
TMPL_PATH+"base.html",
TMPL_PATH+"index.html",
)
if err != nil {
panic("can't load hmtl files")
}
var data struct{
User *UserType
}
data.User = User
t.Execute(w, data)
}
func loginPage(w http.ResponseWriter, r *http.Request) {
t, err := template.ParseFiles(
TMPL_PATH+"base.html",
TMPL_PATH+"login.html",
)
if err != nil {
panic("can't load hmtl files")
}
var data struct{
User *UserType
Error string
}
if r.Method == "POST" {
r.ParseForm()
User = loadUser(r.FormValue("private"))
if User == nil {
data.Error = "Load Private Key Error"
} else {
http.Redirect(w, r, "/", 302)
return
}
}
data.User = User
t.Execute(w, data)
}
func logoutPage(w http.ResponseWriter, r *http.Request) {
User = nil
http.Redirect(w, r, "/", 302)
}
func accountPage(w http.ResponseWriter, r *http.Request) {
t, err := template.ParseFiles(
TMPL_PATH+"base.html",
TMPL_PATH+"account.html",
)
if err != nil {
panic("can't load hmtl files")
}
var data struct{
User *UserType
Address string
Balance string
}
data.User = User
if data.User != nil {
data.Address = User.AddressHex
balance, err := ClientETH.BalanceAt(context.Background(), User.AddressEth, nil)
if err == nil {
data.Balance = balance.String()
}
} else {
http.Redirect(w, r, "/", 302)
return
}
t.Execute(w, data)
}
func blockchainPresentsDoPage(w http.ResponseWriter, r *http.Request) {
t, err := template.ParseFiles(
TMPL_PATH+"base.html",
TMPL_PATH+"presentsDo.html",
)
if err != nil {
panic("can't load hmtl files")
}
if User == nil {
http.Redirect(w, r, "/login", 302)
return
}
var data struct{
User *UserType
Block *EstateStr
Error string
}
data.User = User
var (
index = new(big.Int)
ok bool
)
num := strings.Replace(r.URL.Path, "/blockchain/presents/do/", "", 1)
index, ok = index.SetString(num, 10)
if !ok {
data.Error = "strconv error"
t.Execute(w, data)
return
}
estate := getEstates(index)
if estate == nil {
data.Error = "estate is nil"
t.Execute(w, data)
return
}
data.Block = estatesToString(estate)
if r.Method == "POST" {
r.ParseForm()
_, err := Instance.CreatePresent(
resetAuth(User),
index,
common.HexToAddress(r.FormValue("address")),
)
if err != nil {
data.Error = err.Error()
t.Execute(w, data)
return
}
data.Error = "Success created"
}
t.Execute(w, data)
}
func blockchainSalesDoPage(w http.ResponseWriter, r *http.Request) {
t, err := template.ParseFiles(
TMPL_PATH+"base.html",
TMPL_PATH+"salesDo.html",
)
if err != nil {
panic("can't load hmtl files")
}
if User == nil {
http.Redirect(w, r, "/login", 302)
return
}
var data struct{
User *UserType
Block *EstateStr
Error string
}
data.User = User
var (
index = new(big.Int)
ok bool
)
num := strings.Replace(r.URL.Path, "/blockchain/sales/do/", "", 1)
index, ok = index.SetString(num, 10)
if !ok {
data.Error = "strconv error"
t.Execute(w, data)
return
}
estate := getEstates(index)
if estate == nil {
data.Error = "estate is nil"
t.Execute(w, data)
return
}
data.Block = estatesToString(estate)
if r.Method == "POST" {
r.ParseForm()
var (
price = new(big.Int)
ok bool
)
price, ok = price.SetString(r.FormValue("price"), 10)
if !ok {
data.Error = "strconv error"
t.Execute(w, data)
return
}
_, err := Instance.CreateSale(
resetAuth(User),
index,
price,
)
if err != nil {
data.Error = err.Error()
t.Execute(w, data)
return
}
data.Error = "Success created"
}
t.Execute(w, data)
}
func blockchainRentsDoPage(w http.ResponseWriter, r *http.Request) {
t, err := template.ParseFiles(
TMPL_PATH+"base.html",
TMPL_PATH+"rentsDo.html",
)
if err != nil {
panic("can't load hmtl files")
}
if User == nil {
http.Redirect(w, r, "/login", 302)
return
}
var data struct{
User *UserType
Block *EstateStr
Error string
}
data.User = User
var (
index = new(big.Int)
ok bool
)
num := strings.Replace(r.URL.Path, "/blockchain/rents/do/", "", 1)
index, ok = index.SetString(num, 10)
if !ok {
data.Error = "strconv error"
t.Execute(w, data)
return
}
rent := getEstates(index)
if rent == nil {
data.Error = "rent is nil"
t.Execute(w, data)
return
}
data.Block = estatesToString(rent)
if r.Method == "POST" {
r.ParseForm()
var (
days = new(big.Int)
price = new(big.Int)
ok bool
)
days, ok = days.SetString(r.FormValue("days"), 10)
if !ok {
data.Error = "strconv error"
t.Execute(w, data)
return
}
price, ok = price.SetString(r.FormValue("price"), 10)
if !ok {
data.Error = "strconv error"
t.Execute(w, data)
return
}
_, err := Instance.CreateRent(
resetAuth(User),
index,
days,
price,
)
if err != nil {
data.Error = err.Error()
t.Execute(w, data)
return
}
data.Error = "Success created"
}
t.Execute(w, data)
}
func blockchainEstatesXPage(w http.ResponseWriter, r *http.Request) {
t, err := template.ParseFiles(
TMPL_PATH+"base.html",
TMPL_PATH+"estatesX.html",
)
if err != nil {
panic("can't load hmtl files")
}
if User == nil {
http.Redirect(w, r, "/login", 302)
return
}
var data struct{
User *UserType
Block *EstateStr
Error string
}
data.User = User
var (
index = new(big.Int)
ok bool
)
num := strings.Replace(r.URL.Path, "/blockchain/estates/", "", 1)
index, ok = index.SetString(num, 10)
if !ok {
data.Error = "strconv error"
t.Execute(w, data)
return
}
estate := getEstates(index)
if estate == nil {
data.Error = "estate is nil"
t.Execute(w, data)
return
}
data.Block = estatesToString(estate)
t.Execute(w, data)
}
func blockchainPresentsXPage(w http.ResponseWriter, r *http.Request) {
t, err := template.ParseFiles(
TMPL_PATH+"base.html",
TMPL_PATH+"presentsX.html",
)
if err != nil {
panic("can't load hmtl files")
}
if User == nil {
http.Redirect(w, r, "/login", 302)
return
}
var data struct{
User *UserType
Block *PresentStr
Error string
}
data.User = User
var (
index = new(big.Int)
ok bool
)
num := strings.Replace(r.URL.Path, "/blockchain/presents/", "", 1)
index, ok = index.SetString(num, 10)
if !ok {
data.Error = "strconv error"
t.Execute(w, data)
return
}
present := getPresents(index)
if present == nil {
data.Error = "present is nil"
t.Execute(w, data)
return
}
data.Block = presentsToString(present)
if r.Method == "POST" {
r.ParseForm()
if r.FormValue("cancel") != "" {
_, err := Instance.CancelPresent(
resetAuth(User),
index,
)
if err != nil {
data.Error = err.Error()
t.Execute(w, data)
return
}
data.Error = "Success cancel"
}
if r.FormValue("confirm") != "" {
_, err := Instance.ConfirmPresent(
resetAuth(User),
index,
)
if err != nil {
data.Error = err.Error()
t.Execute(w, data)
return
}
data.Error = "Success confirm"
}
}
t.Execute(w, data)
}
func blockchainSalesXPage(w http.ResponseWriter, r *http.Request) {
t, err := template.ParseFiles(
TMPL_PATH+"base.html",
TMPL_PATH+"salesX.html",
)
if err != nil {
panic("can't load hmtl files")
}
if User == nil {
http.Redirect(w, r, "/login", 302)
return
}
var data struct{
User *UserType
Block *SaleStr
Error string
InCustomers bool
}
data.User = User
var (
index = new(big.Int)
ok bool
)
num := strings.Replace(r.URL.Path, "/blockchain/sales/", "", 1)
index, ok = index.SetString(num, 10)
if !ok {
data.Error = "strconv error"
t.Execute(w, data)
return
}
sale := getSales(index)
if sale == nil {
data.Error = "sale is nil"
t.Execute(w, data)
return
}
data.Block = salesToString(sale)
for _, cust := range data.Block.Customers {
if cust == User.AddressHex {
data.InCustomers = true
break
}
}
if r.Method == "POST" {
r.ParseForm()
if r.FormValue("cancel") != "" {
_, err := Instance.CancelSale(
resetAuth(User),
index,
)
if err != nil {
data.Error = err.Error()
t.Execute(w, data)
return
}
data.Error = "Success cancel"
}
if r.FormValue("confirm") != "" {
var (
saleTo = new(big.Int)
ok bool
)
saleTo, ok = saleTo.SetString(r.FormValue("addrnum"), 10)
if !ok {
data.Error = "strconv error"
t.Execute(w, data)
return
}
_, err := Instance.ConfirmSale(
resetAuth(User),
index,
saleTo,
)
if err != nil {
data.Error = err.Error()
t.Execute(w, data)
return
}
data.Error = "Success confirm"
}
if r.FormValue("buy") != "" {
var (
price = new(big.Int)
ok bool
)
price, ok = price.SetString(r.FormValue("price"), 10)
if !ok {
data.Error = "strconv error"
t.Execute(w, data)
return
}
auth := resetAuth(User)
if auth == nil {
data.Error = "failed: auth is nil"
t.Execute(w, data)
return
}
auth.Value = price
_, err := Instance.CheckToBuy(
auth,
index,
)
if err != nil {
data.Error = err.Error()
t.Execute(w, data)
return
}
data.Error = "Success buy request"
}
if r.FormValue("close") != "" {
_, err := Instance.CancelToBuy(
resetAuth(User),
index,
)
if err != nil {
data.Error = err.Error()
t.Execute(w, data)
return
}
data.Error = "Success close buy request"
}
}
t.Execute(w, data)
}
func blockchainRentsXPage(w http.ResponseWriter, r *http.Request) {
t, err := template.ParseFiles(
TMPL_PATH+"base.html",
TMPL_PATH+"rentsX.html",
)
if err != nil {
panic("can't load hmtl files")
}
if User == nil {
http.Redirect(w, r, "/login", 302)
return
}
var data struct{
User *UserType
Block *RentStr
Error string
Confirmed bool
}
data.User = User
var (
index = new(big.Int)
ok bool
)
num := strings.Replace(r.URL.Path, "/blockchain/rents/", "", 1)
index, ok = index.SetString(num, 10)
if !ok {
data.Error = "strconv error"
t.Execute(w, data)
return
}
rent := getRents(index)
if rent == nil {
data.Error = "rent is nil"
t.Execute(w, data)
return
}
data.Block = rentsToString(rent)
if data.Block.Renter != "0x0000000000000000000000000000000000000000" {
data.Confirmed = true
}
if r.Method == "POST" {
r.ParseForm()
if r.FormValue("cancel") != "" {
_, err := Instance.CancelRent(
resetAuth(User),
index,
)
if err != nil {
data.Error = err.Error()
t.Execute(w, data)
return
}
data.Error = "Success cancel"
}
if r.FormValue("confirm") != "" {
auth := resetAuth(User)
if auth == nil {
data.Error = "failed: auth is nil"
t.Execute(w, data)
return
}
auth.Value = rent.Money
_, err := Instance.ToRent(
auth,
index,
)
if err != nil {
data.Error = err.Error()
t.Execute(w, data)
return
}
data.Error = "Success confirm"
}
if r.FormValue("finish") != "" {
_, err := Instance.FinishRent(
resetAuth(User),
index,
)
if err != nil {
data.Error = err.Error()
t.Execute(w, data)
return
}
data.Error = "Success finish"
}
}
t.Execute(w, data)
}
func blockchainPage(w http.ResponseWriter, r *http.Request) {
t, err := template.ParseFiles(
TMPL_PATH+"base.html",
TMPL_PATH+"blockchain.html",
)
if err != nil {
panic("can't load hmtl files")
}
if User == nil {
http.Redirect(w, r, "/login", 302)
return
}
var data struct{
User *UserType
IsAdmin bool
Error string
}
data.User = User
iamAdmin, err := Instance.IamAdmin(&bind.CallOpts{From: User.AddressEth})
if err != nil {
data.Error = err.Error()
t.Execute(w, data)
return
}
if iamAdmin {
data.IsAdmin = true
}
if r.Method == "POST" {
r.ParseForm()
var (
squere = new(big.Int)
usefulSquere = new(big.Int)
ok bool
)
squere, ok = squere.SetString(r.FormValue("squere"), 10)
if !ok {
data.Error = "strconv error 1"
t.Execute(w, data)
return
}
usefulSquere, ok = usefulSquere.SetString(r.FormValue("usefulsquere"), 10)
if !ok {
data.Error = "strconv error 2"
t.Execute(w, data)
return
}
_, err := Instance.CreateEstate(
resetAuth(User),
User.AddressEth,
r.FormValue("info"),
squere,
usefulSquere,
)
if err != nil {
data.Error = err.Error()
t.Execute(w, data)
return
}
data.Error = "Success created"
}
t.Execute(w, data)
}
func blockchainEstatesPage(w http.ResponseWriter, r *http.Request) {
t, err := template.ParseFiles(
TMPL_PATH+"base.html",
TMPL_PATH+"estates.html",
)
if err != nil {
panic("can't load hmtl files")
}
if User == nil {
http.Redirect(w, r, "/login", 302)
return
}
var data struct{
Error string
Blocks []uint64
Address string
User *UserType
}
data.User = User
data.Address = User.AddressHex
if r.Method == "POST" {
data.Address = r.FormValue("address")
}
var inc = big.NewInt(1)
num, err := Instance.GetEstatesNumber(&bind.CallOpts{From: User.AddressEth})
if err != nil {
data.Error = err.Error()
t.Execute(w, data)
return
}
for index := big.NewInt(0); index.Cmp(num) == -1; index.Add(index, inc) {
block := getEstates(index)
if block == nil {
data.Error = "data is nil"
t.Execute(w, data)
return
}
if data.Address != "all" && strings.ToLower(data.Address) != strings.ToLower(block.Owner.Hex()) {
continue
}
data.Blocks = append(data.Blocks, index.Uint64())
}
t.Execute(w, data)
}
func blockchainPresentsPage(w http.ResponseWriter, r *http.Request) {
t, err := template.ParseFiles(
TMPL_PATH+"base.html",
TMPL_PATH+"presents.html",
)
if err != nil {
panic("can't load hmtl files")
}
if User == nil {
http.Redirect(w, r, "/login", 302)
return
}
var data struct{
Error string
Blocks []uint64
Address string
User *UserType
}
data.User = User
data.Address = User.AddressHex
if r.Method == "POST" {
data.Address = r.FormValue("address")
}
var inc = big.NewInt(1)
num, err := Instance.GetPresentsNumber(&bind.CallOpts{From: User.AddressEth})
if err != nil {
data.Error = err.Error()
t.Execute(w, data)
return
}
for index := big.NewInt(0); index.Cmp(num) == -1; index.Add(index, inc) {
block := getPresents(index)
if block == nil {
data.Error = "data is nil"
t.Execute(w, data)
return
}
if block.Finished {
continue
}
if data.Address != "all" &&
strings.ToLower(data.Address) != strings.ToLower(block.AddressFrom.Hex()) &&
strings.ToLower(data.Address) != strings.ToLower(block.AddressTo.Hex()) {
continue
}
data.Blocks = append(data.Blocks, index.Uint64())
}
t.Execute(w, data)
}
func blockchainSalesPage(w http.ResponseWriter, r *http.Request) {
t, err := template.ParseFiles(
TMPL_PATH+"base.html",
TMPL_PATH+"sales.html",
)
if err != nil {
panic("can't load hmtl files")
}
if User == nil {
http.Redirect(w, r, "/login", 302)
return
}
var data struct{
Error string
Blocks []uint64
Address string
User *UserType
}
data.User = User
data.Address = User.AddressHex
if r.Method == "POST" {
data.Address = r.FormValue("address")
}
var inc = big.NewInt(1)
num, err := Instance.GetSalesNumber(&bind.CallOpts{From: User.AddressEth})
if err != nil {
data.Error = err.Error()
t.Execute(w, data)
return
}
for index := big.NewInt(0); index.Cmp(num) == -1; index.Add(index, inc) {
block := getSales(index)
if block == nil {
data.Error = "data is nil"
t.Execute(w, data)
return
}
if block.Finished {
continue
}
if data.Address != "all" &&
strings.ToLower(data.Address) != strings.ToLower(block.Owner.Hex()) {
continue
}
data.Blocks = append(data.Blocks, index.Uint64())
}
t.Execute(w, data)
}
func blockchainRentsPage(w http.ResponseWriter, r *http.Request) {
t, err := template.ParseFiles(
TMPL_PATH+"base.html",
TMPL_PATH+"rents.html",
)
if err != nil {
panic("can't load hmtl files")
}
if User == nil {
http.Redirect(w, r, "/login", 302)
return
}
var data struct{
Error string
Blocks []uint64
Address string
User *UserType
}
data.User = User
data.Address = User.AddressHex
if r.Method == "POST" {
data.Address = r.FormValue("address")
}
var inc = big.NewInt(1)
num, err := Instance.GetRentsNumber(&bind.CallOpts{From: User.AddressEth})
if err != nil {
data.Error = err.Error()
t.Execute(w, data)
return
}
for index := big.NewInt(0); index.Cmp(num) == -1; index.Add(index, inc) {
block := getRents(index)
if block == nil {
data.Error = "data is nil"
t.Execute(w, data)
return
}
if block.Finished {
continue
}
if data.Address != "all" &&
strings.ToLower(data.Address) != strings.ToLower(block.Owner.Hex()) &&
strings.ToLower(data.Address) != strings.ToLower(block.Renter.Hex()) {
continue
}
data.Blocks = append(data.Blocks, index.Uint64())
}
t.Execute(w, data)
}

View File

@ -0,0 +1,304 @@
package main
import (
contract "blockchain-from-scratch/contracts"
"context"
"crypto/ecdsa"
"io/ioutil"
"math/big"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
)
type UserType struct {
Purse string
AddressHex string
AddressEth common.Address
PublicKey *ecdsa.PublicKey
PrivateKey *ecdsa.PrivateKey
}
type Estate struct {
Id *big.Int
Owner common.Address
Info string
Squere *big.Int
UsefulSquere *big.Int
RenterAddress common.Address
PresentStatus bool
SaleStatus bool
RentStatus bool
}
type Present struct {
Id *big.Int
EstateId *big.Int
AddressFrom common.Address
AddressTo common.Address
Finished bool
}
type Sale struct {
Id *big.Int
EstateId *big.Int
Owner common.Address
Price *big.Int
Customers []common.Address
Prices []*big.Int
Finished bool
}
type Rent struct {
Id *big.Int
EstateId *big.Int
Owner common.Address
Renter common.Address
Time *big.Int
Money *big.Int
DeadLine *big.Int
Finished bool
}
var (
User *UserType
ClientETH = connectToETH("http://127.0.0.1:5555")
Instance = newContract(
common.HexToAddress(readFile("contract.address")),
ClientETH,
)
)
func loadUser(purse string) *UserType {
priv, err := crypto.HexToECDSA(purse)
if err != nil {
return nil
}
pub, ok := priv.Public().(*ecdsa.PublicKey)
if !ok {
return nil
}
addressHex := crypto.PubkeyToAddress(*pub).Hex()
addressEth := common.HexToAddress(addressHex)
return &UserType{
Purse: purse,
AddressHex: addressHex,
AddressEth: addressEth,
PublicKey: pub,
PrivateKey: priv,
}
}
func newContract(contractAddr common.Address, clientEth *ethclient.Client) *contract.Contract {
instance, err := contract.NewContract(contractAddr, clientEth)
if err != nil {
return nil
}
return instance
}
func connectToETH(address string) *ethclient.Client {
client, err := ethclient.Dial(address)
if err != nil {
return nil
}
return client
}
func readFile(filename string) string {
data, err := ioutil.ReadFile(filename)
if err != nil {
return ""
}
return string(data)
}
func resetAuth(user *UserType) *bind.TransactOpts {
nonce, err := ClientETH.PendingNonceAt(context.Background(), crypto.PubkeyToAddress(*user.PublicKey))
if err != nil {
return nil
}
gasPrice, err := ClientETH.SuggestGasPrice(context.Background())
if err != nil {
return nil
}
auth := bind.NewKeyedTransactor(user.PrivateKey)
auth.Nonce = big.NewInt(int64(nonce))
auth.Value = big.NewInt(0)
auth.GasLimit = uint64(3000000)
auth.GasPrice = gasPrice
return auth
}
func getEstates(index *big.Int) *Estate {
// (*big.Int, common.Address, string, *big.Int, *big.Int, common.Address, error)
id, owner, info, squere, usefulsquere, renteraddress, err := Instance.GetEstates(&bind.CallOpts{From: User.AddressEth}, index)
if err != nil {
return nil
}
presentS, saleS, rentS, err := Instance.GetEstatesStatuses(&bind.CallOpts{From: User.AddressEth}, index)
if err != nil {
return nil
}
return &Estate{
Id: id,
Owner: owner,
Info: info,
Squere: squere,
UsefulSquere: usefulsquere,
RenterAddress: renteraddress,
PresentStatus: presentS,
SaleStatus: saleS,
RentStatus: rentS,
}
}
func getPresents(index *big.Int) *Present {
// (*big.Int, common.Address, common.Address, bool, error)
id, from, to, finished, err := Instance.GetPresents(&bind.CallOpts{From: User.AddressEth}, index)
if err != nil {
return nil
}
return &Present{
Id: index,
EstateId: id,
AddressFrom: from,
AddressTo: to,
Finished: finished,
}
}
func getSales(index *big.Int) *Sale {
// (*big.Int, common.Address, *big.Int, []common.Address, []*big.Int, bool, error)
id, owner, price, customers, prices, finished, err := Instance.GetSales(&bind.CallOpts{From: User.AddressEth}, index)
if err != nil {
return nil
}
return &Sale{
Id: index,
EstateId: id,
Owner: owner,
Price: price,
Customers: customers,
Prices: prices,
Finished: finished,
}
}
func getRents(index *big.Int) *Rent {
// (*big.Int, common.Address, common.Address, *big.Int, *big.Int, *big.Int, bool, error)
id, owner, renter, time, money, deadline, finished, err := Instance.GetRents(&bind.CallOpts{From: User.AddressEth}, index)
if err != nil {
return nil
}
return &Rent{
Id: index,
EstateId: id,
Owner: owner,
Renter: renter,
Time: time,
Money: money,
DeadLine: deadline,
Finished: finished,
}
}
type EstateStr struct {
Id *big.Int
Owner string
Info string
Squere *big.Int
UsefulSquere *big.Int
RenterAddress string
PresentStatus bool
SaleStatus bool
RentStatus bool
}
func estatesToString(estate *Estate) *EstateStr {
return &EstateStr{
Id: estate.Id,
Owner: estate.Owner.Hex(),
Info: estate.Info,
Squere: estate.Squere,
UsefulSquere: estate.UsefulSquere,
RenterAddress: estate.RenterAddress.Hex(),
PresentStatus: estate.PresentStatus,
SaleStatus: estate.SaleStatus,
RentStatus: estate.RentStatus,
}
}
type PresentStr struct {
Id *big.Int
EstateId *big.Int
AddressFrom string
AddressTo string
Finished bool
}
func presentsToString(present *Present) *PresentStr {
return &PresentStr{
Id: present.Id,
EstateId: present.EstateId,
AddressFrom: present.AddressFrom.Hex(),
AddressTo: present.AddressTo.Hex(),
Finished: present.Finished,
}
}
type SaleStr struct {
Id *big.Int
EstateId *big.Int
Owner string
Price *big.Int
Customers []string
Prices []*big.Int
Finished bool
}
func salesToString(sale *Sale) *SaleStr {
var customers []string
for _, cust := range sale.Customers {
customers = append(customers, cust.Hex())
}
return &SaleStr{
Id: sale.Id,
EstateId: sale.EstateId,
Owner: sale.Owner.Hex(),
Price: sale.Price,
Customers: customers,
Prices: sale.Prices,
Finished: sale.Finished,
}
}
type RentStr struct {
Id *big.Int
EstateId *big.Int
Owner string
Renter string
Time *big.Int
Money *big.Int
DeadLine *big.Int
Finished bool
}
func rentsToString(rent *Rent) *RentStr {
return &RentStr{
Id: rent.Id,
EstateId: rent.EstateId,
Owner: rent.Owner.Hex(),
Renter: rent.Renter.Hex(),
Time: rent.Time,
Money: rent.Money,
DeadLine: rent.DeadLine,
Finished: rent.Finished,
}
}

129
cmd/node/node.go Normal file
View File

@ -0,0 +1,129 @@
package main
import (
bc "blockchain-from-scratch/blockchain"
"encoding/json"
"fmt"
"os"
"strings"
nt "blockchain-from-scratch/network"
)
func init() {
if len(os.Args) < 2 {
panic("failed: len(os.Args) < 2")
}
var (
serveStr = ""
addrStr = ""
userNewStr = ""
userLoadStr = ""
chainNewStr = ""
chainLoadStr = ""
)
var (
serveExist = false
addrExist = false
userNewExist = false
userLoadExist = false
chainNewExist = false
chainLoadExist = false
)
for i := 1; i < len(os.Args); i++ {
arg := os.Args[i]
switch {
case strings.HasPrefix(arg, "-serve:"):
serveStr = strings.Replace(arg, "-serve:", "", 1)
serveExist = true
case strings.HasPrefix(arg, "-loadaddr:"):
addrStr = strings.Replace(arg, "-loadaddr:", "", 1)
addrExist = true
case strings.HasPrefix(arg, "-newuser:"):
userNewStr = strings.Replace(arg, "-newuser:", "", 1)
userNewExist = true
case strings.HasPrefix(arg, "-loaduser:"):
userLoadStr = strings.Replace(arg, "-loaduser:", "", 1)
userLoadExist = true
case strings.HasPrefix(arg, "-newchain:"):
chainNewStr = strings.Replace(arg, "-newchain:", "", 1)
chainNewExist = true
case strings.HasPrefix(arg, "-loadchain:"):
chainLoadStr = strings.Replace(arg, "-loadchain:", "", 1)
chainLoadExist = true
}
}
if !(userNewExist || userLoadExist) || !(chainNewExist || chainLoadExist) ||
!serveExist || !addrExist {
panic("failed: !(userNewExist || userLoadExist)" +
"|| !(chainNewExist || chainLoadExist) || !serveExist || !addrExist")
}
Serve = serveStr
var addresses []string
err := json.Unmarshal([]byte(readFile(addrStr)), &addresses)
if err != nil {
panic("failed: load addresses")
}
var mapaddr = make(map[string]bool)
for _, addr := range addresses {
if addr == Serve {
continue
}
if _, ok := mapaddr[addr]; ok {
continue
}
mapaddr[addr] = true
Addresses = append(Addresses, addr)
}
if userNewExist {
User = userNew(userNewStr)
}
if userLoadExist {
User = userLoad(userLoadStr)
}
if User == nil {
panic("failed: load user")
}
if chainNewExist {
Filename = chainNewStr
Chain = chainNew(chainNewStr)
}
if chainLoadExist {
Filename = chainLoadStr
Chain = chainLoad(chainLoadStr)
}
if Chain == nil {
panic("failed: load chain")
}
Block = bc.NewBlock(User.Address(), Chain.LastHash())
}
func main() {
nt.Listen(Serve, handleServer)
for {
fmt.Scanln()
}
}
func chainNew(filename string) *bc.BlockChain {
err := bc.NewChain(filename, User.Address())
if err != nil {
return nil
}
return bc.LoadChain(filename)
}
func chainLoad(filename string) *bc.BlockChain {
chain := bc.LoadChain(filename)
if chain == nil {
return nil
}
return chain
}

290
cmd/node/serve.go Normal file
View File

@ -0,0 +1,290 @@
package main
import (
bc "blockchain-from-scratch/blockchain"
"bytes"
"database/sql"
"encoding/hex"
"fmt"
"io"
"os"
"sort"
"strconv"
"strings"
"sync"
nt "blockchain-from-scratch/network"
_ "github.com/mattn/go-sqlite3"
)
var (
Filename string
Serve string
Chain *bc.BlockChain
Block *bc.Block
Mutex sync.Mutex
)
var (
IsMining bool
BreakMining = make(chan bool)
)
func handleServer(conn nt.Conn, pack *nt.Package) {
nt.Handle(ADD_BLOCK, conn, pack, addBlock)
nt.Handle(ADD_TRNSX, conn, pack, addTransaction)
nt.Handle(GET_BLOCK, conn, pack, getBlock)
nt.Handle(GET_LHASH, conn, pack, getLastHash)
nt.Handle(GET_BLNCE, conn, pack, getBalance)
nt.Handle(GET_CSIZE, conn, pack, getChainSize)
}
func getChainSize(pack *nt.Package) string {
return fmt.Sprintf("%d", Chain.Size())
}
func addBlock(pack *nt.Package) string {
splited := strings.Split(pack.Data, SEPARATOR)
if len(splited) != 3 {
return "fail"
}
block := bc.DeserializeBlock(splited[2])
if !block.IsValid(Chain, Chain.Size()) {
currSize := Chain.Size()
num, err := strconv.Atoi(splited[1])
if err != nil {
return "fail"
}
if currSize < uint64(num) {
go compareChains(splited[0], uint64(num))
return "ok"
}
return "fail"
}
Mutex.Lock()
Chain.AddBlock(block)
Block = bc.NewBlock(User.Address(), Chain.LastHash())
Mutex.Unlock()
if IsMining {
BreakMining <- true
IsMining = false
}
return "ok"
}
func compareChains(address string, num uint64) {
filename := "temp_" + hex.EncodeToString(bc.GenerateRandomBytes(8))
file, err := os.Create(filename)
if err != nil {
return
}
file.Close()
defer func() {
os.Remove(filename)
}()
res := nt.Send(address, &nt.Package{
Option: GET_BLOCK,
Data: fmt.Sprintf("%d", 0),
})
if res == nil {
return
}
genesis := bc.DeserializeBlock(res.Data)
if genesis == nil {
return
}
if !bytes.Equal(genesis.CurrHash, hashBlock(genesis)) {
return
}
db, err := sql.Open("sqlite3", filename)
if err != nil {
return
}
defer db.Close()
_, err = db.Exec(bc.CREATE_TABLE)
chain := &bc.BlockChain{
DB: db,
}
chain.AddBlock(genesis)
defer func() {
chain.DB.Close()
}()
for i := uint64(1); i < num; i++ {
res := nt.Send(address, &nt.Package{
Option: GET_BLOCK,
Data: fmt.Sprintf("%d", i),
})
if res == nil {
return
}
block := bc.DeserializeBlock(res.Data)
if block == nil {
return
}
if !block.IsValid(chain, i) {
return
}
chain.AddBlock(block)
}
Mutex.Lock()
Chain.DB.Close()
os.Remove(Filename)
copyFile(filename, Filename)
Chain = bc.LoadChain(Filename)
Block = bc.NewBlock(User.Address(), Chain.LastHash())
Mutex.Unlock()
if IsMining {
BreakMining <- true
IsMining = false
}
return
}
func hashBlock(block *bc.Block) []byte {
var tempHash []byte
for _, tx := range block.Transactions {
tempHash = bc.HashSum(bytes.Join(
[][]byte{
tempHash,
tx.CurrHash,
},
[]byte{},
))
}
var list []string
for hash := range block.Mapping {
list = append(list, hash)
}
sort.Strings(list)
for _, hash := range list {
tempHash = bc.HashSum(bytes.Join(
[][]byte{
tempHash,
[]byte(hash),
bc.ToBytes(block.Mapping[hash]),
},
[]byte{},
))
}
return bc.HashSum(bytes.Join(
[][]byte{
tempHash,
bc.ToBytes(uint64(block.Difficulty)),
block.PrevHash,
[]byte(block.Miner),
[]byte(block.TimeStamp),
},
[]byte{},
))
}
func copyFile(src, dst string) error {
in, err := os.Open(src)
if err != nil {
return err
}
defer in.Close()
out, err := os.Create(dst)
if err != nil {
return err
}
defer out.Close()
_, err = io.Copy(out, in)
if err != nil {
return err
}
return out.Close()
}
func getBlock(pack *nt.Package) string {
num, err := strconv.Atoi(pack.Data)
if err != nil {
return ""
}
size := Chain.Size()
if uint64(num) < size {
return selectBlock(Chain, num)
}
return ""
}
func getLastHash(pack *nt.Package) string {
return bc.Base64Encode(Chain.LastHash())
}
func getBalance(pack *nt.Package) string {
return fmt.Sprintf("%d", Chain.Balance(pack.Data, Chain.Size()))
}
func selectBlock(chain *bc.BlockChain, i int) string {
var block string
row := chain.DB.QueryRow("SELECT Block FROM BlockChain WHERE Id=$1", i+1)
row.Scan(&block)
return block
}
func addTransaction(pack *nt.Package) string {
var tx = bc.DeserializeTX(pack.Data)
if tx == nil || len(Block.Transactions) == bc.TXS_LIMIT {
return "fail"
}
Mutex.Lock()
err := Block.AddTransaction(Chain, tx)
Mutex.Unlock()
if err != nil {
return "fail"
}
if len(Block.Transactions) == bc.TXS_LIMIT {
go func() {
Mutex.Lock()
block := *Block
IsMining = true
Mutex.Unlock()
res := (&block).Accept(Chain, User, BreakMining)
Mutex.Lock()
IsMining = false
if res == nil && bytes.Equal(block.PrevHash, Block.PrevHash) {
Chain.AddBlock(&block)
pushBlockToNet(&block)
}
Block = bc.NewBlock(User.Address(), Chain.LastHash())
Mutex.Unlock()
}()
}
return "ok"
}
func pushBlockToNet(block *bc.Block) {
var (
sblock = bc.SerializeBlock(block)
msg = Serve + SEPARATOR + fmt.Sprintf("%d", Chain.Size()) + SEPARATOR + sblock
)
for _, addr := range Addresses {
go nt.Send(addr, &nt.Package{
Option: ADD_BLOCK,
Data: msg,
})
}
}

60
cmd/node/values.go Normal file
View File

@ -0,0 +1,60 @@
package main
import (
bc "blockchain-from-scratch/blockchain"
"io/ioutil"
)
var (
Addresses []string
User *bc.User
)
const (
SEPARATOR = "_SEPARATOR_"
)
const (
ADD_BLOCK = iota + 1
ADD_TRNSX
GET_BLOCK
GET_LHASH
GET_BLNCE
GET_CSIZE
)
func userNew(filename string) *bc.User {
user := bc.NewUser()
if user == nil {
return nil
}
err := writeFile(filename, user.Purse())
if err != nil {
return nil
}
return user
}
func userLoad(filename string) *bc.User {
priv := readFile(filename)
if priv == "" {
return nil
}
user := bc.LoadUser(priv)
if user == nil {
return nil
}
return user
}
func writeFile(filename string, data string) error {
return ioutil.WriteFile(filename, []byte(data), 0644)
}
func readFile(filename string) string {
data, err := ioutil.ReadFile(filename)
if err != nil {
return ""
}
return string(data)
}