JavaScriptおよびWeb3を䜿甚せずにむヌサリアムスマヌトコントラクトを手動でテストするためのGolangの軜量「フロント゚ンド」

こんにちは


むヌサリアムのスマヌト契玄の手動テストのために、できればシンプルな゜リュヌションを開発するずいうアむデアがありたした。 Remixの[実行]タブの機胜に䌌た操䜜を行うこずは興味深いものになりたした。


アプリケヌションでできるこず


Golangのシンプルなバック゚ンドであるこずが刀明したした。



順番に


Golangを遞択したのは、 ゲスが構築されおいるgoethereumコヌドベヌスが本圓に奜きだったからです。


静的なHTMLを生成するには、暙準のGolangパッケヌゞ「html /テンプレヌト」が䜿甚されたす。 ここでは䜕もペむントしたせん。すべおのテンプレヌトはプロゞェクトテンプレヌトパッケヌゞにありたす。
䞊蚘で曞いたように、むヌサリアムを䜿甚するには、go-ethereumコヌドベヌスバヌゞョン1.7.3を遞択したした。
go-ethereumのモバむルパッケヌゞを䜿甚したかったのですが、モバむルはしばらく曎新されおおらず、珟圚のAbi圢匏では正しく動䜜したせん。 デヌタを凊理するずきに、同様の゚ラヌが衚瀺されたす。


abi: cannot unmarshal *big.Int in to []interface {} 

この゚ラヌはすでに修正されおいたすが、メむンブランチでは、これを曞いた時点での修正はただ远加されおいたせん。


それにもかかわらず、私は別の゜リュヌション、ラッパヌレスを遞択したした。 モバむルパッケヌゞの関数は、基本的に䞻な機胜の䟿利なラッパヌです。


その結果、go-ethereumからabi+ abiに䟝存する耇数のパッケヌゞを操䜜するためのパッケヌゞをプロゞェクトに取り蟌み、プルリク゚ストからコヌドを远加したした。


スマヌトコントラクトを䜿甚する必芁があるため、solファむルから特定のコントラクトを操䜜するためのgoパッケヌゞを生成できるabigenナヌティリティは、私には䞍向きでした。


