@@ -5,6 +5,7 @@ package runes
55
66import (
77 "bytes"
8+ "encoding/binary"
89 "errors"
910 "fmt"
1011 "math/big"
@@ -217,13 +218,39 @@ func (runestone *Runestone) IntoScript() ([]byte, error) {
217218 return nil , err
218219 }
219220
221+ // OP_RETURN + OP_13 + OP_PUSH_<num> + payload.
222+ return append ([]byte {txscript .OP_RETURN , txscript .OP_13 }, serializeDataPushes (payload )... ), nil
223+ }
224+
225+ // serializeDataPushes serialises runestone payload into data pushes.
226+ func serializeDataPushes (payload []byte ) (dataPushes []byte ) {
220227 payloadSize := len (payload )
221- if payloadSize < txscript . OP_DATA_1 || payloadSize > txscript .OP_DATA_75 {
222- return nil , errors . New ( " payload is out of PUSH_DATA bounds" )
228+ if payloadSize <= txscript .OP_DATA_75 {
229+ return append ([] byte { byte ( payloadSize )}, payload ... )
223230 }
224231
225- // OP_RETURN + OP_13 + OP_PUSH_<num> + payload.
226- return append ([]byte {txscript .OP_RETURN , txscript .OP_13 , byte (payloadSize )}, payload ... ), nil
232+ const defaultPushDataSize = 520
233+ var (
234+ idx = 0
235+ dataPushesNumber = payloadSize / defaultPushDataSize
236+ pushSize int
237+ )
238+ if payloadSize % defaultPushDataSize > 0 {
239+ dataPushesNumber ++
240+ }
241+
242+ dataPushes = make ([]byte , 0 , payloadSize + dataPushesNumber * 3 ) // INFO: Payload size + (OP_PUSHDATA2 + 2 bytes size) * data pushes number.
243+
244+ // INFO: Split into OP_PUSHDATA2 data pushes.
245+ for idx < payloadSize {
246+ dataPushes = append (dataPushes , txscript .OP_PUSHDATA2 )
247+ pushSize = min (defaultPushDataSize , payloadSize - idx )
248+ dataPushes = binary .LittleEndian .AppendUint16 (dataPushes , uint16 (pushSize ))
249+ dataPushes = append (dataPushes , payload [idx :idx + pushSize ]... )
250+ idx += pushSize
251+ }
252+
253+ return dataPushes
227254}
228255
229256// Serialize returns Runestone as bytes array.
@@ -453,30 +480,55 @@ func PreparePayload(rawPayload []byte) ([]byte, error) {
453480 payload := make ([]byte , 0 , len (rawPayload )- 3 )
454481 buffer := bytes .NewReader (rawPayload [2 :])
455482 for buffer .Len () > 0 {
456- op , err := buffer .ReadByte ()
483+ pushDataVal , err := buffer .ReadByte ()
457484 if err != nil {
458485 return nil , err
459486 }
460487
461- if op < txscript .OP_DATA_1 || op > txscript .OP_DATA_75 {
462- return nil , errors .New ("missing OP_DATA_<num>" )
488+ data , err := parseDataPush (buffer , int (pushDataVal ))
489+ if err != nil {
490+ return nil , err
463491 }
464492
465- data := make ([]byte , op )
466- _ , err = buffer .Read (data )
493+ payload = append (payload , data ... )
494+ }
495+
496+ return payload , nil
497+ }
498+
499+ // parseDataPush parses data payload from buffer depending on push data byte value.
500+ func parseDataPush (buffer * bytes.Reader , pushDataVal int ) (data []byte , _ error ) {
501+ var size int
502+ switch {
503+ case pushDataVal >= txscript .OP_DATA_1 && pushDataVal <= txscript .OP_DATA_75 :
504+ size = pushDataVal
505+ case pushDataVal >= txscript .OP_PUSHDATA1 && pushDataVal <= txscript .OP_PUSHDATA4 :
506+ bytesLen := 0x0001 << (pushDataVal - txscript .OP_PUSHDATA1 ) // INFO: 2 ^ {76 or 77 or 78} - 76 == 1 or 2 or 4.
507+ sizeBuf := make ([]byte , bytesLen )
508+ _ , err := buffer .Read (sizeBuf )
467509 if err != nil {
468510 return nil , err
469511 }
470512
471- payload = append (payload , data ... )
513+ // INFO: LittleEndian ordering.
514+ for ; bytesLen > 0 ; bytesLen -- {
515+ size <<= 8 // INFO: Prepare place for the next byte.
516+ size |= int (sizeBuf [bytesLen - 1 ]) // INFO: Push the next byte.
517+ }
518+ default :
519+ return nil , fmt .Errorf ("invalid OP_PUSHDATA (0x%x)" , pushDataVal )
472520 }
473521
474- // TODO: figure out where it must be.
475- // if len(payload) > 18 {
476- // return nil, ErrOverflow
477- // }.
522+ data = make ([]byte , size )
523+ read , err := buffer .Read (data )
524+ if err != nil {
525+ return nil , err
526+ }
527+ if read != size {
528+ return nil , fmt .Errorf ("read %d of %d" , read , size )
529+ }
478530
479- return payload , nil
531+ return data , nil
480532}
481533
482534// IsPossibleRunestone returns true if the script starts with rune protocol bytes sequence.
@@ -488,7 +540,7 @@ func IsPossibleRunestone(script []byte) bool {
488540 return false
489541 case script [1 ] != txscript .OP_13 :
490542 return false
491- case script [2 ] < txscript .OP_DATA_1 || script [2 ] > txscript .OP_DATA_75 :
543+ case script [2 ] < txscript .OP_DATA_1 || script [2 ] > txscript .OP_PUSHDATA4 :
492544 return false
493545 }
494546
0 commit comments