import (
"context"
stdjson "encoding/json"
+ "fmt"
"time"
log "github.com/sirupsen/logrus"
"github.com/vapor/blockchain/txbuilder"
+ "github.com/vapor/consensus/federation"
chainjson "github.com/vapor/encoding/json"
"github.com/vapor/errors"
"github.com/vapor/protocol/bc"
"github.com/vapor/protocol/bc/types"
- "github.com/vapor/testutil"
)
// DecodeCrossInAction convert input data to action struct
func (r *Registry) DecodeCrossInAction(data []byte) (txbuilder.Action, error) {
- a := &crossInAction{assets: r}
+ a := &crossInAction{reg: r}
err := stdjson.Unmarshal(data, a)
return a, err
}
type crossInAction struct {
- assets *Registry
+ reg *Registry
bc.AssetAmount
- SourceID string `json:"source_id"` // AnnotatedUTXO
+ SourceID string `json:"source_id"`
SourcePos uint64 `json:"source_pos"`
- Program chainjson.HexBytes `json:"control_program"`
AssetDefinition map[string]interface{} `json:"asset_definition"`
- UpdateAssetDef bool `json:"update_asset_definition"`
- Arguments []chainjson.HexBytes `json:"arguments"`
}
-// TODO: also need to hard-code mapTx
func (a *crossInAction) Build(ctx context.Context, builder *txbuilder.TemplateBuilder) error {
var missing []string
- if len(a.Program) == 0 {
- missing = append(missing, "control_program")
+ if a.SourceID == "" {
+ missing = append(missing, "source_id")
}
if a.AssetId.IsZero() {
missing = append(missing, "asset_id")
return txbuilder.MissingFieldsError(missing...)
}
- // Handle asset definition.
- // Asset issuance's legality is guaranteed by the federation.
- rawDefinition, err := serializeAssetDef(a.AssetDefinition)
- if err != nil {
- return ErrSerializing
+ sourceKey := []byte(fmt.Sprintf("SC:%v:%v", a.SourceID, a.SourcePos))
+ a.reg.assetMu.Lock()
+ defer a.reg.assetMu.Unlock()
+ if existed := a.reg.db.Get(sourceKey); existed != nil {
+ return errors.New("mainchain output double spent")
}
- if preAsset, _ := a.assets.GetAsset(a.AssetId.String()); preAsset != nil {
- // GetAsset() doesn't unmashall for RawDefinitionBytes, so we need to
- // serialize DefinitionMap on our own
- preRawDefinition, err := serializeAssetDef(preAsset.DefinitionMap)
+
+ var err error
+ asset := &Asset{}
+ if preAsset, _ := a.reg.GetAsset(a.AssetId.String()); preAsset != nil {
+ asset = preAsset
+ } else {
+ asset.RawDefinitionByte, err = serializeAssetDef(a.AssetDefinition)
if err != nil {
return ErrSerializing
}
- if !testutil.DeepEqual(preRawDefinition, rawDefinition) && !a.UpdateAssetDef {
- return errors.New("asset definition mismatch with previous definition")
- } else if !testutil.DeepEqual(preRawDefinition, rawDefinition) {
- // update asset def
- // TODO: maybe merge with save
- if !chainjson.IsValidJSON(rawDefinition) {
- return errors.New("asset definition is not in valid json format")
- }
+ if !chainjson.IsValidJSON(asset.RawDefinitionByte) {
+ return errors.New("asset definition is not in valid json format")
}
- }
- // TODO: save AssetDefinition
- if !chainjson.IsValidJSON(rawDefinition) {
- return errors.New("asset definition is not in valid json format")
+ asset.DefinitionMap = a.AssetDefinition
+ asset.VMVersion = 1
+ asset.AssetID = *a.AssetId
+ extAlias := a.AssetId.String()
+ asset.Alias = &(extAlias)
+ a.reg.SaveExtAsset(asset)
}
- arguments := [][]byte{}
- for _, argument := range a.Arguments {
- arguments = append(arguments, argument)
+ var sourceID bc.Hash
+ if err := sourceID.UnmarshalText([]byte(a.SourceID)); err != nil {
+ return errors.New("invalid sourceID format")
}
- sourceID := testutil.MustDecodeHash(a.SourceID)
- txin := types.NewCrossChainInput(arguments, sourceID, *a.AssetId, a.Amount, a.SourcePos, a.Program, rawDefinition)
- log.Info("cross-chain input action build")
+
+ fed := federation.GetFederation()
+ // arguments will be set when materializeWitnesses
+ txin := types.NewCrossChainInput(nil, sourceID, *a.AssetId, a.Amount, a.SourcePos, fed.ControlProgram, asset.RawDefinitionByte)
+ log.Info("cross-chain input action built")
builder.RestrictMinTime(time.Now())
- return builder.AddInput(txin, &txbuilder.SigningInstruction{})
+ tplIn := &txbuilder.SigningInstruction{}
+ tplIn.AddRawWitnessKeys(fed.XPubs, fed.Path, fed.Quorum)
+ a.reg.db.Set(sourceKey, []byte("true"))
+ return builder.AddInput(txin, tplIn)
}
func (a *crossInAction) ActionType() string {