構造ず、この構造がレシヌバヌであるメ゜ッドを䜜成したしたGolangの甚語に間違いがない堎合


 type EthWorker struct { Container string //   sol,     Contract string //  Endpoint string //   Key string //   ContractAddress string //  FormValues url.Values //map    ,  POST form New bool //    } 

完党なむンタヌフェヌスは次のようになりたす。


 type ReadWriterEth interface { Transact() (string, error) //    Call() (string, error) //    Deploy() (string, string, error) //    Info() (*Info, error) //   ,      ParseInput() ([]interface{}, error) //  POST         ParseOutput([]interface{}) (string, error) //      } 

契玄に情報を曞き蟌むための関数


取匕
 func (w *EthWorker) Transact() (string, error) { // POST ,      inputs, err := w.ParseInput() if err != nil { return "", errors.Wrap(err, "parse input") } //    EthWorker       pk := strings.TrimPrefix(w.Key, "0x") key, err := crypto.HexToECDSA(pk) if err != nil { return "", errors.Wrap(err, "hex to ECDSA") } auth := bind.NewKeyedTransactor(key) if !common.IsHexAddress(w.ContractAddress) { return "", errors.New("New Address From Hex") } addr := common.HexToAddress(w.ContractAddress) //    contract := bind.NewBoundContract( addr, Containers.Containers[w.Container].Contracts[w.Contract].Abi, Client, Client, ) //    Gas gasprice, err := Client.SuggestGasPrice(context.Background()) if err != nil { return "", errors.Wrap(err, "suggest gas price") } //      opt := &bind.TransactOpts{ From: auth.From, Signer: auth.Signer, GasPrice: gasprice, GasLimit: GasLimit, Value: auth.Value, } //   tr, err := contract.Transact(opt, w.Endpoint, inputs...) if err != nil { return "", errors.Wrap(err, "transact") } var receipt *types.Receipt //    ,       ,       switch v := Client.(type) { case *backends.SimulatedBackend: v.Commit() receipt, err = v.TransactionReceipt(context.Background(), tr.Hash()) if err != nil { return "", errors.Wrap(err, "transaction receipt") } case *ethclient.Client: receipt, err = bind.WaitMined(context.Background(), v, tr) if err != nil { return "", errors.Wrap(err, "transaction receipt") } } if err != nil { return "", errors.Errorf("error transact %s: %s", tr.Hash().String(), err.Error(), ) } //     responce := fmt.Sprintf(templates.WriteResult, tr.Nonce(), auth.From.String(), tr.To().String(), tr.Value().String(), tr.GasPrice().String(), receipt.GasUsed.String(), new(big.Int).Mul(receipt.GasUsed, tr.GasPrice()), receipt.Status, receipt.TxHash.String(), ) return responce, nil } 

契玄から情報を読み取るための機胜


電話する
 func (w *EthWorker) Call() (string, error) { inputs, err := w.ParseInput() if err != nil { return "", errors.Wrap(err, "parse input") } key, _ := crypto.GenerateKey() auth := bind.NewKeyedTransactor(key) contract := bind.NewBoundContract( common.HexToAddress(w.ContractAddress), Containers.Containers[w.Container].Contracts[w.Contract].Abi, Client, Client, ) opt := &bind.CallOpts{ Pending: true, From: auth.From, } outputs := Containers.Containers[w.Container].Contracts[w.Contract].OutputsInterfaces[w.Endpoint] if err := contract.Call( opt, &outputs, w.Endpoint, inputs..., ); err != nil { return "", errors.Wrap(err, "call contract") } result, err := w.ParseOutput(outputs) if err != nil { return "", errors.Wrap(err, "parse output") } return result, err } 

契玄を展開するための機胜


展開する
 func (w *EthWorker) Deploy() (string, string, error) { inputs, err := w.ParseInput() if err != nil { return "", "", errors.Wrap(err, "parse input") } pk := strings.TrimPrefix(w.Key, "0x") key, err := crypto.HexToECDSA(pk) if err != nil { return "", "", errors.Wrap(err, "hex to ECDSA") } auth := bind.NewKeyedTransactor(key) current_bytecode := Containers.Containers[w.Container].Contracts[w.Contract].Bin current_abi := Containers.Containers[w.Container].Contracts[w.Contract].Abi addr, tr, _, err := bind.DeployContract(auth, current_abi, common.FromHex(current_bytecode), Client, inputs...) if err != nil { log.Printf("error %s", err.Error()) return "", "", errors.Wrap(err, "deploy contract") } var receipt *types.Receipt switch v := Client.(type) { case *backends.SimulatedBackend: v.Commit() receipt, err = v.TransactionReceipt(context.Background(), tr.Hash()) if err != nil { return "", "", errors.Wrap(err, "transaction receipt") } case *ethclient.Client: receipt, err = bind.WaitMined(context.Background(), v, tr) if err != nil { return "", "", errors.Wrap(err, "transaction receipt") } } if err != nil { return "", "", errors.Errorf("error transact %s: %s", tr.Hash().String(), err.Error(), ) } responce := fmt.Sprintf(templates.DeployResult, tr.Nonce(), auth.From.String(), addr.String(), tr.GasPrice().String(), receipt.GasUsed.String(), new(big.Int).Mul(receipt.GasUsed, tr.GasPrice()).String(), receipt.Status, receipt.TxHash.String(), ) return responce, addr.String(), nil } 

ナヌザヌがWebペヌゞのフォヌムに入力したデヌタからCall and Transact機胜に転送できるデヌタを取埗する方法の問題を解決する必芁がありたした。


契玄のabiメ゜ッドから特定のフィヌルドに必芁なデヌタ型を孊習し、ナヌザヌがWebペヌゞのフォヌムに入力したものをもたらすこずほど良いものは思い぀きたせんでした。 すなわち デヌタ型を忘れた堎合、この゜リュヌションはこのデヌタ型では機胜したせん。 コヌドを倉曎する必芁がありたす。 ParseInput関数に実装されたす


解析入力
 func (w *EthWorker) ParseInput() ([]interface{}, error) { //           ,        if w.New && len(Containers.Containers[w.Container].Contracts[w.Contract].Abi.Constructor.Inputs) == 0 { return nil, nil } //           ,        if !w.New && len(Containers.Containers[w.Container].Contracts[w.Contract].Abi.Methods[w.Endpoint].Inputs) == 0 { return nil, nil } //  Form Values inputsMap := make(map[int]string) var inputsArray []int var inputsSort []string for k, v := range w.FormValues { if k == "endpoint" { continue } if len(v) != 1 { return nil, errors.Errorf("incorrect %s field", k) } i, err := strconv.Atoi(k) if err != nil { continue //return nil, errors.Wrap(err, "incorrect inputs: strconv.Atoi") } inputsMap[i] = v[0] } //    ,   ,    if Containers.Containers[w.Container] == nil || Containers.Containers[w.Container].Contracts[w.Contract] == nil { return nil, errors.New("input values incorrect") } //  , ..  Containers  .      if !w.New && len(Containers.Containers[w.Container].Contracts[w.Contract].Abi.Methods[w.Endpoint].Inputs) != 0 && Containers.Containers[w.Container].Contracts[w.Contract].InputsInterfaces[w.Endpoint] == nil { return nil, errors.New("input values incorrect") } //       .     ABI var inputs_args []abi.Argument if w.New { inputs_args = Containers.Containers[w.Container].Contracts[w.Contract].Abi.Constructor.Inputs } else { inputs_args = Containers.Containers[w.Container].Contracts[w.Contract].Abi.Methods[w.Endpoint].Inputs } if len(inputsMap) != len(inputs_args) { return nil, errors.New("len inputs_args != inputsMap: incorrect inputs") } for k := range inputsMap { inputsArray = append(inputsArray, k) } sort.Ints(inputsArray) for k := range inputsArray { inputsSort = append(inputsSort, inputsMap[k]) } var inputs_interfaces []interface{} for i := 0; i < len(inputs_args); i++ { arg_value := inputsMap[i] switch inputs_args[i].Type.Type.String() { case "bool": var result bool result, err := strconv.ParseBool(arg_value) if err != nil { return nil, errors.New("incorrect inputs") } inputs_interfaces = append(inputs_interfaces, result) case "[]bool": var result []bool result_array := strings.Split(arg_value, ",") for _, bool_value := range result_array { item, err := strconv.ParseBool(bool_value) if err != nil { return nil, errors.Wrap(err, "incorrect inputs") } result = append(result, item) } inputs_interfaces = append(inputs_interfaces, result) case "string": inputs_interfaces = append(inputs_interfaces, arg_value) case "[]string": result_array := strings.Split(arg_value, ",") //TODO: NEED REF inputs_interfaces = append(inputs_interfaces, result_array) case "[]byte": inputs_interfaces = append(inputs_interfaces, []byte(arg_value)) case "[][]byte": var result [][]byte result_array := strings.Split(arg_value, ",") for _, byte_value := range result_array { result = append(result, []byte(byte_value)) } inputs_interfaces = append(inputs_interfaces, result) case "common.Address": if !common.IsHexAddress(arg_value) { return nil, errors.New("incorrect inputs: arg_value is not address") } inputs_interfaces = append(inputs_interfaces, common.HexToAddress(arg_value)) case "[]common.Address": var result []common.Address result_array := strings.Split(arg_value, ",") for _, addr_value := range result_array { if !common.IsHexAddress(arg_value) { return nil, errors.New("incorrect inputs: arg_value is not address") } addr := common.HexToAddress(addr_value) result = append(result, addr) } inputs_interfaces = append(inputs_interfaces, result) case "common.Hash": if !common.IsHex(arg_value) { return nil, errors.New("incorrect inputs: arg_value is not hex") } inputs_interfaces = append(inputs_interfaces, common.HexToHash(arg_value)) case "[]common.Hash": var result []common.Hash result_array := strings.Split(arg_value, ",") for _, addr_value := range result_array { if !common.IsHex(arg_value) { return nil, errors.New("incorrect inputs: arg_value is not hex") } hash := common.HexToHash(addr_value) result = append(result, hash) } inputs_interfaces = append(inputs_interfaces, result) case "int8": i, err := strconv.ParseInt(arg_value, 10, 8) if err != nil { return nil, errors.New("incorrect inputs: arg_value is not int8") } inputs_interfaces = append(inputs_interfaces, int8(i)) case "int16": i, err := strconv.ParseInt(arg_value, 10, 16) if err != nil { return nil, errors.New("incorrect inputs: arg_value is not int16") } inputs_interfaces = append(inputs_interfaces, int16(i)) case "int32": i, err := strconv.ParseInt(arg_value, 10, 32) if err != nil { return nil, errors.New("incorrect inputs: arg_value is not int32") } inputs_interfaces = append(inputs_interfaces, int32(i)) case "int64": i, err := strconv.ParseInt(arg_value, 10, 64) if err != nil { return nil, errors.New("incorrect inputs: arg_value is not int64") } inputs_interfaces = append(inputs_interfaces, int64(i)) case "uint8": i, err := strconv.ParseInt(arg_value, 10, 8) if err != nil { return nil, errors.New("incorrect inputs: arg_value is not uint8") } inputs_interfaces = append(inputs_interfaces, big.NewInt(i)) case "uint16": i, err := strconv.ParseInt(arg_value, 10, 16) if err != nil { return nil, errors.New("incorrect inputs: arg_value is not uint16") } inputs_interfaces = append(inputs_interfaces, big.NewInt(i)) case "uint32": i, err := strconv.ParseInt(arg_value, 10, 32) if err != nil { return nil, errors.New("incorrect inputs: arg_value is not uint32") } inputs_interfaces = append(inputs_interfaces, big.NewInt(i)) case "uint64": i, err := strconv.ParseInt(arg_value, 10, 64) if err != nil { return nil, errors.New("incorrect inputs: arg_value is not uint64") } inputs_interfaces = append(inputs_interfaces, big.NewInt(i)) case "*big.Int": bi := new(big.Int) bi, _ = bi.SetString(arg_value, 10) if bi == nil { return nil, errors.New("incorrect inputs: " + arg_value + " not " + inputs_args[i].Type.String()) } inputs_interfaces = append(inputs_interfaces, bi) case "[]*big.Int": var result []*big.Int result_array := strings.Split(arg_value, ",") for _, big_value := range result_array { bi := new(big.Int) bi, _ = bi.SetString(big_value, 10) if bi == nil { return nil, errors.New("incorrect inputs: " + arg_value + " not " + inputs_args[i].Type.String()) } result = append(result, bi) } inputs_interfaces = append(inputs_interfaces, result) } } //    return inputs_interfaces, nil } 

ParseOutput関数でむヌサリアムから取埗したデヌタに察しお同様の倉換を行いたした


解析出力
 func (w *EthWorker) ParseOutput(outputs []interface{}) (string, error) { if len(Containers.Containers[w.Container].Contracts[w.Contract].Abi.Methods[w.Endpoint].Outputs) == 0 { return "", nil } if Containers.Containers[w.Container] == nil || Containers.Containers[w.Container].Contracts[w.Contract] == nil { return "", errors.New("input values incorrect") } if len(Containers.Containers[w.Container].Contracts[w.Contract].Abi.Methods[w.Endpoint].Outputs) != 0 && Containers.Containers[w.Container].Contracts[w.Contract].OutputsInterfaces[w.Endpoint] == nil { return "", errors.New("input values incorrect") } output_args := Containers.Containers[w.Container].Contracts[w.Contract].Abi.Methods[w.Endpoint].Outputs if len(outputs) != len(output_args) { return "", errors.New("incorrect inputs") } var item_array []string for i := 0; i < len(outputs); i++ { switch output_args[i].Type.Type.String() { case "bool": item := strconv.FormatBool(*outputs[i].(*bool)) item_array = append(item_array, item) case "[]bool": boolArray := *outputs[i].(*[]bool) var boolItems []string for _, bool_value := range boolArray { item := strconv.FormatBool(bool_value) boolItems = append(boolItems, item) } item := "[ " + strings.Join(boolItems, ",") + " ]" item_array = append(item_array, item) case "string": item_array = append(item_array, *outputs[i].(*string)) case "[]string": array := *outputs[i].(*[]string) var items []string for _, value := range array { items = append(items, value) } item := "[ " + strings.Join(items, ",") + " ]" item_array = append(item_array, item) case "[]byte": array := *outputs[i].(*[]byte) var items []string for _, value := range array { items = append(items, string(value)) } item := "[ " + strings.Join(items, ",") + " ]" item_array = append(item_array, item) case "[][]byte": array := *outputs[i].(*[][]byte) var items string for _, array2 := range array { var items2 []string for _, value := range array2 { items2 = append(items2, string(value)) } item2 := "[ " + strings.Join(items2, ",") + " ]" items = items + "," + item2 } item_array = append(item_array, items) case "common.Address": item := *outputs[i].(*common.Address) item_array = append(item_array, item.String()) case "[]common.Address": addrArray := *outputs[i].(*[]common.Address) var addrItems []string for _, value := range addrArray { addrItems = append(addrItems, value.String()) } item := "[ " + strings.Join(addrItems, ",") + " ]" item_array = append(item_array, item) case "common.Hash": item := *outputs[i].(*common.Hash) item_array = append(item_array, item.String()) case "[]common.Hash": hashArray := *outputs[i].(*[]common.Hash) var hashItems []string for _, value := range hashArray { hashItems = append(hashItems, value.String()) } item := "[ " + strings.Join(hashItems, ",") + " ]" item_array = append(item_array, item) case "int8": item := *outputs[i].(*int8) str := strconv.FormatInt(int64(item), 10) item_array = append(item_array, str) case "int16": item := *outputs[i].(*int16) str := strconv.FormatInt(int64(item), 10) item_array = append(item_array, str) case "int32": item := *outputs[i].(*int32) str := strconv.FormatInt(int64(item), 10) item_array = append(item_array, str) case "int64": item := *outputs[i].(*int64) str := strconv.FormatInt(item, 10) item_array = append(item_array, str) case "uint8": item := *outputs[i].(*uint8) str := strconv.FormatInt(int64(item), 10) item_array = append(item_array, str) case "uint16": item := *outputs[i].(*uint16) str := strconv.FormatInt(int64(item), 10) item_array = append(item_array, str) case "uint32": item := *outputs[i].(*uint32) str := strconv.FormatInt(int64(item), 10) item_array = append(item_array, str) case "uint64": item := *outputs[i].(*uint64) str := strconv.FormatInt(int64(item), 10) item_array = append(item_array, str) case "*big.Int": item := *outputs[i].(**big.Int) item_array = append(item_array, item.String()) case "[]*big.Int": bigArray := *outputs[i].(*[]*big.Int) var items []string for _, v := range bigArray { items = append(items, v.String()) } item := "[ " + strings.Join(items, ",") + " ]" item_array = append(item_array, item) } } return strings.Join(item_array, " , "), nil } 

前述のabigenナヌティリティのコヌドベヌスから、Solidityコンパむラを操䜜するための機胜を匕き裂きたした。 最終的に、私はほがすべおの契玄でabiずバむトコヌドを取埗したした。 Bind関数に実装されおいたす。


バむンド
 func Bind(dirname, solcfile string) (*ContractContainers, error) { result := &ContractContainers{ Containers: make(map[string]*ContractContainer), } allfiles, err := ioutil.ReadDir(dirname) if err != nil { return nil, errors.Wrap(err, "error ioutil.ReadDir") } for _, v := range allfiles { if v.IsDir() { continue } if hasSuffixCaseInsensitive(v.Name(), ".sol") { contracts, err := compiler.CompileSolidity(solcfile, dirname+string(os.PathSeparator)+v.Name()) if err != nil { return nil, errors.Wrap(err, "CompileSolidity") } c := &ContractContainer{ ContainerName: v.Name(), Contracts: make(map[string]*Contract), } for name, contract := range contracts { a, _ := json.Marshal(contract.Info.AbiDefinition) ab, err := abi.JSON(strings.NewReader(string(a))) if err != nil { return nil, errors.Wrap(err, "abi.JSON") } nameParts := strings.Split(name, ":") var ab_keys []string ouputs_map := make(map[string][]interface{}) inputs_map := make(map[string][]interface{}) for key, method := range ab.Methods { ab_keys = append(ab_keys, key) var o []interface{} var i []interface{} for _, v := range method.Outputs { var ar interface{} switch v.Type.Type.String() { case "bool": ar = new(bool) case "[]bool": ar = new([]bool) case "string": ar = new(string) case "[]string": ar = new([]string) case "[]byte": ar = new([]byte) case "[][]byte": ar = new([][]byte) case "common.Address": ar = new(common.Address) case "[]common.Address": ar = new([]common.Address) case "common.Hash": ar = new(common.Hash) case "[]common.Hash": ar = new([]common.Hash) case "int8": ar = new(int8) case "int16": ar = new(int16) case "int32": ar = new(int32) case "int64": ar = new(int64) case "uint8": ar = new(uint8) case "uint16": ar = new(uint16) case "uint32": ar = new(uint32) case "uint64": ar = new(uint64) case "*big.Int": ar = new(*big.Int) case "[]*big.Int": ar = new([]*big.Int) default: return nil, errors.Errorf("unsupported type: %s", v.Type.Type.String()) } o = append(o, ar) } ouputs_map[method.Name] = o for _, v := range method.Inputs { var ar interface{} switch v.Type.Type.String() { case "bool": ar = new(bool) case "[]bool": ar = new([]bool) case "string": ar = new(string) case "[]string": ar = new([]string) case "[]byte": ar = new([]byte) case "[][]byte": ar = new([][]byte) case "common.Address": ar = new(common.Address) case "[]common.Address": ar = new([]common.Address) case "common.Hash": ar = new(common.Hash) case "[]common.Hash": ar = new([]common.Hash) case "int8": ar = new(int8) case "int16": ar = new(int16) case "int32": ar = new(int32) case "int64": ar = new(int64) case "uint8": ar = new(uint8) case "uint16": ar = new(uint16) case "uint32": ar = new(uint32) case "uint64": ar = new(uint64) case "*big.Int": ar = new(*big.Int) case "[]*big.Int": ar = new([]*big.Int) default: return nil, errors.Errorf("unsupported type: %s", v.Type.Type.String()) } i = append(i, ar) } inputs_map[method.Name] = i } sort.Strings(ab_keys) con := &Contract{ Name: nameParts[len(nameParts)-1], Abi: ab, AbiJson: string(a), Bin: contract.Code, SortKeys: ab_keys, OutputsInterfaces: ouputs_map, InputsInterfaces: inputs_map, } c.ContractNames = append(c.ContractNames, nameParts[len(nameParts)-1]) c.Contracts[nameParts[len(nameParts)-1]] = con } sort.Strings(c.ContractNames) result.ContainerNames = append(result.ContainerNames, c.ContainerName) result.Containers[c.ContainerName] = c } } sort.Strings(result.ContainerNames) return result, err } 

この関数は、モバむルパッケヌゞでの実隓から倧きなコヌドブロックを残したしたが、ただ削陀しおいたせんが、単にリファクタリングしたした。


かなり倧きなContractContainers構造を䜜成し、そこに珟圚の契玄に関するすべおの情報を配眮したした。将来、アプリケヌションはそこからすべおの情報を取埗したす。


最埌に、その仕組みを説明したす。


Linuxでのみプログラムを実行したした。 近くに他のオペレヌティングシステムはありたせん。
WindowsおよびMac甚の実行可胜ファむルをコンパむルしたしたが。


たず、プラットフォヌム甚のSolidityコンパむラが必芁です。 これがおそらく最も難しい点です。


ここでコンパむル枈みのバむナリたたは゜ヌスを取埗するか、 ここで詳现を確認できたす 。 LinuxおよびWindows甚のバヌゞョン0.4.18および0.4.19プロゞェクトのsolcディレクトリに配眮したした。 システムにすでにむンストヌルされおいるコンパむラを䜿甚するこずもできたす。 システムにSolidityコンパむラがあるかどうかを確認するには、コマンドプロンプトで次のように入力したす。


 solc —version 

答えがこれである堎合


 solc, the solidity compiler commandline interface Version: 0.4.18+commit.9cf6e910.Linux.g++ 

その埌、すべおが順調です。
いく぀かのラむブラリが必芁になる堎合は、Ubuntuがこれを芁求した堎合などに、それらをむンストヌルするだけです。


 ./solc: error while loading shared libraries: libz3.so.4: cannot open shared object file: No such file or directory 

、次にlibz3-devを配眮したす


次に、むヌサリアムを䜿甚するモヌドを決定する必芁がありたす。 2぀の方法がありたす。



確かにもっず矎しくするこずはできたすが、䟋ずしおは、既存の゜リュヌションが非垞に適しおいたす。 これらのファむルから、アプリケヌションはむヌサリアムアドレスを取埗し、れロ以倖のバランスを取りたす。
䟋ずしお、キヌストアディレクトリに5぀のファむルを配眮したす。 これらはテスト環境で䜿甚できたす。


config.yaml configを入力したす。



アプリケヌションを起動したす。 構成ファむルのあるディレクトリぞのパスは、-configフラグを介しお指定できたす


 ./efront-v0.0.1-linux-amd64 -config $GOPATH/src/ethereum-front/ 

ブラりザのリンクをたどりたす。デフォルトではhttp// localhost8085です。
秘密鍵を入力する必芁がありたす。 5぀のテストアドレスの秘密キヌは、keys.txtにありたす。 この秘密キヌは、ブラりザのCookieに15分間保存されたす。 次は新しいリク゚ストです。 珟圚、䜕も暗号化されおいたせん。



遞択しお、コンテナ.solファむルおよびアプリケヌションがその䞭に芋぀けたコントラクトを遞択したす。



さらに、察応するチェックボックスをオンにしお、䞀床展開した契玄のアドレスを入力するか、新しい契玄を展開できたす。 [展開]チェックボックスがオンの堎合、アドレスフィヌルドは無芖されたす。


すべおがうたくいった堎合、ブラりザに同様の画像が衚瀺されたす。



゚ラヌがある堎合、それらはむンタヌフェヌスの䞊郚のテキスト゚リアに衚瀺されたす。
ペヌゞの䞊郚には、2぀のログむンリンクずアップロヌドリンクがありたす。


ログむンがリダむレクトされ、新しい秘密鍵が入力されたす。 遞択した契玄ぞのリダむレクトをアップロヌドしたす。


以䞋は、珟圚のセッションに関する情報です。




次は2぀のテヌブルです。
珟圚のコントラクトのメ゜ッドを操䜜するための巊の衚。 遞択した契玄に応じお、動的に倉化したす。


右の衚は、むヌサリアムを操䜜するための䞀般的な機胜です。



泚トランザクションブロックチェヌンぞの曞き蟌み操䜜を実行するずき、ペヌゞがロヌドされるたで埅機したす。これには数秒かかる堎合がありたす。 .


, textarea ( ):





.
C OS. bin.


:



゜ヌスコヌド
どうもありがずう。



Source: https://habr.com/ru/post/J346432/


All Articles