OSDN Git Service

create-asset add user defined contract for issue-program (#1245)
authoroysheng <33340252+oysheng@users.noreply.github.com>
Tue, 14 Aug 2018 11:19:10 +0000 (19:19 +0800)
committerPaladz <yzhu101@uottawa.ca>
Tue, 14 Aug 2018 11:19:10 +0000 (19:19 +0800)
* create-asset add user defined contract for issue-program

* fix signer is nil

* extracting common functions AddContractArgs

16 files changed:
account/builder.go
account/builder_test.go
api/assets.go
api/errors.go
asset/asset.go
asset/asset_test.go
asset/builder.go
blockchain/txbuilder/txbuilder.go
blockchain/txbuilder/txbuilder_test.go
blockchain/txbuilder/types.go
cmd/bytomcli/commands/asset.go
cmd/bytomcli/commands/util.go
test/tx_test_util.go
test/wallet_test_util.go
wallet/unconfirmed_test.go
wallet/wallet_test.go

index da4cb81..7679492 100644 (file)
@@ -2,7 +2,6 @@ package account
 
 import (
        "context"
-       "encoding/hex"
        "encoding/json"
 
        "github.com/bytom/blockchain/signers"
@@ -10,7 +9,6 @@ import (
        "github.com/bytom/common"
        "github.com/bytom/consensus"
        "github.com/bytom/crypto/ed25519/chainkd"
-       chainjson "github.com/bytom/encoding/json"
        "github.com/bytom/errors"
        "github.com/bytom/protocol/bc"
        "github.com/bytom/protocol/bc/types"
@@ -111,26 +109,9 @@ func (m *Manager) DecodeSpendUTXOAction(data []byte) (txbuilder.Action, error) {
 
 type spendUTXOAction struct {
        accounts       *Manager
-       OutputID       *bc.Hash           `json:"output_id"`
-       UseUnconfirmed bool               `json:"use_unconfirmed"`
-       Arguments      []contractArgument `json:"arguments"`
-}
-
-// contractArgument for smart contract
-type contractArgument struct {
-       Type    string          `json:"type"`
-       RawData json.RawMessage `json:"raw_data"`
-}
-
-// rawTxSigArgument is signature-related argument for run contract
-type rawTxSigArgument struct {
-       RootXPub chainkd.XPub         `json:"xpub"`
-       Path     []chainjson.HexBytes `json:"derivation_path"`
-}
-
-// dataArgument is the other argument for run contract
-type dataArgument struct {
-       Value string `json:"value"`
+       OutputID       *bc.Hash                     `json:"output_id"`
+       UseUnconfirmed bool                         `json:"use_unconfirmed"`
+       Arguments      []txbuilder.ContractArgument `json:"arguments"`
 }
 
 func (a *spendUTXOAction) Build(ctx context.Context, b *txbuilder.TemplateBuilder) error {
@@ -164,37 +145,10 @@ func (a *spendUTXOAction) Build(ctx context.Context, b *txbuilder.TemplateBuilde
        }
 
        sigInst = &txbuilder.SigningInstruction{}
-       for _, arg := range a.Arguments {
-               switch arg.Type {
-               case "raw_tx_signature":
-                       rawTxSig := &rawTxSigArgument{}
-                       if err = json.Unmarshal(arg.RawData, rawTxSig); err != nil {
-                               return err
-                       }
-
-                       // convert path form chainjson.HexBytes to byte
-                       var path [][]byte
-                       for _, p := range rawTxSig.Path {
-                               path = append(path, []byte(p))
-                       }
-                       sigInst.AddRawWitnessKeys([]chainkd.XPub{rawTxSig.RootXPub}, path, 1)
-
-               case "data":
-                       data := &dataArgument{}
-                       if err = json.Unmarshal(arg.RawData, data); err != nil {
-                               return err
-                       }
-
-                       value, err := hex.DecodeString(data.Value)
-                       if err != nil {
-                               return err
-                       }
-                       sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness(value))
-
-               default:
-                       return errors.New("contract argument type is not exist")
-               }
+       if err := txbuilder.AddContractArgs(sigInst, a.Arguments); err != nil {
+               return err
        }
+
        return b.AddInput(txInput, sigInst)
 }
 
index 799c334..9ebe5ac 100644 (file)
@@ -1,15 +1,10 @@
 package account
 
 import (
-       "encoding/hex"
-       "encoding/json"
        "testing"
 
        "github.com/bytom/blockchain/txbuilder"
-       "github.com/bytom/crypto/ed25519/chainkd"
-       chainjson "github.com/bytom/encoding/json"
        "github.com/bytom/protocol/bc"
-       "github.com/bytom/testutil"
 )
 
 func TestMergeSpendAction(t *testing.T) {
@@ -355,129 +350,3 @@ func TestMergeSpendAction(t *testing.T) {
                }
        }
 }
-
-func TestSpendUTXOArguments(t *testing.T) {
-       hexXpub, err := hex.DecodeString("ba76bb52574b3f40315f2c01f1818a9072ced56e9d4b68acbef56a4d0077d08e5e34837963e4cdc54eb251aa34aad01e6ae48b140f6a2743fbb0a0abd9cf8aac")
-       if err != nil {
-               t.Fatal(err)
-       }
-
-       var xpub chainkd.XPub
-       copy(xpub[:], hexXpub)
-
-       rawTxSig := rawTxSigArgument{RootXPub: xpub, Path: []chainjson.HexBytes{{1, 1, 0, 0, 0, 0, 0, 0, 0}, {1, 0, 0, 0, 0, 0, 0, 0}}}
-       rawTxSigMsg, err := json.Marshal(rawTxSig)
-       if err != nil {
-               t.Fatal(err)
-       }
-
-       data := dataArgument{Value: "7468697320697320612074657374"}
-       dataMsg, err := json.Marshal(data)
-       if err != nil {
-               t.Fatal(err)
-       }
-
-       cases := []struct {
-               rawAction  string
-               wantResult *spendUTXOAction
-       }{
-               {
-                       rawAction: `{ "type": "spend_account_unspent_output", "output_id": "e304de887423e4e684e483f5ae65236d47018b56cac94ef3fb8b5dd40c897e11",
-                               "arguments": [{"type": "raw_tx_signature", "raw_data": {"derivation_path": ["010100000000000000", "0100000000000000"],
-                   "xpub": "ba76bb52574b3f40315f2c01f1818a9072ced56e9d4b68acbef56a4d0077d08e5e34837963e4cdc54eb251aa34aad01e6ae48b140f6a2743fbb0a0abd9cf8aac"}}]}`,
-                       wantResult: &spendUTXOAction{
-                               OutputID: &bc.Hash{16358444424161912038, 9575923798912607085, 5116523856555233011, 18125684290607480337},
-                               Arguments: []contractArgument{
-                                       {
-                                               Type:    "raw_tx_signature",
-                                               RawData: rawTxSigMsg,
-                                       },
-                               },
-                       },
-               },
-               {
-                       rawAction: `{ "type": "spend_account_unspent_output", "output_id": "8669b5c2e0701ec1ca45cd413e46c4f1d5f794f9d9144f904f3e7da8c68c6410",
-                               "arguments": [{"type": "data", "raw_data": {"value": "7468697320697320612074657374"}}]}`,
-                       wantResult: &spendUTXOAction{
-                               OutputID: &bc.Hash{9685472322230689473, 14575281449155871985, 15417955650135936912, 5710139541391434768},
-                               Arguments: []contractArgument{
-                                       {
-                                               Type:    "data",
-                                               RawData: dataMsg,
-                                       },
-                               },
-                       },
-               },
-               {
-                       rawAction: `{ "type": "spend_account_unspent_output", "output_id": "8669b5c2e0701ec1ca45cd413e46c4f1d5f794f9d9144f904f3e7da8c68c6410",
-                               "arguments": [{"type": "signature", "raw_data": {"value": "7468697320697320612074657374"}}]}`,
-                       wantResult: &spendUTXOAction{
-                               OutputID: &bc.Hash{9685472322230689473, 14575281449155871985, 15417955650135936912, 5710139541391434768},
-                       },
-               },
-               {
-                       rawAction: `{ "type": "spend_account_unspent_output", "output_id": "8669b5c2e0701ec1ca45cd413e46c4f1d5f794f9d9144f904f3e7da8c68c6410"}`,
-                       wantResult: &spendUTXOAction{
-                               OutputID:  &bc.Hash{9685472322230689473, 14575281449155871985, 15417955650135936912, 5710139541391434768},
-                               Arguments: nil,
-                       },
-               },
-       }
-
-       for _, c := range cases {
-               var spendUTXOReq *spendUTXOAction
-               if err := json.Unmarshal([]byte(c.rawAction), &spendUTXOReq); err != nil {
-                       t.Fatalf("unmarshal spendUTXOAction error:%v", err)
-               }
-
-               if !testutil.DeepEqual(spendUTXOReq.OutputID, c.wantResult.OutputID) {
-                       t.Fatalf("OutputID gotResult=%v, wantResult=%v", spendUTXOReq.OutputID, c.wantResult.OutputID)
-               }
-
-               if spendUTXOReq.Arguments == nil {
-                       if c.wantResult.Arguments != nil {
-                               t.Fatalf("Arguments gotResult is nil, wantResult[%v] is not nil", c.wantResult.Arguments)
-                       }
-                       continue
-               }
-
-               for _, arg := range spendUTXOReq.Arguments {
-                       switch arg.Type {
-                       case "raw_tx_signature":
-                               rawTxSig := &rawTxSigArgument{}
-                               if err := json.Unmarshal(arg.RawData, rawTxSig); err != nil {
-                                       t.Fatalf("unmarshal rawTxSigArgument error:%v", err)
-                               }
-
-                               wantRawTxSig := &rawTxSigArgument{}
-                               if err := json.Unmarshal(c.wantResult.Arguments[0].RawData, wantRawTxSig); err != nil {
-                                       t.Fatalf("unmarshal want rawTxSigArgument error:%v", err)
-                               }
-
-                               if !testutil.DeepEqual(rawTxSig, wantRawTxSig) {
-                                       t.Fatalf("rawTxSigArgument gotResult=%v, wantResult=%v", rawTxSig, wantRawTxSig)
-                               }
-
-                       case "data":
-                               data := &dataArgument{}
-                               if err := json.Unmarshal(arg.RawData, data); err != nil {
-                                       t.Fatalf("unmarshal dataArgument error:%v", err)
-                               }
-
-                               wantData := &dataArgument{}
-                               if err := json.Unmarshal(c.wantResult.Arguments[0].RawData, wantData); err != nil {
-                                       t.Fatalf("unmarshal want dataArgument error:%v", err)
-                               }
-
-                               if !testutil.DeepEqual(data, wantData) {
-                                       t.Fatalf("dataArgument gotResult=%v, wantResult=%v", data, wantData)
-                               }
-
-                       default:
-                               if arg.Type == "raw_tx_signature" || arg.Type == "data" {
-                                       t.Fatalf("argument type [%v] is not exist", arg.Type)
-                               }
-                       }
-               }
-       }
-}
index b2ac847..5cacde5 100644 (file)
@@ -6,22 +6,25 @@ import (
 
        "github.com/bytom/asset"
        "github.com/bytom/crypto/ed25519/chainkd"
+       chainjson "github.com/bytom/encoding/json"
 
        log "github.com/sirupsen/logrus"
 )
 
 // POST /create-asset
 func (a *API) createAsset(ctx context.Context, ins struct {
-       Alias      string                 `json:"alias"`
-       RootXPubs  []chainkd.XPub         `json:"root_xpubs"`
-       Quorum     int                    `json:"quorum"`
-       Definition map[string]interface{} `json:"definition"`
+       Alias           string                 `json:"alias"`
+       RootXPubs       []chainkd.XPub         `json:"root_xpubs"`
+       Quorum          int                    `json:"quorum"`
+       Definition      map[string]interface{} `json:"definition"`
+       IssuanceProgram chainjson.HexBytes     `json:"issuance_program"`
 }) Response {
        ass, err := a.wallet.AssetReg.Define(
                ins.RootXPubs,
                ins.Quorum,
                ins.Definition,
                strings.ToUpper(strings.TrimSpace(ins.Alias)),
+               ins.IssuanceProgram,
        )
        if err != nil {
                return NewErrorResponse(err)
index 92b9d0b..e7dd7b9 100644 (file)
@@ -54,17 +54,18 @@ var respErrFormatter = map[error]httperror.Info{
 
        // Transaction error namespace (7xx)
        // Build transaction error namespace (70x ~ 72x)
-       account.ErrInsufficient:    {400, "BTM700", "Funds of account are insufficient"},
-       account.ErrImmature:        {400, "BTM701", "Available funds of account are immature"},
-       account.ErrReserved:        {400, "BTM702", "Available UTXOs of account have been reserved"},
-       account.ErrMatchUTXO:       {400, "BTM703", "Not found UTXO with given hash"},
-       ErrBadActionType:           {400, "BTM704", "Invalid action type"},
-       ErrBadAction:               {400, "BTM705", "Invalid action object"},
-       ErrBadActionConstruction:   {400, "BTM706", "Invalid action construction"},
-       txbuilder.ErrMissingFields: {400, "BTM707", "One or more fields are missing"},
-       txbuilder.ErrBadAmount:     {400, "BTM708", "Invalid asset amount"},
-       account.ErrFindAccount:     {400, "BTM709", "Not found account"},
-       asset.ErrFindAsset:         {400, "BTM710", "Not found asset"},
+       account.ErrInsufficient:         {400, "BTM700", "Funds of account are insufficient"},
+       account.ErrImmature:             {400, "BTM701", "Available funds of account are immature"},
+       account.ErrReserved:             {400, "BTM702", "Available UTXOs of account have been reserved"},
+       account.ErrMatchUTXO:            {400, "BTM703", "Not found UTXO with given hash"},
+       ErrBadActionType:                {400, "BTM704", "Invalid action type"},
+       ErrBadAction:                    {400, "BTM705", "Invalid action object"},
+       ErrBadActionConstruction:        {400, "BTM706", "Invalid action construction"},
+       txbuilder.ErrMissingFields:      {400, "BTM707", "One or more fields are missing"},
+       txbuilder.ErrBadAmount:          {400, "BTM708", "Invalid asset amount"},
+       account.ErrFindAccount:          {400, "BTM709", "Not found account"},
+       asset.ErrFindAsset:              {400, "BTM710", "Not found asset"},
+       txbuilder.ErrBadContractArgType: {400, "BTM711", "Invalid contract argument type"},
 
        // Submit transaction error namespace (73x ~ 79x)
        // Validation error (73x ~ 75x)
index 491d924..8cf3f2a 100644 (file)
@@ -126,10 +126,9 @@ func (reg *Registry) getNextAssetIndex() uint64 {
 }
 
 // Define defines a new Asset.
-func (reg *Registry) Define(xpubs []chainkd.XPub, quorum int, definition map[string]interface{}, alias string) (*Asset, error) {
-       if len(xpubs) == 0 {
-               return nil, errors.Wrap(signers.ErrNoXPubs)
-       }
+func (reg *Registry) Define(xpubs []chainkd.XPub, quorum int, definition map[string]interface{}, alias string, issuanceProgram chainjson.HexBytes) (*Asset, error) {
+       var err error
+       var assetSigner *signers.Signer
 
        alias = strings.ToUpper(strings.TrimSpace(alias))
        if alias == "" {
@@ -140,23 +139,30 @@ func (reg *Registry) Define(xpubs []chainkd.XPub, quorum int, definition map[str
                return nil, ErrInternalAsset
        }
 
-       nextAssetIndex := reg.getNextAssetIndex()
-       assetSigner, err := signers.Create("asset", xpubs, quorum, nextAssetIndex)
-       if err != nil {
-               return nil, err
-       }
-
        rawDefinition, err := serializeAssetDef(definition)
        if err != nil {
                return nil, ErrSerializing
        }
 
-       path := signers.Path(assetSigner, signers.AssetKeySpace)
-       derivedXPubs := chainkd.DeriveXPubs(assetSigner.XPubs, path)
-       derivedPKs := chainkd.XPubKeys(derivedXPubs)
-       issuanceProgram, vmver, err := multisigIssuanceProgram(derivedPKs, assetSigner.Quorum)
-       if err != nil {
-               return nil, err
+       vmver := uint64(1)
+       if len(issuanceProgram) == 0 {
+               if len(xpubs) == 0 {
+                       return nil, errors.Wrap(signers.ErrNoXPubs)
+               }
+
+               nextAssetIndex := reg.getNextAssetIndex()
+               assetSigner, err = signers.Create("asset", xpubs, quorum, nextAssetIndex)
+               if err != nil {
+                       return nil, err
+               }
+
+               path := signers.Path(assetSigner, signers.AssetKeySpace)
+               derivedXPubs := chainkd.DeriveXPubs(assetSigner.XPubs, path)
+               derivedPKs := chainkd.XPubKeys(derivedXPubs)
+               issuanceProgram, vmver, err = multisigIssuanceProgram(derivedPKs, assetSigner.Quorum)
+               if err != nil {
+                       return nil, err
+               }
        }
 
        defHash := bc.NewHash(sha3.Sum256(rawDefinition))
index 7e6d81b..261a9e7 100644 (file)
@@ -20,7 +20,7 @@ import (
 func TestDefineAssetWithLowercase(t *testing.T) {
        reg := mockNewRegistry(t)
        alias := "lower"
-       asset, err := reg.Define([]chainkd.XPub{testutil.TestXPub}, 1, nil, alias)
+       asset, err := reg.Define([]chainkd.XPub{testutil.TestXPub}, 1, nil, alias, nil)
        if err != nil {
                t.Fatal(err)
        }
@@ -32,7 +32,7 @@ func TestDefineAssetWithLowercase(t *testing.T) {
 func TestDefineAssetWithSpaceTrimed(t *testing.T) {
        reg := mockNewRegistry(t)
        alias := " WITH SPACE "
-       asset, err := reg.Define([]chainkd.XPub{testutil.TestXPub}, 1, nil, alias)
+       asset, err := reg.Define([]chainkd.XPub{testutil.TestXPub}, 1, nil, alias, nil)
        if err != nil {
                t.Fatal(err)
        }
@@ -44,7 +44,7 @@ func TestDefineAssetWithSpaceTrimed(t *testing.T) {
 func TestDefineAsset(t *testing.T) {
        ctx := context.Background()
        reg := mockNewRegistry(t)
-       asset, err := reg.Define([]chainkd.XPub{testutil.TestXPub}, 1, nil, "asset-alias")
+       asset, err := reg.Define([]chainkd.XPub{testutil.TestXPub}, 1, nil, "asset-alias", nil)
        if err != nil {
                testutil.FatalErr(t, err)
        }
@@ -61,7 +61,7 @@ func TestDefineAsset(t *testing.T) {
 
 func TestDefineBtmAsset(t *testing.T) {
        reg := mockNewRegistry(t)
-       _, err := reg.Define([]chainkd.XPub{testutil.TestXPub}, 1, nil, consensus.BTMAlias)
+       _, err := reg.Define([]chainkd.XPub{testutil.TestXPub}, 1, nil, consensus.BTMAlias, nil)
        if err == nil {
                testutil.FatalErr(t, err)
        }
@@ -71,7 +71,7 @@ func TestFindAssetByID(t *testing.T) {
        ctx := context.Background()
        reg := mockNewRegistry(t)
        keys := []chainkd.XPub{testutil.TestXPub}
-       asset, err := reg.Define(keys, 1, nil, "TESTASSET")
+       asset, err := reg.Define(keys, 1, nil, "TESTASSET", nil)
        if err != nil {
                testutil.FatalErr(t, err)
 
@@ -92,7 +92,7 @@ func TestUpdateAssetAlias(t *testing.T) {
        oldAlias := "OLD_ALIAS"
        newAlias := "NEW_ALIAS"
 
-       asset, err := reg.Define([]chainkd.XPub{testutil.TestXPub}, 1, nil, oldAlias)
+       asset, err := reg.Define([]chainkd.XPub{testutil.TestXPub}, 1, nil, oldAlias, nil)
        if err != nil {
                testutil.FatalErr(t, err)
        }
@@ -118,12 +118,12 @@ func TestListAssets(t *testing.T) {
        firstAlias := "FIRST_ALIAS"
        secondAlias := "SECOND_ALIAS"
 
-       firstAsset, err := reg.Define([]chainkd.XPub{testutil.TestXPub}, 1, nil, firstAlias)
+       firstAsset, err := reg.Define([]chainkd.XPub{testutil.TestXPub}, 1, nil, firstAlias, nil)
        if err != nil {
                testutil.FatalErr(t, err)
        }
 
-       secondAsset, err := reg.Define([]chainkd.XPub{testutil.TestXPub}, 1, nil, secondAlias)
+       secondAsset, err := reg.Define([]chainkd.XPub{testutil.TestXPub}, 1, nil, secondAlias, nil)
        if err != nil {
                testutil.FatalErr(t, err)
        }
index 0afd03c..433e4c3 100644 (file)
@@ -32,6 +32,7 @@ func (reg *Registry) DecodeIssueAction(data []byte) (txbuilder.Action, error) {
 type issueAction struct {
        assets *Registry
        bc.AssetAmount
+       Arguments []txbuilder.ContractArgument `json:"arguments"`
 }
 
 func (a *issueAction) Build(ctx context.Context, builder *txbuilder.TemplateBuilder) error {
@@ -50,13 +51,16 @@ func (a *issueAction) Build(ctx context.Context, builder *txbuilder.TemplateBuil
                return err
        }
 
-       assetDef := asset.RawDefinitionByte
-
-       txin := types.NewIssuanceInput(nonce[:], a.Amount, asset.IssuanceProgram, nil, assetDef)
-
+       txin := types.NewIssuanceInput(nonce[:], a.Amount, asset.IssuanceProgram, nil, asset.RawDefinitionByte)
        tplIn := &txbuilder.SigningInstruction{}
-       path := signers.Path(asset.Signer, signers.AssetKeySpace)
-       tplIn.AddRawWitnessKeys(asset.Signer.XPubs, path, asset.Signer.Quorum)
+       if a.Arguments == nil {
+               path := signers.Path(asset.Signer, signers.AssetKeySpace)
+               tplIn.AddRawWitnessKeys(asset.Signer.XPubs, path, asset.Signer.Quorum)
+       } else {
+               if err := txbuilder.AddContractArgs(tplIn, a.Arguments); err != nil {
+                       return err
+               }
+       }
 
        log.Info("Issue action build")
        builder.RestrictMinTime(time.Now())
index 2ddbaa6..cab19ac 100644 (file)
@@ -4,10 +4,13 @@ package txbuilder
 
 import (
        "context"
+       "encoding/hex"
+       "encoding/json"
        "time"
 
        log "github.com/sirupsen/logrus"
 
+       "github.com/bytom/crypto/ed25519/chainkd"
        "github.com/bytom/errors"
        "github.com/bytom/math/checked"
        "github.com/bytom/protocol/bc"
@@ -30,6 +33,8 @@ var (
        ErrAction = errors.New("errors occurred in one or more actions")
        //ErrMissingFields means missing required fields
        ErrMissingFields = errors.New("required field is missing")
+       //ErrBadContractArgType means invalid contract argument type
+       ErrBadContractArgType = errors.New("invalid contract argument type")
 )
 
 // Build builds or adds on to a transaction.
@@ -150,3 +155,40 @@ func checkBlankCheck(tx *types.TxData) error {
 func MissingFieldsError(name ...string) error {
        return errors.WithData(ErrMissingFields, "missing_fields", name)
 }
+
+// AddContractArgs add contract arguments
+func AddContractArgs(sigInst *SigningInstruction, arguments []ContractArgument) error {
+       for _, arg := range arguments {
+               switch arg.Type {
+               case "raw_tx_signature":
+                       rawTxSig := &RawTxSigArgument{}
+                       if err := json.Unmarshal(arg.RawData, rawTxSig); err != nil {
+                               return err
+                       }
+
+                       // convert path form chainjson.HexBytes to byte
+                       var path [][]byte
+                       for _, p := range rawTxSig.Path {
+                               path = append(path, []byte(p))
+                       }
+                       sigInst.AddRawWitnessKeys([]chainkd.XPub{rawTxSig.RootXPub}, path, 1)
+
+               case "data":
+                       data := &DataArgument{}
+                       if err := json.Unmarshal(arg.RawData, data); err != nil {
+                               return err
+                       }
+
+                       value, err := hex.DecodeString(data.Value)
+                       if err != nil {
+                               return err
+                       }
+                       sigInst.WitnessComponents = append(sigInst.WitnessComponents, DataWitness(value))
+
+               default:
+                       return ErrBadContractArgType
+               }
+       }
+
+       return nil
+}
index 719d9c3..05e022b 100644 (file)
@@ -3,6 +3,7 @@ package txbuilder
 import (
        "context"
        "encoding/hex"
+       "encoding/json"
        "math"
        "testing"
        "time"
@@ -15,7 +16,7 @@ import (
        "github.com/bytom/crypto"
        "github.com/bytom/crypto/ed25519"
        "github.com/bytom/crypto/ed25519/chainkd"
-       "github.com/bytom/encoding/json"
+       chainjson "github.com/bytom/encoding/json"
        "github.com/bytom/errors"
        "github.com/bytom/protocol/bc"
        "github.com/bytom/protocol/bc/types"
@@ -138,19 +139,19 @@ func TestSignatureWitnessMaterialize(t *testing.T) {
                                Keys: []keyID{
                                        {
                                                XPub:           pubkey1,
-                                               DerivationPath: []json.HexBytes{{0, 0, 0, 0}},
+                                               DerivationPath: []chainjson.HexBytes{{0, 0, 0, 0}},
                                        },
                                        {
                                                XPub:           pubkey2,
-                                               DerivationPath: []json.HexBytes{{0, 0, 0, 0}},
+                                               DerivationPath: []chainjson.HexBytes{{0, 0, 0, 0}},
                                        },
                                        {
                                                XPub:           pubkey3,
-                                               DerivationPath: []json.HexBytes{{0, 0, 0, 0}},
+                                               DerivationPath: []chainjson.HexBytes{{0, 0, 0, 0}},
                                        },
                                },
                                Program: prog,
-                               Sigs:    []json.HexBytes{sig1, sig2, sig3},
+                               Sigs:    []chainjson.HexBytes{sig1, sig2, sig3},
                        },
                },
        }}
@@ -165,7 +166,7 @@ func TestSignatureWitnessMaterialize(t *testing.T) {
 
        // Test with exact amount of signatures required, in correct order
        component := tpl.SigningInstructions[0].WitnessComponents[0].(*SignatureWitness)
-       component.Sigs = []json.HexBytes{sig1, sig2}
+       component.Sigs = []chainjson.HexBytes{sig1, sig2}
        err = materializeWitnesses(tpl)
        if err != nil {
                testutil.FatalErr(t, err)
@@ -312,7 +313,7 @@ func TestCreateTxByUtxo(t *testing.T) {
                WitnessComponents: []witnessComponent{
                        &RawTxSigWitness{
                                Quorum: 1,
-                               Sigs:   []json.HexBytes{sig},
+                               Sigs:   []chainjson.HexBytes{sig},
                        },
                        DataWitness(data),
                },
@@ -326,3 +327,78 @@ func TestCreateTxByUtxo(t *testing.T) {
                t.Errorf("tx:%v result is equal to want:%v", tx, tpl.Transaction)
        }
 }
+
+func TestAddContractArgs(t *testing.T) {
+       hexXpub, err := hex.DecodeString("ba76bb52574b3f40315f2c01f1818a9072ced56e9d4b68acbef56a4d0077d08e5e34837963e4cdc54eb251aa34aad01e6ae48b140f6a2743fbb0a0abd9cf8aac")
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       var xpub chainkd.XPub
+       copy(xpub[:], hexXpub)
+
+       rawTxSig := RawTxSigArgument{RootXPub: xpub, Path: []chainjson.HexBytes{{1, 1, 0, 0, 0, 0, 0, 0, 0}, {1, 0, 0, 0, 0, 0, 0, 0}}}
+       rawTxSigMsg, err := json.Marshal(rawTxSig)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       data := DataArgument{Value: "7468697320697320612074657374"}
+       dataMsg, err := json.Marshal(data)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       cases := []struct {
+               arguments  []ContractArgument
+               wantResult error
+       }{
+               {
+                       arguments: []ContractArgument{
+                               {
+                                       Type:    "raw_tx_signature",
+                                       RawData: rawTxSigMsg,
+                               },
+                               {
+                                       Type:    "data",
+                                       RawData: dataMsg,
+                               },
+                       },
+                       wantResult: nil,
+               },
+               {
+                       arguments: []ContractArgument{
+                               {
+                                       Type:    "data",
+                                       RawData: dataMsg,
+                               },
+                               {
+                                       Type:    "raw_tx_signature",
+                                       RawData: rawTxSigMsg,
+                               },
+                       },
+                       wantResult: nil,
+               },
+               {
+                       arguments: []ContractArgument{
+                               {
+                                       Type:    "data",
+                                       RawData: dataMsg,
+                               },
+                               {
+                                       Type:    "err_data",
+                                       RawData: rawTxSigMsg,
+                               },
+                       },
+                       wantResult: ErrBadContractArgType,
+               },
+       }
+
+       sigInst := &SigningInstruction{}
+       for _, c := range cases {
+               err := AddContractArgs(sigInst, c.arguments)
+               if err != c.wantResult {
+                       t.Fatalf("got result=%v, want result=%v", err, c.wantResult)
+               }
+       }
+}
index 6c6f91e..d6c8742 100644 (file)
@@ -2,7 +2,9 @@ package txbuilder
 
 import (
        "context"
+       "encoding/json"
 
+       "github.com/bytom/crypto/ed25519/chainkd"
        chainjson "github.com/bytom/encoding/json"
        "github.com/bytom/protocol/bc"
        "github.com/bytom/protocol/bc/types"
@@ -36,3 +38,20 @@ type Receiver struct {
        ControlProgram chainjson.HexBytes `json:"control_program,omitempty"`
        Address        string             `json:"address,omitempty"`
 }
+
+// ContractArgument for smart contract
+type ContractArgument struct {
+       Type    string          `json:"type"`
+       RawData json.RawMessage `json:"raw_data"`
+}
+
+// RawTxSigArgument is signature-related argument for run contract
+type RawTxSigArgument struct {
+       RootXPub chainkd.XPub         `json:"xpub"`
+       Path     []chainjson.HexBytes `json:"derivation_path"`
+}
+
+// DataArgument is the other argument for run contract
+type DataArgument struct {
+       Value string `json:"value"`
+}
index d0fab10..6c2dfb7 100644 (file)
@@ -7,6 +7,7 @@ import (
        "github.com/spf13/cobra"
        jww "github.com/spf13/jwalterweatherman"
 
+       "encoding/hex"
        "github.com/bytom/crypto/ed25519/chainkd"
        "github.com/bytom/util"
 )
@@ -15,21 +16,23 @@ func init() {
        createAssetCmd.PersistentFlags().IntVarP(&assetQuorum, "quorom", "q", 1, "quorum must be greater than 0 and less than or equal to the number of signers")
        createAssetCmd.PersistentFlags().StringVarP(&assetToken, "access", "a", "", "access token")
        createAssetCmd.PersistentFlags().StringVarP(&assetDefiniton, "definition", "d", "", "definition for the asset")
+       createAssetCmd.PersistentFlags().StringVarP(&issuanceProgram, "issueprogram", "i", "", "issue program for the asset")
 
        listAssetsCmd.PersistentFlags().StringVar(&assetID, "id", "", "ID of asset")
 }
 
 var (
-       assetID        = ""
-       assetQuorum    = 1
-       assetToken     = ""
-       assetDefiniton = ""
+       assetID         = ""
+       assetQuorum     = 1
+       assetToken      = ""
+       assetDefiniton  = ""
+       issuanceProgram = ""
 )
 
 var createAssetCmd = &cobra.Command{
        Use:   "create-asset <alias> <xpub(s)>",
        Short: "Create an asset",
-       Args:  cobra.MinimumNArgs(2),
+       Args:  cobra.RangeArgs(1, 2),
        Run: func(cmd *cobra.Command, args []string) {
 
                var ins assetIns
@@ -56,6 +59,15 @@ var createAssetCmd = &cobra.Command{
                        ins.Definition = map[string]interface{}{definition[0]: definition[1]}
                }
 
+               if issuanceProgram != "" {
+                       issueProg, err := hex.DecodeString(issuanceProgram)
+                       if err != nil {
+                               jww.ERROR.Println(err)
+                               os.Exit(util.ErrLocalExe)
+                       }
+                       ins.IssuanceProgram = issueProg
+               }
+
                data, exitCode := util.ClientCall("/create-asset", &ins)
                if exitCode != util.Success {
                        os.Exit(exitCode)
index dfaf769..ea07375 100644 (file)
@@ -9,24 +9,26 @@ import (
 
        "github.com/bytom/crypto/ed25519/chainkd"
        "github.com/bytom/encoding/json"
+       chainjson "github.com/bytom/encoding/json"
        "github.com/bytom/util"
 )
 
 // accountIns is used for account related request.
 type accountIns struct {
-       RootXPubs   []chainkd.XPub         `json:"root_xpubs"`
-       Quorum      int                    `json:"quorum"`
-       Alias       string                 `json:"alias"`
-       AccessToken string                 `json:"access_token"`
+       RootXPubs   []chainkd.XPub `json:"root_xpubs"`
+       Quorum      int            `json:"quorum"`
+       Alias       string         `json:"alias"`
+       AccessToken string         `json:"access_token"`
 }
 
 // assetIns is used for asset related request.
 type assetIns struct {
-       RootXPubs   []chainkd.XPub         `json:"root_xpubs"`
-       Quorum      int                    `json:"quorum"`
-       Alias       string                 `json:"alias"`
-       Definition  map[string]interface{} `json:"definition"`
-       AccessToken string                 `json:"access_token"`
+       RootXPubs       []chainkd.XPub         `json:"root_xpubs"`
+       Quorum          int                    `json:"quorum"`
+       Alias           string                 `json:"alias"`
+       Definition      map[string]interface{} `json:"definition"`
+       IssuanceProgram chainjson.HexBytes     `json:"issuance_program"`
+       AccessToken     string                 `json:"access_token"`
 }
 
 type requestQuery struct {
index fab74e6..9202f9f 100644 (file)
@@ -80,7 +80,7 @@ func (g *TxGenerator) createAsset(accountAlias string, assetAlias string) (*asse
        if err != nil {
                return nil, err
        }
-       return g.Assets.Define(acc.XPubs, len(acc.XPubs), nil, assetAlias)
+       return g.Assets.Define(acc.XPubs, len(acc.XPubs), nil, assetAlias, nil)
 }
 
 func (g *TxGenerator) mockUtxo(accountAlias, assetAlias string, amount uint64) (*account.UTXO, error) {
index 759075b..d8bf730 100644 (file)
@@ -152,7 +152,7 @@ func (ctx *walletTestContext) createAsset(accountAlias string, assetAlias string
        if err != nil {
                return nil, err
        }
-       return ctx.Wallet.AssetReg.Define(acc.XPubs, len(acc.XPubs), nil, assetAlias)
+       return ctx.Wallet.AssetReg.Define(acc.XPubs, len(acc.XPubs), nil, assetAlias, nil)
 }
 
 func (ctx *walletTestContext) newBlock(txs []*types.Tx, coinbaseAccount string) (*types.Block, error) {
index d4a264c..0923c94 100644 (file)
@@ -51,7 +51,7 @@ func TestWalletUnconfirmedTxs(t *testing.T) {
        controlProg.KeyIndex = 1
 
        reg := asset.NewRegistry(testDB, nil)
-       asset, err := reg.Define([]chainkd.XPub{xpub1.XPub}, 1, nil, "TESTASSET")
+       asset, err := reg.Define([]chainkd.XPub{xpub1.XPub}, 1, nil, "TESTASSET", nil)
        if err != nil {
                t.Fatal(err)
        }
index fcc19f3..4115b4b 100644 (file)
@@ -62,7 +62,7 @@ func TestWalletUpdate(t *testing.T) {
        controlProg.KeyIndex = 1
 
        reg := asset.NewRegistry(testDB, chain)
-       asset, err := reg.Define([]chainkd.XPub{xpub1.XPub}, 1, nil, "TESTASSET")
+       asset, err := reg.Define([]chainkd.XPub{xpub1.XPub}, 1, nil, "TESTASSET", nil)
        if err != nil {
                t.Fatal(err)
        }