일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- MySQL
- github
- erc
- server
- 네트워크
- 트랜잭션
- ethers
- 스마트 컨트랙트
- solidity
- git
- geth
- ERC165
- truffle
- JavaScript
- 이더리움
- 솔리디티
- web
- Ethereum
- 블록체인
- Programming
- web3
- ERC20
- Docker
- tcp
- web3.js
- blockchain
- erc721
- Python
- NFT
- 제어의역전
- Today
- Total
멍개의 연구소
[블록체인] 블록체인을 위한 고랭(golang) 본문
go는 분산환경에서 특화된 프로그래밍 언어입니다. 블록체인 기반으로 된 대부분이 go언어로 작성되어 있습니다.
필자는 hyperledger fabric에서 체인코드를 만들기 위해 go를 공부합니다.
먼저 프로그래밍의 가장 기본인 조건문과 반복문
· 조건문
package main
import "fmt"
func main() {
var i int = 11;
if i % 2 == 0 {
fmt.Println("짝수")
fmt.Println(i)
} else {
fmt.Println("홀수")
fmt.Println(i)
}
}
go 자체적으로 print함수가 있지만 별로 이쁘지 않아서 fmt를 이용하여 출력합니다. 일단 연산자는 C와 동일하게 사용됩니다. &&, || 등. go에서도 포인터 개념이 등장합니다.
· 반복문
package main
import "fmt"
func main() {
myMap := map[string]string{
"A": "apple",
"B": "Banana",
"C": "Charlie",
}
for key, val := range myMap{
fmt.Println(key, val)
}
myArray := [4]int{1,2,3,4}
for key, val := range myArray{
fmt.Println(key, val)
}
}
go에서도 map이라고 하는 자료형과 array 자료형을 제공합니다. map은 json이나 dict과 같은 자료형으로 이해하면 됩니다. python처럼 range를 이용하여 루프를 돌릴 수 있습니다. map에서는 key, array에서는 index를 출력할 수 있습니다.
· 자료형
go에서 다섯 종류의 타입을 가지고 있습니다. 불리언, 문자열, 정수형, 실수형, 기타타입이 있습니다.
bool
string
int
float
byte
rune
[]
package main
func main(){
var i int = 100
var j uint = uint(i)
var f float32 = float32(i)
println(f, j)
str := "AAA"
bytes := []byte(str)
str2 := string(bytes)
println(bytes, str2)
}
package main
import "fmt"
func main() {
var a [3]int
a[0] = 0
a[1] = 10
a[2] = 20
fmt.Println(a)
fmt.Println(a[1])
fmt.Println(a[2])
var b []int
b = []int{1,2,3,4,5}
fmt.Println(b)
fmt.Println(len(b), cap(b))
}
go에서는 변수를 선언하는 방법이 두 가지 입니다.
var [변수이름] [타입] = 값;
[변수이름] := 값;
var 대신 const키워드를 사용하면 변수가 아니라 상수로 사용할 수 있습니다.
package main
import "fmt"
func main() {
a := map[string]string{
}
a["test"] = "10"
a["t"] = "123"
fmt.Println(a)
}
map 자료형은 key-value의 타입을 정해줘야 합니다.
· 구조체
package main
import "fmt"
type Person struct{
name string
age int
}
func main() {
p1 := Person{}
p1.name = "pjt"
p1.age = 26
fmt.Println(p1)
fmt.Println(p1.name)
fmt.Println(p1.age)
p2 := new(Person)
p2.name = "contructor pjt"
p2.age = 26
fmt.Println(p2)
}
구조체의 사용법은 C와 거의유사합니다. new를 이용하여 객체의 형태로 생성해도 ->가 아닌 .로 참조합니다.
· method
package main
import "fmt"
type Rect struct {
width, height int
}
func (r Rect) areaValue() int{
return r.width * r.height
}
func (r *Rect) areaPointer() int{
return r.width * r.height
}
func main() {
rect1 := Rect{10, 20}
area := rect1.areaValue()
fmt.Println(area)
rect2 := new(Rect)
rect2.width = 11
rect2.height = 20
area2 := rect2.areaPointer()
fmt.Println(area2)
}
메소드를 만들 땐 func와 함수이름 사이에 receiver라고 하는 것을 넣어줍니다. receiver를 만들땐 value 형태와 pointer 형태로 만들수 있습니다. 이 둘의 차이는 call-by-value와 call-by-reference의 차이입니다.
· 인터페이스
package main
import "fmt"
type Rect struct {
width, height int
}
type Shape interface {
area() int
}
func (r *Rect) area() int {
return r.width * r.height
}
func totalArea(shapes ...Shape) int {
var area int
for _, s := range shapes {
area += s.area()
}
return area
}
func main() {
a := new(Rect)
a.width = 10
a.height = 20
b := new(Rect)
b.width = 100
b.height = 200
fmt.Println(totalArea(a, b))
}
struct는 속성값을 가지고 있는 것이라면 interface는 메소드를 가지고 있습니다. 구조체에 억지로 인터페이스를 만들려고 한게 느껴지는 부분입니다.
· 예외처리
package main
import (
"fmt"
"os"
//"log"
)
type error interface {
Error() string
}
func main(){
ff, err := os.Open("./test.txt")
switch err.(type) {
default: // no error
fmt.Println(12345)
case error:
fmt.Println(123)
log.Fatal(err.Error())
}
fmt.Println(ff)
}
go에서는 try~ catch, except를 제공하지 않고 에러를 반환하는 형태로 코드를 작성합니다.
· defer/panic
package main
import "fmt"
func test() {
fmt.Println("test")
}
func main() {
defer test()
fmt.Println(1)
fmt.Println(2)
// panic(1)
fmt.Println(3)
}
# 실행결과
1
2
3
test
defer는 해당 코드를 바로 시작하지 않고 함수 호출이 끝났을 때 호출됩니다. 만약 panic 주석을 풀면 다음과 같이 결과를 보여줍니다.
1
2
test
panic: 1
goroutine 1 [running]:
main.main()
/Users/pjt/Desktop/go/src/defer.go:13 +0xdd
exit status 2
· 고루틴
고에서는 고루틴을 이용하면 매우 편하게 비동기적인 실행을 할 수 있습니다.
package main
import (
"fmt"
"time"
"sync"
"runtime"
)
func say(s string){
for i := 0 ; i < 100 ; i ++ {
fmt.Println(s, "***", i)
}
}
//고루틴
func main() {
go say("test0")
go say("test1")
go say("test2")
go say("test3")
time.Sleep(time.Second *3)
}
package main
import (
"fmt"
"sync"
"runtime"
)
func say(s string){
for i := 0 ; i < 100 ; i ++ {
fmt.Println(s, "***", i)
}
}
// 익명함수 고루틴
func main() {
runtime.GOMAXPROCS(4) // 4개의 CPU를 이용하여 고루틴 처리, go는 기본적으로 CPU 1개를 사용하여 처리
var wait sync.WaitGroup
wait.Add(2)
go func () {
defer wait.Done()
fmt.Println("hello")
} ()
go func () {
defer wait.Done()
fmt.Println("world")
}()
wait.Wait()
}
고루틴은 비동기이기 때문에 코드실행이 완료되기 전에 함수가 끝나버리면 그대로 종료됩니다. 하지만 sync.WaitGroup를 이용하면 비동기로 호출되는 것들을 기다릴 수 있습니다.
· 채널
package main
import "fmt"
func main() {
ch := make(chan int, 10)
go func() {
fmt.Println("goroutine")
ch <- 123
}()
fmt.Println(<-ch)
}
make를 이용하여 ch 변수에 int형 데이터를 받겠다고 채널을 만들어 줍니다. 10은 버퍼크기 입니다.
ch <- 값은 ch로 값을 보내는 것을 의미합니다. <- ch는 ch 채널의 값을 가져옵니다.
package main
import "fmt"
func main() {
ch := make(chan int, 10)
ch <- 11
ch <- 12
ch <- 13
ch <- 14
ch <- 15
ch <- 16
ch <- 17
ch <- 18
ch <- 19
ch <- 20
fmt.Println(<-ch)
fmt.Println(<-ch)
fmt.Println(<-ch)
fmt.Println(<-ch)
}
여기서 10개의 값을 채널로 보내게 됩니다. 만약 make(chan int, 10보다 작은수)로 채널을 생성하면 에러가 발생합니다.
$ go run channel.go
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.main()
/Users/pjt/Desktop/go/src/channel.go:13 +0xe9
exit status 2
빠르게 go를 알아보았습니다.
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/*
* The sample smart contract for documentation topic:
* Writing Your First Blockchain Application
*/
package main
/* Imports
* 4 utility libraries for formatting, handling bytes, reading and writing JSON, and string manipulation
* 2 specific Hyperledger Fabric specific libraries for Smart Contracts
*/
import (
"bytes"
"encoding/json"
"fmt"
"strconv"
"github.com/hyperledger/fabric/core/chaincode/shim"
sc "github.com/hyperledger/fabric/protos/peer"
)
// Define the Smart Contract structure
type SmartContract struct {
}
// Define the car structure, with 4 properties. Structure tags are used by encoding/json library
type Car struct {
Make string `json:"make"`
Model string `json:"model"`
Colour string `json:"colour"`
Owner string `json:"owner"`
}
/*
* The Init method is called when the Smart Contract "fabcar" is instantiated by the blockchain network
* Best practice is to have any Ledger initialization in separate function -- see initLedger()
*/
func (s *SmartContract) Init(APIstub shim.ChaincodeStubInterface) sc.Response {
return shim.Success(nil)
}
/*
* The Invoke method is called as a result of an application request to run the Smart Contract "fabcar"
* The calling application program has also specified the particular smart contract function to be called, with arguments
*/
func (s *SmartContract) Invoke(APIstub shim.ChaincodeStubInterface) sc.Response {
// Retrieve the requested Smart Contract function and arguments
function, args := APIstub.GetFunctionAndParameters()
// Route to the appropriate handler function to interact with the ledger appropriately
if function == "queryCar" {
return s.queryCar(APIstub, args)
} else if function == "initLedger" {
return s.initLedger(APIstub)
}else if function == "createCar" {
return s.createCar(APIstub, args)
}else if function == "testCreate" {
return s.testCreate(APIstub, args)
}else if function == "queryAllCars" {
return s.queryAllCars(APIstub)
} else if function == "changeCarOwner" {
return s.changeCarOwner(APIstub, args)
}
return shim.Error("Invalid Smart Contract function name.")
}
func (s *SmartContract) queryCar(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {
if len(args) != 1 {
return shim.Error("Incorrect number of arguments. Expecting 1")
}
carAsBytes, _ := APIstub.GetState(args[0])
return shim.Success(carAsBytes)
}
func (s *SmartContract) initLedger(APIstub shim.ChaincodeStubInterface) sc.Response {
cars := []Car{
Car{Make: "Toyota", Model: "Prius", Colour: "blue", Owner: "Tomoko"},
Car{Make: "Ford", Model: "Mustang", Colour: "red", Owner: "Brad"},
Car{Make: "Hyundai", Model: "Tucson", Colour: "green", Owner: "Jin Soo"},
Car{Make: "Volkswagen", Model: "Passat", Colour: "yellow", Owner: "Max"},
Car{Make: "Tesla", Model: "S", Colour: "black", Owner: "Adriana"},
Car{Make: "Peugeot", Model: "205", Colour: "purple", Owner: "Michel"},
Car{Make: "Chery", Model: "S22L", Colour: "white", Owner: "Aarav"},
Car{Make: "Fiat", Model: "Punto", Colour: "violet", Owner: "Pari"},
Car{Make: "Tata", Model: "Nano", Colour: "indigo", Owner: "Valeria"},
Car{Make: "Holden", Model: "Barina", Colour: "brown", Owner: "Shotaro"},
}
i := 0
for i < len(cars) {
fmt.Println("i is ", i)
carAsBytes, _ := json.Marshal(cars[i])
APIstub.PutState("CAR"+strconv.Itoa(i), carAsBytes)
fmt.Println("Added", cars[i])
i = i + 1
}
return shim.Success(nil)
}
func (s *SmartContract) createCar(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {
if len(args) != 5 {
return shim.Error("Incorrect number of arguments. Expecting 5")
}
var car = Car{Make: args[1], Model: args[2], Colour: args[3], Owner: args[4]}
carAsBytes, _ := json.Marshal(car)
APIstub.PutState(args[0], carAsBytes)
return shim.Success(nil)
}
func (s *SmartContract) testCreate(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {
if len(args) != 5 {
return shim.Error("Incorrect number of arguments. Expecting 5")
}
var cars.
func main() {
// Create a new Smart Contract
err := shim.Start(new(SmartContract))
if err != nil {
fmt.Printf("Error creating new Smart Contract: %s", err)
}
}
hyperledger fabric의 샘플 체인코드의 일부입니다. SmartContract를 포인터 형태로 인터페이스를 쭉 생성한 것을 알 수 있습니다. 그리고 shim.Start에 생성된 객체를 전달하면 실행 실행이 되는 구조입니다.
'블록체인' 카테고리의 다른 글
[ethereum] docker를 활용한 이더리움 네트워크 구축 - 3편 (0) | 2022.08.27 |
---|---|
[ethereum] docker를 활용한 이더리움 네트워크 구축 - 2편 (0) | 2022.08.27 |
[ethereum] docker를 활용한 이더리움 네트워크 구축 - 1편 (0) | 2022.08.27 |
[ethereum] solidity - 인터페이스를 활용한 다른 컨트랙트 호출 (0) | 2022.08.27 |
[hyperledge fabric] fabric 기반 블록체인 기술을 익히는데 필요한 용어 (0) | 2022.08.27 |