Skip to content
This repository has been archived by the owner on Mar 21, 2024. It is now read-only.

Commit

Permalink
Merge pull request #1386 from Bytom/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
Paladz authored Oct 8, 2018
2 parents ad9586f + 64ada38 commit a737e83
Show file tree
Hide file tree
Showing 34 changed files with 762 additions and 136 deletions.
4 changes: 2 additions & 2 deletions account/accounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -377,11 +377,11 @@ func (m *Manager) ListControlProgram() ([]*CtrlProgram, error) {
return cps, nil
}

func (m *Manager) ListUnconfirmedUtxo(isSmartContract bool) []*UTXO {
func (m *Manager) ListUnconfirmedUtxo(accountID string, isSmartContract bool) []*UTXO {
utxos := m.utxoKeeper.ListUnconfirmed()
result := []*UTXO{}
for _, utxo := range utxos {
if segwit.IsP2WScript(utxo.ControlProgram) != isSmartContract {
if segwit.IsP2WScript(utxo.ControlProgram) != isSmartContract && (accountID == utxo.AccountID || accountID == "") {
result = append(result, utxo)
}
}
Expand Down
2 changes: 1 addition & 1 deletion account/accounts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ func mockAccountManager(t *testing.T) *Manager {
}
defer os.RemoveAll(dirPath)

testDB := dbm.NewDB("testdb", "leveldb", "temp")
testDB := dbm.NewDB("testdb", "memdb", "temp")
defer os.RemoveAll("temp")

store := leveldb.NewStore(testDB)
Expand Down
153 changes: 153 additions & 0 deletions account/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ import (
"github.com/bytom/protocol/vm/vmutil"
)

var (
//chainTxUtxoNum maximum utxo quantity in a tx
chainTxUtxoNum = 10
//chainTxMergeGas chain tx gas
chainTxMergeGas = uint64(10000000)
)

//DecodeSpendAction unmarshal JSON-encoded data of spend action
func (m *Manager) DecodeSpendAction(data []byte) (txbuilder.Action, error) {
a := &spendAction{accounts: m}
Expand All @@ -28,6 +35,10 @@ type spendAction struct {
UseUnconfirmed bool `json:"use_unconfirmed"`
}

func (a *spendAction) ActionType() string {
return "spend_account"
}

// MergeSpendAction merge common assetID and accountID spend action
func MergeSpendAction(actions []txbuilder.Action) []txbuilder.Action {
resultActions := []txbuilder.Action{}
Expand All @@ -51,6 +62,144 @@ func MergeSpendAction(actions []txbuilder.Action) []txbuilder.Action {
return resultActions
}

//calcMergeGas calculate the gas required that n utxos are merged into one
func calcMergeGas(num int) uint64 {
gas := uint64(0)
for num > 1 {
gas += chainTxMergeGas
num -= chainTxUtxoNum - 1
}
return gas
}

func (m *Manager) reserveBtmUtxoChain(builder *txbuilder.TemplateBuilder, accountID string, amount uint64, useUnconfirmed bool) ([]*UTXO, error) {
reservedAmount := uint64(0)
utxos := []*UTXO{}
for gasAmount := uint64(0); reservedAmount < gasAmount+amount; gasAmount = calcMergeGas(len(utxos)) {
reserveAmount := amount + gasAmount - reservedAmount
res, err := m.utxoKeeper.Reserve(accountID, consensus.BTMAssetID, reserveAmount, useUnconfirmed, builder.MaxTime())
if err != nil {
return nil, err
}

builder.OnRollback(func() { m.utxoKeeper.Cancel(res.id) })
reservedAmount += reserveAmount + res.change
utxos = append(utxos, res.utxos[:]...)
}
return utxos, nil
}

func (m *Manager) buildBtmTxChain(utxos []*UTXO, signer *signers.Signer) ([]*txbuilder.Template, *UTXO, error) {
if len(utxos) == 0 {
return nil, nil, errors.New("mergeSpendActionUTXO utxos num 0")
}

tpls := []*txbuilder.Template{}
if len(utxos) == 1 {
return tpls, utxos[len(utxos)-1], nil
}

acp, err := m.GetLocalCtrlProgramByAddress(utxos[0].Address)
if err != nil {
return nil, nil, err
}

buildAmount := uint64(0)
builder := &txbuilder.TemplateBuilder{}
for index := 0; index < len(utxos); index++ {
input, sigInst, err := UtxoToInputs(signer, utxos[index])
if err != nil {
return nil, nil, err
}

if err = builder.AddInput(input, sigInst); err != nil {
return nil, nil, err
}

buildAmount += input.Amount()
if builder.InputCount() != chainTxUtxoNum && index != len(utxos)-1 {
continue
}

outAmount := buildAmount - chainTxMergeGas
output := types.NewTxOutput(*consensus.BTMAssetID, outAmount, acp.ControlProgram)
if err := builder.AddOutput(output); err != nil {
return nil, nil, err
}

tpl, _, err := builder.Build()
if err != nil {
return nil, nil, err
}

bcOut, err := tpl.Transaction.Output(*tpl.Transaction.ResultIds[0])
if err != nil {
return nil, nil, err
}

utxos = append(utxos, &UTXO{
OutputID: *tpl.Transaction.ResultIds[0],
AssetID: *consensus.BTMAssetID,
Amount: outAmount,
ControlProgram: acp.ControlProgram,
SourceID: *bcOut.Source.Ref,
SourcePos: bcOut.Source.Position,
ControlProgramIndex: acp.KeyIndex,
Address: acp.Address,
})

tpls = append(tpls, tpl)
buildAmount = 0
builder = &txbuilder.TemplateBuilder{}
if index == len(utxos)-2 {
break
}
}
return tpls, utxos[len(utxos)-1], nil
}

// SpendAccountChain build the spend action with auto merge utxo function
func SpendAccountChain(ctx context.Context, builder *txbuilder.TemplateBuilder, action txbuilder.Action) ([]*txbuilder.Template, error) {
act, ok := action.(*spendAction)
if !ok {
return nil, errors.New("fail to convert the spend action")
}
if *act.AssetId != *consensus.BTMAssetID {
return nil, errors.New("spend chain action only support BTM")
}

utxos, err := act.accounts.reserveBtmUtxoChain(builder, act.AccountID, act.Amount, act.UseUnconfirmed)
if err != nil {
return nil, err
}

acct, err := act.accounts.FindByID(act.AccountID)
if err != nil {
return nil, err
}

tpls, utxo, err := act.accounts.buildBtmTxChain(utxos, acct.Signer)
if err != nil {
return nil, err
}

input, sigInst, err := UtxoToInputs(acct.Signer, utxo)
if err != nil {
return nil, err
}

if err := builder.AddInput(input, sigInst); err != nil {
return nil, err
}

if utxo.Amount > act.Amount {
if err = builder.AddOutput(types.NewTxOutput(*consensus.BTMAssetID, utxo.Amount-act.Amount, utxo.ControlProgram)); err != nil {
return nil, errors.Wrap(err, "adding change output")
}
}
return tpls, nil
}

func (a *spendAction) Build(ctx context.Context, b *txbuilder.TemplateBuilder) error {
var missing []string
if a.AccountID == "" {
Expand Down Expand Up @@ -114,6 +263,10 @@ type spendUTXOAction struct {
Arguments []txbuilder.ContractArgument `json:"arguments"`
}

func (a *spendUTXOAction) ActionType() string {
return "spend_account_unspent_output"
}

func (a *spendUTXOAction) Build(ctx context.Context, b *txbuilder.TemplateBuilder) error {
if a.OutputID == nil {
return txbuilder.MissingFieldsError("output_id")
Expand Down
Loading

0 comments on commit a737e83

Please sign in to comment.