语言 go,参照bytom git 上项目改造
https://github.com/Bytom/bytom
https://github.com/Bytom-Community/Bytom-Mobile-Wallet-SDK
1、创建key:
func (a *API) PseudohsmCreateRootKey(seed string) Response { seedByte, error := hex.DecodeString(seed) if error != nil { return NewErrorResponse(error) } xprv := chainkd.RootXPrv(seedByte) xpub := xprv.XPub() rootKey := &chainkd.RootKey{Xpriv: xprv[:], Xpub: xpub[:]} return NewSuccessResponse(rootKey) }
2、创建 P2PKH 地址
func (a *API) PseudohsmCreateP2PKH(xpub string) Response { xpubByte, error := hex.DecodeString(xpub) if error != nil { return NewErrorResponse(error) } address, err := account.CreateP2PKHByPub(xpubByte) if err != nil { return NewErrorResponse(error) } return NewSuccessResponse(&txbuilder.Receiver{ ControlProgram: address.ControlProgram, Address: address.Address, }) }
3、构建交易:
func Build(utxos []account.UTXO, outpus []account.Output, pubs []chainkd.XPub) (*Template, error) { maxTime := time.Now().Add(5 * time.Minute) builder := TemplateBuilder{ maxTime: maxTime, } // Build all of the actions, updating the builder. for _, utxo := range utxos { txInput := types.NewSpendInput(nil, utxo.SourceID, utxo.AssetID, utxo.Amount, utxo.SourcePos, utxo.ControlProgram) sigInst := &SigningInstruction{} if utxo.Address == "" { log.Errorf("utxo address can not null ") return nil, nil } address, err := common.DecodeAddress(utxo.Address, &consensus.ActiveNetParams) if err != nil { log.Error("decode address error") return nil, err } switch address.(type) { case *common.AddressWitnessPubKeyHash: derivedPK := pubs[0].PublicKey() sigInst.WitnessComponents = append(sigInst.WitnessComponents, DataWitness([]byte(derivedPK))) case *common.AddressWitnessScriptHash: derivedPKs := chainkd.XPubKeys(pubs) script, err := vmutil.P2SPMultiSigProgram(derivedPKs, len(derivedPKs)) if err != nil { return nil, err } sigInst.WitnessComponents = append(sigInst.WitnessComponents, DataWitness(script)) } builder.AddInput(txInput, sigInst) } for _, output := range outpus { address, err := common.DecodeAddress(output.Address, &consensus.ActiveNetParams) if err != nil { return nil, err } redeemContract := address.ScriptAddress() program := []byte{} switch address.(type) { case *common.AddressWitnessPubKeyHash: program, err = vmutil.P2WPKHProgram(redeemContract) case *common.AddressWitnessScriptHash: program, err = vmutil.P2WSHProgram(redeemContract) default: return nil, errors.New("un support address type") } out := types.NewTxOutput(output.AssetID, output.Amount, program) builder.AddOutput(out) } // Build the transaction template. tpl, _, err := builder.Build() if err != nil { builder.rollback() return nil, err } //if tpl.SigningInstructions == nil { // tpl.SigningInstructions = []*txbuilder.SigningInstruction{} //} /*TODO: This part is use for check the balance, but now we are using btm as gas fee the rule need to be rewrite when we have time err = checkBlankCheck(tx) if err != nil { builder.rollback() return nil, err }*/ return tpl, nil }
4、签名:
func SignTx(tpl *Template, xprv chainkd.XPrv) error { for i, sigInst := range tpl.SigningInstructions { h := tpl.Hash(uint32(i)).Byte32() sig := xprv.Sign(h[:]) pub := xprv.XPub() log.Errorf("tiger current pub : %v ",pub) ret := pub.Verify(h[:],sig) log.Errorf("tiger verify ret : %v ",ret) rawTxSig := &RawTxSigWitness{ Quorum: 1, Sigs: []json.HexBytes{sig}, } var data []witnessComponent data = append(data,rawTxSig) sigInst.WitnessComponents = append(data, sigInst.WitnessComponents...) } return materializeWitnesses(tpl) }
5、解析交易:
func (a *API) PseudohsmDecodeTx(raw types.RawTx) Response { tx := &RawTx{ Version: raw.Raw.Version, Size: raw.Raw.SerializedSize, TimeRange: raw.Raw.TimeRange, Inputs: []*query.AnnotatedInput{}, Outputs: []*query.AnnotatedOutput{}, } for i := range raw.Raw.Inputs { tx.Inputs = append(tx.Inputs, a.WalletInfo.BuildAnnotatedInput(&raw.Raw, uint32(i))) } for i := range raw.Raw.Outputs { tx.Outputs = append(tx.Outputs, a.WalletInfo.BuildAnnotatedOutput(&raw.Raw, i)) } totalInputBtm := uint64(0) totalOutputBtm := uint64(0) for _, input := range tx.Inputs { if input.AssetID.String() == consensus.BTMAssetID.String() { totalInputBtm += input.Amount } } for _, output := range tx.Outputs { if output.AssetID.String() == consensus.BTMAssetID.String() { totalOutputBtm += output.Amount } } tx.Fee = int64(totalInputBtm) - int64(totalOutputBtm) return NewSuccessResponse(tx) }