1: <?php declare( strict_types = 1 );
2:
3: namespace Waves\Transactions;
4:
5: use Exception;
6: use Waves\Account\PrivateKey;
7: use Waves\Common\Base58String;
8: use Waves\Account\PublicKey;
9: use Waves\Common\ExceptionCode;
10: use Waves\Common\Json;
11: use Waves\Model\ChainId;
12: use Waves\Model\DataEntry;
13:
14: use Waves\Transactions\DataTransaction as CurrentTransaction;
15:
16: class DataTransaction extends Transaction
17: {
18: const TYPE = 12;
19: const LATEST_VERSION = 2;
20: const MIN_FEE = 100_000;
21:
22: /**
23: * @var array<int, DataEntry>
24: */
25: private array $data;
26:
27: /**
28: * @param PublicKey $sender
29: * @param array<int, DataEntry> $data
30: * @return CurrentTransaction
31: */
32: static function build( PublicKey $sender, array $data ): CurrentTransaction
33: {
34: $tx = new CurrentTransaction;
35: $tx->setBase( $sender, CurrentTransaction::TYPE, CurrentTransaction::LATEST_VERSION, CurrentTransaction::MIN_FEE );
36:
37: // DATA TRANSACTION
38: {
39: $tx->setData( $data );
40: }
41:
42: // ADDITIONAL FEE CALCULATION
43: $tx->setFee( Amount::of( CurrentTransaction::calculateFee( strlen( $tx->bodyBytes() ) ) ) );
44:
45: return $tx;
46: }
47:
48: static function calculateFee( int $bodyBytesLen ): int
49: {
50: return 100_000 * ( 1 + intdiv( $bodyBytesLen - 1, 1024 ) );
51: }
52:
53: function getUnsigned(): CurrentTransaction
54: {
55: // VERSION
56: if( $this->version() !== CurrentTransaction::LATEST_VERSION )
57: throw new Exception( __FUNCTION__ . ' unexpected version = ' . $this->version(), ExceptionCode::UNEXPECTED );
58:
59: // BASE
60: $pb_Transaction = $this->getProtobufTransactionBase();
61:
62: // DATA TRANSACTION
63: {
64: $pb_TransactionData = new \Waves\Protobuf\DataTransactionData;
65: // DATA
66: {
67: $pb_Data = [];
68: foreach( $this->data() as $dataEntry )
69: $pb_Data[] = $dataEntry->toProtobuf();
70: $pb_TransactionData->setData( $pb_Data );
71: }
72: }
73:
74: // DATA TRANSACTION
75: $this->setBodyBytes( $pb_Transaction->setDataTransaction( $pb_TransactionData )->serializeToString() );
76: return $this;
77: }
78:
79: /**
80: * @return array<int, DataEntry>
81: */
82: function data(): array
83: {
84: if( !isset( $this->data ) )
85: $this->data = $this->json->get( 'data' )->asJson()->asArrayDataEntry();
86: return $this->data;
87: }
88:
89: /**
90: * @param array<int, DataEntry> $data
91: * @return CurrentTransaction
92: */
93: function setData( array $data ): CurrentTransaction
94: {
95: $this->data = $data;
96:
97: $data = [];
98: foreach( $this->data as $dataEntry )
99: $data[] = $dataEntry->json()->data();
100: $this->json->put( 'data', $data );
101: return $this;
102: }
103:
104: // COMMON
105:
106: function __construct( Json $json = null )
107: {
108: parent::__construct( $json );
109: }
110:
111: function addProof( PrivateKey $privateKey, int $index = null ): CurrentTransaction
112: {
113: $proof = (new \deemru\WavesKit)->sign( $this->bodyBytes(), $privateKey->bytes() );
114: if( $proof === false )
115: throw new Exception( __FUNCTION__ . ' unexpected sign() error', ExceptionCode::UNEXPECTED );
116: $proof = Base58String::fromBytes( $proof )->encoded();
117:
118: $proofs = $this->proofs();
119: if( !isset( $index ) )
120: $proofs[] = $proof;
121: else
122: $proofs[$index] = $proof;
123: return $this->setProofs( $proofs );
124: }
125:
126: /**
127: * @return CurrentTransaction
128: */
129: function setType( int $type )
130: {
131: parent::setType( $type );
132: return $this;
133: }
134:
135: /**
136: * @return CurrentTransaction
137: */
138: function setSender( PublicKey $sender )
139: {
140: parent::setSender( $sender );
141: return $this;
142: }
143:
144: /**
145: * @return CurrentTransaction
146: */
147: function setVersion( int $version )
148: {
149: parent::setVersion( $version );
150: return $this;
151: }
152:
153: /**
154: * @return CurrentTransaction
155: */
156: function setFee( Amount $fee )
157: {
158: parent::setFee( $fee );
159: return $this;
160: }
161:
162: /**
163: * @return CurrentTransaction
164: */
165: function setChainId( ChainId $chainId = null )
166: {
167: parent::setChainId( $chainId );
168: return $this;
169: }
170:
171: /**
172: * @return CurrentTransaction
173: */
174: function setTimestamp( int $timestamp = null )
175: {
176: parent::setTimestamp( $timestamp );
177: return $this;
178: }
179:
180: /**
181: * @param array<int, string> $proofs
182: * @return CurrentTransaction
183: */
184: function setProofs( array $proofs = null )
185: {
186: parent::setProofs( $proofs );
187: return $this;
188: }
189:
190: function bodyBytes(): string
191: {
192: if( !isset( $this->bodyBytes ) )
193: $this->getUnsigned();
194: return parent::bodyBytes();
195: }
196: }
197: