// BuildRequest is main struct when building transactions
type BuildRequest struct {
- Tx *types.TxData `json:"base_transaction"`
- Actions []map[string]interface{} `json:"actions"`
- TTL json.Duration `json:"ttl"`
+ Tx *types.TxData `json:"base_transaction"`
+ Actions []map[string]interface{} `json:"actions"`
+ TTL json.Duration `json:"ttl"`
+ TimeRange uint64 `json:"time_range"`
}
func (bcr *BlockchainReactor) filterAliases(ctx context.Context, br *BuildRequest) error {
}
maxTime := time.Now().Add(ttl)
- tpl, err := txbuilder.Build(ctx, req.Tx, actions, maxTime)
+ tpl, err := txbuilder.Build(ctx, req.Tx, actions, maxTime, req.TimeRange)
if errors.Root(err) == txbuilder.ErrAction {
// append each of the inner errors contained in the data.
var Errs string
signingInstructions []*SigningInstruction
minTime time.Time
maxTime time.Time
+ timeRange uint64
referenceData []byte
rollbacks []func()
callbacks []func() error
tpl.Local = true
}
+ if b.timeRange != 0 {
+ tx.TimeRange = b.timeRange
+ }
+
// Add all the built outputs.
tx.Outputs = append(tx.Outputs, b.outputs...)
// Build partners then satisfy and consume inputs and destinations.
// The final party must ensure that the transaction is
// balanced before calling finalize.
-func Build(ctx context.Context, tx *types.TxData, actions []Action, maxTime time.Time) (*Template, error) {
+func Build(ctx context.Context, tx *types.TxData, actions []Action, maxTime time.Time, timeRange uint64) (*Template, error) {
builder := TemplateBuilder{
- base: tx,
- maxTime: maxTime,
+ base: tx,
+ maxTime: maxTime,
+ timeRange: timeRange,
}
// Build all of the actions, updating the builder.
testAction(bc.AssetAmount{AssetId: &assetID1, Amount: 5}),
}
expiryTime := time.Now().Add(time.Minute)
- got, err := Build(ctx, nil, actions, expiryTime)
+ got, err := Build(ctx, nil, actions, expiryTime, 0)
if err != nil {
testutil.FatalErr(t, err)
}
gasOnlyTx := false
gasStatus, err := validation.ValidateTx(newTx, block)
if err != nil {
- if !gasStatus.GasVaild {
+ if gasStatus == nil || !gasStatus.GasVaild {
c.txPool.AddErrCache(&newTx.ID, err)
return err
}
"github.com/bytom/protocol/vm"
)
-const muxGasCost = int64(10)
+const (
+ muxGasCost = int64(10)
+ // timeRangeGash is the block height we will reach after 100 years
+ timeRangeGash = uint64(21024000)
+)
// GasState record the gas usage status
type GasState struct {
coinbaseValue := consensus.BlockSubsidy(b.BlockHeader.Height)
gasUsed := uint64(0)
for i, tx := range b.Transactions {
- if b.Version == 1 && tx.Version != 1 {
- return errors.WithDetailf(errTxVersion, "block version %d, transaction version %d", b.Version, tx.Version)
- }
- if tx.TimeRange > b.Timestamp {
- return errors.New("invalid transaction time range")
- }
gasStatus, err := ValidateTx(tx, b)
gasOnlyTx := false
if err != nil {
// ValidateTx validates a transaction.
func ValidateTx(tx *bc.Tx, block *bc.Block) (*GasState, error) {
+ if block.Version == 1 && tx.Version != 1 {
+ return nil, errors.WithDetailf(errTxVersion, "block version %d, transaction version %d", block.Version, tx.Version)
+ }
+
+ if tx.TimeRange > timeRangeGash && tx.TimeRange < block.Timestamp {
+ return nil, errors.New("transaction max timestamp is lower than block's")
+ } else if tx.TimeRange != 0 && tx.TimeRange < block.Height {
+ return nil, errors.New("transaction max block height is lower than block's")
+ }
+
if tx.TxHeader.SerializedSize > consensus.MaxTxSize || tx.TxHeader.SerializedSize == 0 {
return nil, errWrongTransactionSize
}
+
if len(tx.ResultIds) == 0 {
return nil, errors.New("tx didn't have any output")
}
}
}
+func TestTimeRange(t *testing.T) {
+ cases := []struct {
+ timeRange uint64
+ err bool
+ }{
+ {
+ timeRange: 0,
+ err: false,
+ },
+ {
+ timeRange: 334,
+ err: false,
+ },
+ {
+ timeRange: 332,
+ err: true,
+ },
+ {
+ timeRange: 1521625824,
+ err: false,
+ },
+ {
+ timeRange: 1421625824,
+ err: true,
+ },
+ }
+
+ block := &bc.Block{
+ BlockHeader: &bc.BlockHeader{
+ Height: 333,
+ Timestamp: 1521625823,
+ },
+ }
+
+ tx := types.MapTx(&types.TxData{
+ SerializedSize: 1,
+ TimeRange: 0,
+ Inputs: []*types.TxInput{
+ mockGasTxInput(),
+ },
+ Outputs: []*types.TxOutput{
+ types.NewTxOutput(*consensus.BTMAssetID, 1, []byte{0x6a}),
+ },
+ })
+
+ for i, c := range cases {
+ tx.TimeRange = c.timeRange
+ if _, err := ValidateTx(tx, block); (err != nil) != c.err {
+ t.Errorf("#%d got error %s, want %s", i, !c.err, c.err)
+ }
+ }
+}
+
func TestBlockHeaderValid(t *testing.T) {
base := bc.NewBlockHeader(1, 1, &bc.Hash{}, 1, &bc.Hash{}, &bc.Hash{}, 0, 0)
baseBytes, _ := proto.Marshal(base)