Bytom交易说明(UTXO用户自己管理模式)

比原项目仓库:

Github地址:https://github.com/Bytom/bytom

Gitee地址:https://gitee.com/BytomBlockchain/bytom

该部分主要针对用户自己管理私钥和地址,并通过utxo来构建和发送交易。

  • 1.创建私钥和公钥
  • 2.根据公钥创建接收对象
  • 3.找到可花费的utxo
  • 4.通过utxo构造交易
  • 5.组合交易的inputoutput构成交易模板
  • 6.对构造的交易进行签名
  • 7.提交交易上链

注意事项:

以下步骤以及功能改造仅供参考,具体代码实现需要用户根据实际情况进行调试,具体可以参考单元测试案例代码blockchain/txbuilder/txbuilder_test.go#L255

1.创建私钥和公钥

该部分功能可以参考代码crypto/ed25519/chainkd/util.go#L11,可以通过 NewXKeys(nil) 创建主私钥和主公钥

func NewXKeys(r io.Reader) (xprv XPrv, xpub XPub, err error) {
	xprv, err = NewXPrv(r)
	if err != nil {
		return
	}
	return xprv, xprv.XPub(), nil
}

2.根据公钥创建接收对象

接收对象包含两种形式:address形式和program形式,两者是一一对应的,任选其一即可。其中创建单签地址参考代码account/accounts.go#L267进行相应改造为:

func (m *Manager) createP2PKH(xpub chainkd.XPub) (*CtrlProgram, error) {
	pubKey := xpub.PublicKey()
	pubHash := crypto.Ripemd160(pubKey)

	// TODO: pass different params due to config
	address, err := common.NewAddressWitnessPubKeyHash(pubHash, &consensus.ActiveNetParams)
	if err != nil {
		return nil, err
	}

	control, err := vmutil.P2WPKHProgram([]byte(pubHash))
	if err != nil {
		return nil, err
	}

	return &CtrlProgram{
		Address:        address.EncodeAddress(),
		ControlProgram: control,
	}, nil
}

创建多签地址参考代码account/accounts.go#L294进行相应改造为:

func (m *Manager) createP2SH(xpubs []chainkd.XPub) (*CtrlProgram, error) {
	derivedPKs := chainkd.XPubKeys(xpubs)
	signScript, err := vmutil.P2SPMultiSigProgram(derivedPKs, len(derivedPKs))
	if err != nil {
		return nil, err
	}
	scriptHash := crypto.Sha256(signScript)

	// TODO: pass different params due to config
	address, err := common.NewAddressWitnessScriptHash(scriptHash, &consensus.ActiveNetParams)
	if err != nil {
		return nil, err
	}

	control, err := vmutil.P2WSHProgram(scriptHash)
	if err != nil {
		return nil, err
	}

	return &CtrlProgram{
		Address:        address.EncodeAddress(),
		ControlProgram: control,
	}, nil
}

3.找到可花费的utxo

