< HTTP >
func ApplyCareer(w http.ResponseWriter, req *http.Request) {
}
func FindAllbyAddr(w http.ResponseWriter, req *http.Request) {
}
/cabb/user/httppkg/findAllByAddr.go
package httppkg
import (
"cabb/user/blockpkg"
"cabb/user/txpkg"
"encoding/hex"
"encoding/json"
"fmt"
"net/http"
)
type findReqBody struct {
Address string `json:"address"`
}
type resBody struct {
TxID []string `json:"txID"`
Career []string `json:"career"`
Company []string `json:"company"`
}
//월렛 주소에 해당하는 전체 거래 내역 리스트 조회
func FindAllbyAddr(w http.ResponseWriter, req *http.Request) {
var body findReqBody
decoder := json.NewDecoder(req.Body)
decoder.DisallowUnknownFields()
err := decoder.Decode(&body)
//에러 체크
if err != nil {
fmt.Print(err)
return
}
//테스트용 더미 데이터
txs := txpkg.CreateTxDB()
gb := blockpkg.GenesisBlock()
bs := blockpkg.NewBlockchain(gb)
prev := gb.Hash
height := gb.Height + 1
address := "address123"
for j := 0; j < 10; j++ {
tx := txpkg.NewTx("user_"+fmt.Sprint(j), "company_"+fmt.Sprint(j), fmt.Sprint(j)+"개월", "card", "블록체인 개발자", "proof.png", address)
txs.AddTx(tx)
b := blockpkg.NewBlock(prev, height, tx.TxID, "data")
bs.AddBlock(b)
prev = b.Hash
height = b.Height + 1
}
list := txs.FindTxByAddr(body.Address, bs)
res := &resBody{}
for i := 0; i < len(list); i++ {
res.TxID = append(res.TxID, hex.EncodeToString((list[i].TxID[:]))) // TxID는 해시값이어서 string()을 사용하면 정상적인 문자열이 나오지 않는다.
res.Career = append(res.Career, string(list[i].Career))
res.Company = append(res.Company, string(list[i].Company))
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(res)
}
func main() {
router := mux.NewRouter()
router.HandleFunc("/refTx", httppkg.FindAllbyAddr).Methods("Get")
http.ListenAndServe(":9000", router)
}
/cabb/user/httppkg/generateTransaction.go
package httppkg
import (
"bytes"
"cabb/user/txpkg"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
_ "net/http"
)
// Request 구조체
type Request struct {
Address string `json:"address"`
Data string `json:"data"`
//T *txpkg.Tx `json:"transaction"`
Applier string `json:"applier"`
Company string `json:"company"`
Career string `json:"career"`
Payment string `json:"payment"`
Job string `json:"job"`
Proof string `json:"proof"`
}
//Json 타입으로 리턴해주기 위한 구조체
type JsonResponse struct {
Address string `json:"address"`
Txid [32]byte `json:"txid"`
}
// Generate Transaction
func ApplyCareer(w http.ResponseWriter, req *http.Request) {
var body Request
decoder := json.NewDecoder(req.Body)
decoder.DisallowUnknownFields()
err := decoder.Decode(&body)
//에러 체크
if err != nil {
fmt.Print(err)
return
}
T := txpkg.NewTx(body.Applier, body.Company, body.Career, body.Payment, body.Job, body.Proof, body.Address)
Txs := txpkg.CreateTxDB() // [임시] 최초에 만들어서 운용중인 Txs(DB) 가져와야함
Txid := Txs.AddTx(T) // Txs(임시)에 트랜잭션 등록
T.PrintTx()
fmt.Println("Tx-TxID: ", T.TxID)
value := map[string]string{
"txID": hex.EncodeToString(T.TxID[:]),
"data": body.Data}
json_data, _ := json.Marshal(value)
resp, err := http.Post("http://localhost:9000/newBlk", "application/json", bytes.NewBuffer(json_data))
if err != nil {
panic(err)
}
defer resp.Body.Close()
// Response 체크.
respBody, err := ioutil.ReadAll(resp.Body)
if err == nil {
str := string(respBody)
println(str)
}
var response = JsonResponse{Address: body.Address, Txid: Txid}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}
기존에는 각각의 기능들을 독립적으로 분리해놨었지만, 서비스의 시나리오상 트랜잭션 생성과 블록 생성은 늘 동시에 진행되어야 하기 때문에 코드들은 분리되어 있지만 실제 동작은 한번의 호출로 진행되도록 수정했다.
트랜잭션 생성 코드에 http
호출을 추가해서 트랜잭션 생성 코드 내부에서 블록 생성 요청을 보내도록 수정한 것이다.
/cabb/user/httppkg/generateBlock.go
package httppkg
import (
"cabb/user/blockpkg"
"encoding/hex"
"encoding/json"
"fmt"
"net/http"
)
// Response 데이터를 담을 구조체
type blkID struct {
BlockID [32]byte `json:"BlockID"`
}
// Request 데이터가 담길 구조체
type reqBody struct {
TxID string `json:"txID"`
Data string `json:"data"`
}
func CreateNewBlock(w http.ResponseWriter, req *http.Request) {
//request용 구조체 생성
var body reqBody
headerContentTtype := req.Header.Get("Content-Type")
if headerContentTtype != "application/json" {
fmt.Println("content type 오류")
return
}
//Json 데이터 파싱
decoder := json.NewDecoder(req.Body)
decoder.DisallowUnknownFields()
err := decoder.Decode(&body)
//에러 체크
if err != nil {
fmt.Print(err)
return
}
prevHash := [32]byte{} // (임시)가장 최근의 블록 해시를 불러와야 함
height := 0 // (임시)가장 최근 블록의 height 또는 블록체인의 길이를 저장
// string으로 받은 TxID를 [32]byte로 변환
var txID [32]byte
hex.Decode(txID[:], []byte(body.TxID))
fmt.Println("BLK - txID[32]: ", txID)
data := body.Data
fmt.Println(data)
// response용 구조체 생성
res := &blkID{}
// 블록 패키지에 구현해놓은 NewBlock() 실행후 해시값 저장
b := blockpkg.NewBlock(prevHash, height, txID, data)
b.PrintBlock()
res.BlockID = b.Hash
//Content Type을 JSON으로 설정
w.Header().Set("Content-Type", "application/json")
// response 구조체 JSON으로 인코딩후 전송
json.NewEncoder(w).Encode(res)
}
트랜잭션 생성을 요청할 때 트랜잭션 생성에 필요한 데이터뿐만 아니라 블록 생성에 필요한 데이터까지 함께 받도록 해서 트랜잭션 내부에서 블록 요청을 할 때 데이터 처리도 함께 해주도록 되어있다.
func main() {
router := mux.NewRouter()
router.HandleFunc("/Apply/Career", httppkg.ApplyCareer).Methods("Post")
router.HandleFunc("/newBlk", httppkg.CreateNewBlock).Methods("Post")
http.ListenAndServe(":9000", router)
}
실제 response 데이터는 트랜잭션의 address와 id 값 뿐이지만 생성되는 블록에 대해서는 터미널에 출력을 해서 정상적인 output인지 테스트 하였다.