找到可花费的utxo,其实就是找到接收地址或接收program是你自己的unspend_output。其中utxo的结构为:(参考代码account/reserve.go#L39

// UTXO describes an individual account utxo.
type UTXO struct {
	OutputID bc.Hash
	SourceID bc.Hash

	// Avoiding AssetAmount here so that new(utxo) doesn't produce an
	// AssetAmount with a nil AssetId.
	AssetID bc.AssetID
	Amount  uint64

	SourcePos      uint64
	ControlProgram []byte

	AccountID           string
	Address             string
	ControlProgramIndex uint64
	ValidHeight         uint64
	Change              bool
}

涉及utxo构造交易的相关字段说明如下:

  • SourceID 前一笔关联交易的mux_id, 根据该ID可以定位到前一笔交易的output
  • AssetID utxo的资产ID
  • Amount utxo的资产数目
  • SourcePos 该utxo在前一笔交易的output的位置
  • ControlProgram utxo的接收program
  • Address utxo的接收地址

上述这些utxo的字段信息可以从get-block接口返回结果的transaction中找到,其相关的结构体如下:(参考代码api/block_retrieve.go#L26

// BlockTx is the tx struct for getBlock func
type BlockTx struct {
	ID         bc.Hash                  `json:"id"`
	Version    uint64                   `json:"version"`
	Size       uint64                   `json:"size"`
	TimeRange  uint64                   `json:"time_range"`
	Inputs     []*query.AnnotatedInput  `json:"inputs"`
	Outputs    []*query.AnnotatedOutput `json:"outputs"`
	StatusFail bool                     `json:"status_fail"`
	MuxID      bc.Hash                  `json:"mux_id"`
}

//AnnotatedOutput means an annotated transaction output.
type AnnotatedOutput struct {
	Type            string             `json:"type"`
	OutputID        bc.Hash            `json:"id"`
	TransactionID   *bc.Hash           `json:"transaction_id,omitempty"`
	Position        int                `json:"position"`
	AssetID         bc.AssetID         `json:"asset_id"`
	AssetAlias      string             `json:"asset_alias,omitempty"`
	AssetDefinition *json.RawMessage   `json:"asset_definition,omitempty"`
	Amount          uint64             `json:"amount"`
	AccountID       string             `json:"account_id,omitempty"`
	AccountAlias    string             `json:"account_alias,omitempty"`
	ControlProgram  chainjson.HexBytes `json:"control_program"`
	Address         string             `json:"address,omitempty"`
}

utxo跟get-block返回结果的字段对应关系如下:

`SourceID`       - `json:"mux_id"`
`AssetID`        - `json:"asset_id"`
`Amount`         - `json:"amount"`
`SourcePos`      - `json:"position"`
`ControlProgram` - `json:"control_program"`
`Address`        - `json:"address,omitempty"`

4.通过utxo构造交易

通过utxo构造交易就是使用spend_account_unspent_output的方式来花费指定的utxo。

第一步,通过utxo构造交易输入TxInput和签名需要的数据信息SigningInstruction,该部分功能可以参考代码account/builder.go#L169进行相应改造为:

// UtxoToInputs convert an utxo to the txinput
func UtxoToInputs(xpubs []chainkd.XPub, u *UTXO) (*types.TxInput, *txbuilder.SigningInstruction, error) {
	txInput := types.NewSpendInput(nil, u.SourceID, u.AssetID, u.Amount, u.SourcePos, u.ControlProgram)
	sigInst := &txbuilder.SigningInstruction{}

	if u.Address == "" {
		return txInput, sigInst, nil
	}

	address, err := common.DecodeAddress(u.Address, &consensus.ActiveNetParams)
	if err != nil {
		return nil, nil, err
	}

	switch address.(type) {
	case *common.AddressWitnessPubKeyHash:
		derivedPK := xpubs[0].PublicKey()
		sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness([]byte(derivedPK)))

	case *common.AddressWitnessScriptHash:
		derivedPKs := chainkd.XPubKeys(xpubs)
		script, err := vmutil.P2SPMultiSigProgram(derivedPKs, len(derivedPKs))
		if err != nil {
			return nil, nil, err
		}
		sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness(script))

	default:
		return nil, nil, errors.New("unsupport address type")
	}

	return txInput, sigInst, nil
}

第二步,通过utxo构造交易输出TxOutput

该部分功能可以参考代码protocol/bc/types/txoutput.go#L20:

// NewTxOutput create a new output struct
func NewTxOutput(assetID bc.AssetID, amount uint64, controlProgram []byte) *TxOutput {
	return &TxOutput{
		AssetVersion: 1,
		OutputCommitment: OutputCommitment{
			AssetAmount: bc.AssetAmount{
				AssetId: &assetID,
				Amount:  amount,
			},
			VMVersion:      1,
			ControlProgram: controlProgram,
		},
	}
}

5.组合交易的input和output构成交易模板

通过上面已经生成的交易信息构造交易txbuilder.Template,该部分功能可以参考blockchain/txbuilder/builder.go#L92进行改造为:

type InputAndSigInst struct {
	input *types.TxInput
	sigInst *SigningInstruction
}

// Build build transactions with template
func BuildTx(inputs []InputAndSigInst, outputs []*types.TxOutput) (*Template, *types.TxData, error) {
	tpl := &Template{}
	tx := &types.TxData{}
	// Add all the built outputs.
	tx.Outputs = append(tx.Outputs, outputs...)

	// Add all the built inputs and their corresponding signing instructions.
	for _, in := range inputs {
		// Empty signature arrays should be serialized as empty arrays, not null.
		in.sigInst.Position = uint32(len(inputs))
		if in.sigInst.WitnessComponents == nil {
			in.sigInst.WitnessComponents = []witnessComponent{}
		}
		tpl.SigningInstructions = append(tpl.SigningInstructions, in.sigInst)
		tx.Inputs = append(tx.Inputs, in.input)
	}

	tpl.Transaction = types.NewTx(*tx)
	return tpl, tx, nil
}

6.对构造的交易进行签名

账户模型是根据密码找到对应的私钥对交易进行签名,这里用户可以直接使用私钥对交易进行签名,可以参考签名代码blockchain/txbuilder/txbuilder.go#L82进行改造为:(以下改造仅支持单签交易,多签交易用户可以参照该示例进行改造)

// Sign will try to sign all the witness
func Sign(tpl *Template, xprv chainkd.XPrv) error {
	for i, sigInst := range tpl.SigningInstructions {
		h := tpl.Hash(uint32(i)).Byte32()
		sig := xprv.Sign(h[:])
		rawTxSig := &RawTxSigWitness{
			Quorum: 1,
			Sigs:   []json.HexBytes{sig},
		}
		sigInst.WitnessComponents = append([]witnessComponent(rawTxSig), sigInst.WitnessComponents...)
	}
	return materializeWitnesses(tpl)
}

7.提交交易上链

该步骤无需更改任何内容,直接参照wiki中提交交易的APIsubmit-transaction的功能即可

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏SeanCheney的专栏

Hyperledger Fabric 架构设计整理

整个功能架构如下图所示。 ? 包括三大组件:区块链服务(Blockchain)、链码服务(Chaincode)、成员权限管理(Membership)。 概念术语...

39460
来自专栏极客编程

以太坊智能合约开发DApp应用示例

智能合约开发是以太坊的核心,学习过程主要是搭建以太坊开发环境和solidity语言的编程。本文不用任何以太坊Dapp框架,直接在ganache下开发智能合约。

15520
来自专栏区块链大本营

教程 | 只用5步,教你从零用Truffle打造第一个以太坊Dapp!

77660
来自专栏DannyHoo的专栏

iOS开发中解决Compiling IB documents for earlier than ios 7 is no longer supported的问题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010105969/article/details/...

9810
来自专栏丑胖侠

以太坊实战之《如何正确处理nonce》

问题概述 以太坊系列(ETH&ETC)在发送交易有三个对应的RPC接口,分别是eth_sendTransaction、eth_sendRawTransactio...

40960
来自专栏极客编程

如何用Python和Flask框架开发以太坊智能合约

将数据存储在数据库中是任何软件应用程序不可或缺的一部分。无论如何控制该数据库都有一个该数据的主控。区块链技术将数据存储到区块链网络内的区块中。因此,只要某个节点...

17330
来自专栏区块链

以太坊开发实战(第 2 章: Truffle, Ganache, Geth, 和 Mist)

见识了智能合约以及以太坊的工作方式,现在我们就尝试将它部署到两种测试网络里面。

1.9K150
来自专栏极客编程

比特币钱包隔离认证开发指南 原

本文件的大部分内容可以在与隔离认证相关的BIP中找到,包括BIP141,BIP143,BIP144和BIP145。请将此视为阅读其他相关文件的第一个参考点,并作...

11010
来自专栏极客编程

使用truffle部署以太坊智能合约到区块链

truffle是以太坊(ethereum)开发智能合约(smart contract)过程中最受欢迎的框架,本教程来安装构建一个基本的Truffle项目并部署一...

14330
来自专栏汇智网教程

以太坊开发教程

45060

扫码关注云+社区

领取腾讯云代金券